Futures & Streams in the UI
สะพานจาก async ไปเป็น widget
หัวข้อที่มีชื่อว่า “สะพานจาก async ไปเป็น widget”คุณ await ใน build ไม่ได้ — build ต้องเป็น synchronous และอาจรันหลายครั้ง Flutter เลยให้ widget สองตัวที่ subscribe แหล่งข้อมูล async แล้ว rebuild เมื่อมีค่าใหม่ปล่อยออกมา: FutureBuilder สำหรับ Future แบบ one-shot และ StreamBuilder สำหรับ Stream ที่มีหลายค่า
ทั้งคู่ส่ง AsyncSnapshot มาให้ builder ของคุณ ซึ่งบอก state ปัจจุบันของแหล่ง async: ยังรออยู่, มีข้อมูลแล้ว, หรือ error?
FutureBuilder
หัวข้อที่มีชื่อว่า “FutureBuilder”FutureBuilder<User>( future: _userFuture, // Future ที่สร้าง OUTSIDE build (ดูกับดัก) builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); // loading } if (snapshot.hasError) { return Text('Failed: ${snapshot.error}'); // error } final user = snapshot.data!; // ข้อมูลพร้อมแล้ว return Text(user.name); },)อ่าน snapshot ตามลำดับตายตัว: waiting → error → data การ handle ครบทั้งสาม (บวก empty state เมื่อจำเป็น) คือสิ่งที่แยกหน้าจอที่ทนทานออกจากหน้าที่แว้บ null error ชั่วขณะ
StreamBuilder
หัวข้อที่มีชื่อว่า “StreamBuilder”StreamBuilder มีรูปแบบเดียวกันสำหรับ Stream — rebuild ทุกครั้งที่ stream ปล่อยค่าใหม่ จึงเหมาะกับข้อมูล live (chat feed, location stream, Bloc state)
StreamBuilder<int>( stream: _tickerStream, initialData: 0, builder: (context, snapshot) { return Text('Count: ${snapshot.data}'); // rebuild ทุกค่าใหม่ },)กับดัก create-in-build
หัวข้อที่มีชื่อว่า “กับดัก create-in-build”นี่คือบั๊ก FutureBuilder ที่พบบ่อยที่สุด: สร้าง future ข้างใน build
// ❌ ผิด — future ตัวใหม่ถูกสร้างทุก rebuild// request จึงยิงซ้ำ และ loading spinner กระพริบไม่จบWidget build(BuildContext context) { return FutureBuilder( future: fetchUser(42), // <-- สร้างใหม่ทุก build builder: ..., );}
// ✅ ถูก — สร้างครั้งเดียวแล้วเก็บไว้ (ใน initState สำหรับ StatefulWidget)late final Future<User> _userFuture;@overridevoid initState() { super.initState(); _userFuture = fetchUser(42); // สร้างครั้งเดียว}เพราะ build รันได้หลายสาเหตุ (parent rebuild, เปลี่ยน theme, keyboard เด้งขึ้น) future ที่สร้างตรงนั้นจึงถูกสร้างใหม่ทุกครั้ง — trigger งานซ้ำและ reset snapshot กลับเป็น waiting ให้สร้าง future ครั้งเดียวใน initState หรือใน state-management layer แล้วส่ง future ตัวเดิม เข้าไป