riverpodstateproviderMenengah5 mnt baca

Riverpod: Provider Dasar

State management modern: setup, Provider, dan ConsumerWidget.

Masalah yang Diselesaikan Riverpod

Di aplikasi kecil, oper data lewat constructor masih oke. Tapi saat membesar kamu kena "prop drilling": data dioper dari kakek ke bapak ke anak ke cucu, padahal yang butuh cuma si cucu. Setiap widget di tengah meneruskan parameter yang tak dipakai; ubah satu nama, semuanya ikut berubah.

Riverpod menyelesaikan ini dengan menyediakan nilai secara global tapi aman: widget mana pun bisa "menonton" nilai tanpa perantara, dan yang rebuild hanya widget yang menonton, bukan seluruh tree.

Kenapa Riverpod, bukan package provider biasa? Tiga alasan: provider dideklarasikan di luar widget (bebas error "Provider not found"), 100% aman saat compile (salah tipe ketahuan sebelum jalan), dan mudah di-test karena bisa di-override.

Setup

bash
flutter pub add flutter_riverpod

Bungkus seluruh app dengan ProviderScope, sekali saja di main:

dart
void main() {
  runApp(const ProviderScope(child: MyApp()));
}

Lupa langkah ini adalah error nomor satu pemula: crash "ProviderScope not found" saat widget pertama yang memakai provider di-build.

Contoh 1: Provider Nilai Sederhana

dart
// Deklarasikan di top-level file, di luar widget:
final namaTokoProvider = Provider<String>((ref) => 'Warkop Senja');
final diskonProvider = Provider<double>((ref) => 0.1);

class Sapaan extends ConsumerWidget {
  const Sapaan({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final nama = ref.watch(namaTokoProvider);
    final diskon = ref.watch(diskonProvider);
    return Text('Selamat datang di $nama (diskon ${(diskon * 100).toInt()}%)');
  }
}

ConsumerWidget sama seperti StatelessWidget tapi method build-nya menerima WidgetRef tambahan. ref.watch membuat widget otomatis rebuild kalau nilai provider berubah. Provider biasa bersifat immutable: nilainya tetap selama app berjalan, cocok untuk konfigurasi, repository, atau service.

Contoh 2: Provider untuk Service (Pola Dunia Nyata)

Pola paling berguna dalam praktik: daftarkan class service atau API client sebagai provider, supaya gampang diganti saat testing:

dart
final apiClientProvider = Provider<ApiClient>((ref) {
  return ApiClient(baseUrl: 'https://api.warkop.com');
});

class DaftarMenu extends ConsumerWidget {
  const DaftarMenu({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final api = ref.watch(apiClientProvider);
    // pakai api.ambilMenu() di FutureBuilder / FutureProvider ...
    return const Placeholder();
  }
}

// Saat testing, override dengan mock:
ProviderScope(
  overrides: [
    apiClientProvider.overrideWithValue(ApiClientPalsu()),
  ],
  child: const DaftarMenu(),
)

Dengan overrideWithValue, widget-mu tak tahu bedanya API asli dan palsu. Inilah kenapa Riverpod disebut test-friendly.

watch vs read vs listen

  • ref.watch(p): dipakai di dalam build. Widget berlangganan dan rebuild otomatis saat nilai berubah.
  • ref.read(p): dipakai di dalam callback seperti onPressed. Baca sekali, tidak berlangganan, tidak memicu rebuild.
  • ref.listen(p, (prev, next) {...}): dipakai untuk efek samping saat nilai berubah (tampilkan SnackBar, pindah halaman), tanpa rebuild.

Kesalahan klasik: memanggil ref.watch di dalam onPressed. Itu tidak me-rebuild apa-apa dan Riverpod akan protes. Di callback selalu pakai read.

Catatan teknis: Deklarasikan provider sebagai variabel top-level final, jangan di dalam build atau di dalam class widget. Provider yang dibuat di dalam build akan dibuat ulang setiap rebuild dan state-nya hilang begitu saja.

Edge Case

  1. ref.watch di initState itu dilarang. Di initState belum ada build yang bisa di-rebuild. Pakai ref.read, atau pakai ConsumerStatefulWidget dan akses ref di didChangeDependencies.
  2. Provider yang dideklarasikan di dalam fungsi build menghasilkan provider baru setiap rebuild. Selalu taruh di top-level file.
  3. Lupa ProviderScope membuat semua provider crash saat runtime, bukan saat compile. Kalau ada error provider yang misterius, cek main.dart dulu.

Kapan JANGAN Pakai Provider

  • Untuk state yang berubah-ubah (counter, form, tab aktif), Provider biasa tidak bisa karena immutable. Pakai StateProvider atau NotifierProvider (modul berikutnya).
  • Untuk data async (hasil API), jangan bungkus Future mentah di Provider. Pakai FutureProvider yang punya .when() untuk menangani loading, error, dan data dengan rapi.
  • Untuk logika yang cuma dipakai satu widget kecil, setState biasa lebih simpel dan jujur. Riverpod bersinar saat state dipakai lintas banyak widget.

Tantangan

Provider konfigurasi

Buat Provider<String> berisi nama tokomu, lalu tampilkan di AppBar memakai ConsumerWidget + ref.watch.