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

Stateless vs Stateful

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 ส่วนใหญ่

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 ไม่รู้ว่ามีอะไรเปลี่ยน

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
widget ถูกสร้างใหม่ ส่วน State อยู่ต่อเนื่องบน element

เมื่อ parent rebuild และสร้าง Counter widget ตัวใหม่ Flutter จะ match widget ตัวใหม่นั้นกับ element ที่มีอยู่แล้วส่ง _CounterState ตัวเดิม กลับไป — _count จึงถูกรักษาไว้ widget ใหม่ แต่ state เดิม (didUpdateWidget ให้ State react กับข้อมูลของ widget ใหม่ได้ ส่วน lifecycle เต็ม ๆ อยู่ในโมดูล State Management)

mutable state ของ StatefulWidget อยู่ที่ไหน?
การเรียก setState ทำอะไร?
ทำไม State ถึงเป็น object แยกจาก widget?
ควรเลือก StatelessWidget เมื่อไร?