Riverpod — การจัดการ dependencies แบบคอมไพล์สำหรับ Flutter

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-19 เวลาอ่าน: 7 นาที

Riverpod — ตัวจัดการสถานะและ dependencies แบบคอมไพล์สำหรับ Flutter สร้างโดย Remi Rousselet ในปี 2021 เพื่อสืบทอดต่อจาก Provider Riverpod แก้ปัญหาพื้นฐานของ Provider: ขาดการตรวจสอบในเวลาคอมไพล์ การพึ่งพา BuildContext และความซับซ้อนกับ ProviderNotFoundException ตามข้อมูลจาก pub.dev แพ็คเกจนี้มีมากกว่า 5 พันไลค์และกำลังแทนที่ Provider ในโปรเจกต์ใหม่ ๆ อย่างจริงจัง

ประเด็นสำคัญ

  • ProviderRef — ออบเจกต์สำหรับเข้าถึง providers อื่นภายใน provider
  • AsyncValue — ตัวห่อหุ้มสำหรับข้อมูลแบบอะซิงโครนัสที่มีสถานะ loading/error/data
  • Notifier — คลาสสำหรับสถานะที่เปลี่ยนแปลงได้พร้อมวิธีการเปลี่ยน
  • ProviderScope — วิดเจ็ตรากที่จัดการ providers ทั้งหมด
  • Code Generation — คำอธิบายประกอบ @riverpod สำหรับการสร้าง providers อัตโนมัติ

Riverpod คืออะไร?

Riverpod เป็นไลบรารีการจัดการสถานะและการฉีด dependencies สำหรับ Flutter ที่คอมไพล์คำอธิบาย providers เป็นโค้ด Dart ที่ปลอดภัย แตกต่างจาก Provider ตรงที่ providers ของ Riverpod ไม่ผูกติดกับ BuildContext: พวกมันถูกสร้างขึ้นในระดับสากลหรือใน ProviderScope และเข้าถึงได้จากทุกที่ คอมไพเลอร์ตรวจสอบชนิด dependencies และความสมบูรณ์ของกราฟ providers ในเวลาคอมไพล์ ซึ่งกำจัดข้อผิดพลาดรันไทม์เช่น ProviderNotFoundException

Riverpod ใช้โมเดล override สำหรับการทดสอบ: แต่ละ provider สามารถถูกแทนที่ผ่าน ProviderScope.overrideWith โดยไม่ต้องสร้างคลาสย่อยหรือ mock อินเทอร์เฟซ ซึ่งทำให้การทดสอบถูกแยกออกจากกัน: แต่ละการทดสอบได้รับสำเนาของกราฟ dependencies ของตัวเองที่ถูกควบคุมอย่างสมบูรณ์

ตาม Flutter Community Survey 2025 Riverpod อยู่อันดับสามในด้านความนิยมรองจาก Provider และ BLoC อย่างไรก็ตาม Riverpod เป็นแพ็คเกจที่เติบโตเร็วที่สุด: +120% การติดตั้งในปี 2024 เหตุผลหลัก: ความปลอดภัยในเวลาคอมไพล์ ไม่มี ProviderNotFoundException รองรับอะซิงโครนัสในตัวผ่าน AsyncValue

ประเภทของ Providers

Riverpod มี providers 8 ประเภท แต่ละประเภทสำหรับสถานการณ์เฉพาะ: Provider (ค่าคงที่/บริการ), StateProvider (สถานะพื้นฐาน), StateNotifierProvider (ตรรกะซับซ้อนกับ StateNotifier), ChangeNotifierProvider (สำหรับการย้ายจาก Provider), FutureProvider (ข้อมูลอะซิงโครนัส ครั้งเดียว), StreamProvider (สตรีมเชิงตอบสนอง), NotifierProvider (API ใหม่, Flutter 3.10+) และ AsyncNotifierProvider (Notifier แบบอะซิงโครนัส)

Dart
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
  void decrement() => state--;
}

class CounterScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

ProviderRef — ออบเจกต์ที่ส่งให้แต่ละ provider เพื่อเข้าถึง providers อื่น ref.watch — การติดตามการเปลี่ยนแปลง ref.read — การอ่านครั้งเดียว ref.invalidate — การรีเซ็ตแคช ProviderRef แทนที่ BuildContext จาก Provider: provider ใดๆ สามารถอ่าน providers อื่นได้โดยไม่ต้องเข้าถึงโครงสร้างวิดเจ็ต ซึ่งช่วยให้สร้างกราฟ dependencies นอกเลเยอร์ UI

ProviderScope — วิดเจ็ตรากที่จำเป็นสำหรับการทำงานของ Riverpod ProviderScope จัดเก็บ providers ทั้งหมด จัดการวงจรชีวิตและแคชค่า หากไม่มี ProviderScope แอปจะล่มด้วย ProviderNotFoundException ProviderScope สามารถซ้อนกันได้ — ขอบเขตที่ซ้อนกันจะแทนที่ providers หลัก ซึ่งใช้สำหรับการทดสอบและการแยกคุณสมบัติ

AsyncValue และการทำงานกับอะซิงโครนัส

AsyncValue — คลาส sealed ของ Riverpod สำหรับแสดงสถานะอะซิงโครนัส AsyncValue มีสามรูปแบบ: AsyncData (ข้อมูลสำเร็จ), AsyncError (ข้อผิดพลาด), AsyncLoading (กำลังโหลด) แทนที่จะสลับระหว่าง loading/error/data ด้วยตนเอง แต่ละ FutureProvider หรือ StreamProvider จะคืนค่า AsyncValue โดยอัตโนมัติ และวิดเจ็ตจัดการทั้งสามสถานะผ่าน ref.watch

Dart
final userProvider = FutureProvider((ref) async {
  final api = ref.watch(apiProvider);
  return await api.fetchUser();
});

class UserScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider);
    return userAsync.when(
      data: (user) => UserWidget(user),
      error: (e, _) => ErrorWidget(e.toString()),
      loading: () => CircularProgressIndicator(),
    );
  }
}

AsyncValue.when — เมธอดสำหรับการจับคู่รูปแบบทั้งสามสถานะ คอมไพเลอร์ตรวจสอบว่าทั้งสามกรณีถูกจัดการ — หากคุณลืม loading หรือ error โค้ดจะไม่คอมไพล์ AsyncValue.whenData — สำหรับ data เท่านั้น (หากไม่จำเป็นต้องใช้ loading/error) AsyncValue.guard — ตัวห่อหุ้ม try-catch สำหรับแปลงข้อยกเว้นเป็น AsyncError keepAlive — แฟล็กที่ป้องกันไม่ให้แคช provider ถูกทำลายเมื่อออกจากขอบเขต

การสร้างโค้ดและ @riverpod

การสร้างโค้ด — คุณสมบัติหลักของ Riverpod 2.0+ คำอธิบายประกอบ @riverpod บนฟังก์ชันจะสร้าง provider โดยอัตโนมัติพร้อมชนิดที่ถูกต้อง การสนับสนุนการปรับโครงสร้าง และการเติมข้อความอัตโนมัติ การสร้างโค้ดใช้ riverpod_generator และ build_runner นักพัฒนาเขียนฟังก์ชันบริสุทธิ์ และทุกอย่างอื่น — ชนิด คลาส ตัวสร้างแบบ factory — ถูกสร้างขึ้นโดยอัตโนมัติ

Dart
@riverpod
String helloWorld(HelloWorldRef ref) {
  return 'Hello World';
}

// สร้าง: final helloWorldProvider = Provider((ref) => 'Hello World');

@riverpod
class Counter extends _$Counter {
  int build() => 0;
  void increment() => state++;
}

Notifier — API ใหม่สำหรับสถานะที่เปลี่ยนแปลงได้พร้อมการสร้างโค้ด Notifier เป็นคลาสที่มีเมธอด build() และเมธอดการเปลี่ยนสถานะ แตกต่างจาก StateNotifier ตรงที่ Notifier ไม่ต้องการคลาสสถานะแยกต่างหากและให้การเข้าถึง state โดยตรงผ่าน getter/setter Riverpod สร้าง NotifierProvider โดยอัตโนมัติสำหรับแต่ละคลาส Notifier ที่มีคำอธิบายประกอบ @riverpod

