State Management

การจัดการข้อมูลที่เปลี่ยนแปลงได้ (state) ใน Flutter app — ตั้งแต่วิธีง่ายๆ จนถึง package เต็มรูปแบบ

ระดับของ State Management

1. Local State — setState()

state อยู่ใน widget ตัวเดียว ไม่ต้อง share:

class _CounterState extends State<Counter> {
  var count = 0;
 
  void increment() {
    setState(() { count++; });  // บอก Flutter ให้ rebuild
  }
}

เหมาะกับ: UI state ง่ายๆ เช่น toggle, counter, form input

2. Lifting State Up — ย้าย state ขึ้น parent

เมื่อ 2+ widget ต้อง share state → ย้าย state ไปไว้ที่ parent widget ร่วม:

// Parent เก็บ state + ส่ง function ลงไป
class _QuizState extends State<Quiz> {
  var activeScreen = 'start-screen';
 
  void switchScreen() {
    setState(() { activeScreen = 'questions-screen'; });
  }
 
  @override
  Widget build(BuildContext context) {
    return activeScreen == 'start-screen'
        ? StartScreen(switchScreen)        // ส่ง function ลงไป
        : QuestionsScreen(chooseAnswer);
  }
}

เหมาะกับ: 2-3 widget share state, แอปขนาดเล็ก

3. Riverpod — State Management Package

เมื่อแอปซับซ้อน การส่ง state ผ่าน constructor หลายชั้น (prop drilling) ยุ่งยากเกินไป:

// สร้าง Provider กลาง
final favoritesProvider = StateNotifierProvider<FavNotifier, List<Meal>>(
  (ref) => FavNotifier(),
);
 
// widget ไหนก็เข้าถึงได้ตรงๆ
class MyScreen extends ConsumerWidget {
  Widget build(BuildContext context, WidgetRef ref) {
    final favs = ref.watch(favoritesProvider);
    return ListView(...);
  }
}

เหมาะกับ: แอปขนาดกลาง-ใหญ่, หลาย widget share state

เลือกแบบไหนดี

state ใช้ใน widget เดียว?
  ├── ใช่ → setState() (local state)
  └── ไม่ → share กี่ widget?
        ├── 2-3 ตัว, ใกล้กัน → Lifting State Up
        └── หลายตัว, กระจาย → Riverpod

Conditional Rendering — แสดง UI ตามเงื่อนไข

pattern ที่ใช้บ่อยร่วมกับ state management:

// Ternary expression (แนะนำ)
child: isStartScreen
    ? StartScreen(switchScreen)
    : QuestionsScreen(onSelectAnswer: chooseAnswer),
 
// if-else ใน build()
Widget screenWidget;
if (activeScreen == 'start') {
  screenWidget = StartScreen(switchScreen);
} else if (activeScreen == 'questions') {
  screenWidget = QuestionsScreen(chooseAnswer);
} else {
  screenWidget = ResultsScreen(restartQuiz);
}

Key Points

  • เริ่มจาก setState() → ย้ายไป Lifting State Up → ย้ายไป Riverpod เมื่อซับซ้อนขึ้น
  • setState() บอก Flutter ให้ rebuild — ถ้าลืมใส่ UI จะไม่อัปเดต
  • Lifting State Up = ย้าย state ขึ้น parent + ส่ง function ลงมา
  • Riverpod แก้ปัญหา prop drilling — widget เข้าถึง state ได้ตรงจากไหนก็ได้
  • Flutter — framework ที่ต้องจัดการ state
  • Riverpod — state management package หลักที่คอร์สสอน
  • Lifting State Up — pattern พื้นฐาน
  • Widgets — StatefulWidget, ConsumerWidget
  • Three Trees — เข้าใจว่า setState ทำงานข้างในยังไง