Riverpod — esensi, kompilasi dependensi di Flutter

Penulis: IT Sectr Diterbitkan: 2026-02-19 Waktu membaca: 7 mnt

Riverpod — manajer state dan dependensi yang dapat dikompilasi untuk Flutter, dibuat oleh Rémi Roussel pada tahun 2021 sebagai penerus Provider. Riverpod memecahkan masalah fundamental Provider: kurangnya pemeriksaan kompilasi, ketergantungan pada BuildContext dan kesulitan dengan ProviderNotFoundException. Menurut data pub.dev, paket ini telah mengumpulkan lebih dari 5 ribu like dan secara aktif menggantikan Provider di proyek-proyek baru.

Poin Utama

  • ProviderRef — objek untuk mengakses provider lain di dalam sebuah provider
  • AsyncValue — pembungkus untuk data asinkron dengan status loading/error/data
  • Notifier — kelas untuk state yang dapat diubah dengan metode perubahan
  • ProviderScope — widget root yang mengelola semua provider
  • Code Generation — anotasi @riverpod untuk pembuatan provider secara otomatis

Apa itu Riverpod?

Riverpod — sebuah pustaka untuk manajemen state dan injeksi dependensi di Flutter yang mengkompilasi deskripsi provider menjadi kode Dart yang aman. Tidak seperti Provider, provider Riverpod tidak terikat pada BuildContext: mereka dibuat secara global atau di ProviderScope dan dapat diakses dari mana saja. Kompilator memeriksa tipe, dependensi, dan integritas graf provider pada tahap build, menghilangkan kesalahan runtime seperti ProviderNotFoundException.

Riverpod menggunakan model override untuk pengujian: setiap provider dapat ditimpa melalui ProviderScope.overrideWith tanpa perlu membuat subkelas atau mem-mock antarmuka. Ini membuat pengujian menjadi terisolasi: setiap pengujian mendapatkan salinan graf dependensinya sendiri yang sepenuhnya terkontrol.

Menurut Flutter Community Survey 2025, Riverpod menempati posisi ketiga dalam popularitas setelah Provider dan BLoC. Pada saat yang sama, Riverpod adalah paket dengan pertumbuhan tercepat: +120% instalasi pada tahun 2024. Alasan utama: keamanan kompilasi, tidak adanya ProviderNotFoundException, dukungan asinkronisitas bawaan melalui AsyncValue.

Tipe-tipe Provider

Riverpod menyediakan 8 tipe provider, masing-masing untuk skenario tertentu: Provider (konstanta/layanan), StateProvider (state primitif), StateNotifierProvider (logika kompleks dengan StateNotifier), ChangeNotifierProvider (untuk migrasi dari Provider), FutureProvider (data asinkron, sekali), StreamProvider (aliran reaktif), NotifierProvider (API baru, Flutter 3.10+) dan AsyncNotifierProvider (Notifier asinkron).

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 — objek yang diteruskan ke setiap provider untuk mengakses provider lain. ref.watch — berlangganan perubahan, ref.read — pembacaan sekali, ref.invalidate — mereset cache. ProviderRef menggantikan BuildContext dari Provider: provider mana pun dapat membaca provider lain tanpa akses ke pohon widget. Ini memungkinkan pembangunan graf dependensi di luar lapisan UI.

ProviderScope — widget root wajib untuk menjalankan Riverpod. ProviderScope menyimpan semua provider, mengelola siklus hidupnya, dan menyimpan cache nilai. Tanpa ProviderScope, aplikasi akan crash dengan ProviderNotFoundException. ProviderScope dapat bersarang — scope bersarang menimpa provider induk, yang digunakan untuk pengujian dan isolasi fitur.

AsyncValue dan bekerja dengan asinkronisitas

AsyncValue — kelas sealed Riverpod untuk merepresentasikan state asinkron. AsyncValue memiliki tiga varian: AsyncData (data berhasil), AsyncError (kesalahan), AsyncLoading (memuat). Alih-alih beralih secara manual antara loading/error/data, setiap FutureProvider atau StreamProvider secara otomatis mengembalikan AsyncValue, dan widget menangani ketiga status melalui 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 — metode untuk pencocokan pola ketiga status. Kompilator memeriksa bahwa ketiga kasus telah ditangani — jika loading atau error dilupakan, kode tidak akan dikompilasi. AsyncValue.whenData — hanya untuk data (jika loading/error tidak diperlukan). AsyncValue.guard — pembungkus try-catch untuk mengonversi pengecualian menjadi AsyncError. keepAlive — bendera yang mencegah penghancuran cache provider saat keluar dari cakupan visibilitas.

Code Generation dan @riverpod

Pembuatan kode — fitur utama Riverpod 2.0+. Anotasi @riverpod di atas fungsi secara otomatis menghasilkan provider dengan tipe yang benar, dukungan refactoring, dan penyelesaian otomatis. Pembuatan kode menggunakan riverpod_generator dan build_runner. Pengembang menulis fungsi murni, dan sisanya — tipe, kelas, konstruktor factory — dihasilkan secara otomatis.

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

// Dihasilkan: final helloWorldProvider = Provider((ref) => 'Hello World');

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

Notifier — API baru untuk state yang dapat diubah dengan pembuatan kode. Notifier adalah kelas dengan metode build() dan metode untuk mengubah state. Tidak seperti StateNotifier, Notifier tidak memerlukan kelas state terpisah dan memberikan akses langsung ke state melalui getter/setter. Riverpod secara otomatis menghasilkan NotifierProvider untuk setiap kelas Notifier dengan anotasi @riverpod.

