Widget — is het centrale concept van het Flutter-framework, dat de configuratie van een gebruikersinterface-element beschrijft. Elk visueel onderdeel, van een knop tot een complexe animatie, is een Widget. In tegenstelling tot andere frameworks, waar UI wordt beschreven in aparte XML-bestanden of imperatief wordt getekend, bouwt Flutter de interface door middel van Widget-compositie — het combineren van kleine ondeelbare elementen in een hiërarchische boom. Volgens Flutter Documentation (2025) bevat de Flutter SDK-bibliotheek meer dan 260 ingebouwde Widgets, die Material Design, Cupertino en aangepaste stijlen ondersteunen.
Belangrijkste punten
Widget in Flutter is een onveranderlijke (immutable) beschrijving van een deel van de gebruikersinterface. Elke Widget bevat configuratie-eigenschappen: grootte, kleur, positie, tekst, gebeurtenisafhandelaars en onderliggende Widgets. De Widgets zelf worden niet direct weergegeven — ze zijn recepten (blueprints) op basis waarvan de Flutter Engine een RenderObject creëert — een echt grafisch object op het scherm.
De filosofie van Flutter zegt: „Everything is a Widget”. Dit betekent dat niet alleen zichtbare elementen (Text, Image, Button) Widgets zijn, maar ook structurele blokken (Padding, Center, Column, Stack), gedragsblokken (GestureDetector, AnimatedBuilder) en zelfs de applicatie zelf (MaterialApp, CupertinoApp). Deze aanpak zorgt voor uniformiteit: elk schermelement kan met elk ander element worden gecombineerd door eenvoudig nesten.
Volgens gegevens van Google I/O 2024 — Flutter Widgets Deep Dive bevat een gemiddelde Flutter-applicatie op een gegeven moment tussen de 200 en 1500 Widgets. Ondanks dit aantal handhaaft Flutter 60 FPS, zelfs op budgetapparaten, dankzij optimalisaties op C++-niveau van de Skia/Impeller-engine. Widgets zijn lichte objecten (40–80 bytes elk), dus het maken ervan is geen prestatieknelpunt.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hallo, Flutter!'),
),
),
);
}
}
Om te begrijpen hoe Widget werkt, moet u de architectuur van Flutter kennen, die bestaat uit drie onderling verbonden bomen. De eerste — Widget tree — beschrijft de UI-configuratie. Het is een lichte boom die bij elk frame volledig kan worden herbouwd (garbage collector verwijdert oude Widgets en maakt nieuwe). Widgets zijn onveranderlijk (immutable): als de tekstkleur verandert, wordt een nieuwe Text Widget met de nieuwe kleur gemaakt en wordt de oude weggegooid.
De tweede boom — Element tree — is de schakel tussen Widget en RenderObject. Element bevat een verwijzing naar Widget (configuratie) en naar RenderObject (weergave). Wanneer Widget verandert, vergelijkt Flutter de nieuwe Widget met het oude Element en beslist: de bestaande RenderObject bijwerken (als Widget van hetzelfde type is) of een nieuwe maken (als het Widget-type is veranderd). Dit proces heet Reconciliation en is het equivalent van React Virtual DOM.
De derde boom — RenderObject tree — is verantwoordelijk voor de daadwerkelijke weergave op het scherm. RenderObject bevat concrete afmetingen, posities en paint-methoden. Flutter Engine (C++ Skia of Impeller) doorloopt de RenderObject tree en geeft elk knooppunt weer. RenderObject tree is de zwaarste boom, daarom minimaliseert Flutter de wijzigingen door RenderObject te hergebruiken bij het wijzigen van een Widget van hetzelfde type.
| Boom | Doel | Immutable? | Levenscyclus |
|---|---|---|---|
| Widget | UI-configuratie (recept) | Ja | Opnieuw gemaakt bij elke build |
| Element | Verbinding Widget ↔ RenderObject | Nee | Bestaat zolang de widget in de boom zit |
| RenderObject | Weergave en layout | Nee | Zwaar, wordt indien mogelijk hergebruikt |
Flutter verdeelt Widgets in twee fundamentele typen: StatelessWidget en StatefulWidget. StatelessWidget is een widget die geen veranderlijke toestand bevat. Het uiterlijk van StatelessWidget wordt volledig bepaald door de constructor en kan niet veranderen na weergave. Voorbeelden: Text, Icon, Divider, Padding. Alle eigenschappen van StatelessWidget worden als final in de constructor gedeclareerd en zijn alleen-lezen.
StatefulWidget is een widget met veranderlijke toestand. Het bestaat uit twee klassen: de Widget zelf (onveranderlijke configuratie, net als StatelessWidget) en State (veranderlijke toestand). Het scheiden van Widget van State is een belangrijke architectuurbeslissing van Flutter. Widget wordt bij elke build opnieuw gemaakt, maar het State-object blijft leven gedurende de hele levenscyclus van de widget in de boom, waarbij het zijn toestand behoudt.
Wanneer setState() wordt aangeroepen, markeert Flutter State als „vuil” en roept bij het volgende frame de methode build() aan om de subboom te herbouwen. Belangrijk: setState() maakt de Widget zelf niet opnieuw — het activeert alleen de build()-aanroep in de bestaande State. Dit betekent dat StatefulWidget de UI kan bijwerken zonder de toestand van onderliggende Widgets te verliezen, zolang de sleutels (Key) van onderliggende elementen stabiel blijven.
// StatelessWidget — uiterlijk verandert nooit
class GreetingWidget extends StatelessWidget {
const GreetingWidget({super.key, required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text('Hallo, $name');
}
}
// StatefulWidget — teller met veranderlijke toestand
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Teller: $_count'),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: const Text('Verhogen'),
),
],
);
}
}
Alle Widgets in Flutter kunnen worden onderverdeeld in drie hoofdcategorieën op basis van functioneel doel. Layout Widget — zijn verantwoordelijk voor de plaatsing van onderliggende elementen op het scherm. Row en Column plaatsen kinderen in een lijn, Stack legt de ene over de andere, Expanded en Flexible verdelen de beschikbare ruimte. Layout Widgets hebben geen eigen visuele weergave — ze beheren de positie en grootte van onderliggende widgets.
Painting Widget — zijn verantwoordelijk voor de visuele vormgeving. Container combineert decoraties (kleur, verloop, schaduw, rand) met layout-eigenschappen. Padding voegt opvulling toe, DecoratedBox tekent de achtergrond, Transform past transformaties toe (rotatie, schaling). Painting Widgets zijn de bouwstenen van visuele stijl en worden vaak samen met layout Widgets gebruikt om de gewenste uitstraling te bereiken.
Interactive Widget — verwerken gebruikersinvoer. GestureDetector detecteert gebaren (tap, swipe, pinch), InkWell voegt een Material-rimpeleffect toe, TextField accepteert tekstinvoer, Slider en Switch bieden standaard bedieningselementen. Interactive Widgets verhogen gebeurtenissen via callback-functies die aan de constructor worden doorgegeven of worden afgehandeld via toestandsproviders.
| Categorie | Voorbeelden van Widget | Doel |
|---|---|---|
| Layout | Row, Column, Stack, Expanded, Flexible, Align | Plaatsing en grootte van onderliggende elementen |
| Painting | Container, Padding, DecoratedBox, RotatedBox | Kleur, achtergrond, randen, schaduwen, transformaties |
| Interactive | GestureDetector, InkWell, TextField, Slider | Verwerking van aanraking, invoer, gebaren |
| Platform | MaterialApp, CupertinoApp, Theme, MediaQuery | Integratie met platform, thema's, aanpassing |
| Async | FutureBuilder, StreamBuilder, ValueListenableBuilder | Reactieve updates uit asynchrone gegevens |
Volgens gegevens van Flutter Widget of the Week (Google, 2025) gebruikt de Flutter-gemeenschap actief de combinatie van layout + painting + interactive Widgets om vrijwel elke interface te bouwen. Bijvoorbeeld een knop: InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). Deze modulariteit maakt het mogelijk om standaardblokken in verschillende contexten te hergebruiken zonder code te dupliceren.
Widget-compositie is het proces van het bouwen van UI door Widgets in elkaar te nesten. In tegenstelling tot klassieke overerving (extends), waarbij de onderliggende klasse het gedrag van de ouder erft, gebruikt Flutter aggregatie: elke Widget bevat andere Widgets via de parameter child (voor één) of children (voor meerdere). Deze aanpak biedt meer flexibiliteit en herbruikbaarheid.
BuildContext is het tweede belangrijkste concept na Widget. BuildContext is een descriptor van de positie van Widget in de elementenboom. Via BuildContext heeft Widget toegang tot voorouderlijke Widgets (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). BuildContext wordt doorgegeven aan de methode build() en wordt gebruikt voor interactie met bovenliggende en onderliggende elementen. Elke Widget heeft precies één BuildContext die zijn positie in de boom uniek identificeert.
Volgens Flutter Architectural Overview (Google, 2025) is BuildContext de basis voor InheritedWidget — een mechanisme waarmee gegevens door de boom naar beneden kunnen worden doorgegeven zonder expliciete overdracht via constructors. Theme, MediaQuery, Navigator en Provider gebruiken InheritedWidget onder de motorkap. Elke diep geneste Widget heeft toegang tot vooroudergegevens via BuildContext.dependOnInheritedWidgetOfExactType, wat BuildContext tot de sleutel maakt van de reactieve architectuur van Flutter.
// Widget-compositie door nesten
Scaffold(
appBar: AppBar(title: const Text('Mijn App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Welkom bij Flutter',
style: TextStyle(fontSize: 24)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () { /* nav */ },
child: const Text('Aan de slag'),
),
],
),
),
)
// Toegang tot thema's via BuildContext
Text(
'Gestileerde Tekst',
style: Theme.of(context).textTheme.headlineMedium,
)
De eerste en meest voorkomende fout — StatefulWidget gebruiken waar StatelessWidget voldoende is. Veel beginnende Flutter-ontwikkelaars maken StatefulWidget voor alle widgets, zelfs als de toestand is opgeslagen in een externe provider (Provider, Riverpod, BLoC). Dit is overbodig en verslechtert de prestaties. Regel: gebruik StatelessWidget als de toestand extern wordt beheerd of als de widget geen eigen veranderlijke toestand heeft.
De tweede fout — Widgets maken binnen de build-methode zonder const-constructor. Elke Widget die zonder const wordt gemaakt, wordt bij elke build opnieuw toegewezen. Als Widgets binnen build() met een const-constructor worden gemaakt, kan Flutter dezelfde instantie hergebruiken, waardoor de belasting van de garbage collector wordt verminderd. Voeg const toe waar mogelijk — vooral voor Text, Icon, SizedBox, Padding en andere StatelessWidgets.
De derde fout — onjuist werken met sleutels (Key). Flutter gebruikt Key om Widgets te identificeren bij het herbouwen van de boom. Als een lijst met Widgets zonder Key wordt herbouwd, kan Flutter de volgorde van elementen door elkaar halen, wat leidt tot onjuiste animatie of verlies van toestand. Voeg altijd Key (bijv. ValueKey of ObjectKey) toe voor elementen in lijsten, vooral bij gebruik van ListView.builder met dynamische gegevens.
Veelgestelde vragen
StatelessWidget — widget zonder veranderlijke toestand, het uiterlijk wordt volledig bepaald door de constructor. StatefulWidget — widget met veranderlijke toestand, die wordt opgeslagen in een apart State-object en kan worden bijgewerkt via setState() zonder de widget zelf opnieuw te maken. Gebruik StatelessWidget waar mogelijk, StatefulWidget — wanneer lokale toestand vereist is.
Widget immutable — is een architectuurbeslissing van Flutter voor prestaties. Als Widgets mutable zouden zijn, zou Flutter de oude en nieuwe configuratie niet veilig kunnen vergelijken bij elke build. Immutability stelt Flutter in staat om snel te bepalen of een Widget is veranderd (via de operator ==) en de bestaande RenderObject te hergebruiken, waardoor dure weergavebewerkingen worden geminimaliseerd.
BuildContext is een descriptor van de positie van Widget in de elementenboom. Via het heeft Widget toegang tot voorouderlijke Widgets (Theme, MediaQuery, Navigator) en InheritedWidget. BuildContext wordt ook gebruikt voor navigatie (Navigator.of(context)), het tonen van SnackBar en interactie met Provider. Elke Widget ontvangt BuildContext via de methode build() en geeft het door aan zijn opvolgers.
Gebruik Row voor horizontale plaatsing van elementen, Column — voor verticale plaatsing, Stack — voor het over elkaar heen leggen van elementen. Row en Column werken volgens het flexbox-principe: children nemen ruimte in volgens mainAxisSize, mainAxisAlignment en crossAxisAlignment. Stack gebruikt positioned-children voor precieze positionering ten opzichte van randen of het midden.
Flutter bereikt hoge prestaties via drie mechanismen: (1) Widget are cheap — lichte immutable objecten (40–80 bytes), het maken ervan belast de GC niet. (2) RenderObject reuse — bij het wijzigen van een Widget van hetzelfde type wordt RenderObject hergebruikt, waardoor dure herbouw wordt vermeden. (3) Skia/Impeller engine — weergave op C++-niveau met minimalisatie van tekenaanroepen via repaint boundaries.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook