import 'dart:developer'; import 'dart:io'; import 'package:hydrated_bloc/hydrated_bloc.dart'; /// Opens the HydratedBloc box; a corrupt box is dropped and rebuilt, and if /// even that fails the app runs on [InMemoryStorage] instead of never /// reaching `runApp`. Future buildHydratedStorageWithFallback(String directoryPath) async { final directory = HydratedStorageDirectory(directoryPath); try { return await _build(directory); } catch (e, s) { log('HydratedStorage open failed, rebuilding: $e', stackTrace: s); } try { await _deleteHydratedBox(directoryPath); return await _build(directory); } catch (e, s) { log('HydratedStorage rebuild failed, using memory: $e', stackTrace: s); return InMemoryStorage(); } } final Expando Function()> _keyReaders = Expando(); Future _build(HydratedStorageDirectory directory) async { final storage = await HydratedStorage.build(storageDirectory: directory); // hydrated_bloc exposes no key listing; the Hive instance is the only way // in. Every build replaces the static one, so it is captured per storage. // ignore: invalid_use_of_visible_for_testing_member final hive = HydratedStorage.hive; _keyReaders[storage] = () => hive.box('hydrated_box').keys.map((k) => '$k').toList(); return storage; } /// All keys persisted in [storage], sorted; `null` when they can't be /// enumerated. List? hydratedStorageKeys(Storage storage) { if (storage is InMemoryStorage) return storage.keys.toList()..sort(); try { return _keyReaders[storage]?.call()?..sort(); } on Object { return null; } } Future _deleteHydratedBox(String directoryPath) async { final directory = Directory(directoryPath); if (!directory.existsSync()) return; await for (final entity in directory.list()) { final name = entity.uri.pathSegments.lastWhere((s) => s.isNotEmpty); if (entity is File && name.startsWith('hydrated_box')) { await entity.delete(); } } } /// Non-persistent [Storage]; state lives for the session only. class InMemoryStorage implements Storage { final Map _values = {}; Iterable get keys => _values.keys; @override dynamic read(String key) => _values[key]; @override Future write(String key, dynamic value) async => _values[key] = value; @override Future delete(String key) async => _values.remove(key); @override Future clear() async => _values.clear(); @override Future close() async {} }