Saját widget létrehozása azt jelenti, hogy Dart nyelven írunk egy osztályt, azaz class-t.
class GradientContainer {
}
Az osztály létrehozás a class kulcsszóval történik. Az osztály beszédes neve (vagyis utal a funkciójára) nagybetűvel kezdődik és, ha több szóból áll, akkor minden szókezdő betű nagybetű, valamint az egészet egybeírjuk. Még az is helyes, ha a szavakat aláhúzásjellel tagoljuk.
Öröklés
Egy osztály örökölhet bizonyos dolgokat más osztályoktól. Erre való az extends kulcsszó, ami után jön annak az osztálynak a neve, amitől örököl valamit:
class GradientContainer extends StatelessWidget {
}
A StatelessWidget egy olyan osztály, ami a Flutter keretrendszerből jön (material.dart).
Már azzal rengeteg szín falak mögötti adat és logika került a saját osztályunkba pusztán azzal, hogy örököltettük a StatelessWidget osztályból. És még semmit nem írtunk a { } – ek közé.
A kötelező build metódus
De ez a class önmagában még nem működne és a VSCode ezt jelzi is. Hiányzik belőle egy kötelezően megadandó build nevű metódus:
Ennek a build metódusnak vissza kell adnia egy widget-et, emiatt meg is kell adni a build metódus típusát, ami Widget lesz:
class GradientContainer extends StatelessWidget {
Widget build() {
}
}
A metódus elé célszerű még kirakni egy magyarázatot, ún. annotációt is, ami az @override. Technikailag ez nem kötelező, de melegen javasolt amiatt, hogy ezzel magunknak és más programozók számára is jelezzük azt, hogy mi itt most felülírunk egy létező metódust.
build metódus paramétere
Sokkal fontosabb ennél az, hogy a build metódusnak át kell adni egy paramétert. A build metódus hívását a Flutter fogja megtenni. Tehát amikor a Flutter észreveszi, hogy ott van a widget fában a widgetünk, akkor meghívja annak build metódusát. És a színfalak mögött a Flutter adja át a paramétert a metódusnak.
Widget build(context) {
}
build visszatérési értéke
Van még egy fontos lépés: a build metódusnak kötelezően vissza kell adnia egy widgetet. Legyen ez most az egyszerűség kedvéért egy Container:
Widget build(context) {
return Container(
//... ide jön a konténer kódja
);
}
Saját widget hívása
Egy hívási minta például, ahol meghívjuk a saját widgetünk konstruktorát:
void main() {
runApp(
MaterialApp(
home: Scaffold(body: GradientContainer()),
),
);
}
Finomhangolás
A VSCode cikcakkos kék vonallal aláhúzza az osztály nevét, szóval azzal ott van valami javítani való:
A fenti figyelmeztetés azzal van összefüggésben, hogy azoknak az osztályoknak, amik örökölnek más osztályoktól, kellene rendelkezni egy konstruktor függvénnyel. Nem kötelező egyébként, mert automatikusan létezik egy konstruktor a színfalak mögött. De, ha valamiféle extra konfigurációval szeretnénk az osztályunkat indítani (amikor a Widget létrejön / meghívódik a kódban), akkor célszerű saját konstruktort írni.
class GradientContainer extends StatelessWidget {
GradientContainer() {
// konstruktor kódja
}
A konstruktor egy metódus és a neve ugyanaz, mint az osztály neve. Nem mindig kell megadni a konstruktor törzsét, tehát nem mindig kellenek a { } zárójelek.
Ehelyett nevesített paramétert kell átadni a konstruktonak. Itt kell megadni azt a „key” paramétert, amit a VSCode hiányol. Ez lesz továbbadva a StatelessWidget-nek, mint szülő osztálynak.
GradientContainer({key});
A key továbbadásához két lehetőségünk van. Az egyik:
GradientContainer({key}) : super(key: key);
A super-rel hívjuk meg a szülő osztály konstruktorát. Ennek a key tulajdonságába adjuk át a mi key tulajdonságunkat.
A rövidebb és szebb megoldás:
GradientContainer({super.key});
A VSCode itt is súg nekünk, hogy a kód optimalizálás miatt tegyünk const kulcsszót a konstruktor neve elé:
const GradientContainer({super.key});
Saját fájl a class-nak
Célszerű a saját class-nak egy külön fájlt csinálni, hogy ne minden a main.dart fájlban legyen.
Az elnevezésnél a követendő javaslat az, hogy a fájl neve végig kisbetűs, és amennyiben több szóból áll a fájlnév, akkor aláhúzásjellel választjuk el egymástól a szavakat. Tehát a fenti példánál maradva a lib könyvtáron belül létre kell hozni egy gradient_container.dart nevű fájlt és ebbe belemásolni a GradientContainer class kódját.
Az osztály hiányolni fog egy csomó másik class-t, és Flutter specifikus típust, widgetet. Így be kell importálni a material.dart csomagot:
import 'package:flutter/material.dart';
Ahhoz, hogy a main.dart fájlban használni tudjuk a külső fájlba helyezett GradientContainer osztályt, ezt pedig be kell importálni a main.dart fájlba:
import './gradient_container.dart';
A ./ jelent azt az útvonalban, hogy a main.dart fájllal azonos könyvtárban van az importálandó class.
A „best practice” egy másik importálási mód. Szebb is és jobban is olvasható:
import 'package:roll_dice_app/gradient_container.dart';
Csomagként importáltuk. Erre jó a „package:” rész. Utána megadtuk a projekt nevét és azon belül pedig a dart fájl nevét. Egyébként a VSCode erre javaslatot is tett:
Osztály változók
Van egy osztályunk, ami egy szöveget jelenít és formáz meg:
class StyledText extends StatelessWidget {
const StyledText({super.key});
@override
Widget build(context) {
return const Text(
'Hello World :)',
style: TextStyle(
color: Colors.white,
fontSize: 28,
),
);
}
}
Azt akarjuk, hogy a szöveg legyen dinamikus, azaz kívülről (a konstruktoron keresztül) megadható. Első lépés, hogy a konstruktorban felvesszük a változót:
const StyledText(String text, {super.key});
Ha használni akarnánk a ‘Hello World :)’ szöveg helyett, akkor hibát kapnánk:
Ennek az az oka, hogy a konstruktoron belül létrehozott változót a build metódus abszolút nem látja.
A megoldás, hogy létrehozunk egy változót a class-on belül:
String outputText;
Most az lesz a VSCode baja, hogy a változó nem kapott kezdőértéket és null is lehet. Illetve össze kell kötni az osztályváltozót a konstruktorban kapott értékkel:
StyledText(String text, {super.key}): outputText = text;
this használata
Az előző megoldáshoz képest a best practice a this használata:
const StyledText(this.text, {super.key});
Ha most megnézzük, akkor a VSCode elég sok hibát jelez:
Hamarosan minden „kizöldül”. Az első lépés, hogy a tagváltozó nevének meg kell egyezni a konstruktorban levővel, valamint a használat helyén is át kell írni:
String text;
@override
Widget build(context) {
return const Text(
text,
A konstruktorban levő this.text egy „shortcut”, ami egy text nevű változót keres. Mégpedig azt, amit osztályváltozóként hoztunk létre. A this.text kapcsolja össze a kívülről érkező szöveg paramétert az osztályváltozóval.
A this kulcsszót az osztályokon belül használjuk és magára az osztályra hivatkozunk vele. A konstruktorban kell használni, az osztályon belül nem kötelező, mert a Dart tudni fogja, hogy osztályváltozóról van szó.
És akkor jöjjön a VSCode által jelzett hibák eltávolítása. Az első, hogy a konstruktor neve elől kivesszük a const kulcsszót:
StyledText(this.text, {super.key});
Azért nem kell a const, mert azzal, hogy megadtuk az osztályváltozót, ami kívülről kap értéket, már biztos, hogy nem lehet futási idejű konstans.
Hasonló okok miatt kivesszük a Text widget hívás elől is a const kulcsszót. A TextStyle konfigurációs objektum elé viszont beírjuk, mert ez a konfigurációs rész továbbra sem változik:
Widget build(context) {
return Text(
text,
style: const TextStyle(
color: Colors.white,
És ekkorra már csak az osztály nevét húzza alá a VSCode:
Jöhet egy kiváló példája a final kulcsszó használatának. Az osztályváltozó értéke a definíció szerint, ugyanis eddig a pontig változhat. Azonban mi azon kívül, hogy kívülről értéket adunk a konstruktoron keresztül ennek az osztályváltozónak, ezen kívül nem tervezzük, hogy megváltoztatjuk. Futási idejű konstans lesz belőle.
Amint a final szót kitettük, a VSCode újabb jelzést ad, hogy a konstruktort most már ismét elláthatjuk a const kulcsszóval:
Ennek oka az, hogy a Dart teljesítmény optimalizálni akarja a kódot. Ugyanis a text már az osztályon belül nem fog megváltozni sosem. Ez garantálja azt, hogy az objektum az inicializálás után nem változik meg, a Dart be tudja cachelni és újra fel tudja használni ott ahol kell.
class StyledText extends StatelessWidget {
const StyledText(this.text, {super.key});
final String text;
@override
Widget build(context) {
return Text(
text,
style: const TextStyle(
color: Colors.white,
fontSize: 28,
),
);
}
}