Çırpınmaq: Bir Viktorina Oyunu necə qurulur

GÜNCELLEME (01.06.2019): Yenidənqurma paketi ilə alternativ bir versiya tapa bilərsiniz.

giriş

Bu yazıda Flutter və frideos paketini istifadə edərək bu viktorina oyununu necə hazırladığımı göstərmək istəyirəm (necə işlədiyini görmək üçün bu iki nümunəyə baxın, nümunə 1, nümunə 2). Bu olduqca sadə bir oyun, lakin bir neçə maraqlı arqumenti əhatə edir.

Tətbiqin dörd ekranı var:

  • İstifadəçinin bir kateqoriya seçib oyuna başladığı əsas səhifə.
  • İstifadəçinin sual sayını, verilənlər bazasının növünü (yerli və ya uzaq), hər sual üçün vaxt limitini və çətinlik səviyyəsini seçə biləcəyi bir ayar səhifəsi.
  • Sualları, hesabı, düzəlişlərin sayını, səhvləri və cavabsız sualları göstərən kiçik bir səhifə.
  • Bütün sualları düzgün / səhv cavablarla göstərən xülasə səhifəsi.

Bu alt xətt:

Burada daha yaxşı bir GIF görə bilərsiniz.
  • Hissə 1: layihə qurulması
  • Hissə 2: Tətbiq memarlığı
  • Hissə 3: API və JSON
  • Bölüm 4: Ana səhifə və digər ekranlar
  • Hissə 5: TriviaBloc
  • Hissə 6: Animasiyalar
  • Hissə 7: Xülasə səhifəsi
  • Nəticə
Hissə 1 - Layihə Quraşdırma

1 - Yeni bir çırpıntı layihəsi yaradın:

flutter_projectname yaratmaq

2 - pubspec.yaml faylını redaktə edin və http və frideos paketlərini əlavə edin:

Asılılıqlar: çırpınma: sdk: çırpınmaq http: ^ 0.12.0 video: ^ 0.6.0

3- main.dart faylının tərkibini silin

4- Layihə quruluşunu aşağıdakı kimi yaradın:

Struktur detallar

  • API: Burada "Açıq Trivia Verilənlər Bazası" nın API üçün dart sənədləri və yerli testlər üçün süni bir API tapa bilərsiniz: api_interface.dart, mock_api.dart, trivia_api.dart.
  • Bloklar: trivia_bloc.dart tətbiqetməsinin yeganə BLoC-nin yeri.
  • Modellər: appstate.dart, category.dart, models.dart, question.dart, theme.dart, trivia_stats.dart.
  • Ekranlar: main_page.dart, settings_page.dart, xülasə_sayfa.dart, trivia_page.dart.
Hissə 2 - Tətbiq memarlığı

Son məqaləmdə birdən çox widget və səhifəyə məlumat göndərmə və paylaşma üsullarını izah etdim. Bu vəziyyətdə bir az daha inkişaf etmiş bir yanaşma tətbiq edirik: tətbiq vəziyyətini, bəzi şirkət məntiqlərini və nümunəsini ehtiva edən InheritedWidget provayderi (AppStateProvider) istifadə edərək appState adlı bir singleton sinifinin bir nümunəsi widget ağacına təqdim olunur. tətbiqin "viktorina hissəsini" idarə edən yeganə BLOC. Beləliklə, sonunda Singleton və BLoC nümunələri arasında bir növ qarışıq olacaqdır.

Hər hansı bir widgetda AppState sinifinin nümunəsini aşağıdakı zənglərlə əldə edə bilərsiniz.

final appState = AppStateProvider.of (Kontekst);

1 - ana.dart

Bu tətbiqin giriş nöqtəsidir. App sinfi, AppState sinifinin bir nümunəsi elan edildiyi və AppStateProvider istifadə edərək widget ağacına təqdim edildiyi vətəndaşlığı olmayan bir widgetdır. AppState nümunəsi, AppStateProvider sinifinin atma metodunda atılır və bütün axınlar bağlanır.

