flutterriverpodstate-managementMahir3 mnt baca

Riverpod Dasar: Provider dan Consumer

Kelola state lintas halaman dengan Riverpod: Provider, FutureProvider, dan perbedaan ref.watch vs ref.read.

Kenapa state management?

setState hanya hidup di satu widget. Begitu data seperti keranjang belanja atau user login dibutuhkan banyak halaman, meneruskan lewat constructor menjadi mimpi buruk. State management memisahkan data dari UI: data tinggal di satu tempat, widget mana pun bisa membaca dan bereaksi saat data berubah.

Setup Riverpod

Tambahkan dependensi dan bungkus aplikasi dengan ProviderScope:

yaml
dependencies:
  flutter_riverpod: ^2.5.0
dart
void main() {
  runApp(const ProviderScope(child: MyApp()));
}

Tanpa ProviderScope di root, semua provider akan error saat dibaca.

Provider paling sederhana

dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Provider untuk nilai yang tidak berubah
final namaTokoProvider = Provider<String>((ref) => 'Warkop Doa Ambu');

// Membaca di widget
class HeaderToko extends ConsumerWidget {
  const HeaderToko({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final nama = ref.watch(namaTokoProvider);
    return Text(nama, style: const TextStyle(fontSize: 20));
  }
}

ConsumerWidget mirip StatelessWidget tapi build-nya menerima WidgetRef. Untuk stateful, ada ConsumerStatefulWidget dengan ConsumerState.

ref.watch vs ref.read

Ini konsep yang wajib dipahami:

  • ref.watch(provider): berlangganan. Widget rebuild otomatis saat nilai provider berubah. Pakai di dalam build.
  • ref.read(provider): baca sekali tanpa berlangganan. Pakai di event handler seperti onPressed.
dart
ElevatedButton(
  onPressed: () {
    // Aksi: pakai read, bukan watch
    final nama = ref.read(namaTokoProvider);
    debugPrint('Toko: $nama');
  },
  child: const Text('Cek'),
)

Memakai watch di dalam onPressed adalah error umum yang menyebabkan rebuild tak perlu atau warning lint.

FutureProvider untuk data async

Untuk data dari API, FutureProvider membungkus Future menjadi AsyncValue yang rapi:

dart
final daftarProdukProvider = FutureProvider<List<Produk>>((ref) {
  return repository.ambilProduk();
});

// Di UI
class Daftar extends ConsumerWidget {
  const Daftar({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncProduk = ref.watch(daftarProdukProvider);
    return asyncProduk.when(
      data: (produk) => ListView.builder(
        itemCount: produk.length,
        itemBuilder: (context, i) => ListTile(title: Text(produk[i].nama)),
      ),
      loading: () => const Center(child: CircularProgressIndicator()),
      error: (e, _) => Center(child: Text('Gagal: $e')),
    );
  }
}

Method .when memaksa kamu menangani tiga state: data, loading, error. Tidak ada lagi lupa menangani error.

Catatan teknis: Provider bersifat global dan lazy: ia baru dibuat saat pertama kali di-watch. Jangan menaruh logika berat di body provider yang jalan di main thread tanpa disadari. Dan ingat, ref.watch hanya boleh dipanggil di dalam build atau body provider lain, tidak di initState atau event handler. Butuh nilai di initState? Pakai ref.read atau pindahkan ke provider.