🌊 Riverpod
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Riverpod là phiên bản cải tiến của Provider, khắc phục nhiều hạn chế:
- Compile-time safety
- Không cần BuildContext
- Testable
- Có nhiều loại providers
dependencies: flutter_riverpod: ^2.4.02. Setup cơ bản
Phần tiêu đề “2. Setup cơ bản”Wrap app với ProviderScope
Phần tiêu đề “Wrap app với ProviderScope”void main() { runApp( ProviderScope( child: MyApp(), ), );}Tạo Provider
Phần tiêu đề “Tạo Provider”// State đơn giảnfinal counterProvider = StateProvider<int>((ref) => 0);
// State từ asyncfinal userProvider = FutureProvider<User>((ref) async { return await api.fetchUser();});Consume
Phần tiêu đề “Consume”class CounterPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider);
return Text('Count: $count'); }}3. Các loại Provider
Phần tiêu đề “3. Các loại Provider”| Provider | Mục đích |
|---|---|
Provider |
Giá trị read-only |
StateProvider |
State primitive đơn giản |
StateNotifierProvider |
State phức tạp với logic |
FutureProvider |
Async data |
StreamProvider |
Stream data |
ChangeNotifierProvider |
Migrate từ Provider |
4. Provider - Read-only
Phần tiêu đề “4. Provider - Read-only”final greetingProvider = Provider<String>((ref) { return 'Hello, World!';});
// Sử dụngclass MyWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final greeting = ref.watch(greetingProvider); return Text(greeting); }}5. StateProvider - State đơn giản
Phần tiêu đề “5. StateProvider - State đơn giản”final counterProvider = StateProvider<int>((ref) => 0);
class CounterPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider);
return Column( children: [ Text('$count'), ElevatedButton( onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('+'), ), ], ); }}6. StateNotifier - State phức tạp
Phần tiêu đề “6. StateNotifier - State phức tạp”Định nghĩa
Phần tiêu đề “Định nghĩa”class Todo { final String id, title; final bool completed; Todo({required this.id, required this.title, this.completed = false});
Todo copyWith({String? title, bool? completed}) { return Todo( id: id, title: title ?? this.title, completed: completed ?? this.completed, ); }}
class TodoNotifier extends StateNotifier<List<Todo>> { TodoNotifier() : super([]);
void add(String title) { state = [...state, Todo(id: DateTime.now().toString(), title: title)]; }
void toggle(String id) { state = [ for (final todo in state) if (todo.id == id) todo.copyWith(completed: !todo.completed) else todo, ]; }
void remove(String id) { state = state.where((t) => t.id != id).toList(); }}
final todoProvider = StateNotifierProvider<TodoNotifier, List<Todo>>((ref) { return TodoNotifier();});Sử dụng
Phần tiêu đề “Sử dụng”class TodoPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final todos = ref.watch(todoProvider);
return ListView.builder( itemCount: todos.length, itemBuilder: (context, index) { final todo = todos[index]; return ListTile( title: Text(todo.title), leading: Checkbox( value: todo.completed, onChanged: (_) => ref.read(todoProvider.notifier).toggle(todo.id), ), ); }, ); }}7. FutureProvider - Async data
Phần tiêu đề “7. FutureProvider - Async data”final userProvider = FutureProvider<User>((ref) async { return await api.fetchCurrentUser();});
class ProfilePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final userAsync = ref.watch(userProvider);
return userAsync.when( data: (user) => Text('Hello, ${user.name}'), loading: () => CircularProgressIndicator(), error: (err, stack) => Text('Error: $err'), ); }}8. ref.watch vs ref.read
Phần tiêu đề “8. ref.watch vs ref.read”| Method | Rebuild | Khi nào dùng |
|---|---|---|
ref.watch() |
Có | Trong build() |
ref.read() |
Không | Trong callbacks |
@overrideWidget build(BuildContext context, WidgetRef ref) { // watch: rebuild khi provider thay đổi final count = ref.watch(counterProvider);
return ElevatedButton( // read: không cần rebuild onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('$count'), );}9. Provider với dependencies
Phần tiêu đề “9. Provider với dependencies”final userIdProvider = StateProvider<int>((ref) => 1);
final userProvider = FutureProvider<User>((ref) async { final userId = ref.watch(userIdProvider); // Phụ thuộc vào userId return await api.fetchUser(userId);});// Khi userIdProvider thay đổi, userProvider tự động refresh10. Best Practices
Phần tiêu đề “10. Best Practices”- Khai báo providers ở global scope - top-level
- Dùng ref.watch trong build - ref.read trong callbacks
- Ưu tiên StateNotifier cho logic phức tạp
- Dùng .family cho parameterized providers
// Family providerfinal userProvider = FutureProvider.family<User, int>((ref, userId) async { return await api.fetchUser(userId);});
// Sử dụngref.watch(userProvider(123));📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Loại | Khi nào dùng |
|---|---|
Provider |
Constant, computed values |
StateProvider |
Primitive state (int, bool, string) |
StateNotifierProvider |
Complex state với logic |
FutureProvider |
Async one-time data |
StreamProvider |
Reactive data streams |
| Method | Mục đích |
|---|---|
ref.watch |
Lắng nghe và rebuild |
ref.read |
Đọc một lần |
ref.listen |
Side effects |