MaterialApp widgetı bir ValueBuilder widgetına inteqrasiya olunur ki, hər dəfə yeni bir mövzu seçildikdə, bütün widgetlar ağacı yenidən yaradılsın və mövzu yenilənsin.

2 - dövlət idarəsi

Daha əvvəl də qeyd edildiyi kimi, AppState nümunəsi tətbiqin vəziyyətini ehtiva edir. Bu sinif aşağıdakılar üçün istifadə olunur:

  • Ayarlar: hazırda istifadə olunan tema, SharedPreferences ilə yükləyin / saxlayın. API tətbiqi, saxta və ya uzaqdan (opentdb.com-dan API istifadə edərək). Hər sual üçün müəyyən vaxt.
  • Mövcud nişanı göstərin: Əsas səhifə, Bilməli şeylər, Xülasə.
  • Sualları yükləyin.
  • (uzaq API-dədirsə) sualların kateqoriyası, sayı və çətinlik səviyyəsi üçün parametrləri qeyd edin.

Sinif konstruktorunda:

  • _createThemes tətbiqetmə üçün mövzular yaradır.
  • _loadCategories əsas səhifədəki açılır menyuda seçiləcək sual kateqoriyasını yükləyir.
  • geri sayma, növlər şəklində bir paketi yayımlayan bir Transformed mətn qutusundan geri sayma qurmaq üçün dəyər almaq üçün istifadə olunur.
  • questionsAmount, viktorina oyunu zamanı göstəriləcək sualların sayını ehtiva edir (standart olaraq 5).
  • ClassTriviaBloc nümunəsi başlatıldı. Axınlar geri sayımı, sualların siyahısını və baxılacaq səhifəni işləyir.
Hissə 3 - API və JSON

İstifadəçinin yerli və uzaq bir verilənlər bazası arasında seçim etməsinə icazə vermək üçün QuestionApi interfeysini iki metod və onu tətbiq edən iki siniflə yaratdım: MockApi və TriviaApi.

mücərrəd sinif sualları API {gələcək getCategories (StreamedList -Kateqoriyalar); Gələcək getQuestions ({StreamedList haqqında suallar , int sayı, kateqoriya kateqoriyası, çətinlik səviyyəsi, QuestionType növü}); }

MockApi tətbiqi varsayılan olaraq AppState-də qurulur (tətbiqin parametrləri səhifəsində dəyişdirilə bilər):

// API sualları API api = MockAPI (); final apiType = StreamedValue (initialData: ApiType.mock);

ApiTyp yalnız parametrlər səhifəsindəki verilənlər bazasını dəyişdirmək üçün bir siyahıdır:

enum ApiType {istehza, uzaq}

mock_api.dart:

trivia_api.dart:

1 - API seçimi

Ayarlar səhifəsində istifadəçi açılır menyudan hansı verilənlər bazasını istifadə edəcəyini seçə bilər:

ValueBuilder (yayımlandı: appState.apiType, qurucu: (kontekst, anlıq görüntü) {return DropdownButton (dəyər: snapshot.data, onChanged: appState.setApiType, məqalə: [const DropdownMenuItem (dəyər: ApiType.mock, Kind: Text ("Demo"),) const DropdownMenuItem (dəyər: ApiType.remote, Kind: Text ('opentdb.com'),),]); }),

Hər dəfə yeni bir verilənlər bazası seçildikdə, setApiType metodu API tətbiqini dəyişdirir və kateqoriyalar yenilənir.

boşluq setApiType (ApiType növü) {if (apiType.value! = type) {apiType.value = type; əgər (yazın == ApiType.mock) {api = MockAPI (); } başqa {api = TriviaAPI (); } _loadCategories (); }}

2 - kateqoriyalar

Kateqoriyalarların siyahısını almaq üçün bu url-ə keçək:

https://opentdb.com/api_category.php

Cavabın çıxarışı:

