You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 01:15:05