Stateless vs Stateful
Stateless: description ล้วน ๆ
หัวข้อที่มีชื่อว่า “Stateless: description ล้วน ๆ”StatelessWidget ไม่มี mutable state ของตัวเอง เมื่อให้ input เดิม (constructor argument) build ของตัวเองจะคืน UI เดิมเสมอ ถ้า input ไม่เปลี่ยน UI ก็ไม่ต้องเปลี่ยน
class Greeting extends StatelessWidget { const Greeting({super.key, required this.name}); final String name;
@override Widget build(BuildContext context) => Text('Hello, $name');}ทุกอย่างที่ต้องใช้เป็น final และถูกส่งเข้ามา เลือก StatelessWidget เมื่อ widget แค่ render input ของตัวเอง — ที่เป็น widget ส่วนใหญ่
Stateful: state ที่อยู่นานกว่า widget
หัวข้อที่มีชื่อว่า “Stateful: state ที่อยู่นานกว่า widget”StatefulWidget มีไว้สำหรับ UI ที่เปลี่ยนไปตามเวลาเอง — counter, toggle, animation, form แต่นี่คือจุดที่ทำคนสะดุด: widget ยังคง immutable อยู่ดี ส่วนที่ mutable อยู่ใน object State แยกต่างหาก
class Counter extends StatefulWidget { const Counter({super.key});
@override State<Counter> createState() => _CounterState();}
class _CounterState extends State<Counter> { int _count = 0; // mutable state อยู่ตรงนี้ ไม่ใช่บน widget
void _increment() { setState(() => _count++); // บอก Flutter ให้ rebuild ด้วย state ใหม่ }
@override Widget build(BuildContext context) { return TextButton(onPressed: _increment, child: Text('$_count')); }}setState ทำสองอย่าง: รัน mutation ของคุณ แล้ว mark element เป็น dirty เพื่อให้ Flutter rebuild ถ้าไม่มี setState คุณจะเปลี่ยน _count ทั้งวันก็ได้ แต่จอไม่อัปเดต — framework ไม่รู้ว่ามีอะไรเปลี่ยน
ทำไมต้องแยก? เพื่อให้ state อยู่รอดข้าม rebuild
หัวข้อที่มีชื่อว่า “ทำไมต้องแยก? เพื่อให้ state อยู่รอดข้าม rebuild”Widget ถูกทิ้งและสร้างใหม่ทุก rebuild จึง ถือ state ที่ต้องอยู่ต่อเนื่องไม่ได้ ส่วน object State ถูกถือโดย element ที่อยู่ต่อเนื่องข้าม rebuild นั่นคือเหตุผลทั้งหมดของการออกแบบเป็นสอง class
flowchart TB w1["Counter widget (rebuild, ทิ้ง)"] -.->|createState ครั้งเดียว| s["_CounterState (อยู่ต่อเนื่องบน element, เก็บ _count)"] w2["Counter widget (instance ใหม่ build ถัดไป)"] -.->|ใช้ State เดิมซ้ำ| s
เมื่อ parent rebuild และสร้าง Counter widget ตัวใหม่ Flutter จะ match widget ตัวใหม่นั้นกับ element ที่มีอยู่แล้วส่ง _CounterState ตัวเดิม กลับไป — _count จึงถูกรักษาไว้ widget ใหม่ แต่ state เดิม (didUpdateWidget ให้ State react กับข้อมูลของ widget ใหม่ได้ ส่วน lifecycle เต็ม ๆ อยู่ในโมดูล State Management)