{"trivia_categories": [{"id": 9, "name": "General Knowledge"}, {"id": 10, "name": "Entertainment: Books"})

Dartın jsonDecode funksiyası ilə JSON kodunu açdıqdan sonra kitabxananı çevir:

final jsonResponse = convert.jsonDecode (response.body);

Bu quruluşa sahibik:

  • jsonResponse ['trivia_categories']: Kateqoriya siyahısı
  • jsonResponse ['trivia_categories'] [INDEX] ['id']: kateqoriyanın kimliyi
  • jsonResponse ['trivia_categories'] [INDEX] ['name']: kateqoriyanın adı

Beləliklə model olacaqdır:

Sinif kateqoriyası {kateqoriya ({this.id, this.name});
fabrika kateqoriyası.fromJson (xəritə json) {qayıtma kateqoriyası (id: json ['id'], ad: json ['ad']); }
int id; Simli ad; }

3 - suallar

Bu url-ə gedəndə:

https://opentdb.com/api.php?amount=2&dif çətinlik = orta&type=multiple

Cavab budur:

{"response_code": 0, "results": [{"kateqoriya": "Əyləncə: Musiqi", "növü": "çoxsaylı", "çətin": "orta", "sual": "Fransız sənətçisi nədir \ / qrup MIDI alətində "Launchpad"? "," correct_answer ":" Madeon "," wrong_answers ": [" Daft Punk "," Disclosure "," David Guetta "]}, {" kateqoriya ": "İdman", "Tip": "Birden çox", "Çətinlik": "Orta", "Sual": "2015 Milli Kollec Futbol Pley-off (CFP) çempionatını kim qazandı?", "Düzgün_ cavab": "Ohio State Buckeyes "," wrong_answers ": [" Alabama Crimson Tide "," Clemson Tigers "," Wisconsin Badgers "]}]}

Bu vəziyyətdə JSON kodunu açarkən aşağıdakı quruluşa sahibik:

  • jsonResponse ['nəticələr']: Sualların siyahısı.
  • jsonResponse ['nəticələr'] [INDEX] ['kateqoriya']: Sualın kateqoriyası.
  • jsonResponse ['nəticələr'] [INDEX] ['növ']: Sual növü, çoxsaylı və ya məntiqi.
  • jsonResponse ['nəticələr'] [INDEX] ['sual']: sual.
  • jsonResponse ['nəticələr'] [INDEX] ['correct_answer']: düzgün cavab.
  • jsonResponse ['nəticələr'] [INDEX] ['yanlış_ cavablar']: Yanlış cavabların siyahısı.

Model:

Sınıf QuestionModel {
QuestionModel ({this.question, this.correctAnswer, this.incorrectAnswers});
fabrik QuestionModel.fromJson (Xəritə json) {return QuestionModel (sual: json ['question'], correctAnswer: json ['correct_answer'], wrongAnswers: (json ['wrong_answers'] siyahısı kimi) .map ((answer) => answer.toString ()) .list ()); }
Simli sual; Simli doğruCavab; Hiyləgər yanlış cavablar; }

4 - TriviaApi sinfi

Sinif, QuestionsApi interfeysinin getCategories və getQuestions iki metodunu tətbiq edir:

  • Kateqoriyalar alın

Birinci hissədə JSON kodu model istifadə edilərək dekodlaşdırılır. Daha sonra "Kateqoriya" tipli bir siyahı təhlil edilir. Nəticə kateqoriyalara ötürülür (əsas səhifədəki kateqoriyalar siyahısını doldurmaq üçün istifadə olunan "Kateqoriya" tipli bir axın siyahısı).

final jsonResponse = convert.jsonDecode (response.body); Nəticə = (jsonResponse ['trivia_categories'] siyahı kimi) .map ((kateqoriya) => Category.fromJson (kateqoriya));
Categories.value = []; Kateqoriyalar ..addAll (Nəticə) ..addElement (Kateqoriya (ID: 0, Ad: "Hər hansı bir kateqoriya"));
  • Suallar alın

Suallar üçün oxşar bir şey olur, amma bu vəziyyətdə JSON-un orijinal quruluşunu (QuestionModel) tətbiqdə istifadə üçün daha rahat bir quruluşa çevirmək üçün bir model (sual) istifadə edirik.

final jsonResponse = convert.jsonDecode (response.body); Yekun nəticə = (jsonResponse ['nəticələr'] Siyahı kimi) .map ((sual) => QuestionModel.fromJson (sual));
questions.value = result .map ((question) => Question.fromQuestionModel (question)) .list ();

5 - sual sinfi

Əvvəlki bənddə qeyd edildiyi kimi, tətbiq suallar üçün fərqli bir quruluşdan istifadə edir. Bu sinifdə dörd xüsusiyyətimiz və iki üsulumuz var:

Sinif sualı {Sual ({this.question, this.answers, this.correctAnswerIndex});
fabrik Question.fromQuestionModel (QuestionModel modeli) {son siyahı Cavablar = [] ..add (model.correctAnswer) ..addAll (model.conlawAnswers) ..Qarışdırma ();
son indeks = Answer.indexOf (model.correctAnswer);
return Question (Sual: model.question, Cavablar: cavablar, correctAnswerIndex: indeks); }
Simli sual; Hiyləgər cavablar; int correctAnswerIndex; int selectedAnswerIndex;
bool isCorrect (string answer) {return Answer.indexOf (answer) == correctAnswerIndex; }
bool isChosen (string cavab) {return Answer.indexOf (answer) == selectedAnswerIndex; }
}

Zavodda cavabların siyahısı əvvəlcə bütün cavablarla doldurulur və sonra qarışdırılır ki, sifariş həmişə fərqli olsun. Burada sualın konstruktoru vasitəsi ilə düzgün cavab indeksinə təyin edə bilmək üçün hətta düzgün cavabın indeksini alırıq. İki metod, parametr olaraq verilən cavabın düzgün cavab və ya seçilmiş cavab olub olmadığını müəyyənləşdirmək üçün istifadə olunur (növbəti bənddə daha ətraflı izah ediləcək).

Bölüm 4 - Ana səhifə və digər ekranlar

1 - HomePage widgetı

AppState, AppTab axın dəyəri (saylama) olan və HomePage widgetında (vətəndaşlığı olmayan) göstəriləcək səhifəni yayımlamaq üçün istifadə olunan tabControll adlı bir xüsusiyyət göstərir. Bu belə işləyir: hər dəfə fərqli bir AppTabis dəsti yaradıldıqda, ValueBuilder widgetı ekranı yeni səhifə ilə yenidən qurur.

  • Əsas səhifə sinfi:
Widget yaradılması (BuildContext məzmunu) {final appState = AppStateProvider.of (Kontekst); ValueBuilder (axın: appState.tabController, builder: (kontekst, snapshot) => framework (appBar: snapshot.data! = AppTab.main? null: AppBar (), çekmece: DrawerWidget (), gövdə: _switchTab (snapshot.data)) , appState),),); }

