ข้ามไปยังเนื้อหา

build and BuildContext

build มีหน้าที่เดียว: คืน widget tree ที่บรรยาย UI สำหรับ state ปัจจุบัน ควรเป็น function ที่ pure และเร็วของ field กับ state ของ widget เท่านั้น ไม่มากไปกว่านั้น

กฎที่สำคัญ: build ถูกเรียกได้บ่อยมาก — ทุกเฟรมของ animation, ทุก setState, ทุกครั้งที่ ancestor rebuild ดังนั้น build ต้องถูกและปราศจาก side effect

@override
Widget build(BuildContext context) {
// ✅ ดี: แค่บรรยาย UI จาก state ปัจจุบัน
return Text('Count: $_count');
}
@override
Widget build(BuildContext context) {
// ❌ แย่: side effect ใน build อันนี้ยิงทุก rebuild
_apiClient.fetchUser(); // network call — leak, ยิงซ้ำ ๆ
_controller = AnimationController(vsync: this); // สร้างใหม่ทุก build!
return Text('...');
}

side effect — network call, สร้าง controller, subscribe stream — ควรอยู่ใน initState (ครั้งเดียว) หรือ event handler ไม่ใช่ใน build มอง build เป็นการ render แบบอ่านอย่างเดียว

ทุก build ได้รับ BuildContext ไม่ใช่ถุงของ global แต่เป็น handle ไปยังตำแหน่งของ widget นี้ใน element tree (จริง ๆ แล้ว BuildContext คือ element) ตำแหน่งนั้นแหละที่ทำให้ widget มองขึ้นไปด้านบนของ tree เพื่อหา inherited data ได้

@override
Widget build(BuildContext context) {
// context รู้ว่าเราอยู่ตรงไหน จึงค้น ancestor ได้:
final theme = Theme.of(context); // Theme ที่ใกล้ที่สุดเหนือเรา
final media = MediaQuery.of(context); // MediaQuery ที่ใกล้ที่สุดเหนือเรา
return Text('Screen is ${media.size.width} wide', style: theme.textTheme.bodyMedium);
}

Theme.of(context) เดินขึ้นจาก element นี้เพื่อหา Theme ancestor ที่ใกล้ที่สุด นี่คือเหตุผลที่ context สำคัญ: การเรียก .of(context) เดียวกัน คืนผลต่างกันขึ้นกับว่า widget อยู่ตรงไหนใน tree

flowchart BT
  here["widget นี้
(context = element ของตัวเอง)"] --> p1["Padding"]
  p1 --> p2["Theme (เจอ!)"]
  p2 --> root["MaterialApp"]
  here -.->|"Theme.of(context)
ค้นขึ้นบน"| p2
context.of เดินขึ้น element tree เพื่อหา ancestor

เพราะ .of(context) ค้น ขึ้นบน บั๊กที่พบบ่อยคือใช้ context ที่อยู่ เหนือ widget ที่คุณกำลังหา เช่น เรียก Scaffold.of(context) ด้วย context ของ build method เดียวกับที่ สร้าง Scaffold นั้นจะล้มเหลว — context นั้นอยู่เหนือ Scaffold ไม่ใช่ใต้ Scaffold วิธีแก้คือใช้ Builder (หรือ widget ลูก) เพื่อให้ได้ context ที่อยู่ ใต้ เป้าหมาย

ทำไม build method ต้องปราศจาก side effect?
ควรสร้าง AnimationController หรือเริ่ม network request ที่ไหน?
BuildContext คืออะไร?
`Theme.of(context)` ทำอะไร?