StreamBuilder — isang Flutter widget na awtomatikong nagtatayo muli ng interface kapag nakatanggap ng bagong data mula sa asynchronous na stream. Hindi tulad ng FutureBuilder na gumagana sa isang beses na resulta, sinusuportahan ng StreamBuilder ang patuloy na pag-update ng UI sa buong siklo ng buhay ng Stream. Ayon sa opisyal na dokumentasyon ng Flutter (2026), ginagamit ang StreamBuilder sa mga real-time na application: chat, news feed, sensor monitoring, at financial ticker. Ito ay isang pangunahing tool ng reactive programming, kung saan ang UI ay sumasalamin sa estado ng data nang walang manu-manong pagtawag ng setState.
Mga Pangunahing Punto
StreamBuilder — ay isang widget mula sa Flutter SDK package na nag-subscribe sa Stream at nagtatayo muli ng child element nito sa bawat bagong pangyayari ng stream. Tumatanggap ang StreamBuilder ng Stream object at nagbabalik ng widget batay sa huling snapshot na nakuha mula sa stream.
Sa arkitektura ng Flutter, ang StreamBuilder ay kabilang sa grupo ng Builder widgets na naghihiwalay sa pagbuo ng UI mula sa estado ng data. Hindi tulad ng StatefulWidget, kung saan ang pagbabago ng estado ay nangangailangan ng tahasang pagtawag ng setState, ang StreamBuilder ay awtomatikong tumutugon sa mga asynchronous na pangyayari, pinapasimple ang code at binabawasan ang panganib ng mga error sa synchronisasyon.
Hindi tulad ng FutureBuilder, na nagpoproseso ng isang asynchronous na halaga, ang StreamBuilder ay idinisenyo para sa patuloy na mga stream ng data. Ang FutureBuilder ay nagtatapos pagkatapos matanggap ang unang resulta, habang ang StreamBuilder ay patuloy na nakikinig sa stream at nag-a-update ng UI sa bawat bagong pangyayari.
Ginagamit ang StreamBuilder sa lahat ng senaryo kung saan patuloy na dumarating ang data: WebSocket connections, sensor callbacks, Firebase notification, Bluetooth event queue, at pagpapadala ng estado ng application sa pamamagitan ng BLoC. Ayon sa pagsusuri ng mga Flutter project sa GitHub (2025), ang StreamBuilder ay nasa tatlong pinakaginagamit na Builder widgets kasama ng FutureBuilder at LayoutBuilder.
Konklusyon: gamitin ang StreamBuilder kahit saan kung saan ang UI ay dapat sumalamin sa patuloy na nagbabagong data, iwasan ang manu-manong pamamahala ng estado sa pamamagitan ng StatefulWidget.
StreamBuilder ay nag-subscribe sa Stream sa oras ng pagbuo at nag-unsubscribe kapag nawasak ang widget. Sa bawat pagkakataon na naglalabas ng pangyayari ang Stream, ang StreamBuilder ay tumatanggap ng bagong AsyncSnapshot at tinatawagan ang builder function upang itayo muli ang UI.
Ang proseso ay binubuo ng tatlong yugto. Una: ang StreamBuilder ay lumilikha ng subscription sa ipinadalang Stream sa pamamagitan ng stream.listen method. Pangalawa: sa bawat pangyayari, ina-update ng StreamBuilder ang internal na AsyncSnapshot at minamarkahan ang widget bilang “marumi” para sa muling pagbuo. Pangatlo: tinatawagan ng framework ang builder function na may bagong snapshot, at ang UI ay nagpapakita ng kasalukuyang data.
Mahalaga: gumagamit ang StreamBuilder ng StreamSubscription sa loob. Kung ang Stream ay direktang ipinadala, ang StreamBuilder ay nag-subscribe nang isang beses sa initialization. Kung magbago ang Stream (halimbawa, sa muling pagbuo ng parent), ang StreamBuilder ay mag-unsubscribe mula sa lumang stream at mag-subscribe sa bago. Ang pag-uugaling ito ay kinokontrol ng mga parameter na initialData at buildWhen, na nagpapahintulot sa pag-optimize ng bilang ng mga muling pagbuo.
Konklusyon: ang pag-unawa sa siklo ng buhay ng subscription ay ang pundasyon ng tamang paggamit ng StreamBuilder. Ang maling pamamahala ng mga stream ay humahantong sa pagtagas ng memorya o lumang data sa UI.
Ang property na connectionState ng AsyncSnapshot object ay tumutukoy kung saang yugto ng paggawa gamit ang stream matatagpuan ang StreamBuilder. Apat na estado ang nakikilala: none, waiting, active, done.
None — paunang estado kapag ang Stream ay hindi pa nagsimulang magpadala ng data. Sa estadong ito, ang snapshot.connectionState ay katumbas ng ConnectionState.none, at ang snapshot.data ay null. Karaniwan, sa estadong ito ay nagpapakita ng placeholder o paghihintay para sa unang pangyayari. Kung hindi nagbibigay ang Stream ng paunang data, ang StreamBuilder ay magsisimula mula sa estadong ito.
Waiting — estado ng paghihintay para sa data mula sa asynchronous na stream. Aktibo ang Stream ngunit hindi pa dumarating ang data. Ang estadong ito ay nangyayari, halimbawa, kapag naglo-load ng data mula sa network o nagbubukas ng pangmatagalang koneksyon. Sa estadong ito, karaniwang nagpapakita ng CircularProgressIndicator o skeleton loading.
Active — naglalabas ng data ang stream at ang UI ay nagpapakita ng kasalukuyang impormasyon. Sa estadong ito, ang snapshot.hasData ay true, at ang snapshot.data ay naglalaman ng huling halaga mula sa stream. Kung ang Stream ay Broadcast Stream, ang aktibong estado ay maaaring umiral kasabay ng paghihintay ng bagong data.
Done — ang stream ay natapos na, walang bagong data. Ang Snapshot.data ay naglalaman ng huling halaga na ipinadala bago isara ang stream. Kung matagumpay na natapos ang stream, ang snapshot.hasError ay false. Ang estadong ito ay ginagamit para sa pagpapakita ng huling resulta: mensaheng “Natapos ang pag-load” o paglipat sa susunod na screen.
Konklusyon: sa pagbuo ng UI sa pamamagitan ng StreamBuilder, kailangang iproseso ang lahat ng apat na estado upang maipakita nang tama ng interface ang pag-load, data, error, at pagtatapos.
StreamController — ay isang klase mula sa dart:async package na lumilikha at namamahala ng Stream. Pinapayagan ng StreamController ang pagdaragdag ng data, pagproseso ng error, at pagsasara ng stream, na kinokontrol ang siklo ng buhay nito.
Ang StreamController ay may dalawang uri: single-subscription (isang subscriber) at broadcast (maraming subscriber). Ang single-subscription controller ay tumatanggap lamang ng isang tagapakinig sa isang pagkakataon — ang muling pag-subscribe ay magdudulot ng exception. Ang broadcast controller ay nagpapahintulot sa maraming StreamBuilder na sabay na makinig sa isang stream, na kapaki-pakinabang para sa BLoC at shared application state.
Kapag gumagawa ng StreamController sa pamamagitan ng StreamController<T>.broadcast(), ang data na idinagdag bago ang unang subscription ay hindi nape-play muli para sa bagong subscriber. Kung kailangan makuha ang huling halaga kapag kumokonekta, gamitin ang BehaviourSubject mula sa rxdart package, na nag-cache ng huling pangyayari.
Matapos tapos ang paggawa gamit ang controller, dapat tawagan ang controller.close(). Ang hindi pagtawag ng close ay humahantong sa pagtagas ng resources: nananatiling bukas ang stream, nananatili ang mga subscriber sa memorya, at hindi pinapalaya ng GC ang mga kaugnay na bagay.
Konklusyon: gamitin ang StreamController na may tahasang pamamahala ng siklo ng buhay. Para sa single-subscription stream — standard controller, para sa shared state — broadcast controller o BehaviourSubject.
Halimbawa 1 ay nagpapakita ng timer na may countdown gamit ang StreamController at 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();
}
});
}
}
Sa halimbawa, isang controller ang ginawa para sa pagbuo ng mga numero mula 0 hanggang 10 na may interval na 1 segundo. Pagkatapos maabot ang 10, tinatawagan ang close at nagtatapos ang stream. Ang StreamBuilder, na naka-subscribe sa stream ng controller na ito, ay magpapakita ng bawat bagong halaga.
Halimbawa 2 — paggamit ng StreamBuilder na may Broadcast Stream para sa pagpapakita ng data mula sa maraming source.
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}');
},
)
Ang pangalawang halimbawa ay nagpapakita ng pagproseso ng lahat ng estado: initialData para sa paunang display, waiting para sa loading indicator, hasError para sa mga error, at data para sa matagumpay na resulta. Ang pattern na ito ay pamantayan para sa production code na may StreamBuilder.
Konklusyon: gamitin ang initialData upang maiwasan ang blangkong screen sa unang sandali at laging iproseso ang hasError para sa tamang pagpapakita ng mga error sa user.
Pagkakamali 1: paggawa ng bagong Stream sa bawat muling pagbuo ng parent. Kung ang Stream ay ipinadala sa pamamagitan ng expression na gumagawa ng bagong object sa bawat pagbuo, ang StreamBuilder ay mag-unsubscribe mula sa lumang stream at mag-subscribe sa bago, na nagdudulot ng walang katapusang cycle ng muling pagbuo. Solusyon: gumamit ng remembered variable o StatefulWidget na may fixed Stream.
Pagkakamali 2: kawalan ng pagproseso ng error. Ang Stream ay maaaring maglabas ng error sa pamamagitan ng controller.sink.addError, at kung hindi sinusuri ng builder ang snapshot.hasError, ang user ay makakakita ng blangkong screen o walang katapusang pag-load. Solusyon: laging suriin ang hasError at magpakita ng naiintindihang mensahe.
Pagkakamali 3: pagtagas ng memorya dahil sa hindi nakasarang StreamController. Kung ang controller ay hindi sarado sa dispose, ang stream ay patuloy na umiiral at ang GC ay hindi nagpapalaya ng memorya. Solusyon: tawagan ang controller.close() sa dispose at makinig sa done event para sa mga pangwakas na aksyon.
Pagkakamali 4: paggamit ng StreamBuilder na may mabagal na builder function. Dahil ang builder ay tinatawagan sa bawat pangyayari ng stream, ang mabibigat na kalkulasyon sa loob nito ay humahantong sa paglaktaw ng frame. Solusyon: ilipat ang mga kalkulasyon sa hiwalay na isolate o gamitin ang Stream.map para sa pagbabago ng data.
Konklusyon: ang StreamBuilder ay isang makapangyarihan ngunit hinihinging tool. Bantayan ang siklo ng buhay ng Stream, iproseso ang mga error, at iwasan ang mabibigat na operasyon sa builder.
Mga Madalas Itanong
FutureBuilder ay idinisenyo para sa isang beses na asynchronous na resulta: nag-subscribe ito sa Future, tumatanggap ng isang halaga, at tinatapos ang trabaho. Ang StreamBuilder ay nag-subscribe sa Stream, na maaaring maglabas ng maraming halaga sa paglipas ng panahon, at nagtatayo muli ng UI sa bawat bagong pangyayari.
AsyncSnapshot — hindi nababagong bagay na naglalaman ng kasalukuyang estado ng subscription (connectionState), huling natanggap na halaga (data), at object ng error (error), kung ang stream ay naglabas ng exception.
Error ay pinoproseso sa pamamagitan ng snapshot.hasError at snapshot.error properties sa builder function. Kung ang stream ay naglabas ng error sa pamamagitan ng sink.addError method, ang AsyncSnapshot ay nakakatanggap ng error, at ang builder ay dapat magpakita ng angkop na mensahe o fallback UI.
Oo, kung ang Stream ay broadcast (ginawa sa pamamagitan ng StreamController.broadcast). Ang single-subscription Stream ay nagpapahintulot lamang ng isang subscriber. Para sa pagbabahagi ng isang stream sa maraming widget, gumamit ng broadcast controller o rxdart package na may BehaviourSubject.
Gamitin ang parameter na buildWhen para sa pag-filter ng mga pangyayari kung saan kailangan itayo muli ang UI. Gayundin, ilapat ang Stream.transformer o Stream.where para sa pag-filter ng data bago ipadala sa StreamBuilder.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din