NB Bu halda AppBar yalnız əsas səhifədə görünəcəkdir.

  • _switchTab metodu:
Widget _switchTab (AppTab nişanı, AppState appState) {Switch (Tab) {case AppTab.main: return MainPage (); fasilə; case AppTab.trivia: return TriviaPage (); fasilə; case AppTab.summary: return SummaryPage (stats: appState.triviaBloc.stats); fasilə; Standart: qayıt MainPage (); }}

2 - Ayarlar səhifəsi

Ayarlar səhifəsi göstəriləcək sualların sayını, çətinlik səviyyəsini, geri sayma müddətini və istifadə ediləcək verilənlər bazasının növünü seçməyə imkan verir. Əsas səhifədə daha sonra bir kateqoriya seçib oyuna başlaya bilərsiniz. Bu parametrlərin hər biri üçün bir StreamedValue istifadə edirəm ki, ValueBuilder widgetı hər dəfə yeni bir dəyər təyin olunduqda səhifəni yeniləsin.

Hissə 5 - TriviaBloc

Tətbiqin iş məntiqi TriviaBloc adlı yeganə BLoC-dır. Gəlin bu sinfi araşdıraq.

Konstruktorda:

TriviaBloc ({this.countdownStream, this.questions, this.tabController}) {
// API-dən suallar alın.onChange ((data) {if (data.isNotEmpty) {last questions = data..shuffle (); _startTrivia (questions);}});
countdownStream.outTransformed.listen ((data) {Countdown = int.parse (data) * 1000;}); }