build_runner: pembuatan kode dijalankan dengan perintah dart run build_runner build. File yang dihasilkan memiliki akhiran .g.dart dan diimpor ke kode sumber. Saat mengubah anotasi atau tipe provider, pembuatan kode harus dijalankan ulang. Riverpod 2.x merekomendasikan pembuatan kode untuk semua proyek baru — pembuatan provider secara manual mulai usang.

Riverpod vs Provider

Perbedaan utama Riverpod dari Provider: independensi dari BuildContext, keamanan kompilasi, kerja bawaan dengan asinkronisitas, caching otomatis dan pengujian melalui override. Provider memerlukan BuildContext untuk mengakses state (context.watch, context.read), Riverpod menggunakan WidgetRef dan provider yang dideklarasikan secara global.

KarakteristikProviderRiverpod
Ketergantungan pada BuildContextYaTidak
Pemeriksaan kompilasiTidakYa (melalui @riverpod)
ProviderNotFoundExceptionRuntimeTidak mungkin
AsinkronisitasManualAsyncValue (bawaan)
PengujianPembungkus di ProviderProviderScope.overrideWith
CachingTidakOtomatis + keepAlive

Migrasi dari Provider: Riverpod mendukung ChangeNotifierProvider.adaptive untuk menggunakan ChangeNotifier yang ada tanpa menulis ulang. Migrasi bertahap: pertama fitur baru ditulis dengan Riverpod, kemudian Provider lama diganti dengan provider Riverpod melalui adaptor. Kedua paket dapat hidup berdampingan dalam satu proyek, memungkinkan migrasi tanpa menghentikan pengembangan.

Menguji Riverpod

Menguji Riverpod didasarkan pada ProviderScope.overrideWith. Setiap provider ditimpa di dalam ProviderScope pengujian tanpa mock dan container DI. ProviderContainer — lingkungan terisolasi untuk pengujian tanpa Flutter (Dart murni), memungkinkan pengujian provider tanpa merender widget.

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 — tanpa Flutter. Gunakan ProviderContainer untuk pengujian unit provider tanpa widget. overrideWithValue — mengganti provider dengan nilai tertentu. overrideWith — mengganti dengan pabrik provider (untuk mem-mock layanan). autodispose — dalam pengujian, periksa apakah provider dihancurkan saat keluar dari cakupan visibilitas, dengan container.dispose().

Pertanyaan yang Sering Diajukan

Apa perbedaan Riverpod dengan BLoC?

Riverpod — pustaka manajemen state dengan provider global, AsyncValue dan pembuatan kode. BLoC — pola arsitektur dengan Event → Stream → State. Riverpod lebih mudah dipelajari dan memiliki DX yang lebih baik melalui anotasi @riverpod. BLoC memberikan isolasi ketat logika bisnis dan pelacakan Event melalui BlocObserver. Pilihan tergantung pada paradigma proyek: Riverpod lebih dekat ke Provider, BLoC — ke aliran reaktif.

Apa itu autodispose di Riverpod?

Autodispose — mekanisme penghancuran otomatis provider ketika tidak ada yang berlangganan. Secara default, semua provider Riverpod adalah autodispose: saat widget keluar dari pohon, provider dihapus dari memori. keepAlive — bendera yang menonaktifkan autodispose untuk provider yang harus selalu hidup (klien API, repositori, pengaturan). Ini mencegah kebocoran memori — provider yang tidak digunakan dihancurkan secara otomatis.

Bagaimana cara kerja ref.invalidate?

ref.invalidate — metode yang memaksa mereset cache provider. Setelah invalidate, provider dibuat ulang pada pembacaan berikutnya: FutureProvider menjalankan ulang fungsi async, StreamProvider berlangganan ulang ke aliran. Gunakan invalidate untuk pembaruan data paksa (pull-to-refresh, pergantian pengguna). ref.refresh — kombinasi invalidate + pembacaan: mereset dan langsung membaca nilai baru dalam satu operasi.

Bisakah Riverpod digunakan tanpa pembuatan kode?

Ya. Riverpod 1.x hanya bekerja tanpa pembuatan kode — provider dibuat secara manual melalui Provider(), StateNotifierProvider(), FutureProvider() dll. Riverpod 2.x mendukung kedua pendekatan. Tanpa pembuatan kode ada lebih banyak boilerplate, tetapi tidak ada ketergantungan pada build_runner dan dart run build_runner build. Untuk proyek kecil (hingga 30 provider), pembuatan manual dapat dibenarkan, untuk proyek besar, pembuatan kode wajib.

Apa itu Family provider?

Family — pengubah provider yang menerima parameter eksternal. Misalnya, userProvider(123) — provider yang memuat pengguna dengan ID 123. Family provider menyimpan cache hasil untuk setiap parameter unik secara terpisah. Gunakan Family untuk daftar elemen di mana setiap elemen dimuat berdasarkan ID. Pengubah Family tersedia untuk semua tipe provider: Provider.family, FutureProvider.family, StreamProvider.family.

Ringkasan

  • Riverpod — manajer state yang dapat dikompilasi, penerus Provider tanpa ProviderNotFoundException
  • ProviderRef — pengganti BuildContext untuk mengakses provider di dalam provider lain
  • AsyncValue — kelas sealed dengan status loading/error/data untuk data asinkron
  • Pembuatan kode @riverpod — inferensi tipe dan pabrik provider secara otomatis
  • ProviderScope.overrideWith — pengujian terisolasi tanpa mock dan container DI
  • Family — provider berparameter dengan caching individual
  • autodispose dan keepAlive — manajemen siklus hidup provider secara otomatis

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga