StreamBuilder — en Flutter-widget som automatiskt bygger om gränssnittet när nya data tas emot från en asynkron ström. Till skillnad från FutureBuilder som arbetar med ett engångsresultat, stöder StreamBuilder kontinuerlig UI-uppdatering under hela Streams livscykel. Enligt den officiella Flutter-dokumentationen (2026) används StreamBuilder i realtidsapplikationer: chattar, nyhetsflöden, sensorövervakning och finansiella tickers. Detta är ett nyckelverktyg inom reaktiv programmering, där UI återspeglar datatillståndet utan manuella anrop till setState.
Huvudpunkter
StreamBuilder — är en widget från Flutter SDK-paketet som prenumererar på en Stream och bygger om sitt underordnade element vid varje ny händelse i strömmen. StreamBuilder tar emot ett Stream-objekt och returnerar en widget baserat på den senaste momentbilden från strömmen.
I Flutter-arkitekturen tillhör StreamBuilder gruppen Builder-widgets som separerar UI-bygget från datatillståndet. Till skillnad från StatefulWidget, där tillståndsändring kräver ett explicit anrop till setState, reagerar StreamBuilder automatiskt på asynkrona händelser, vilket förenklar koden och minskar risken för synkroniseringsfel.
Till skillnad från FutureBuilder, som bearbetar ett enda asynkront värde, är StreamBuilder utformat för kontinuerliga dataströmmar. FutureBuilder avslutas efter att det första resultatet mottagits, medan StreamBuilder fortsätter att lyssna på strömmen och uppdaterar UI vid varje ny händelse.
StreamBuilder används i alla scenarier där data anländer kontinuerligt: WebSocket-anslutningar, sensor-återanrop, Firebase-notiser, Bluetooth-händelseköer och sändning av applikationstillstånd via BLoC. Enligt analys av Flutter-projekt på GitHub (2025) är StreamBuilder bland de tre mest använda Builder-widgetsen tillsammans med FutureBuilder och LayoutBuilder.
Slutsats: använd StreamBuilder överallt där UI måste spegla kontinuerligt föränderlig data och undvik manuell tillståndshantering via StatefulWidget.
StreamBuilder prenumererar på Stream vid byggnadstillfället och avregistrerar sig när widgeten förstörs. Varje gång Stream sänder en händelse får StreamBuilder en ny AsyncSnapshot och anropar builder-funktionen för att bygga om UI.
Processen består av tre steg. Första: StreamBuilder skapar en prenumeration på den överförda Stream via metoden stream.listen. Andra: vid varje händelse uppdaterar StreamBuilder den interna AsyncSnapshot och markerar widgeten som ”smut sig“ för ombyggnad. Tredje: ramverket anropar builder-funktionen med den nya momentbilden och UI visar aktuell data.
Viktigt: StreamBuilder använder internt StreamSubscription. Om Stream skickas direkt prenumererar StreamBuilder en gång vid initiering. Om Stream ändras (t.ex. vid ombyggnad av föräldern) avregistrerar sig StreamBuilder från den gamla strömmen och prenumererar på den nya. Detta beteende styrs av parametrarna initialData och buildWhen, som möjliggör optimering av antalet ombyggnader.
Slutsats: förståelse av prenumerationens livscykel är grunden för korrekt användning av StreamBuilder. Felaktig hantering av strömmar leder till minnesläckor eller föråldrad data i UI.
Egenskapen connectionState för AsyncSnapshot-objektet bestämmer i vilket skede av arbetet med strömmen StreamBuilder befinner sig. Fyra tillstånd särskiljs: none, waiting, active, done.
None — initialt tillstånd när Stream ännu inte har börjat överföra data. I detta tillstånd är snapshot.connectionState lika med ConnectionState.none och snapshot.data är null. Vanligtvis visas en platshållare eller väntan på den första händelsen i detta tillstånd. Om Stream inte tillhandahåller initial data börjar StreamBuilder från detta tillstånd.
Waiting — tillstånd av väntan på data från den asynkrona strömmen. Stream är aktiv men data har ännu inte anlänt. Detta tillstånd uppstår till exempel vid inläsning av data från nätverket eller vid öppnande av en långvarig anslutning. I detta tillstånd visas vanligtvis en CircularProgressIndicator eller ett laddningsskelett.
Active — strömmen sänder data och UI visar aktuell information. I detta tillstånd är snapshot.hasData true och snapshot.data innehåller det senaste värdet från strömmen. Om Stream är en Broadcast Stream kan det aktiva tillståndet samexistera med väntan på ny data.
Done — strömmen är avslutad, ingen ny data kommer. Snapshot.data innehåller det sista värdet som skickades innan strömmen stängdes. Om strömmen avslutades framgångsrikt är snapshot.hasError false. Detta tillstånd används för att visa slutresultatet: meddelandet ”Laddning slutförd“ eller övergång till nästa skärm.
Slutsats: vid byggande av UI via StreamBuilder måste alla fyra tillstånd bearbetas för att gränssnittet korrekt ska visa laddning, data, fel och slutförande.
StreamController — är en klass från paketet dart:async som skapar och hanterar en Stream. StreamController gör det möjligt att lägga till data, bearbeta fel och stänga strömmen, och kontrollerar dess livscykel.
StreamController finns i två typer: single-subscription (en prenumerant) och broadcast (flera prenumeranter). Single-subscription-styrenheten accepterar endast en lyssnare åt gången — förnyad prenumeration orsakar ett undantag. Broadcast-styrenheten tillåter flera StreamBuilders att samtidigt lyssna på en ström, vilket är användbart för BLoC och delad applikationsstatus.
Vid skapande av StreamController via StreamController<T>.broadcast() spelas data som lagts till före den första prenumerationen inte upp för en ny prenumerant. Om du behöver få det senaste värdet vid anslutning, använd BehaviourSubject från paketet rxdart, som cachar den senaste händelsen.
Efter avslutat arbete med styrenheten måste controller.close() anropas. Att inte anropa close leder till resursläckor: strömmen förblir öppen, prenumeranter ligger kvar i minnet och GC frigör inte relaterade objekt.
Slutsats: använd StreamController med explicit livscykelhantering. För single-subscription-strömmar — standardstyrenhet, för delad status — broadcast-styrenhet eller BehaviourSubject.
Exempel 1 demonstrerar en timer med nedräkning med hjälp av StreamController och 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();
}
});
}
}
I exemplet skapas en styrenhet för att generera siffror från 0 till 10 med 1 sekunds intervall. Efter att ha nått 10 anropas close och strömmen avslutas. StreamBuilder som prenumererar på denna styrenhets stream visar varje nytt värde.
Exempel 2 — användning av StreamBuilder med Broadcast Stream för att visa data från flera källor.
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}');
},
)
Det andra exemplet visar bearbetning av alla tillstånd: initialData för initial visning, waiting för laddningsindikator, hasError för fel och data för framgångsrikt resultat. Detta mönster är standard för produktionskod med StreamBuilder.
Slutsats: använd initialData för att undvika en tom skärm i första ögonblicket och bearbeta alltid hasError för korrekt visning av fel till användaren.
Misstag 1: skapa en ny Stream vid varje ombyggnad av föräldern. Om Stream skickas via ett uttryck som skapar ett nytt objekt vid varje byggnad, avregistrerar StreamBuilder sig från den gamla strömmen och prenumererar på den nya, vilket orsakar en oändlig ombyggnadscykel. Lösning: använd en remembered variabel eller StatefulWidget med en fast Stream.
Misstag 2: brist på felhantering. Stream kan sända fel via controller.sink.addError, och om builder inte kontrollerar snapshot.hasError ser användaren en tom skärm eller oändlig laddning. Lösning: kontrollera alltid hasError och visa ett begripligt meddelande.
Misstag 3: minnesläcka på grund av ej stängd StreamController. Om styrenheten inte stängs i dispose fortsätter strömmen att existera och GC frigör inte minnet. Lösning: anropa controller.close() i dispose och lyssna på done-händelsen för avslutande åtgärder.
Misstag 4: användning av StreamBuilder med långsam builder-funktion. Eftersom builder anropas vid varje strömhändelse leder tunga beräkningar inuti den till bildruteförluster. Lösning: flytta beräkningar till en separat isolat eller använd Stream.map för datatransformering.
Slutsats: StreamBuilder är ett kraftfullt men krävande verktyg. Övervaka Streams livscykel, bearbeta fel och undvik tunga operationer i builder.
Vanliga frågor
FutureBuilder är utformat för ett engångs asynkront resultat: det prenumererar på en Future, får ett värde och avslutar arbetet. StreamBuilder prenumererar på en Stream, som kan sända flera värden över tid, och bygger om UI vid varje ny händelse.
AsyncSnapshot — ett oföränderligt objekt som innehåller det aktuella prenumerationstillståndet (connectionState), det senast mottagna värdet (data) och felobjektet (error), om strömmen har sänt ett undantag.
Fel bearbetas via egenskaperna snapshot.hasError och snapshot.error i builder-funktionen. Om strömmen sänder ett fel via metoden sink.addError får AsyncSnapshot error, och builder måste visa ett lämpligt meddelande eller reserv-UI.
Ja, om Stream är av typen broadcast (skapad via StreamController.broadcast). Single-subscription Stream tillåter endast en prenumerant. För att dela en ström mellan flera widgets, använd en broadcast-styrenhet eller rxdart-paketet med BehaviourSubject.
Använd parametern buildWhen för att filtrera händelser där UI måste byggas om. Tillämpa också Stream.transformer eller Stream.where för att filtrera data innan de skickas till StreamBuilder.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också