无法获取新Hive Box,登出重登报错:Box已关闭
问题分析与解决方案
问题根源
登出时调用Hive.close()会关闭所有已打开的Box实例,但全局的CardsDataBase(Provider单例)仍持有之前被关闭的Box引用。重新登录后虽然打开了新的Box,但CardsDataBase没有更新内部的_myBox指向新实例,导致UI访问的还是已关闭的旧Box,触发Box has already been closed错误。
具体修改步骤
1. 登录时主动通知数据库更新Box实例
修改登录方法,打开新Box后立即更新CardsDataBase的引用:
Future signInWithEmailAndPassword(String email, String password) async { try { UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password); User? user = result.user; if(user == null) return null; prefs!.setString(AppConstants.kBoxPreferenceKey, user.uid); await Hive.openBox(user.uid); // 通知数据库更新当前Box final dbProvider = Provider.of<CardsDataBase>(context, listen: false); dbProvider.updateCurrentBox(); return user; } catch (error) { print(error.toString()); return null; } }
2. 重构CardsDataBase,增加Box状态校验与更新逻辑
优化内部引用管理,确保每次访问的都是有效打开的Box:
class CardsDataBase extends ChangeNotifier { Box? _myBox; // 改为可空类型,避免未初始化风险 // 访问时自动校验Box状态,失效则重新获取 Box get myBox { if(_myBox == null || !_myBox!.isOpen) { final boxName = prefs!.getString(AppConstants.kBoxPreferenceKey)!; _myBox = Hive.box(boxName); } return _myBox!; } CardsDataBase() { initBox(); } void initBox() { final boxName = prefs!.getString(AppConstants.kBoxPreferenceKey); if(boxName != null) { _myBox = Hive.box(boxName); } notifyListeners(); } // 新增:更新当前Box实例 void updateCurrentBox() { final boxName = prefs!.getString(AppConstants.kBoxPreferenceKey)!; _myBox = Hive.box(boxName); notifyListeners(); } // 重置Box引用(登出时调用) void resetBox() { _myBox = null; notifyListeners(); } void deleteFolder(String folderName) { myBox.delete(folderName); // 直接使用校验后的myBox notifyListeners(); } void updateDataBase(String listName, List listContent) { myBox.put(listName, listContent); notifyListeners(); } }
3. 登出时重置数据库的Box引用
修改登出方法,清空CardsDataBase的旧Box引用:
Future signOut() async { prefs!.remove(AppConstants.kBoxPreferenceKey); await Hive.close(); // 重置数据库的Box引用 final dbProvider = Provider.of<CardsDataBase>(context, listen: false); dbProvider.resetBox(); try { return await _auth.signOut(); } catch (error) { print(error.toString()); return null; } }
4. UI层增加Box状态校验
在LibraryScreen中先检查Box状态,避免无效访问:
@override Widget build(BuildContext context) { final provider = Provider.of<CardsDataBase>(context); // Box未打开时显示加载/空状态 if(!provider.myBox.isOpen) { return const Center(child: CircularProgressIndicator()); } Iterable listOfKeysFromBox = provider.myBox.keys; List<FolderWidget> folderWidgetsList = listOfKeysFromBox.map((e)=> FolderWidget(folderName: e, folderLocation: FolderLocation.downloaded, folderContents: provider.myBox.get(e)) ).toList(); List<StatelessWidget> listForDownloadedColumn = [ const Text('Downloaded Folders', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700)), const MyDivider(), ]; listForDownloadedColumn.addAll(folderWidgetsList); return Scaffold(...); }
内容的提问来源于stack exchange,提问作者ogz stn
相关产品推荐
相关产品推荐

