https://www.bilibili.com/video/BV1jj42197c8/
原文 https://ducafecat.com/blog/flutter-todo-list-with-riverpod-guide-02
学习如何使用Riverpod在Flutter中构建一个功能完整的Todo列表应用。通过Consumer组件、ConsumerStatefulWidget类、ref.read方法和provider build重写,了解Riverpod的状态管理和更新状态机制。
https://pub.dev/packages/riverpod
https://flutter.ducafecat.com/
lib/entity/todo.dart
class TodoEntity { final int? id; final String? title; final String? description; final bool? completed; TodoEntity({this.id, this.title, this.description, this.completed}); Map<String, dynamic> toMap() { return { 'id': id, 'title': title, 'description': description, 'completed': completed, }; } static TodoEntity fromMap(Map<String, dynamic> map) { return TodoEntity( id: map['id'], title: map['title'], description: map['description'], completed: map['completed'], ); } }
lib/provider/todo_list.dart
定义 TodoList provider 类,todo 数据集合
part 'todo_list.g.dart'; class TodoList extends _$TodoList { static List<TodoEntity> items = []; Future<List<TodoEntity>> build() async { return items; } ...
实现新增 add 功能
Future<void> addTodo(TodoEntity todo) async { // await http.post( // Uri.https('your_api.com', '/todos'), // // 我们序列化 Todo 对象并将其 POST 到服务器。 // headers: {'Content-Type': 'application/json'}, // body: jsonEncode(todo.toJson()), // ); // 延迟 1 秒 await Future.delayed(const Duration(seconds: 1)); // 我们将新的 Todo 添加到 items 列表中。 items.add(todo); }
lib/pages/todo/widgets/bar.dart
TodoBarWidget 类
class TodoBarWidget extends StatelessWidget { const TodoBarWidget({super.key});
Consumer 方式实现关联 riverpod
Widget _buildView() { // Consumer 提供监听功能的小部件 return Consumer(builder: ( BuildContext context, WidgetRef ref, Widget? widget, ) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () { // 事件处理用 read 方式读取 provider ref.read(todoListProvider.notifier).addTodo( TodoEntity( description: 'This is a new todo', ), ); }, child: const Text('Add todo'), ), ], ); }); }
注意使用 ref.read,虽然 ref.watch 也能用
Widget build(BuildContext context) { return _buildView(); }
lib/provider/todo_list.dart
直接设置新状态值
Future<void> addTodo(TodoEntity todo) async { ... // 直接设置 state 值 state = AsyncData(items); }
将本地缓存标记为脏
Future<void> addTodo(TodoEntity todo) async { ... // 将本地缓存标记为脏 ref.invalidateSelf(); // 重新构建 TodoList, 呼叫 build 方法 await future; }
lib/pages/todo/widgets/list.dart
TodoListWidget 类 继承 ConsumerWidget
class TodoListWidget extends ConsumerWidget { const TodoListWidget({super.key});
监听 todo 列表
Widget _buildList(ref) { var todos = ref.watch(todoListProvider); return switch (todos) { AsyncData<List<TodoEntity>>(:final value) => ListView.builder( itemCount: value.length, itemBuilder: (context, index) { return ListTile( title: Text(value[index].description ?? ""), subtitle: Text((value[index].completed ?? false) ? "yes" : "no"), ); }, ), AsyncError() => const Text('Oops, something unexpected happened'), _ => const CircularProgressIndicator(), }; }
Widget build(BuildContext context, WidgetRef ref) { return _buildList(ref); }
build 回调会有一个 ref 对象,直接传入 _buildList 使用
lib/pages/todo/index.dart
TodoPage 类,使用 StatelessWidget 就行
class TodoPage extends StatelessWidget { const TodoPage({super.key});
界面 build
Widget _buildView(BuildContext context) { return const Center( child: Column( children: <Widget>[ // StatelessWidget 的组件 TodoBarWidget(), // StatefulWidget 的组件 // TodoBarStfWidget(), // ConsumerWidget 的列表 Expanded( child: TodoListWidget(), ), ], ), ); }
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Todo List')), body: _buildView(context), ); }
lib/pages/index.dart
Widget _buildView(BuildContext context) { return Center( ... _buildBtn(context, '03 TODO 列表', const TodoPage()), ], ), ); }
编写 TodoBarStfWidget 继承 ConsumerStatefulWidget
可以用插件 Flutter Riverpod Snippets 简化输入代码块
https://marketplace.visualstudio.com/items?itemName=robert-brunhage.flutter-riverpod-snippets
class TodoBarStfWidget extends ConsumerStatefulWidget { const TodoBarStfWidget({super.key}); ConsumerState<ConsumerStatefulWidget> createState() => _TodoBarStfWidgetState(); } class _TodoBarStfWidgetState extends ConsumerState<TodoBarStfWidget> {
定义 Future 状态对象监听变化
// 待处理的 addTodo 操作。如果没有待处理的,则为 null。 Future<void>? _pendingAddTodo;
通过 FutureBuilder 更新 UI
Widget _buildView() { return FutureBuilder( // 我们监听待处理的操作,以相应地更新 UI。 future: _pendingAddTodo, builder: (context, snapshot) { // 计算是否存在错误状态。 // 检查 connectionState 用于在重试操作时进行处理。 // 是否错误 final isErrored = snapshot.hasError && snapshot.connectionState != ConnectionState.waiting; // 是否等待 final isWaiting = snapshot.connectionState == ConnectionState.waiting; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 按钮 ElevatedButton( style: ButtonStyle( // 如果出现错误,我们会将该按钮显示为红色 backgroundColor: MaterialStateProperty.all( isErrored ? Colors.red : null, ), ), // 设置 null 后按钮灰色禁止点击 onPressed: isWaiting == true ? null : () { // 我们将 addTodo 返回的 future 保存在变量中 final future = ref .read(todoListProvider.notifier) .addTodo( TodoEntity(description: 'This is a new todo')); // 我们将这个 future 存储在本地的状态中 setState(() { _pendingAddTodo = future; }); }, child: const Text('Add todo V2'), ), // 操作正在等待,让我们显示一个进度指示器 if (isWaiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), ] ], ); }, ); }
Widget build(BuildContext context) { return _buildView(); }
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_riverpod
本文介绍了如何使用Riverpod在Flutter中实现一个Todo列表功能。通过Consumer组件和ConsumerStatefulWidget类,我们可以订阅和监听数据变化,并在需要时更新UI。使用ref.read方法可以获取Provider的值,从而实现数据的读取和操作。通过provider build重写,我们可以在数据变化时重新构建UI。这篇文章帮助读者更深入地了解了如何使用Riverpod进行状态管理,并实现了一个基本的Todo列表应用。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
© 猫哥 ducafecat.com
end
Copyright 2024 ducafecat. All rights reserved.
微信: ducafecat, line: ducafecat,京ICP备2021009050号-3