Flutter Bloc(Cubit)状态管理:如何优雅处理Todos的创建/更新状态?
解决Flutter Cubit中操作后Todo列表消失的问题
你的核心问题是:TodoCreated/TodoUpdated这类状态没有持有Todo列表数据,且BlocBuilder仅在TodoLoaded状态时渲染列表,导致状态切换后列表丢失。下面是几种简洁的处理方式:
方案一:简化状态结构,操作后直接更新并emit TodoLoaded
这是最简洁易维护的方案——移除TodoCreated/TodoUpdated这类单独的操作状态,在创建、更新、删除Todo完成后,直接获取最新的Todo列表并emitTodoLoaded状态;如果操作出错,让TodoError状态携带当前已加载的列表,确保错误时列表不会消失。
修改后的状态定义
@immutable sealed class TodosState {} final class TodoInitial extends TodosState {} final class TodoLoading extends TodosState {} final class TodoLoaded extends TodosState { final List<Todo> todos; TodoLoaded(this.todos); // 重写equals和hashCode,避免不必要的UI重建 @override bool operator ==(Object other) => identical(this, other) || other is TodoLoaded && runtimeType == other.runtimeType && listEquals(todos, other.todos); @override int get hashCode => todos.hashCode; } final class TodoError extends TodosState { final String message; final List<Todo> currentTodos; // 携带当前列表 TodoError(this.message, this.currentTodos); }
Cubit中的操作逻辑示例
class TodoCubit extends Cubit<TodosState> { final TodoRepository repository; TodoCubit(this.repository) : super(TodoInitial()); // 初始加载列表 Future<void> fetchTodos() async { emit(TodoLoading()); try { final todos = await repository.getTodos(); emit(TodoLoaded(todos)); } catch (e) { emit(TodoError(e.toString(), [])); } } // 创建Todo Future<void> createTodo(Todo newTodo) async { emit(TodoLoading()); try { await repository.addTodo(newTodo); // 创建完成后重新获取最新列表 final updatedTodos = await repository.getTodos(); emit(TodoLoaded(updatedTodos)); // 可在这里通过SnackBar提示创建成功 } catch (e) { // 出错时保留当前列表(如果之前已加载) final currentTodos = state is TodoLoaded ? (state as TodoLoaded).todos : []; emit(TodoError(e.toString(), currentTodos)); } } // 更新Todo同理 Future<void> updateTodo(Todo updatedTodo) async { emit(TodoLoading()); try { await repository.updateTodo(updatedTodo); final updatedTodos = await repository.getTodos(); emit(TodoLoaded(updatedTodos)); } catch (e) { final currentTodos = state is TodoLoaded ? (state as TodoLoaded).todos : []; emit(TodoError(e.toString(), currentTodos)); } } }
BlocBuilder中的UI处理
BlocBuilder<TodoCubit, TodosState>( builder: (context, state) { if (state is TodoInitial || state is TodoLoading) { return const Center(child: CircularProgressIndicator()); } if (state is TodoError) { // 显示错误信息,同时保留列表 WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.message), backgroundColor: Colors.red), ); }); return _buildTodoList(state.currentTodos); } if (state is TodoLoaded) { // 操作完成后自动展示最新列表 return _buildTodoList(state.todos); } // 兜底返回空状态 return const Center(child: Text('暂无数据')); }, ) // 抽离列表构建逻辑 Widget _buildTodoList(List<Todo> todos) { return ListView.builder( itemCount: todos.length, itemBuilder: (context, index) => TodoItem(todo: todos[index]), ); }
方案二:保留操作状态,让所有状态携带列表数据
如果不想移除TodoCreated/TodoUpdated状态,可以修改这些状态,让它们都持有当前的Todo列表,同时在BlocBuilder中统一处理所有状态的列表渲染:
修改后的状态定义
@immutable sealed class TodosState { // 所有状态都包含列表(初始/加载状态可以是空列表) final List<Todo> todos; const TodosState(this.todos); } final class TodoInitial extends TodosState { const TodoInitial() : super([]); } final class TodoLoading extends TodosState { const TodoLoading(List<Todo> todos) : super(todos); } final class TodoLoaded extends TodosState { const TodoLoaded(List<Todo> todos) : super(todos); } final class TodoError extends TodosState { final String message; const TodoError(this.message, List<Todo> todos) : super(todos); } final class TodoCreated extends TodosState { const TodoCreated(List<Todo> todos) : super(todos); } final class TodoUpdated extends TodosState { const TodoUpdated(List<Todo> todos) : super(todos); }
Cubit中emit状态示例
Future<void> createTodo(Todo newTodo) async { final currentTodos = state.todos; emit(TodoLoading(currentTodos)); try { await repository.addTodo(newTodo); final updatedTodos = await repository.getTodos(); emit(TodoCreated(updatedTodos)); } catch (e) { emit(TodoError(e.toString(), currentTodos)); } }
BlocBuilder中统一处理
BlocBuilder<TodoCubit, TodosState>( builder: (context, state) { if (state is TodoLoading) { return Stack( children: [ _buildTodoList(state.todos), // 加载时保留旧列表 const Center(child: CircularProgressIndicator()), ], ); } if (state is TodoError) { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.message), backgroundColor: Colors.red), ); }); } else if (state is TodoCreated) { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('创建成功')), ); }); } else if (state is TodoUpdated) { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('更新成功')), ); }); } return _buildTodoList(state.todos); }, )
两种方案中,方案一更简洁易维护,推荐优先使用。
内容的提问来源于stack exchange,提问作者Michael Bervo
相关产品推荐
相关产品推荐