Burada suallar xassəsi (StreamedList tipli Sual) sualların siyahısı axına göndərildikdə və oyuna başlamaq üçün _startTrivia metodu çağırıldıqda dəyişiklik gözləyir.

Bunun əvəzinə, Parametrlər səhifəsində, CountdownStream, TriviaBloc sinifində istifadə olunan geri sayma xüsusiyyətinin yenilənə bilməsi üçün yalnız geri sayma dəyərində dəyişiklikləri gözləyir.

  • _startTrivia (Siyahı məlumat)

Bu üsul oyuna başlayır. Əsasən, xüsusiyyətlərin vəziyyəti sıfırlanır, göstəriləcək ilk sual qoyulur və bir saniyədən sonra playTrivia metodu çağırılır.

void _startTrivia (Siyahı məlumat) {indeks = 0; triviaState.value.questionIndex = 1;
// Ana Səhifə və Xülasə düymələrini göstərmək üçün triviaState.value.isTriviaEnd = false;
// statistikanı sıfırlayın stats.reset ();
// İlk sualı vermək üçün (bu vəziyyətdə geri sayma // bar animasiyası başlamaz). currentQuestion.value = data.first;
Taymer (müddəti (milisaniyələr: 1000), () {// sualı dəyişdirsəniz bu bayrağı doğru olaraq təyin edin // geri sayma çubuğu animasiyası başlayır. TriviaState.value.isTriviaPlaying = true; // ilk sual yenidən Axın geri sayma çubuğu // animasiya. CurrentQuestion.value = data [index]; playTrivia ();}); }

triviaState, trivia statusunu idarə etmək üçün istifadə olunan bir TriviaState tipli bir StreamedValue dəyəridir.

sinif TriviaState {bool isTriviaPlaying = yalan; bool isTriviaEnd = yalan; bool isAnswerChosen = yalan; int questionIndex = 1; }
  • playTrivia ()

Bu metod çağırıldıqda, bir taymer vaxtaşırı vaxtın geri sayma parametrindən çox olub olmadığını görmək üçün taymeri yeniləyir. Bu halda taymeri ləğv edir, cari sualı cavabsız kimi qeyd edir və yeni bir sual göstərmək üçün _nextQuestion metodunu çağırır.

ləğv playTrivia () {
timer = Timer.periodic (müddət (milisaniyə: refreshTime), (timer t) {currentTime.value = refreshTime * t.tick;
əgər (currentTime.value> geri sayma) {currentTime.value = 0; timer.cancel (); cavab vermədi (currentQuestion.value); _başqa sual (); }
}); }
  • cavab verilmədi (sual sualı)

Bu metod, statistikanı yeniləmək üçün cavabsız hər hansı bir sual üçün TriviaStats sinifinin statistika instansiyasının addNoAnswer metodunu çağırır.

etibarsız cavab verildi (sual sualı) {stats.addNoAnswer (sual); }
  • _başqa sual ()

Bu metod sualların indeksini artırır. Siyahıda daha çox sual varsa, ValueBuilder-in səhifəni yeni sual ilə yeniləməsi üçün cariQuestion axınına yeni bir sual göndərilir. Əks təqdirdə _endTriva metodu çağırılır və oyun bitir.

etibarsız _nextQuestion () {
indeks ++;
əgər (indeks
  • endTrivia ()

Burada taymer ləğv olunur və isTriviaEnd bayrağı doğrudur. Baxış səhifəsi oyunun bitməsindən 1,5 saniyə sonra göstərilir.

boşluq _endTrivia () {
// RESET timer.cancel (); currentTime.value = 0; triviaState.value.isTriviaEnd = doğru; triviaState.refresh (); stopTimer ();
Taymer (müddəti (milisaniyə: 1500), () {// bu xülasə səhifəsini gözləyərkən // geri sayma animasiyasının başlanğıcını başlatmamaq üçün sıfırlanır. TriviaState.value.isAnswerChosen = false;
// 1.5s-dən sonra baxış səhifəsini göstər tabController.value = AppTab.summary;
// son sualı görünməməsi üçün silin // növbəti oyunda currentQuestion.value = null; }); }
  • checkAnswer (sual sualı, simli cavab)

İstifadəçi cavabı tıkladıqda, bu metod düzgün olduğunu yoxlayır və statistikaya müsbət və ya mənfi qiymət əlavə etmək üçün metodu çağırır. Sonra taymer sıfırlanır və yeni bir sual yüklənir.

void checkAnswer (sual sualı, simli cavab) {if (! triviaState.value.isTriviaEnd) {question.chosenAnswerIndex = question.answers.indexOf (cavab); əgər (question.isCorrect (cavab)) {stats.addCorrect (sual); } başqa {stats.addWrong (sual); } timer.cancel (); currentTime.value = 0; _başqa sual (); }}
  • stopTimer ()

Bu metod çağırıldıqda, vaxt ləğv edilir və isAnswerChosen bayrağı CountdownWidget-a animasiyanı dayandırmasını söyləmək üçün doğru olaraq ayarlanır.

void stopTimer () {// taymeri dayandırmaq timer.cancel ();
// Bu bayrağı doğru olaraq təyin etsəniz, geri sayma animasiyası dayandırılacaq triviaState.value.isAnswerChosen = true; triviaState.refresh (); }
  • onChosenAnswer (simli cavab)

Cavab seçildikdə, zamanlayıcı ləğv olunur və cavabın indeksi AnswerAnimation sinfinin AnswerAnimation instansiyasının selectedAnswerIndex xassəsində saxlanılır. Bu indeks bu cavabı bütün digər cavablarla əhatə olunmamaq üçün widget yığınına qoymaq üçün istifadə olunur.

void onChosenAnswer (String answer) {selectedAnswer = cavab; stopTimer (); // seçilmiş cavabı cavab vidceti son // yığınına yerləşdirə bilməsi üçün qoyun. AnswerAnimation.value.chosenAnswerIndex = currentQuestion.value.answers.indexOf (cavab);
AnswerAnimation.refresh (); }

Cavab Animasiya sinfi:

Class AnswerAnimation {AnswerAnimation ({this.chosenAnswerIndex, this.startPlaying}); int selectedAnswerIndex; bool startPlaying = yalan; }
  • onChosenAnswerAnimationEnd ()

Cavabların animasiyası bitdikdə, geri sayma animasiyasını yenidən başlatmaq üçün isAnswerChosen bayrağı yalan olaraq ayarlanır. Daha sonra cavabın doğru olduğunu yoxlamaq üçün checkAnswer metodu çağırılır.

void onChosenAnwserAnimationEnd () {// geri sayma animasiyasının triviaState.value.isAnswerChosen = false başlaya bilməsi üçün bayrağı sıfırlayın. triviaState.refresh (); checkAnswer (currentQuestion.value, selectedAnswer); }
  • TriviaStats sinfi

Qiyməti təyin etmək üçün bu sinif metodlarından istifadə olunur. İstifadəçi düzgün cavabı seçərsə, bal on bal artırılır və cari suallar xülasə səhifəsində göstərilə bilməsi üçün düzəliş siyahısına əlavə olunur. Cavab səhvdirsə, bal dördə endirilir, cavab olmadan hesab iki bala endirilir.

sinif TriviaStats {TriviaStats () {corrected = []; səhv = []; noAnswered = []; hesab = 0; }
siyahı düzəldilmişdir; Hiyləgər səhvlər; siyahı yoxCavab verildi; int hesabı;
void addCorrect (sual sualı) {corrects.add (question); Hesab + = 10; }
void addWrong (sual sualı) {Wrongs.add (sual); Qiymət - = 4; }
void addNoAnswer (sual sualı) {noAnswered.add (sual); Qiymət - = 2; }
void reset () {corrected = []; səhv = []; noAnswered = []; hesab = 0; }}
Hissə 6 - Animasiyalar

Bu tətbiqdə iki növ animasiya var: cavabların altındakı cizgi çubuğu cavab üçün qalan vaxtı və cavab seçildikdə oynayan animasiyanı göstərir.

1 - Geri sayma çubuğu animasiyası

Bu olduqca sadə bir animasiya. Widget çubuğun genişliyini, müddətini və oyunun vəziyyətini parametrlər kimi istifadə edir. Animasiya hər dəfə widget yenidən qurulduqda başlayır və cavab seçildikdə dayanır.

İlkin rəng yaşıldır və zamanın bitdiyini göstərmək üçün tədricən qırmızıya çevrilir.

2 - Cavab animasiya

Bu animasiya hər dəfə cavab seçiləndə başlayır. Cavabların mövqeyinin sadə bir hesablanması ilə hər biri tədricən seçilmiş cavab mövqeyinə köçürülür. Seçilmiş cavab yığının üstündə qalması üçün, widget siyahısının son elementi ilə dəyişdirilir.

// Son elementi seçilmiş cavab elementi ilə dəyişdirin ki, // yığında son görünsün. final last = widgets.last; final selected = widgets [widget.answerAnimation.chosenAnswerIndex]; final selectedIndex = widgets.indexOf (seçilmiş); widgets.last = seçilmiş; Widgets [selectedIndex] = son;
Qaytarma konteyner (uşaq: yığın (uşaqlar: widgets,),);

Cavab düzgün olduqda qutuların rəngi yaşıl, səhv olduqda qırmızı olur.

var newColor;
əgər (isCorrect) {newColor = Colors.green; } başqa {newColor = Colors.red; }
colorAnimation = ColorTween (başlayın: answerBoxColor, son: newColor,) .anim (nəzarətçi);
controller.forward () gözləyin;
Hissə 7 - Xülasə səhifəsi

1 - Xülasə Səhifəsi

Bu səhifə parametr, düzgün, səhv və seçilməmiş sualların siyahısını ehtiva edən TriviaStats sinifinin bir nümunəsini alır və hər bir sualı düzgün yerdə göstərən ListView yaradır. Mövcud sual daha sonra cavabların siyahısını yaradan SummaryAnswers widgetına ötürülür.

2 - Xülasə cavabları

Bu widget, sualın indeksini və sualın özünü parametr kimi götürür və cavabların siyahısını yaradır. Düzgün cavab yaşıl rənglə işarələnmişdir. İstifadəçi səhv bir cavab seçmişsə, qırmızı ilə vurğulanacaq və həm düzgün, həm də səhv cavabları göstərəcəkdir.

Nəticə

Bu nümunə mükəmməl və ya qəti deyil, amma işləmək üçün yaxşı bir başlanğıc nöqtəsi ola bilər. Məsələn, oynanan hər oyunun nəticələrini əks etdirən bir stats səhifəsi və ya istifadəçinin xüsusi suallar və kateqoriyalar yarada biləcəyi bir bölmə yaratmaqla yaxşılaşdırıla bilər (bu verilənlər bazaları ilə təcrübə etmək üçün əla bir məşq ola bilər). Bunun faydalı ola biləcəyini ümid edirəm, yaxşılaşdırma, təklif və ya digər təklif etməkdən çəkinməyin.

Mənbə kodunu bu GitHub deposunda tapa bilərsiniz.