A függvények átadása paraméterben egy gyakori eset a programozásban. És nem is ördögtől való dolog, mivel a függvényeket változóba tudjuk tenni. Tehát ebben az esetben a változót adjuk át paraméterben, amiben pedig egy függvény lakozik.
Van egy függvényünk, aminek az a feladata, hogy egy state változó értékét állítsa be:
void switchScreen() {
setState(
() {
activeScreen = const QuestionsScreen();
},
);
}
Ezt a függvényt kellene átadni egy másik widgetnek:
class _QuizState extends State<Quiz> {
Widget activeScreen = StartScreen();
void switchScreen() {
setState(
A kódból látszik, hogy mindez egy StatefulWidgeten belüli state class-ban van. Az activeScreen maga a state változó, ami egy widgetet tartalmaz. Ennek kezdőértéke a StartScreen() widget.
Ebben a StartScreen widgetben van egy gomb, amit megnyomva kell valamit csinálni ezzel a state-el:
OutlinedButton.icon(
onPressed: () {
},
Tehát az onPressed-en belül kellene valamilyen függvényhívást megvalósítanunk. És jelen példában ez az a függvény, ami kívülről (paraméterként) fog érkezni.
Adjuk át a függvényt paraméterben
Hát adjuk, még pedig így:
Widget activeScreen = StartScreen(switchScreen);
A VSCode jelez egy hibát:
Konkrétan az neki a baja, hogy a StartScreen widget-ben nem mondtuk meg, hogy várjuk ezt a függvényt a paraméterlistában.
A StartScreen widget konstruktorában tehát meg kell adni ezt a függvény paramétert:
class StartScreen extends StatelessWidget {
const StartScreen(Function startQuiz, {super.key});
Ha precízek akarunk lenni, és miért ne akarnánk, akkor visszagörgetve azt látjuk, hogy maga az átadandó függvény (switchScreen) egy típus nélküli (void) függvény volt. Így ezt illik jelölni a konstruktornál is a függvény paraméter típusánál:
const StartScreen(void Function() startQuiz, {super.key});
Ezt helyesen úgy kell ilyenkor megadni, hogy a Function szó elé beírjuk a void kulcsszót, a Function után pedig () zárójelpárt írunk, mint amikor meghívunk egy függvényt.
Van egy ennél még haladóbb módszer is:
const StartScreen(this.startQuiz, {super.key});
final void Function() startQuiz;
Amennyiben mindenképpen számítunk a függvényre, tehát kötelezően várjuk annak átadását, akkor még a required kulcsszót is ki kell tennünk a konstruktorban:
const StartScreen(required this.startQuiz, {super.key});
Átadott függvény használata
A függvényt aztán kétféleképpen tudjuk használni egy gomb onPress tulajdonságában:
onPressed: () {
startQuiz();
},
Vagy rövidebben:
onPressed: startQuiz,
Átadott függvény hibájának javítása
Ha megnézzük, akkor a VSCode egy újabb hibát jelez ott, ahol a függvényt átadtuk a widgetnek paraméterként:
A hiba azzal van összefüggésben, hogy a függvény paramétert akkor adjuk át, amikor éppen létrehozzuk (és kezdőértékkel ellátjuk) a változót:
class _QuizState extends State<Quiz> {
Widget activeScreen = StartScreen(switchScreen);
void switchScreen() {
setState(
() {
activeScreen = const QuestionsScreen();
},
);
}
Tehát a változónak és a függvénynek a létrehozása is ugyanakkor megy végbe időben. Konkrétan akkor, amikor az osztályt példányosítjuk. Tehát az activeScreen és switchScreen létrehozása kb. egymással párhuzamosan történik. Így a Dart úgy gondolja, hogy túl korai hivatkozni a változó inicializálásánál a függvényre, mert alapvetően nem garantált, hogy a függvény / metódus ekkor már létezik. És ez tök jogos is.
Megoldás az initState beépített metódusa:
Widget activeScreen = StartScreen(switchScreen);
@override
void initState() {
// TODO: implement initState
super.initState();
}
void switchScreen() {
Az initState egy olyan metódus, amit a State osztály nyújt nekünk aranytálcán. Az initState-et felül tudjuk írni (@override) a saját inicializációs logikánkkal.
Tehát a nevében is benne van, hogy arra használjuk, amikor extra inicializációs lépéseket akarunk írni akkor, amikor a State objektum először létrejön.
Az initState egyszer fog lefutni, miután az objektum megszületett. Ezután már sosem fog mégegyszer lefutni. Logikus, hogy ezen a metóduson belül kellene elhelyeznünk az activeScreen inicializálását:
Widget? activeScreen;
@override
void initState() {
activeScreen = StartScreen(switchScreen);
super.initState();
}
Az activeScreen létrehozásakor a Widget típusmegnevezés utáni kérdőjellel mondtuk meg azt a Dart-nak, hogy igen, tudomásul vettük, hogy null érték is lehet. Ellenkező esetben a VSCode ismét jelez, ha nem használunk kérdőjelet:
Szóval a ? azt mondja a Dart-nak, hogy a változó tartalmazhat Widgetet vagy null értéket is.