Flutter: hogyan kell felépíteni egy kvíz játékot

UPDATE (2016.06.01.): Itt található az alternatív verzió az újjáépítő csomag használatával.

Bevezetés

Ebben a cikkben meg szeretném mutatni, hogyan készítettem ezt a trivia játék példáját a Flutter és a frideos csomag segítségével (nézd meg ezt a két példát, hogy megtudd, hogyan működik 1. példa, 2. példa). Ez egy nagyon egyszerű játék, de különféle érdekes érveket tartalmaz.

Az alkalmazásnak négy képernyője van:

  • Főoldal, ahol a felhasználó kiválaszt egy kategóriát, és elindítja a játékot.
  • Beállítási oldal, ahol a felhasználó kiválaszthatja a kérdések számát, az adatbázis típusát (helyi vagy távoli), az egyes kérdésekre vonatkozó határidőt és a nehézségeket.
  • Trivia oldal, ahol megjelennek a kérdések, a pontszám, a javítások száma, a tévedések és a nem válaszolt kérdések.
  • Összefoglaló oldal, amely bemutatja az összes kérdést a helyes / rossz válaszokkal.

Ez a végeredmény:

Itt láthat egy jobb gif-et.
  • 1. rész: A projekt beállítása
  • 2. rész: Az alkalmazás architektúrája
  • 3. rész: API és JSON
  • 4. rész: Honlap és egyéb képernyők
  • 5. rész: TriviaBloc
  • 6. rész: Animációk
  • 7. rész: Összefoglaló oldal
  • Következtetés
1. rész - A projekt beállítása

1 - Hozzon létre egy új csapkodási projektet:

flutter létrehozza a_projekt_nevét

2 - Szerkessze a „pubspec.yaml” fájlt, és adja hozzá a http és a frideos csomagokat:

függőségek:
  csapkod:
    sdk: csapkodás
  http: ^ 0.12.0
  frideos: ^ 0.6.0

3- Törölje a main.dart fájl tartalmát

4- Hozza létre a projekt szerkezetét a következő képként:

A szerkezet részletei

  • API: itt lesznek az „Open trivia adatbázis” API kezelésére szolgáló dart fájlok, valamint a helyi teszteléshez használt ál-API: api_interface.dart, mock_api.dart, trivia_api.dart.
  • Blokkok: a trivia_bloc.dart alkalmazás egyetlen BLoC helye.
  • Modellek: appstate.dart, category.dart, models.dart, question.dart, theme.dart, trivia_stats.dart.
  • Képernyők: main_page.dart, settings_page.dart, összegzés_page.dart, trivia_page.dart.
2. rész - App architektúra

Utolsó cikkemben az adatok küldésének és megosztásának különböző lehetőségeiről írtam több widgeten és oldalon. Ebben az esetben egy kicsit fejlettebb megközelítést fogunk alkalmazni: az AppState nevű szingulett osztály példányát az InheritedWidget szolgáltató (AppStateProvider) használatával juttatjuk el a kütyüfahoz, ez megtartja az alkalmazás állapotát, néhány üzleti logika és az egyetlen olyan BLoC példánya, amely kezeli az alkalmazás „kvíz részét”. Tehát végül ez egyfajta keverék lesz a szingulett és a BLoC mintázat között.

Az egyes modulokon belül beolvashatja az AppState osztály példányát:

végső appState = AppStateProvider.of  (kontextus);

1 - main.dart

Ez az alkalmazás belépési pontja. Az App osztály egy állapot nélküli widget, ahol azt az AppState osztály példányává nyilvánítják, és ahol ezt az AppStateProvider használatával továbbítják a kütyüfahoz. Az appState példányt az összes adatfolyam bezárásával az AppStateProvider osztály diszpozíciós módszerében elhelyezzük.

A MaterialApp widget be van csomagolva egy ValueBuilder widgetbe, így minden új téma kiválasztásakor a teljes widget fa újraépül, frissítve a témát.

2 - Állami vezetés

Mint korábban már említettük, az appState példány tartja az alkalmazás állapotát. Ez az osztály a következőkre lesz felhasználva:

  • Beállítások: az aktuális használt téma, töltse be / mentse el a SharedPreferences segítségével. API megvalósítás, ál vagy távoli (az opentdb.com API használatával). Az egyes kérdésekre beállított idő.
  • Az aktuális lap megjelenítése: főoldal, trivia, összefoglaló.
  • A kérdések betöltése
  • (ha a távoli API-n található) Tárolja a kérdések kategóriájának, számának és nehézségének beállításait.

Az osztály kivitelezője:

  • _createThemes épít az alkalmazás témáira.
  • _loadCategories betölti a választandó kérdések kategóriáit a főoldal legördülő menüjébe.
  • A visszaszámlálás a típusú frideos csomag StreamedTransformálva, amelyet arra használnak, hogy a szövegmezőből megkapják az értéket a visszaszámlálás beállításához.
  • questionsAmount tartalmazza a trivia játék során megjelenítendő kérdések számát (alapértelmezés szerint 5).
  • A classTriviaBloc példánya inicializálódik, és továbbítja az adatfolyamokat a visszaszámlálás kezdeti kezébe, a kérdések listáját és a megjelenítendő oldalt.
3. rész - API és JSON

Annak érdekében, hogy a felhasználó választhasson egy helyi és egy távoli adatbázis között, létrehoztam a QuestionApi felületet két módszerrel és két, az azt végrehajtó osztálytal: MockApi és TriviaApi.

absztrakt osztály KérdésekAPI {
  Jövő  getCategories (StreamedList  kategóriák);
  
  Jövő  getQuestions (
    {StreamedList  kérdések,
     int szám,
     Kategória kategória,
     Kérdés nehézségi nehézség,
     QuestionType type});
}

A MockApi implementáció alapértelmezés szerint van beállítva (megváltoztatható az alkalmazás beállításainak oldalán) az appState alkalmazásban:

// API
KérdésekAPI api = MockAPI ();
final apiType = StreamedValue  (InitData: ApiType.mock);

Míg az apiTypeis csak egy enum, hogy kezelje az adatbázis megváltoztatását a beállítások oldalon:

enum ApiType {ál, távoli}

mock_api.dart:

trivia_api.dart:

1 - API kiválasztása

A beállítások oldalon a felhasználó kiválaszthatja a használni kívánt adatbázist egy legördülő menüből:

ValueBuilder  (
  streaming: appState.apiType,
  építő: (kontextus, pillanatkép) {
    return DropdownButton  (
      érték: snapshot.data,
      onChanged: appState.setApiType,
      tételek: [
        const DropdownMenuItem  (
          érték: ApiType.mock,
          gyerek: Szöveg ('Demo'),
        ),
        const DropdownMenuItem  (
          érték: ApiType.remote,
          gyermek: Szöveg ('opentdb.com'),
       ),
    ]);
}),

Minden alkalommal, amikor új adatbázist választ ki, a setApiType módszer megváltoztatja az API megvalósítását, és a kategóriák frissülnek.

void setApiType (ApiType típus) {
  if (apiType.value! = type) {
    apiType.value = type;
    if (írja be == ApiType.mock) {
      api = MockAPI ();
    } más {
      api = TriviaAPI ();
    }
    _loadCategories ();
  }
}

2 - Kategóriák

A kategóriák listájának beszerzéséhez ezt az URL-t hívjuk:

https://opentdb.com/api_category.php

A válasz kivonata:

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

Tehát, a JSON dekódolása után a dart jsonDecode függvényével: konvertálja a könyvtárat:

végleges jsonResponse = convert.jsonDecode (response.body);

megvan ez a szerkezet:

  • jsonResponse ['trivia_categories']: kategóriák felsorolása
  • jsonResponse ['trivia_categories'] [INDEX] ['id']: a kategória azonosítója
  • jsonResponse ['trivia_categories'] [INDEX] ['name']: a kategória neve

