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

Flutter双视图事件自动同步优化:单数据源与逻辑分离方案咨询

问题描述

我是Flutter新手,正在开发一款个人日程管理应用。应用包含两个视图,分别展示已确认事件和未确认事件。我使用calendar_view库,为两个视图分别实现了自定义Provider(继承自EventController)。

现有代码

自定义Provider代码

class PrivateProvider extends EventController {
  static final PrivateProvider _instance = PrivateProvider._internal();

  factory PrivateProvider() {
    return _instance;
  }
  PrivateProvider._internal() : super();
  
  addEvents(List<Event> events){
    super.addAll(events);
    notifyListeners();
  }
  addEvent(Event event){
    super.add(event);
    notifyListeners();
  }
}

事件视图Widget代码

@override
  Widget build(BuildContext context) {
    var eventsProvider = widget.private ? PrivateProvider() : SharedProvider();

    return Scaffold(
      body: WeekView(
        controller: eventsProvider,
        onEventTap: (events, date) => showCustomDialog(
            context,
            EventDetail(
                initialEvent: events.whereType<Event>().toList().first,
                date: null,
                confirmed: widget.private)),
      ),
);

当前事件筛选与初始化逻辑

void addEvents(List<Event> events) {

    List<Event> sharedEvents = events
        .where((ev) => ev.confirmed == false)
        .toList();
    List<Event> privateEvents = events
        .where((ev) => ev.confirmed == true)
        .toList();

    PrivateProvider().addEvents(privateEvents);
    SharedProvider().addEvents(sharedEvents);
  }

疑问与需求

目前两个事件列表仅通过confirmed布尔值区分,用户修改该状态后,事件需在两个视图间切换。我当前的做法是手动从一个控制器移除事件并添加到另一个控制器,想了解是否能实现自动同步:比如维护单一事件列表,根据confirmed状态自动在两个视图展示。此外,后续计划实现实时更新,希望事件更新时无需在控制器中查找即可自动同步。请问是否有办法分离业务逻辑与视图逻辑?比如通过中央事件Provider实现?我也愿意下载并修改calendar_view的控制器逻辑。


解决方案

1. 实现中央事件Provider

创建全局事件管理类,维护单一数据源,监听事件状态变化并自动同步到视图控制器:

class EventManager extends ChangeNotifier {
  final List<Event> _allEvents = [];
  
  // 两个视图对应的控制器实例
  final PrivateProvider _privateProvider = PrivateProvider();
  final SharedProvider _sharedProvider = SharedProvider();

  List<Event> get allEvents => List.unmodifiable(_allEvents);

  EventManager() {
    _syncEventsToControllers();
  }

  void addEvent(Event event) {
    _allEvents.add(event);
    event.addListener(_onEventStatusChanged);
    _syncEventsToControllers();
    notifyListeners();
  }

  void addEvents(List<Event> events) {
    _allEvents.addAll(events);
    events.forEach((event) => event.addListener(_onEventStatusChanged));
    _syncEventsToControllers();
    notifyListeners();
  }

  void removeEvent(Event event) {
    _allEvents.remove(event);
    event.removeListener(_onEventStatusChanged);
    _syncEventsToControllers();
    notifyListeners();
  }

  // 事件状态变更时自动同步到控制器
  void _onEventStatusChanged() {
    _syncEventsToControllers();
    notifyListeners();
  }

  // 按状态筛选事件并同步到对应控制器
  void _syncEventsToControllers() {
    final privateEvents = _allEvents.where((e) => e.confirmed).toList();
    final sharedEvents = _allEvents.where((e) => !e.confirmed).toList();
    
    _privateProvider.clear();
    _privateProvider.addEvents(privateEvents);
    
    _sharedProvider.clear();
    _sharedProvider.addEvents(sharedEvents);
  }

  // 对外提供控制器访问入口
  PrivateProvider get privateProvider => _privateProvider;
  SharedProvider get sharedProvider => _sharedProvider;
}

注意:需要让Event类继承ChangeNotifier,以便状态变化时通知EventManager:

class Event extends ChangeNotifier {
  bool _confirmed;
  // 其他事件字段...

  bool get confirmed => _confirmed;

  set confirmed(bool value) {
    if (_confirmed != value) {
      _confirmed = value;
      notifyListeners(); // 状态变更触发通知
    }
  }

  // 构造函数及其他方法...
}

2. 修改视图Widget使用中央Provider

在视图中通过EventManager获取对应控制器,替代直接实例化:

@override
Widget build(BuildContext context) {
  final eventManager = Provider.of<EventManager>(context);
  var eventsProvider = widget.private ? eventManager.privateProvider : eventManager.sharedProvider;

  return Scaffold(
    body: WeekView(
      controller: eventsProvider,
      onEventTap: (events, date) => showCustomDialog(
          context,
          EventDetail(
              initialEvent: events.whereType<Event>().toList().first,
              date: null,
              confirmed: widget.private)),
    ),
  );
}

3. 初始化事件时使用中央Provider

替换原有初始化逻辑,直接调用EventManager的添加方法:

// 通过Provider获取EventManager实例
final eventManager = Provider.of<EventManager>(context, listen: false);
eventManager.addEvents(yourEventList);

4. (可选)修改calendar_view的EventController支持过滤

如果想进一步简化逻辑,可以修改EventController,让它支持基于条件的事件过滤,无需维护两个独立控制器:

class FilterableEventController extends EventController {
  final bool Function(Event) filter;

  FilterableEventController({required this.filter});

  // 重写获取事件的方法,只返回符合过滤条件的事件
  @override
  List<Event> getEventsOnDay(DateTime day) {
    return super.getEventsOnDay(day).where(filter).toList();
  }
}

然后在EventManager中维护单一数据源,给两个视图提供带不同过滤条件的控制器:

class EventManager extends ChangeNotifier {
  final FilterableEventController _privateController = FilterableEventController(filter: (e) => e.confirmed);
  final FilterableEventController _sharedController = FilterableEventController(filter: (e) => !e.confirmed);
  
  final List<Event> _allEvents = [];

  void addEvent(Event event) {
    _allEvents.add(event);
    // 事件状态变化时直接通知两个控制器刷新
    event.addListener(() {
      _privateController.notifyListeners();
      _sharedController.notifyListeners();
    });
    _privateController.addEvent(event);
    _sharedController.addEvent(event);
    notifyListeners();
  }

  // 其他增删事件方法类似...
}

这种方式无需在状态变化时重新同步事件,控制器会自动根据过滤条件返回对应事件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 11:59:51