build_runner: การสร้างโค้ดเริ่มต้นด้วยคำสั่ง dart run build_runner build ไฟล์ที่สร้างขึ้นมีส่วนต่อท้าย .g.dart และถูกนำเข้าในซอร์สโค้ด เมื่อคำอธิบายประกอบหรือชนิด provider เปลี่ยนไป จำเป็นต้องรันการสร้างโค้ดอีกครั้ง Riverpod 2.x แนะนำให้ใช้การสร้างโค้ดสำหรับโปรเจกต์ใหม่ทั้งหมด — การสร้าง provider ด้วยตนเองกำลังล้าสมัย

Riverpod vs Provider

ความแตกต่างหลัก ระหว่าง Riverpod และ Provider: ความเป็นอิสระจาก BuildContext ความปลอดภัยในเวลาคอมไพล์ การรองรับอะซิงโครนัสในตัว การแคชอัตโนมัติ และการทดสอบผ่าน override Provider ต้องการ BuildContext เพื่อเข้าถึงสถานะ (context.watch, context.read) ในขณะที่ Riverpod ใช้ WidgetRef และ providers ที่ประกาศในระดับสากล

คุณลักษณะProviderRiverpod
การพึ่งพา BuildContextใช่ไม่
การตรวจสอบในเวลาคอมไพล์ไม่ใช่ (ผ่าน @riverpod)
ProviderNotFoundExceptionรันไทม์เป็นไปไม่ได้
อะซิงโครนัสด้วยตนเองAsyncValue (ในตัว)
การทดสอบตัวห่อหุ้มใน ProviderProviderScope.overrideWith
การแคชไม่อัตโนมัติ + keepAlive

การย้ายจาก Provider: Riverpod รองรับ ChangeNotifierProvider.adaptive สำหรับใช้ ChangeNotifier ที่มีอยู่โดยไม่ต้องเขียนใหม่ การย้ายแบบค่อยเป็นค่อยไป: ขั้นแรกเขียนคุณสมบัติใหม่ด้วย Riverpod จากนั้นแทนที่อินสแตนซ์ Provider เก่าด้วย Riverpod providers ผ่านอะแดปเตอร์ ทั้งสองแพ็คเกจสามารถอยู่ร่วมกันในโปรเจกต์เดียว ทำให้สามารถย้ายได้โดยไม่หยุดการพัฒนา

การทดสอบ Riverpod

การทดสอบ Riverpod สร้างขึ้นบน ProviderScope.overrideWith แต่ละ provider จะถูกแทนที่ภายใน ProviderScope การทดสอบโดยไม่ต้องใช้ mock หรือคอนเทนเนอร์ DI ProviderContainer — สภาพแวดล้อมที่แยกออกมาสำหรับการทดสอบโดยไม่มี Flutter (Dart บริสุทธิ์) ทำให้สามารถทดสอบ providers ได้โดยไม่ต้องเรนเดอร์วิดเจ็ต

Dart
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod/riverpod.dart';

void main() {
  test('Counter increments correctly', () {
    final container = ProviderContainer();
    container.read(counterProvider.notifier).increment();
    expect(container.read(counterProvider), 1);
  });

  testWidgets('UI updates on increment', (tester) async {
    await tester.pumpWidget(
      ProviderScope(
        overrides: [counterProvider.overrideWithValue(5)],
        child: CounterScreen(),
      ),
    );
    expect(find.text('5'), findsOneWidget);
  });
}

ProviderContainer — ไม่มี Flutter ใช้ ProviderContainer สำหรับการทดสอบหน่วยของ providers โดยไม่ต้องใช้วิดเจ็ต overrideWithValue — แทนที่ provider ด้วยค่าเฉพาะ overrideWith — แทนที่ด้วยโรงงาน provider (สำหรับการ mock บริการ) autodispose — ในการทดสอบ ตรวจสอบว่า provider ถูกทำลายเมื่อออกจากขอบเขตโดยใช้ container.dispose()

คำถามที่พบบ่อย

Riverpod แตกต่างจาก BLoC อย่างไร?

Riverpod เป็นไลบรารีการจัดการสถานะที่มี providers ระดับสากล AsyncValue และการสร้างโค้ด BLoC เป็นรูปแบบสถาปัตยกรรมที่มี Event → Stream → State Riverpod เรียนรู้ง่ายกว่าและมี DX ที่ดีกว่าผ่านคำอธิบายประกอบ @riverpod BLoC ให้การแยกตรรกะทางธุรกิจที่เข้มงวดและการติดตาม Event ผ่าน BlocObserver การเลือกขึ้นอยู่กับกระบวนทัศน์ของโปรเจกต์: Riverpod ใกล้เคียงกับ Provider มากกว่า BLoC — ใกล้เคียงกับสตรีมเชิงตอบสนอง

