StreamBuilder — een Flutter-widget die automatisch de interface herbouwt bij het ontvangen van nieuwe gegevens uit een asynchrone stroom. In tegenstelling tot FutureBuilder, die werkt met een eenmalig resultaat, ondersteunt StreamBuilder continue UI-updates gedurende de volledige levenscyclus van de Stream. Volgens de officiële Flutter-documentatie (2026) wordt StreamBuilder gebruikt in real-time applicaties: chats, nieuwsfeeds, sensorbewaking en financiële tickers. Het is een essentieel hulpmiddel voor reactief programmeren, waarbij de UI de gegevensstatus weerspiegelt zonder handmatige setState-aanroepen.
Belangrijkste
StreamBuilder — is een widget uit het Flutter SDK-pakket dat zich abonneert op een Stream en zijn onderliggende element herbouwt bij elke nieuwe gebeurtenis van de stroom. StreamBuilder ontvangt een Stream-object en retourneert een widget op basis van de laatste snapshot die uit de stroom is verkregen.
In de Flutter-architectuur behoort StreamBuilder tot de groep Builder-widgets die de UI-opbouw scheiden van de gegevensstatus. In tegenstelling tot StatefulWidget, waar statuswijziging een expliciete setState-aanroep vereist, reageert StreamBuilder automatisch op asynchrone gebeurtenissen, wat de code vereenvoudigt en het risico op synchronisatiefouten vermindert.
In tegenstelling tot FutureBuilder, die een enkele asynchrone waarde verwerkt, is StreamBuilder ontworpen voor continue gegevensstromen. FutureBuilder eindigt na ontvangst van het eerste resultaat, terwijl StreamBuilder de stroom blijft beluisteren en de UI bij elke nieuwe gebeurtenis bijwerkt.
StreamBuilder wordt gebruikt in alle scenario's waar gegevens continu binnenkomen: WebSocket-verbindingen, sensor-callbacks, Firebase-meldingen, Bluetooth-gebeurtenisrijen en applicatiestatusuitzendingen via BLoC. Volgens een analyse van Flutter-projecten op GitHub (2025) behoort StreamBuilder tot de top drie meest gebruikte Builder-widgets, samen met FutureBuilder en LayoutBuilder.
Conclusie: gebruik StreamBuilder overal waar de UI continu veranderende gegevens moet weerspiegelen, en vermijd handmatig statusbeheer via StatefulWidget.
StreamBuilder abonneert zich op de Stream op het moment van bouwen en meldt zich af bij vernietiging van de widget. Elke keer dat de Stream een gebeurtenis uitzendt, ontvangt StreamBuilder een nieuwe AsyncSnapshot en roept de builder-functie aan om de UI te herbouwen.
Het proces bestaat uit drie fasen. Eerste: StreamBuilder maakt een abonnement op de doorgegeven Stream via de methode stream.listen. Tweede: bij elke gebeurtenis werkt StreamBuilder de interne AsyncSnapshot bij en markeert de widget als „vuil“ voor herbouw. Derde: het framework roept de builder-functie aan met de nieuwe snapshot en de UI toont de actuele gegevens.
Belangrijk: StreamBuilder gebruikt intern StreamSubscription. Als de Stream direct wordt doorgegeven, abonneert StreamBuilder zich eenmalig bij initialisatie. Als de Stream verandert (bijvoorbeeld bij herbouw van de ouder), meldt StreamBuilder zich af van de oude stroom en abonneert zich op de nieuwe. Dit gedrag wordt gecontroleerd door de parameters initialData en buildWhen, die optimalisatie van het aantal herbouwen mogelijk maken.
Conclusie: inzicht in de levenscyclus van het abonnement is de basis voor correct gebruik van StreamBuilder. Onjuist beheer van stromen leidt tot geheugenlekken of verouderde gegevens in de UI.
De eigenschap connectionState van het AsyncSnapshot-object bepaalt in welke fase van het werken met de stroom StreamBuilder zich bevindt. Er worden vier toestanden onderscheiden: none, waiting, active, done.
None — de begintoestand wanneer de Stream nog geen gegevens heeft verzonden. In deze toestand is snapshot.connectionState gelijk aan ConnectionState.none en snapshot.data is null. Gewoonlijk wordt in deze toestand een placeholder of wachten op de eerste gebeurtenis getoond. Als de Stream geen initiële gegevens levert, begint StreamBuilder vanuit deze toestand.
Waiting — de toestand van wachten op gegevens uit de asynchrone stroom. De Stream is actief, maar de gegevens zijn nog niet aangekomen. Deze toestand treedt bijvoorbeeld op bij het laden van gegevens uit een netwerk of het openen van een langdurige verbinding. In deze toestand wordt gewoonlijk een CircularProgressIndicator of een laadskelet getoond.
Active — de stroom zendt gegevens uit en de UI toont actuele informatie. In deze toestand is snapshot.hasData true en snapshot.data bevat de laatste waarde uit de stroom. Als de Stream een Broadcast Stream is, kan de actieve toestand samengaan met het wachten op nieuwe gegevens.
Done — de stroom is voltooid, er komen geen nieuwe gegevens meer. Snapshot.data bevat de laatste waarde die vóór het sluiten van de stroom is doorgegeven. Als de stroom succesvol is voltooid, is snapshot.hasError false. Deze toestand wordt gebruikt voor het tonen van het eindresultaat: het bericht „Laden voltooid“ of overgang naar het volgende scherm.
Conclusie: bij het bouwen van UI via StreamBuilder moeten alle vier toestanden worden verwerkt, zodat de interface correct laden, gegevens, fouten en voltooiing weergeeft.
StreamController — is een klasse uit het dart:async-pakket die een Stream creëert en beheert. StreamController maakt het mogelijk gegevens toe te voegen, fouten te verwerken en de stroom te sluiten, waarbij de levenscyclus wordt gecontroleerd.
StreamController bestaat in twee typen: single-subscription (één abonnee) en broadcast (meerdere abonnees). De single-subscription-controller accepteert slechts één luisteraar tegelijk — hernieuwd abonnement veroorzaakt een uitzondering. De broadcast-controller staat meerdere StreamBuilders toe tegelijkertijd naar één stroom te luisteren, wat handig is voor BLoC en gedeelde applicatiestatus.
Bij het maken van een StreamController via StreamController<T>.broadcast() worden gegevens die vóór het eerste abonnement zijn toegevoegd niet afgespeeld voor een nieuwe abonnee. Als u de laatste waarde bij verbinding wilt verkrijgen, gebruikt u BehaviourSubject uit het rxdart-pakket, dat de laatste gebeurtenis cached.
Na het voltooien van het werk met de controller moet controller.close() worden aangeroepen. Het niet aanroepen van close leidt tot resourcelekken: de stroom blijft open, abonnees blijven in het geheugen hangen en de GC maakt bijbehorende objecten niet vrij.
Conclusie: gebruik StreamController met expliciet levenscyclusbeheer. Voor single-subscription-stromen — standaard controller, voor gedeelde status — broadcast controller of BehaviourSubject.
Voorbeeld 1 demonstreert een timer met aftellen met behulp van StreamController en StreamBuilder.
import 'dart:async';
class TimerWidget extends StatefulWidget {
const TimerWidget({super.key});
final StreamController<int> controller = StreamController<int>();
void startTimer() {
int count = 0;
Timer.periodic(Duration(seconds: 1), (timer) {
controller.sink.add(count++);
if (count > 10) {
controller.close();
timer.cancel();
}
});
}
}
In het voorbeeld wordt een controller gemaakt voor het genereren van getallen van 0 tot 10 met een interval van 1 seconde. Na het bereiken van 10 wordt close aangeroepen en eindigt de stroom. StreamBuilder, geabonneerd op de stream van deze controller, zal elke nieuwe waarde tonen.
Voorbeeld 2 — gebruik van StreamBuilder met Broadcast Stream voor het weergeven van gegevens uit meerdere bronnen.
final StreamController<String> broadcastController =
StreamController<String>.broadcast();
StreamBuilder<String>(
stream: broadcastController.stream,
initialData: 'Waiting for data...',
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Data: ${snapshot.data}');
},
)
Het tweede voorbeeld toont de verwerking van alle toestanden: initialData voor initiële weergave, waiting voor de laadindicator, hasError voor fouten en data voor een succesvol resultaat. Dit patroon is de standaard voor productiecode met StreamBuilder.
Conclusie: gebruik initialData om een leeg scherm in het eerste moment te voorkomen en verwerk altijd hasError voor correcte weergave van fouten aan de gebruiker.
Fout 1: het maken van een nieuwe Stream bij elke herbouw van de ouder. Als de Stream wordt doorgegeven via een expressie die bij elke bouw een nieuw object maakt, meldt StreamBuilder zich af van de oude stroom en abonneert zich op de nieuwe, wat een oneindige herbouwcyclus veroorzaakt. Oplossing: gebruik een remembered variabele of StatefulWidget met een vaste Stream.
Fout 2: het ontbreken van foutafhandeling. De Stream kan fouten uitzenden via controller.sink.addError, en als de builder snapshot.hasError niet controleert, ziet de gebruiker een leeg scherm of oneindig laden. Oplossing: controleer altijd hasError en toon een begrijpelijk bericht.
Fout 3: geheugenlek door een niet-gesloten StreamController. Als de controller niet wordt gesloten in dispose, blijft de stroom bestaan en maakt de GC het geheugen niet vrij. Oplossing: roep controller.close() aan in dispose en luister naar de done-gebeurtenis voor afsluitende acties.
Fout 4: gebruik van StreamBuilder met een trage builder-functie. Omdat de builder bij elke gebeurtenis van de stroom wordt aangeroepen, leiden zware berekeningen erin tot framedrops. Oplossing: verplaats berekeningen naar een aparte isolaat of gebruik Stream.map voor gegevenstransformatie.
Conclusie: StreamBuilder is een krachtig maar veeleisend hulpmiddel. Houd de levenscyclus van de Stream in de gaten, verwerk fouten en vermijd zware operaties in de builder.
Veelgestelde vragen
FutureBuilder is ontworpen voor een eenmalig asynchroon resultaat: het abonneert zich op een Future, ontvangt één waarde en beëindigt zijn werk. StreamBuilder abonneert zich op een Stream, die meerdere waarden in de tijd kan uitzenden, en herbouwt de UI bij elke nieuwe gebeurtenis.
AsyncSnapshot — een onveranderlijk object dat de huidige abonnementsstatus (connectionState), de laatst ontvangen waarde (data) en het foutobject (error) bevat, als de stroom een uitzondering heeft uitgezonden.
Fout wordt verwerkt via de eigenschappen snapshot.hasError en snapshot.error in de builder-functie. Als de stroom een fout uitzendt via de methode sink.addError, ontvangt AsyncSnapshot error en moet de builder een passend bericht of fallback-UI tonen.
Ja, als de Stream van het type broadcast is (gemaakt via StreamController.broadcast). Een single-subscription Stream staat slechts één abonnee toe. Voor het delen van één stroom tussen meerdere widgets gebruikt u een broadcast-controller of het rxdart-pakket met BehaviourSubject.
Gebruik de parameter buildWhen voor het filteren van gebeurtenissen waarbij de UI moet worden herbouwd. Pas ook Stream.transformer of Stream.where toe voor het filteren van gegevens voordat ze naar StreamBuilder worden gestuurd.
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