Flutter/Hive内存List修改未保存却跨页面留存问题
Flutter Hive 异常问题:未保存的内存修改意外保留
问题现象
在第一个页面从Hive数据库获取DataCategory列表后,仅修改内存中的List对象,未执行Hive的put保存操作就关闭页面;打开第二个页面重新从Hive获取该列表时,发现之前未保存的修改仍然存在。
相关代码
GeneralBox 工具类
import 'dart:convert'; import 'package:hive/hive.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:mg_planner_app/data/data_default_category.dart'; import 'package:mg_planner_app/data/data_general.dart'; import 'package:mg_planner_app/setting/model/model_country.dart'; class GeneralBox { static const String data_general = "data_general"; static const String data_category = "data_category"; static const String data_sub_category = "data_sub_category"; static const String data_item = "data_item"; static const String data_setting = "data_setting"; static Box<DataGeneral> GetGeneral() => Hive.box(data_general); static Box<DataCategory> GetCategory() => Hive.box(data_category); static Box<DataSubCategory> GetExpense() => Hive.box(data_sub_category); static Box<DataItem> GetAccounts() => Hive.box(data_item); static Box<DataSetting> GetDataSetting() => Hive.box(data_setting); static DataSetting GetDataSettingObject() { var box_setting = GetDataSetting(); if(box_setting.values.length > 0) { return box_setting.values.toList().cast<DataSetting>()[0]; } else { final dataSetting = DataSetting() ..key = "0" ..user_name = "Guest" ..due_term = "7" ..number_formate_index = "3" ..number_formate_char = "," ..date_format = "dd/MM/yyyy" ..time_format = "hh:mm a" ..uuid = "uuid.v1()" ..modelCountry = jsonEncode(ModelCountry.countries[185]) ..pass_code = false ..is_show_board = true ..is_purchased = false ..currency_format = 4 ..password = ""; box_setting.put("0", dataSetting); return dataSetting; } } static void saveDataSetting(DataSetting dataSetting) { var box_setting = GetDataSetting(); box_setting.put("0", dataSetting); } static GetHiveFunction() async { await Hive.initFlutter(); if(!Hive.isAdapterRegistered(0)) { Hive.registerAdapter(DataGeneralAdapter()); } if(!Hive.isAdapterRegistered(1)) { Hive.registerAdapter(DataCategoryAdapter()); } if(!Hive.isAdapterRegistered(2)) { Hive.registerAdapter(DataSubCategoryAdapter()); } if(!Hive.isAdapterRegistered(3)) { Hive.registerAdapter(DataItemAdapter()); } if(!Hive.isAdapterRegistered(4)) { Hive.registerAdapter(DataSettingAdapter()); } if(!Hive.isBoxOpen(data_general)) { await Hive.openBox<DataGeneral>(data_general); } if(!Hive.isBoxOpen(data_category)) { await Hive.openBox<DataCategory>(data_category); } if(!Hive.isBoxOpen(data_sub_category)) { await Hive.openBox<DataSubCategory>(data_sub_category); } if(!Hive.isBoxOpen(data_item)) { await Hive.openBox<DataItem>(data_item); } if(!Hive.isBoxOpen(data_setting)) { await Hive.openBox<DataSetting>(data_setting); } } static String getNewKey() { return DateTime.now().millisecondsSinceEpoch.toString(); } static List<DataCategory> ListDefaultCategory() { List<DataCategory> list = []; list.add(dataCategory1); list.add(dataCategory2); list.add(dataCategory3); list.add(dataCategory4); list.add(dataCategory5); list.add(dataCategory6); list.add(dataCategory7); list.add(dataCategory8); list.add(dataCategory9); list.add(dataCategory10); list.add(dataCategory11); list.add(dataCategory12); list.add(dataCategory13); return list; } static DataCategory AddNewCategory(String category_type, String category_name) { return DataCategory() ..key = getNewKey() ..type = category_type ..name = category_name ..list_item = [] ..sub_category = []; } static DataSubCategory AddNewSubCategory(String category_type, String category_name) { return DataSubCategory() ..key = getNewKey() ..type = category_type ..name = category_name ..list_item = []; } }
页面中获取列表的代码
ValueListenableBuilder<Box<DataCategory>>( valueListenable: GeneralBox.GetCategory().listenable(), builder: (context, box, _) { List<DataCategory> list_category = box.values.toList().cast<DataCategory>(); // 此处修改list_category或其中的DataCategory对象 // ... }, )
问题原因
Hive中的对象是引用类型,当你从Box中获取对象列表时,得到的是内存中对象的直接引用,而非副本。由于你的GeneralBox全局复用打开的Box实例,只要Box保持打开状态,后续从Box中获取的对象都是同一个内存实例。因此,即使未调用put将修改持久化到磁盘,内存中的对象修改仍会被保留,直到Box关闭或应用重启。
解决方案
1. 修改对象前创建副本
修改内存对象时,先创建对象的深拷贝,避免直接修改Hive持有的原对象:
// 为DataCategory创建副本方法 DataCategory copyCategory(DataCategory original) { return DataCategory() ..key = original.key ..type = original.type ..name = original.name ..list_item = List.from(original.list_item) ..sub_category = List.from(original.sub_category); } // 使用时生成副本列表 List<DataCategory> list_category = box.values.toList().cast<DataCategory>().map(copyCategory).toList();
2. 规范修改流程
仅在需要持久化时更新原对象,修改后立即调用put保存:
// 修改后立即保存到Hive void updateCategory(DataCategory updatedCategory) { final box = GeneralBox.GetCategory(); box.put(updatedCategory.key, updatedCategory); }
3. 页面销毁时重置内存对象(可选)
若不需要保留页面临时修改,可在页面销毁时用磁盘数据覆盖内存对象:
@override void dispose() { final box = GeneralBox.GetCategory(); // 遍历所有key,用磁盘数据重新覆盖内存对象 for (var key in box.keys) { final diskData = box.get(key); box.put(key, diskData); } super.dispose(); }
内容的提问来源于stack exchange,提问作者sifat ullah shah
相关产品推荐
相关产品推荐

