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
相关产品推荐
相关产品推荐