autodispose ใน Riverpod คืออะไร?

Autodispose เป็นกลไกในการทำลาย provider โดยอัตโนมัติเมื่อไม่มีใครติดตามอยู่ โดยค่าเริ่มต้น providers ทั้งหมดของ Riverpod จะ autodispose: เมื่อวิดเจ็ตออกจากโครงสร้าง provider จะถูกลบออกจากหน่วยความจำ keepAlive — แฟล็กที่ปิดการใช้งาน autodispose สำหรับ providers ที่ควรมีชีวิตอยู่เสมอ (API clients, repositories, การตั้งค่า) ซึ่งป้องกันการรั่วไหลของหน่วยความจำ — providers ที่ไม่ได้ใช้จะถูกทำลายโดยอัตโนมัติ

ref.invalidate ทำงานอย่างไร?

ref.invalidate — เมธอดที่บังคับรีเซ็ตแคชของ provider หลังจาก invalidate provider จะถูกสร้างใหม่ในการอ่านครั้งถัดไป: FutureProvider ดำเนินการฟังก์ชันอะซิงโครนัสอีกครั้ง StreamProvider สมัครรับสตรีมอีกครั้ง ใช้ invalidate เพื่อบังคับรีเฟรชข้อมูล (pull-to-refresh, การเปลี่ยนผู้ใช้) ref.refresh — การรวมกันของ invalidate + การอ่าน: รีเซ็ตและอ่านค่าใหม่ทันทีในการดำเนินการเดียว

สามารถใช้ Riverpod โดยไม่ต้องสร้างโค้ดได้หรือไม่?

ได้ Riverpod 1.x ทำงานโดยไม่ต้องสร้างโค้ดเท่านั้น — providers ถูกสร้างด้วยตนเองโดยใช้ Provider(), StateNotifierProvider(), FutureProvider() ฯลฯ Riverpod 2.x รองรับทั้งสองวิธี หากไม่มีการสร้างโค้ดจะมี boilerplate มากขึ้น แต่ไม่มีการพึ่งพา build_runner และ dart run build_runner build สำหรับโปรเจกต์ขนาดเล็ก (สูงสุด 30 providers) การสร้างด้วยตนเองเป็นสิ่งที่สมเหตุสมผล สำหรับโปรเจกต์ขนาดใหญ่ การสร้างโค้ดเป็นสิ่งจำเป็น

Family providers คืออะไร?

Family — ตัวปรับแต่ง provider ที่ยอมรับพารามิเตอร์ภายนอก ตัวอย่างเช่น userProvider(123) — provider ที่โหลดผู้ใช้ที่มี ID 123 Family providers แคชผลลัพธ์สำหรับแต่ละพารามิเตอร์ที่ไม่ซ้ำกันแยกต่างหาก ใช้ Family สำหรับรายการของรายการที่แต่ละรายการถูกโหลดโดย ID ตัวปรับแต่ง Family พร้อมใช้งานสำหรับ providers ทุกประเภท: Provider.family, FutureProvider.family, StreamProvider.family

สรุป

  • Riverpod — ตัวจัดการสถานะแบบคอมไพล์ ผู้สืบทอดของ Provider ที่ไม่มี ProviderNotFoundException
  • ProviderRef — ตัวแทนของ BuildContext สำหรับเข้าถึง providers ภายใน providers อื่น
  • AsyncValue — คลาส sealed ที่มีสถานะ loading/error/data สำหรับข้อมูลอะซิงโครนัส
  • การสร้างโค้ด @riverpod — การอนุมานชนิดอัตโนมัติและโรงงาน providers
  • ProviderScope.overrideWith — การทดสอบแบบแยกโดยไม่ต้องใช้ mock และคอนเทนเนอร์ DI
  • Family — providers แบบมีพารามิเตอร์พร้อมการแคชเฉพาะราย
  • autodispose และ keepAlive — การจัดการวงจรชีวิต providers อัตโนมัติ

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม