Notifier dan AsyncNotifier di Riverpod
Buat state yang bisa diubah dengan Notifier dan tangani operasi async dengan AsyncNotifier yang elegan.
Dari nilai statis ke state yang berubah
Provider hanya untuk nilai tetap. Untuk state yang berubah karena aksi user, seperti isi keranjang, pakai Notifier lewat NotifierProvider.
Notifier: state mutable yang terkontrol
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 1. Definisikan Notifier
class Keranjang extends Notifier<List<String>> {
@override
List<String> build() => [];
void tambah(String produk) {
state = [...state, produk];
}
void hapus(String produk) {
state = state.where((p) => p != produk).toList();
}
void kosongkan() => state = [];
}
// 2. Daftarkan providernya
final keranjangProvider =
NotifierProvider<Keranjang, List<String>>(Keranjang.new);
// 3. Pakai di UI
class HalamanKasir extends ConsumerWidget {
const HalamanKasir({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isi = ref.watch(keranjangProvider);
return Scaffold(
appBar: AppBar(title: Text('Keranjang (${isi.length})')),
body: ListView.builder(
itemCount: isi.length,
itemBuilder: (context, i) => ListTile(
title: Text(isi[i]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () =>
ref.read(keranjangProvider.notifier).hapus(isi[i]),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>
ref.read(keranjangProvider.notifier).tambah('Kopi ${isi.length + 1}'),
child: const Icon(Icons.add),
),
);
}
}Pola penting: ref.watch(keranjangProvider) untuk membaca state (rebuild saat berubah), ref.read(keranjangProvider.notifier) untuk memanggil method aksi. Jangan pernah mengubah state langsung dari widget.
State immutable
Perhatikan state = [...state, produk]: kita membuat list baru, bukan state.add(produk). Riverpod mendeteksi perubahan dari pergantian objek state. Mutasi langsung tidak memicu rebuild dan adalah bug yang sulit dilacak.
AsyncNotifier: operasi async yang rapi
Untuk state yang melibatkan loading, seperti login atau simpan ke API:
class Auth extends AsyncNotifier<String?> {
@override
Future<String?> build() async => null;
Future<void> login(String email, String password) async {
state = const AsyncLoading();
state = await AsyncValue.guard(() async {
await Future.delayed(const Duration(seconds: 1));
if (password.length < 6) throw Exception('Password terlalu pendek');
return email;
});
}
void logout() => state = const AsyncData(null);
}
final authProvider = AsyncNotifierProvider<Auth, String?>(Auth.new);AsyncValue.guard menangkap error otomatis menjadi AsyncError, sehingga UI cukup memakai .when seperti biasa. Pola ini menghilangkan try/catch manual yang berantakan di widget.
Catatan teknis: Method
build()di Notifier boleh sinkron atau async (untuk AsyncNotifier). Jangan memanggil method async lain di dalambuildtanpa mekanisme pembatalan, karena build bisa dipanggil ulang dan menimbulkan race condition. Untuk dependensi antar provider, pakairef.watch(providerLain)di dalam build, Riverpod otomatis me-rebuild saat dependensinya berubah.