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

解决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分支中:

  1. 构建完整的日期-事件映射表,供日历显示预订标记
  2. 实时计算当前选中日期的预订列表,直接传递给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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:44:54