Tehát a modell a következő lesz:

osztály kategória {
  Kategória ({this.id, this.name});
  gyár Kategória.fromJson (Térkép  json) {
    return kategória (azonosító: json ['id'], név: json ['név']);
  }
  int id;
  Karakterlánc neve;
}

3 - Kérdések

Ha ezt az URL-t hívjuk:

https://opentdb.com/api.php?amount=2&difficulty=medium&type=multiple

ez lesz a válasz:

{"response_code": 0, "eredmények": [{"kategória": "Szórakozás: Zene", "típus": "többes", "nehézség": "közepes", "kérdés": "Milyen francia előadó / együttes" ismert a midi eszközön való lejátszásról "Launchpad"? "," helyes_érzékelő ":" Madeon "," helytelen_utasok ": [" Daft Punk "," Nyilvántartás "," David Guetta "]}, {" kategória ":" Sport "," típus ":" többszörös "," nehézségek ":" közepes "," kérdés ":" Ki nyerte a 2015. évi Főiskolai Labdarúgó-rájátszás (CFP) nemzeti bajnokságot? "," Helyes_érzékelő ":" Ohio State Buckeyes "," wrong_answers ": [" Alabama Crimson Tide "," Clemson Tigers "," Wisconsin Badgers "]}]}

Ebben az esetben a JSON dekódolása a következő struktúrával rendelkezik:

  • jsonResponse ['eredmények']: kérdések listája.
  • jsonResponse ['eredmények'] [INDEX] ['kategória']: a kérdés kategóriája.
  • jsonResponse ['results'] [INDEX] ['type']: kérdés típusa, többszörös vagy logikai.
  • jsonResponse ['eredmények'] [INDEX] ['kérdés']: a kérdés.
  • jsonResponse ['eredmények'] [INDEX] ['helyes_megjegyzés']: a helyes válasz.
  • jsonResponse ['results'] [INDEX] ['wrong_answers']: a helytelen válaszok listája.

Modell:

osztály QuestionModel {
  QuestionModel ({ez.kérdés, this.correctAnswer, this.incorrectAnswers});
  gyári kérdésModel.fromJson (Térkép  json) {
    return QuestionModel (
      kérdés: json ['kérdés'],
      helyes válasz: json ['helyes_megjegyzés'],
      helytelen válaszok: (json ['helytelen_keresők'] mint lista)
        .map ((válasz) => answer.toString ())
        .toList ());
  }
  Húros kérdés;
  Helyes karakterláncVálasz;
   helytelen válaszok felsorolása;
}

4 - TriviaApi osztály

Az osztály a QuestionApi felület, a getCategories és a getQuestions két módszerét valósítja meg:

  • A kategóriák megszerzése

Az első részben a JSON dekódolódik, majd a modell felhasználásával elemzi, és megkapja a Kategória típus listáját, végül az eredmény kategóriáknak adódik (a StreamedList a Type típusból a kategória listájának feltöltésére szolgál a fő oldalon ).

végleges jsonResponse = convert.jsonDecode (response.body);
végeredmény = (jsonResponse ['trivia_categories'] Listaként)
.map ((kategória) => Category.fromJson (kategória));
kategorijas.érték = [];
kategóriák
..addAll (eredmény)
..addElement (Kategória (id: 0, név: 'Bármely kategória'));
  • A kérdések megszerzése

Valami hasonló történik a kérdéseknél, de ebben az esetben egy modellt (kérdést) használunk a JSON eredeti struktúrájának (QuestionModel) „konvertálására” az alkalmazásban használható kényelmesebb struktúrára.

végleges jsonResponse = convert.jsonDecode (response.body);
végeredmény = (jsonResponse ['results'] as List)
.map ((kérdés) => QuestionModel.fromJson (kérdés));
kérdések.érték = eredmény
.map ((kérdés) => Question.fromQuestionModel (kérdés))
.toList ();

5 - Kérdés osztály

Amint azt az előző bekezdésben említettük, az alkalmazás más kérdésfelépítést használ. Ebben az osztályban négy tulajdonságunk és két módszerünk van:

osztály kérdés {
  Kérdés ({ez.kérdés, ez.válaszok, ez.korrekciós válaszokIndex});
  gyári kérdés.fromQuestionModel (QuestionModel modell) {
    végleges lista  válaszok = []
      ..add (model.correctAnswer)
      ..addAll (model.incorrectAnswers)
      ..keverés();
    végleges index = ans.indexOf (model.correctAnswer);
    visszatérő kérdés (kérdés: modell.kérdés, válaszok: válaszok, helyesAnswerIndex: index);
  }
  Húros kérdés;
  Sorolja fel a  válaszokat;
  int pareizAnswerIndex;
  int selectedAnswerIndex;
  bool isCorrect (karakterlánc válasz) {
    visszatér válaszok.indexOf (válasz) == helyesAnswerIndex;
  }
  bool isChosen (karakterlánc válasz) {
    válaszok visszaadása.indexOf (answer) == selectedAnswerIndex;
  }
}

A gyárban a válaszok listáját először kitöltik az összes választ, majd összekeverik, hogy a sorrend mindig eltérő legyen. Itt még a helyes válasz mutatószámát is megkapjuk, így azt a Question konstruktoron keresztül hozzárendelhetjük a editAnswerIndexhez. A két módszert annak meghatározására használják, hogy a paraméterként adott válasz a helyes vagy a választott-e (ezeket a következő bekezdések egyikében részletesebben ismertetjük).

4. rész - Honlap és egyéb képernyők

1 - HomePage widget

AzAppState-ben egy olyan tabControllert nevű tulajdonságot láthat, amely egy StreamedValue, AppTab (enum) típusú, amelyet az oldal streamingjére használnak a HomePage widgetben (állapot nélküli). Ilyen módon működik: minden alkalommal, amikor egy másik AppTabis beállítódik, az ValueBuilder widget újraépíti az új oldalt megjelenítő képernyőt.

  • HomePage osztály:
Widget felépítése (BuildContext kontextus) {
  végső appState = AppStateProvider.of  (kontextus);
  
  visszatérő ValueBuilder (
    streaming: appState.tabController,
    építő: (kontextus, pillanatkép) => Állvány (
      appBar: snapshot.data! = AppTab.main? null: AppBar (),
      fiók: DrawerWidget (),
      test: _switchTab (snapshot.data, appState),
      ),
  );
}

Nota bene Ebben az esetben az appBar csak a főoldalon jelenik meg.

  • _switchTab módszer:
Widget _switchTab (AppTab fül, AppState appState) {
  kapcsoló (fül) {
    eset AppTab.main:
      vissza a MainPage ();
      szünet;
    AppTab.trivia eset:
      vissza a TriviaPage ();
      szünet;
    AppTab.summary:
      visszatérés az Összegzés oldal (statisztika: appState.triviaBloc.stats);
      szünet;
    alapértelmezett:
    vissza a MainPage ();
  }
}

2 - SettingsPage

A Beállítások oldalon kiválaszthatja a megjelenítendő kérdések számát, a nehézséget, a visszaszámlálás időtartamát és az használni kívánt adatbázistípust. A főoldalon kiválaszthatja a kategóriát, és végül elindíthatja a játékot. E beállítások mindegyikére StreamedValue-t használok, hogy az ValueBuilder widget minden egyes érték új beállításakor frissítse az oldalt.

5. rész - TriviaBloc

Az alkalmazás üzleti logikája az egyetlen TriviaBloc nevű BLoC-ben található. Vizsgáljuk meg ezt az osztályt.

A kivitelezőben:

TriviaBloc ({this.countdownStream, this.questions, this.tabController}) {
// A kérdések beolvasása az API-tól
  questions.onChange ((adatok) {
    if (data.isNotEmpty) {
      végső kérdések = adatok..keverés ();
     _startTrivia (kérdések);
    }
  });
  countdownStream.outTransformed.listen ((adatok) {
     visszaszámlálás = int.parse (adatok) * 1000;
  });
}

Itt a kérdés tulajdonság (a StreamedList típusú kérdés) megvizsgálja a változásokat, amikor a kérdések listáját elküldik a patakhoz, a _startTrivia metódust hívják, a játék elindításával.

Ehelyett a visszaszámlálás csak a Beállítások oldalon figyeli a visszaszámlálás értékének változásait, hogy frissítse a TriviaBloc osztályban használt visszaszámláló tulajdonságot.

  • _startTrivia ( adatok listája)

Ez a módszer elindítja a játékot. Alapvetően visszaállítja a tulajdonságok állapotát, beállítja az első megjelenítendő kérdést, és egy másodperc múlva meghívja a playTrivia módszert.

void _startTrivia ( adatok listája) {
  index = 0;
  triviaState.value.questionIndex = 1;
  // A főoldal és az összefoglaló gombok megjelenítése
  triviaState.value.isTriviaEnd = false;
  // Alaphelyzetbe állítja a statisztikákat
  stats.reset ();
  // A kezdeti kérdés (ebben az esetben a visszaszámlálás) beállításához
  // a bar animáció nem indul el).
  currentQuestion.value = data.first;
  Időzítő (Időtartam (milliszekundumok: 1000), () {
    // A zászló igazra állítása a kérdés megváltoztatásakor
    // elindul a visszaszámláló sáv animációja.
    triviaState.value.isTriviaPlaying = true;
  
    // Az első kérdést ismét közvetítse a visszaszámláló sáv segítségével
    // élénkség.
    currentQuestion.value = adatok [index];
  
    playTrivia ();
  });
}

A triviaState egy TriviaState típusú StreamedValue, egy osztály, amely a trivia állapotának kezelésére szolgál.

osztály TriviaState {
  bool isTriviaPlaying = false;
  bool isTriviaEnd = hamis;
  bool isAnswerChosen = hamis;
  int questionIndex = 1;
}
  • playTrivia ()

Amikor ezt a módszert hívják, egy időzítő időről időre frissíti az időzítőt, és ellenőrzi, hogy az eltelt idő meghaladja-e a visszaszámlálás beállítást, ebben az esetben törli az időzítőt, megjelöli az aktuális kérdést nem válaszoltként, és felhívja a _nextQuestion metódust egy új kérdés megmutatására. .

void playTrivia () {
  timer = Timer.periodic (Időtartam (ezredmásodperc: refreshTime), (Timer t) {
    currentTime.value = refreshTime * t.tick;
    if (currentTime.value> visszaszámlálás) {
      currentTime.value = 0;
      timer.cancel ();
      notAnswered (currentQuestion.value);
     _következő kérdés();
    }
  });
}
  • nem válaszolt (kérdés kérdés)

Ez a módszer minden olyan kérdésre meghívja a TriviaStats osztály statisztikai példányának addNoAnswer metódust, amelyre nincs válasz, a statisztikák frissítése érdekében.

érvénytelen nemVálaszolva (kérdés kérdés) {
  stats.addNoAnswer (kérdés);
}
  • _következő kérdés()

Ebben a módszerben a kérdések indexe növekszik, és ha vannak más kérdések a listában, akkor egy új kérdést küldünk a currentQuestion pataknak, hogy az ValueBuilder frissítse az oldalt az új kérdéssel. Ellenkező esetben a _endTriva metódust hívják, a játék befejezésével.

void _nextQuestion () {
  index ++;
   if (index 
  • endTrivia ()

Itt az időzítő törlődik, és az isTriviaEnd jelző igazra van állítva. 1,5 perc múlva a játék befejezése után az összefoglaló oldal jelenik meg.

void _endTrivia () {
  // VISSZAÁLLÍTÁS
  timer.cancel ();
  currentTime.value = 0;
  triviaState.value.isTriviaEnd = true;
  triviaState.refresh ();
  stopTimer ();
  Időzítő (Időtartam (milliszekundumok: 1500), () {
     // ezt itt visszaállítják, hogy ne indítsák el a
     // visszaszámláló animáció, amíg az összefoglaló oldalra nem vár.
     triviaState.value.isAnswerChosen = false;
     // Mutassa az összefoglaló oldalt 1,5 s után
     tabController.value = AppTab.summary;
     // Törölje az utolsó kérdést, hogy nem jelenjen meg
     // a következő játékban
     currentQuestion.value = null;
  });
}
  • checkAnswer (kérdés kérdés, karakterlánc-válasz)

Amikor a felhasználó rákattint egy válaszra, ez a módszer megvizsgálja, hogy helyes-e, és azt a módszert hívja fel, hogy pozitív vagy negatív pontszámot adjon a statisztikákhoz. Ezután az időzítő nullázódik, és új kérdést tölt be.

érvénytelen checkAnswer (kérdés kérdés, karakterlánc válasz) {
  if (! triviaState.value.isTriviaEnd) {
     question.chosenAnswerIndex = question.answers.indexOf (válasz);
     if (kérdés.is helyes (válasz)) {
       stats.addCorrect (kérdés);
     } más {
       stats.addWrong (kérdés);
     }
     timer.cancel ();
     currentTime.value = 0;
    _következő kérdés();
  }
}
  • stopTimer ()

Amikor ezt a módszert hívják, az idő törlődik, és az isAnswerChosen jelző igazra van állítva, hogy mondja a CountdownWidgetnek az animáció leállítását.

void stopTimer () {
  // Állítsa le az időzítőt
  timer.cancel ();
  // Ha ezt a zászlót igazra állítja, a visszaszámláló animáció leáll
  triviaState.value.isAnswerChosen = true;
  triviaState.refresh ();
}
  • onChosenAnswer (karakterlánc válasz)

Amikor egy választ választanak, az időzítőt törlik, és a válasz indexét a AnswerAnimation osztály AnswersAnimation példányának kiválasztottAnswerIndex tulajdonságába menti. Ezt az indexet arra használják, hogy ezt a választ utoljára a kütyü-halomra helyezzék el, hogy elkerüljék, hogy az összes többi válasz lefedi.

érvénytelen onChosenAnswer (karakterlánc válasz) {
  selectedAnswer = válasz;
  stopTimer ();
  // Állítsa be a selectedAnswer-t úgy, hogy a válasz widget utoljára tegye azt
  // Kazal.
  
  ansAnimation.value.chosenAnswerIndex =
  currentQuestion.value.answers.indexOf (válasz);
  answersAnimation.refresh ();
}

VálaszAnimációs osztály:

osztály AnswerAnimation {
  AnswerAnimation ({this.chosenAnswerIndex, this.startPlaying});
  int selectedAnswerIndex;
  bool startPlaying = false;
}
  • onChosenAnswerAnimationEnd ()

Amikor a válaszok animációja véget ér, az isAnswerChosen jelzőt hamisra állítja, hogy a visszaszámlálás animáció újra indulhasson, majd hívja a checkAnswer módszert, hogy ellenőrizze, hogy a válasz helyes-e.

érvénytelen onChosenAnwserAnimationEnd () {
  // Állítsa vissza a zászlót, hogy a visszaszámláló animáció elinduljon
  triviaState.value.isAnswerChosen = false;
  triviaState.refresh ();
  checkAnswer (currentQuestion.value, selectedAnswer);
}
  • TriviaStats osztály

Az osztály módszereit használják a pontszámok kiosztására. Ha a felhasználó választja a helyes választ, akkor a pontszám tíz ponttal növekszik, és az aktuális kérdéseket hozzáadja a javítások listájához úgy, hogy azok megjelenjenek az összefoglaló oldalon, ha a válasz nem helyes, akkor a pontszám négyre csökken, végül ha nincs válasz. A pontszám két ponttal csökken.

osztály TriviaStats {
  TriviaStats () {
    helyesbíti = [];
    tévedés = [];
    noAnswered = [];
    pontszám = 0;
  }
A  lista javítja;
  Sorold fel a  hibákat;
  Lista  noAveswered;
  int pontszám;
void addCorrect (kérdés kérdés) {
    corrects.add (kérdés);
    pontszám + = 10;
  }
void addWrong (kérdés kérdés) {
    wrongs.add (kérdés);
    pontszám - = 4;
  }
void addNoAnswer (kérdés kérdés) {
    noAnswered.add (kérdés);
    pontszám - = 2;
  }
érvénytelen visszaállítás () {
    helyesbíti = [];
    tévedés = [];
    noAnswered = [];
    pontszám = 0;
  }
}
6. rész - Animációk

Ebben az alkalmazásban kétféle animáció van: a válaszok alatt található animált sáv jelzi a válaszadásra hátralévő időt, és az animáció a válasz kiválasztásakor lejátszott.

1 - Visszaszámláló sáv animáció

Ez egy nagyon egyszerű animáció. A widget paraméterként veszi fel a sáv szélességét, az időtartamát és a játék állapotát. Az animáció minden alkalommal elindul, amikor a widget újraépül, és leáll, ha választ választanak.

A kezdeti szín zöld és fokozatosan vörösre vált, jelezve, hogy az idő véget ér.

2 - Válasz animációra

Ez az animáció minden egyes válasz választásakor elindul. A válaszok helyzetének egyszerű kiszámításával mindegyiket fokozatosan mozgatják a választott helyzetbe. Annak érdekében, hogy a választott válasz a verem tetején maradjon, ezt felváltják a kütyü lista utolsó elemére.

// Cserélje ki az utolsó cikket a kiválasztott anwser-re úgy, hogy képes legyen
// az utolsóként jelenik meg a veremben.
végső utolsó = widgets.last;
véglegesen kiválasztott = kütyü [widget.answerAnimation.chosenAnswerIndex]; final selectedIndex = widget.indexOf (választott);
widgets.last = választott;
kütyü [kiválasztottIndex] = utolsó;
visszatérő tartály (
   gyerek: verem (
      gyerekek: kütyü,
   ),
);

A dobozok színe zöldre vált, ha a válasz helyes, és pirosra, ha helytelen.

var newColor;
if (isCorrect) {
  newColor = Colors.green;
} más {
  newColor = Colors.red;
}
colorAnimation = ColorTween (
  kezdődik: answerBoxColor,
  vége: newColor,
) .Animate (vezérlő);
várja meg a vezérlőt.forward ();
7. rész - Összefoglaló oldal

1 - Összegzés

Ez az oldal paraméterként a TriviaStats osztály példányát veszi fel, amely tartalmazza a helyesbített kérdések, a helytelen kérdések és a válasz nélküli válaszok kérdéseinek listáját, és felépít egy listát, amely minden kérdést a megfelelő helyen mutat. Az aktuális kérdést ezután továbbítják a SummaryAnswers widgetnek, amely összeállítja a válaszok listáját.

2 - ÖsszegzésVálaszok

Ez a widget paraméterként veszi fel a kérdés indexét és magát a kérdést, és összeállítja a válaszok listáját. A helyes válasz zöld színű, míg ha a felhasználó helytelen választ választott, akkor ezt pirosra jelzi, és a helyes és a helytelen válaszokat is mutatja.

Következtetés

Ez a példa messze tökéletes vagy határozott, de jó kiindulási pont lehet a munkához. Javulhat például úgy, hogy létrehoz egy statisztikai oldalt az összes lejátszott játék pontszámával, vagy egy olyan szakaszt, ahol a felhasználó létrehozhat egyedi kérdéseket és kategóriákat (ezek nagyszerű gyakorlatok lehetnek az adatbázisokkal való gyakorláshoz). Remélem, hogy ez hasznos lehet, nyugodtan javasoljon fejlesztéseket, javaslatokat vagy más.

A forráskódot a GitHub lerakatban találja meg.