Widget — wat is het, typen en compositie in Flutter

Auteur: IT Sectr Gepubliceerd: 2026-06-30 Leestijd: 10 min

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 — de basisbouwsteen van UI in Flutter, die de configuratie van een element beschrijft.
  • Compositie — UI wordt gebouwd door Widgets in elkaar te nesten, niet door overerving.
  • StatelessWidget — widget die niet verandert na weergave (tekst, pictogram, opvulling).
  • StatefulWidget — widget met veranderlijke toestand (formulieren, animaties, gegevenslijsten).
  • Element tree — Flutter onderhoudt drie bomen: Widget, Element en RenderObject.

Wat is Widget in Flutter

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.

dart
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!'),
        ),
      ),
    );
  }
}

De drie bomen van Flutter: Widget, Element en RenderObject

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.

BoomDoelImmutable?Levenscyclus
WidgetUI-configuratie (recept)JaOpnieuw gemaakt bij elke build
ElementVerbinding Widget ↔ RenderObjectNeeBestaat zolang de widget in de boom zit
RenderObjectWeergave en layoutNeeZwaar, wordt indien mogelijk hergebruikt

StatelessWidget vs StatefulWidget

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.

dart
// 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'),
        ),
      ],
    );
  }
}

Typen Widget: layout, painting, interactive

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.

CategorieVoorbeelden van WidgetDoel
LayoutRow, Column, Stack, Expanded, Flexible, AlignPlaatsing en grootte van onderliggende elementen
PaintingContainer, Padding, DecoratedBox, RotatedBoxKleur, achtergrond, randen, schaduwen, transformaties
InteractiveGestureDetector, InkWell, TextField, SliderVerwerking van aanraking, invoer, gebaren
PlatformMaterialApp, CupertinoApp, Theme, MediaQueryIntegratie met platform, thema's, aanpassing
AsyncFutureBuilder, StreamBuilder, ValueListenableBuilderReactieve 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 en BuildContext

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.

dart
// 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,
)

Veelvoorkomende fouten met Widget

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

Wat is het verschil tussen StatelessWidget en StatefulWidget?

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.

Waarom worden Widgets immutable genoemd?

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.

Wat is BuildContext en waarvoor dient het?

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.

Hoe kies ik tussen Row, Column en Stack voor layout?

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.

Hoe handhaaft Flutter 60 FPS met duizenden Widgets?

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

  • Widget — immutable configuratie van een deel van UI in Flutter, basisbouwsteen die uiterlijk en gedrag beschrijft.
  • Drie bomen — Flutter gebruikt Widget tree (configuratie), Element tree (verbinding), RenderObject tree (weergave) voor optimaal renderen.
  • Stateless vs Stateful — StatelessWidget zonder toestand, StatefulWidget met veranderlijke State en setState()-methode voor UI-updates.
  • Compositie — UI wordt gebouwd door Widgets te nesten via child/children, zonder overerving, wat flexibiliteit en herbruikbaarheid biedt.
  • BuildContext — descriptor van de Widget-positie in de boom voor toegang tot Theme, Navigator, MediaQuery en InheritedWidget.
  • Prestaties — Widgets worden bij elke build opnieuw gemaakt (60 FPS), maar RenderObject wordt hergebruikt bij overeenkomend type.
  • Categorieën — Widgets worden onderverdeeld in layout (Row, Column, Stack), painting (Container, Padding), interactive (GestureDetector, TextField) en platform (MaterialApp, Theme).

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.

Bespreek het project

Lees ook