如何用同一Bloc结合日历查询Firestore指定日期的任务条目?
Hey there! Great job getting your Firestore CRUD working with Bloc—that’s a solid foundation. Let’s tackle your date filtering problem step by step, and clear up your confusion along the way.
First off: you don’t need a separate Calendar Bloc. Date selection is a UI trigger that should feed into your existing TasksBloc to filter tasks. Here’s exactly what to modify:
1. Add a New Event to Trigger Date Filtering
First, update your TasksEvent to include an event that tells the bloc to filter tasks by the selected date:
abstract class TasksEvent extends Equatable { const TasksEvent(); @override List<Object> get props => []; } // Keep your existing events (LoadTasks, AddTask, etc.) // Add this new event: class FilterTasksByDate extends TasksEvent { final DateTime selectedDate; const FilterTasksByDate(this.selectedDate); @override List<Object> get props => [selectedDate]; }
2. Update TasksBloc to Handle the Filter Event
Modify your TasksBloc to listen for the new FilterTasksByDate event, and fetch only tasks from the selected day. We’ll also fix the initial load to fetch today’s tasks properly:
class TasksBloc extends Bloc<TasksEvent, TasksState> { final TaskRepository _taskRepository; StreamSubscription _taskSubscription; TasksBloc({@required TaskRepository taskRepository}) : assert(taskRepository != null), _taskRepository = taskRepository; @override TasksState get initialState => TasksLoading(); @override Stream<TasksState> mapEventToState(TasksEvent event) async* { if (event is LoadTasks) { yield* _mapLoadTasksToState(DateTime.now()); } else if (event is AddTask) { yield* _mapAddTaskToState(event); } else if (event is UpdateTask) { yield* _mapUpdateTaskToState(event); } else if (event is DeleteTask) { yield* _mapDeleteTaskToState(event); } else if (event is TasksUpdated) { yield* _mapTaskUpdatedToState(event); } else if (event is FilterTasksByDate) { // Handle the new filter event yield* _mapFilterTasksByDateToState(event.selectedDate); } } // Updated initial load to fetch only today's tasks Stream<TasksState> _mapLoadTasksToState(DateTime currentDate) async* { _taskSubscription?.cancel(); final startOfDay = DateTime(currentDate.year, currentDate.month, currentDate.day); final endOfDay = startOfDay.add(const Duration(days: 1)); _taskSubscription = _taskRepository.tasks(startOfDay, endOfDay).listen((tasks) { add(TasksUpdated(tasks)); }); } // New method to handle date filtering Stream<TasksState> _mapFilterTasksByDateToState(DateTime selectedDate) async* { _taskSubscription?.cancel(); // Define the start/end of the selected day for precise Firestore query final startOfDay = DateTime(selectedDate.year, selectedDate.month, selectedDate.day); final endOfDay = startOfDay.add(const Duration(days: 1)); _taskSubscription = _taskRepository.tasks(startOfDay, endOfDay).listen((tasks) { add(TasksUpdated(tasks)); }); } // Keep your existing _mapAddTaskToState, _mapUpdateTaskToState, etc. }
3. Fix Firestore Repository Query
Your current tasks() method only uses isGreaterThan, which won’t restrict results to a single day. Update it to query a date range (start of the selected day to start of the next day):
class FirebaseTaskRepository extends TaskRepository { final taskCollection = Firestore.instance.collection('tasks'); // Keep your addTask, deleteTask, updateTask methods @override Stream<List<Task>> tasks(DateTime startOfDay, DateTime endOfDay) { return taskCollection .where('date', isGreaterThanOrEqualTo: startOfDay) .where('date', isLessThan: endOfDay) .snapshots() .map((snapshot) { return snapshot.documents .map((doc) => Task.fromEntity(TaskEntity.fromJson(doc.data))) .toList(); }); } }
4. Trigger the Filter from the Calendar Component
Update your _buildTableCalendar to dispatch the FilterTasksByDate event when a day is selected. Make sure to pass the BuildContext to access the bloc:
Widget _buildTableCalendar(BuildContext context) { return TableCalendar( onDaySelected: (DateTime day, List events) { pickedDate = day; // Tell the TasksBloc to filter tasks for the selected day BlocProvider.of<TasksBloc>(context).add(FilterTasksByDate(day)); }, calendarController: _calendarController, startingDayOfWeek: StartingDayOfWeek.monday, calendarStyle: CalendarStyle( selectedColor: Colors.blue, todayColor: Colors.blue[200], ), ); }
5. Verify MultiBlocProvider Usage
Your MultiBlocProvider setup is correct! Since it wraps the MaterialApp, all routes (including CalendarScreen) can access the TasksBloc via BlocProvider.of<TasksBloc>(context). No changes needed here.
Key Notes to Test
- Ensure your
Taskmodel stores thedateas aDateTime(and your Firestore entity converts it to/from a Timestamp correctly—Firestore handles DateTime ↔ Timestamp comparisons automatically). - When you select a date, the
TasksBlocwill fetch the filtered tasks, update theTasksLoadedstate, and your existingBlocBuilderin_buildCalendarListViewwill automatically refresh the ListView with only that day’s tasks.
内容的提问来源于stack exchange,提问作者Fluzzy

