Riverpod

State Management package สำหรับ Flutter — แก้ปัญหาการส่ง state ผ่าน constructor ข้าม widget หลายชั้น

ปัญหาที่แก้

เมื่อแอปซับซ้อนขึ้น การส่ง state ผ่าน constructor ทีละชั้น (prop drilling) จะยุ่งยากมาก:

Quiz (เก็บ state)
  → StartScreen (รับ function)
    → QuestionsScreen (รับ function)
      → AnswerButton (รับ function)
        → ...ส่งต่อไปเรื่อยๆ

Riverpod ให้ widget ไหนก็ได้เข้าถึง state ได้ตรงๆ โดยไม่ต้องส่งผ่าน constructor

Setup

# pubspec.yaml
dependencies:
  flutter_riverpod: ^2.0.0
// main.dart — ห่อ app ด้วย ProviderScope
void main() {
  runApp(const ProviderScope(child: MyApp()));
}

Provider Types

Provider — ค่าที่ไม่เปลี่ยน

final mealsProvider = Provider((ref) {
  return dummyMeals;  // ค่าคงที่
});

StateNotifierProvider — ค่าที่เปลี่ยนได้ + มี logic

// 1. สร้าง Notifier class
class FavoriteMealsNotifier extends StateNotifier<List<Meal>> {
  FavoriteMealsNotifier() : super([]);  // เริ่มต้นว่าง
 
  bool toggleMealFavoriteStatus(Meal meal) {
    final isExisting = state.contains(meal);
    if (isExisting) {
      state = state.where((m) => m.id != meal.id).toList();
      return false;
    } else {
      state = [...state, meal];  // สร้าง list ใหม่ ไม่แก้ตัวเดิม
      return true;
    }
  }
}
 
// 2. สร้าง Provider
final favoriteMealsProvider =
    StateNotifierProvider<FavoriteMealsNotifier, List<Meal>>(
  (ref) => FavoriteMealsNotifier(),
);

สำคัญ: ต้อง assign state ใหม่ทั้งก้อน (ไม่ใช่แก้ list เดิม) → Riverpod ถึงจะรู้ว่า state เปลี่ยน

ใช้ใน Widget

เปลี่ยน StatelessWidgetConsumerWidget หรือ StatefulWidgetConsumerStatefulWidget:

class MealsScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // อ่านค่า + rebuild อัตโนมัติเมื่อ state เปลี่ยน
    final favMeals = ref.watch(favoriteMealsProvider);
 
    // เรียก action (ไม่ rebuild)
    ref.read(favoriteMealsProvider.notifier).toggleMealFavoriteStatus(meal);
 
    return ListView(
      children: favMeals.map((meal) => MealItem(meal: meal)).toList(),
    );
  }
}

ref.watch vs ref.read

ref.watch(provider)ref.read(provider)
ทำอะไรอ่านค่า + สมัครรับ updatesอ่านค่าครั้งเดียว
rebuildใช่ — rebuild เมื่อ state เปลี่ยนไม่
ใช้ตรงไหนใน build()ใน event handler (onPressed ฯลฯ)

เปรียบเทียบกับ Lifting State Up

Lifting State UpRiverpod
เหมาะกับแอปเล็ก, 2-3 widget share stateแอปใหญ่, หลาย widget share state
วิธีแชร์ส่ง function/data ผ่าน constructorProvider กลาง, widget เข้าถึงตรง
ข้อเสียprop drillingต้องเรียนรู้เพิ่ม
setupไม่ต้องลง packageลง flutter_riverpod + ProviderScope

Key Points

  • ProviderScope ต้องห่อทั้งแอปใน main()
  • ConsumerWidget แทน StatelessWidget เมื่อต้องใช้ provider
  • ref.watch() ใน build() / ref.read() ใน event handler
  • state ต้อง assign ใหม่ทั้งก้อน (immutable pattern)
  • Provider types: Provider (ค่าคงที่), StateNotifierProvider (ค่าเปลี่ยนได้)
  • Flutter — framework ที่ใช้ Riverpod
  • State Management — concept ภาพรวม
  • Lifting State Up — วิธี share state แบบง่าย (ก่อนจะใช้ Riverpod)
  • Widgets — ConsumerWidget เป็น widget type พิเศษ