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

Flutter结合Hive与ChangeNotifierProvider替代FutureProvider方法

Hive 结合 ChangeNotifierProvider 实现日历事件存储方案

核心改造思路

将原有负责Hive操作的工具类改为继承ChangeNotifier,内部自行处理Hive初始化、数据读写逻辑,所有数据变更操作完成后主动调用notifyListeners()通知监听的UI组件刷新,不再依赖FutureProvider做异步预初始化。


第一步:改造数据操作类

import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
// 导入自定义的EventsBox、CleanCalendarEvent对应文件

class EventHiveProvider extends ChangeNotifier {
  late final Box<EventsBox> _eventsBox;
  // 内存缓存已加载的事件数据,UI可直接读取
  EventsBox? _currentEvents;
  EventsBox? get currentEvents => _currentEvents;
  // 初始化状态标记,用于UI判断是否显示加载态
  bool _isInitialized = false;
  bool get isInitialized => _isInitialized;

  // 构造函数自动触发Hive初始化
  EventHiveProvider() {
    _initHive();
  }

  Future<void> _initHive() async {
    // 加重复注册判断,避免热重载/多次初始化抛适配器冲突异常
    if (!Hive.isAdapterRegistered(0)) {
      Hive.registerAdapter(EventsBoxAdapter());
    }
    // 打开Hive存储盒
    _eventsBox = await Hive.openBox<EventsBox>('events');
    // 读取本地已存的初始数据
    _currentEvents = _eventsBox.get('events');
    // 标记初始化完成,通知UI刷新
    _isInitialized = true;
    notifyListeners();
  }

  // 保存/全量更新事件
  Future<void> saveEvents(EventsBox eventsData) async {
    await _eventsBox.put('events', eventsData);
    _currentEvents = eventsData;
    notifyListeners();
  }

  // 清空所有事件
  Future<void> clearAllEvents() async {
    await _eventsBox.delete('events');
    _currentEvents = null;
    notifyListeners();
  }
}

第二步:应用根节点挂载Provider

在APP入口处初始化Hive路径,挂载ChangeNotifierProvider,无需额外处理异步等待逻辑:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:path_provider/path_provider.dart';
// 省略其他导入

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // 移动端/桌面端建议用path_provider获取应用专属存储目录,避免数据被系统清理
  final appDir = await getApplicationDocumentsDirectory();
  Hive.init(appDir.path);

  runApp(
    ChangeNotifierProvider(
      create: (context) => EventHiveProvider(),
      child: const MyApp(),
    ),
  );
}

第三步:页面层调用示例

直接通过context.watch/context.read获取状态和操作方法,根据初始化状态做加载态判断:

class CalendarPage extends StatelessWidget {
  const CalendarPage({super.key});

  @override
  Widget build(BuildContext context) {
    final eventProvider = context.watch<EventHiveProvider>();
    // 初始化未完成时显示加载组件
    if (!eventProvider.isInitialized) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }

    final eventData = eventProvider.currentEvents;
    return Scaffold(
      appBar: AppBar(title: const Text('日历笔记')),
      body: eventData == null
          ? const Center(child: Text('暂无存储的事件'))
          : ListView.builder(
              itemCount: eventData.eventsList.length,
              itemBuilder: (context, index) {
                final event = eventData.eventsList[index];
                // 按CleanCalendarEvent的实际字段调整展示逻辑
                return ListTile(
                  title: Text(event.summary ?? '无标题事件'),
                  subtitle: Text(eventData.date.toString()),
                );
              },
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 示例:新增事件后调用保存方法
          final newEventData = EventsBox(
            date: DateTime.now(),
            eventsList: [
              // 传入新的CleanCalendarEvent实例
            ],
          );
          context.read<EventHiveProvider>().saveEvents(newEventData);
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

注意事项

  • 如果CleanCalendarEvent是第三方包提供的类,无法直接添加Hive注解,需要手动编写对应TypeAdapter实现序列化,否则Hive存储时会报类型不支持错误
  • 若需要按日期分别存储事件,不要把所有事件存在同一个eventskey下,可以把日期格式化为yyyy-MM-dd字符串作为存储key,每个key对应当天的EventsBox实例,查询时按日期读取即可,性能更高
  • 所有修改Hive数据的方法,必须在数据库操作完成后更新内存缓存值,再调用notifyListeners(),否则UI无法拿到最新数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:00:53