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

SfCalendar添加新预订后不更新,setState无效问题排查

问题:添加新预订后SfCalendar无法实时刷新,切换页面后才显示

我尝试在添加新预订时更新/刷新SfCalendar组件,已使用setState()但仍无效,新添加的预订仅在切换到AboutFragment再切回CalendarFragment时才显示。请问我哪里出错了?

相关代码:home.dart

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';

import '../booking.dart';
import '../pages/login.dart';

List<Booking> bookingList = <Booking>[];

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedFragment = 0;
  BookingDataSource _bookingDataSource = BookingDataSource(bookingList);

  void addNewBooking() {
    setState(() {
      Future<DateTime?> bookingDateTime = showBookingPicker(context: context);
      bookingDateTime.then((DateTime? value) {
        if (value != null) {
          bookingList.add(Booking('Reservation', value, value.add(const Duration(minutes: 30)), 'M'));
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Booking Added'))
          );
          _bookingDataSource = BookingDataSource(bookingList);
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('STS'),
        centerTitle: true,
        automaticallyImplyLeading: false,
        actions: [
          MenuAnchor(
            builder: (BuildContext context, MenuController menuController, Widget? child) {
              return IconButton(
                onPressed: () {
                  if (menuController.isOpen) {
                    menuController.close();
                  } else {
                    menuController.open();
                  }
                },
                icon: const Icon(Icons.more_vert),
              );
            },
            menuChildren: [
              MenuItemButton(
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => const LoginPage(title: 'Log In'),
                    ),
                  );
                },
                child: const Padding(
                  padding: EdgeInsets.all(4.0),
                  child: Row(
                    children: [
                      Icon(Icons.logout),
                      SizedBox(width: 4.0),
                      Text('Log Out'),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
      body: _selectedFragment == 0 ? CalendarFragment(bookingDataSource: _bookingDataSource) : const AboutFragment(),
      floatingActionButton: FloatingActionButton(
        elevation: 0.0,
        tooltip: 'Add booking',
        onPressed: () {
          addNewBooking();
        },
        child: const Icon(Icons.add),
      ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _selectedFragment,
        onDestinationSelected: (int index) {
          setState(() {
            _selectedFragment = index;
          });
        },
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.calendar_today),
            label: 'My Booking',
          ),
          NavigationDestination(
            icon: Icon(Icons.info),
            label: 'About Us',
          ),
        ],
      ),
    );
  }
}

class CalendarFragment extends StatefulWidget {
  final BookingDataSource bookingDataSource;

  const CalendarFragment({Key? key, required this.bookingDataSource}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _CalendarFragmentState();
}

class _CalendarFragmentState extends State<CalendarFragment>{
  @override
  Widget build(BuildContext context) {
    return SfCalendar(
      view: CalendarView.month,
      headerStyle: CalendarHeaderStyle(
        textStyle: Theme.of(context).textTheme.labelLarge,
      ),
      dataSource: widget.bookingDataSource,
      initialSelectedDate: DateTime.now(),
      monthViewSettings: const MonthViewSettings(showAgenda: true),
    );
  }
}

class AboutFragment extends StatelessWidget { ... }

问题分析与解决

核心问题

你把setState()包裹在了showBookingPicker()的调用外层,但showBookingPicker()是异步操作,它的回调会在setState()执行完成后才触发。这意味着回调里更新_bookingDataSource的操作不会触发HomePage的UI重建,CalendarFragment自然无法拿到新的数据源。

修复方案

将setState()移到Future的回调内部,确保更新数据源的操作能触发UI重建:

void addNewBooking() {
  showBookingPicker(context: context).then((DateTime? value) {
    if (value != null) {
      setState(() {
        bookingList.add(Booking('Reservation', value, value.add(const Duration(minutes: 30)), 'M'));
        _bookingDataSource = BookingDataSource(bookingList);
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Booking Added'))
      );
    }
  });
}

更优的实现方式

不需要每次都创建新的BookingDataSource实例,直接调用数据源的notifyListeners()方法通知日历组件刷新,这样更高效:

  1. 保持数据源初始化不变:
BookingDataSource _bookingDataSource = BookingDataSource(bookingList);
  1. 修改添加预订的逻辑:
void addNewBooking() {
  showBookingPicker(context: context).then((DateTime? value) {
    if (value != null) {
      setState(() {
        bookingList.add(Booking('Reservation', value, value.add(const Duration(minutes: 30)), 'M'));
        _bookingDataSource.notifyListeners(); // 通知数据源更新
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Booking Added'))
      );
    }
  });
}

另外,建议把全局的bookingList移到_HomePageState的状态中,避免全局状态带来的潜在问题,让状态管理更封闭可控。


内容的提问来源于stack exchange,提问作者Hadif Hatta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 11:07:06