如何用Riverpod修改TaskCard布尔状态(含GoRouter场景)
解决方案
1. 给TaskModel添加copyWith方法
TaskModel是不可变类(所有字段为final),更新状态必须生成新实例,补充copyWith方法:
class TaskModel { final String id; final String taskTitle; final String taskDescription; final bool taskStatus; final Timestamp? timestamp; final String projectId; TaskModel({ required this.id, required this.taskTitle, required this.taskDescription, required this.taskStatus, this.timestamp, required this.projectId, }); // 添加copyWith方法,用于生成新实例 TaskModel copyWith({ String? id, String? taskTitle, String? taskDescription, bool? taskStatus, Timestamp? timestamp, String? projectId, }) { return TaskModel( id: id ?? this.id, taskTitle: taskTitle ?? this.taskTitle, taskDescription: taskDescription ?? this.taskDescription, taskStatus: taskStatus ?? this.taskStatus, timestamp: timestamp ?? this.timestamp, projectId: projectId ?? this.projectId, ); } // 补全toMap和fromMap方法(确保与Firebase数据交互) Map<String, dynamic> toMap() { return { 'id': id, 'taskTitle': taskTitle, 'taskDescription': taskDescription, 'taskStatus': taskStatus, 'timestamp': timestamp, 'projectId': projectId, }; } factory TaskModel.fromMap(Map<String, dynamic> map) { return TaskModel( id: map['id'], taskTitle: map['taskTitle'], taskDescription: map['taskDescription'], taskStatus: map['taskStatus'], timestamp: map['timestamp'], projectId: map['projectId'], ); } }
2. 修正TaskRepository的状态更新逻辑
原方法逻辑易混淆,改为直接接收新状态(或可选toggle逻辑):
class TasksRepo { final FirebaseFirestore _firestore = FirebaseFirestore.instance; // ... 保留其他原有方法 // 方案1:直接接收要设置的新状态 Future<void> changeTaskStatus({ required String taskId, required bool newTaskStatus, }) async { try { await _firestore.collection('tasks').doc(taskId) .update({'taskStatus': newTaskStatus}); printSuccess('任务状态更新成功!'); } catch (e) { printError('更新任务状态失败:$e'); throw e; } } // 可选方案2:直接取反当前状态(无需传入status) // Future<void> toggleTaskStatus({required String taskId}) async { // try { // final docSnapshot = await _firestore.collection('tasks').doc(taskId).get(); // if (docSnapshot.exists) { // final currentStatus = docSnapshot['taskStatus'] as bool; // await _firestore.collection('tasks').doc(taskId) // .update({'taskStatus': !currentStatus}); // printSuccess('任务状态已切换!'); // } // } catch (e) { // printError('切换任务状态失败:$e'); // throw e; // } // } // 添加单个任务的实时监听流 Stream<TaskModel?> getTaskStream({required String taskId}) { return _firestore.collection('tasks').doc(taskId) .snapshots() .map((doc) => doc.exists ? TaskModel.fromMap(doc.data()!) : null); } }
3. 简化TaskController的状态更新方法
移除冗余参数,只保留必要的taskId和新状态:
class TaskController extends StateNotifier<TasksState> { final TasksRepo _tasksRepo; TaskController(super.state, this._tasksRepo); // ... 保留其他原有方法 Future<void> changeTaskStatus({ required String taskId, required bool newTaskStatus, }) async { state = state.copyWith(isLoading: true); try { await _tasksRepo.changeTaskStatus( taskId: taskId, newTaskStatus: newTaskStatus, ); state = state.copyWith(isLoading: false); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); } } // 如果用toggle方案,对应方法: // Future<void> toggleTaskStatus({required String taskId}) async { // state = state.copyWith(isLoading: true); // try { // await _tasksRepo.toggleTaskStatus(taskId: taskId); // state = state.copyWith(isLoading: false); // } catch (e) { // state = state.copyWith(isLoading: false, error: e.toString()); // } // } }
4. 修正TaskDetails页面的按钮逻辑与UI更新
因为GoRouter传递的是TaskModel副本,需通过Stream监听实时变化更新UI,同时修正按钮点击逻辑:
class TaskDetails extends ConsumerWidget { final TaskModel taskModel; const TaskDetails({super.key, required this.taskModel}); @override Widget build(BuildContext context, WidgetRef ref) { // 监听当前任务的实时数据变化 final taskStream = ref.watch(taskRepoProvider).getTaskStream(taskId: taskModel.id); return Scaffold( appBar: AppBar(title: Text(taskModel.taskTitle)), body: StreamBuilder<TaskModel?>( stream: taskStream, initialData: taskModel, builder: (context, snapshot) { final currentTask = snapshot.data ?? taskModel; return Column( children: [ Text("描述: ${currentTask.taskDescription}"), const SizedBox(height: 20), Text("任务状态: ${currentTask.taskStatus ? '已完成' : '未完成'}"), ElevatedButton( onPressed: () { final controller = ref.read(tasksStateNotifierProvider.notifier); // 取反当前状态并更新 controller.changeTaskStatus( taskId: currentTask.id, newTaskStatus: !currentTask.taskStatus, ); }, child: const Text("切换任务状态"), ), ], ); }, ), ); } }
5. 前置页面的自动更新
task_page.dart中已使用StreamBuilder监听fetchTasks流,当Firebase中任务状态更新后,流会自动推送新数据,TaskCard的UI会自动刷新,无需额外处理。
内容的提问来源于stack exchange,提问作者xDelusion
相关产品推荐
相关产品推荐

