解决Flutter中新增预订后ListView.builder无法自动实时更新的问题
问题描述
在Flutter项目中通过Stream(结合Riverpod)获取bookings流,搭配TableCalendar组件实现点击日历日期后,更新ListView展示该日期的过滤后预订列表。当前问题:用户新增一条预订时,流会更新,但由于ListView的更新依赖日历的点击操作,导致新增预订后ListView无法自动刷新。
现有代码
class _AdminHomePageState extends ConsumerState<AdminHomePage> { CalendarFormat _calendarFormat = CalendarFormat.month; DateTime _focusedDay = DateTime.now(); DateTime _selectedDay = DateTime.now(); DBHandler dbhandler = DBHandler(); List<Booking> allBookings = []; List<Booking> filteredBooking = []; //需要实现选中日期时返回对应事件的功能 Map<DateTime, List<Booking>> events = {}; @override void initState() { super.initState(); } List<Booking> filterBookingsByDate( DateTime selectedDate, List<Booking> unfiltered) { List<Booking> filteredBookings = []; for (var booking in unfiltered) { if (isSameDay(booking.bookingDate, selectedDate)) { filteredBookings.add(booking); } } return filteredBookings; } @override Widget build(BuildContext context) { final bookingsAsyncValue = ref.watch(bookingsProvider); return Scaffold( appBar: AppBar( title: Text('Bookings'), ), body: bookingsAsyncValue.when( data: (bookings) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Expanded( flex: 1, child: Container( height: 300, child: TableCalendar( firstDay: DateTime.utc(2010, 10, 16), lastDay: DateTime.utc(2030, 3, 14), focusedDay: DateTime.now(), calendarFormat: _calendarFormat, calendarStyle: const CalendarStyle(outsideDaysVisible: false), onFormatChanged: (format) { setState(() { _calendarFormat = format; }); }, selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; // 同时更新聚焦日期 filteredBooking = filterBookingsByDate(selectedDay, bookings); // 显示选中日期的预订 }); }, onPageChanged: (focusedDay) { _focusedDay = focusedDay; }, eventLoader: (day) { // 移除时间信息,只保留日期 DateTime dateOnly = DateTime(day.year, day.month, day.day); return events[dateOnly] ?? []; }, ), ), ), Container( height: 300, child: ListView.builder( itemCount: filteredBooking.length, itemBuilder: (context, index) { final booking = filteredBooking[index]; return ListTile( title: Text(booking.serviceTitle), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Email: ${booking.userEmail}'), Text( 'Date: ${DateFormat('yyyy-MM-dd').format(booking.bookingDate)}'), Text( 'StartTime: ${DateFormat('HH:mm').format(booking.startTime)}'), ], ), ); }, ), ), ], ), ); }, loading: () { return Center( child: CircularProgressIndicator(), ); }, error: (error, stackTrace) { return Center( child: Text('Error: $error'), ); }, ), ); } }
解决方法
核心思路:每次bookings流更新时,自动重新计算当前选中日期的过滤列表,同时同步更新日历的事件映射,摆脱对日历点击操作的依赖。
步骤1:移除冗余状态变量,实时计算结果
删除类中的filteredBooking和events成员变量,改为在bookings数据更新时,基于当前_selectedDay实时计算所需数据。
步骤2:优化过滤方法(可选)
将循环过滤改为更简洁的where写法:
List<Booking> filterBookingsByDate(DateTime selectedDate, List<Booking> unfiltered) { return unfiltered.where((booking) => isSameDay(booking.bookingDate, selectedDate)).toList(); }
步骤3:修改build方法核心逻辑
在bookingsAsyncValue.when的data分支中:
- 构建完整的日期-事件映射表,供日历显示预订标记
- 实时计算当前选中日期的预订列表,直接传递给ListView
修改后的关键代码片段:
data: (bookings) { // 构建日期-事件映射,供日历显示标记 final Map<DateTime, List<Booking>> events = {}; for (final booking in bookings) { final dateKey = DateTime(booking.bookingDate.year, booking.bookingDate.month, booking.bookingDate.day); events.update(dateKey, (value) => [...value, booking], ifAbsent: () => [booking]); } // 实时计算当前选中日期的预订列表 final currentFilteredBookings = filterBookingsByDate(_selectedDay, bookings); return Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Expanded( flex: 1, child: Container( height: 300, child: TableCalendar( firstDay: DateTime.utc(2010, 10, 16), lastDay: DateTime.utc(2030, 3, 14), focusedDay: _focusedDay, // 绑定到状态中的聚焦日期 calendarFormat: _calendarFormat, calendarStyle: const CalendarStyle(outsideDaysVisible: false), onFormatChanged: (format) { setState(() => _calendarFormat = format); }, selectedDayPredicate: (day) => isSameDay(_selectedDay, day), onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; }); }, onPageChanged: (focusedDay) => _focusedDay = focusedDay, eventLoader: (day) { final dateOnly = DateTime(day.year, day.month, day.day); return events[dateOnly] ?? []; }, ), ), ), Container( height: 300, child: ListView.builder( itemCount: currentFilteredBookings.length, itemBuilder: (context, index) { final booking = currentFilteredBookings[index]; return ListTile( title: Text(booking.serviceTitle), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Email: ${booking.userEmail}'), Text('Date: ${DateFormat('yyyy-MM-dd').format(booking.bookingDate)}'), Text('StartTime: ${DateFormat('HH:mm').format(booking.startTime)}'), ], ), ); }, ), ), ], ), ); },
步骤4:修正日历的focusedDay绑定
将TableCalendar的focusedDay属性绑定到状态变量_focusedDay,而非固定的DateTime.now(),保证日历分页时状态同步。
修改后,每当bookings流更新(比如新增预订),build方法会自动重新执行,实时计算当前选中日期的过滤列表,ListView和日历标记都会同步刷新,无需用户手动点击日历。
内容的提问来源于stack exchange,提问作者Milan Labus
相关产品推荐
相关产品推荐

