import 'dart:convert'; import 'package:hgdj/hj_utils/text_util.dart'; import 'package:mmkv/mmkv.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../tools_base/debug_log.dart'; /// 轻量级别存储模型(kv键值对)内部包含文件和内存缓存 final lightKV = _LightModel(); class _LightModel { MMKV? mmkv; /// MMKV 不可用时的兜底:iOS release 包 FFI 符号被 strip,MMKV 初始化抛 symbol not found, /// 会导致本地存储静默读写失败(如锁屏密码设置不上)。SharedPreferences 走 method channel, /// 不依赖 FFI,作为兜底保证纯本地功能可用。 SharedPreferences? _prefs; bool get _useMmkv => mmkv != null; Future config() async { await init(); } init() async { if (mmkv != null || _prefs != null) return; // 已初始化(任一可用),避免重复 try { final rootDir = await MMKV.initialize(); mmkv = MMKV.defaultMMKV(); // iOS release 若符号被 strip,这里会抛 symbol not found debugLog('MMKV for flutter with rootDir = $rootDir'); } catch (e, s) { // 兜底:MMKV 初始化失败不能把 main()/启动流程带崩,否则会卡在启动页 debugLog('MMKV init FAILED >>> $e\n$s'); } // MMKV 起不来就启用 SharedPreferences 兜底 if (mmkv == null) { try { _prefs = await SharedPreferences.getInstance(); debugLog('MMKV unavailable, fallback to SharedPreferences'); } catch (e, s) { debugLog('SharedPreferences init FAILED >>> $e\n$s'); } } } void remove(String key) async { await init(); if (_useMmkv) { mmkv?.removeValue(key); } else { await _prefs?.remove(key); } } Future getString(String key) async { await init(); if (TextUtil.isEmpty(key)) return null; if (_useMmkv) return mmkv?.decodeString(key); return _prefs?.getString(key); } Future setString(String key, String? value, [bool genNewKey = true]) async { await init(); if (TextUtil.isEmpty(key)) return false; if (_useMmkv) return mmkv?.encodeString(key, value); if (value == null) return await _prefs?.remove(key); return await _prefs?.setString(key, value); } Future getInt(String? key) async { await init(); if (TextUtil.isEmpty(key!)) return null; if (_useMmkv) return mmkv?.decodeInt(key); return _prefs?.getInt(key); } Future setInt(String key, int value) async { await init(); if (TextUtil.isEmpty(key)) return false; if (_useMmkv) return mmkv?.encodeInt(key, value); return await _prefs?.setInt(key, value); } Future getBool(String? key, {bool defaultValue = false}) async { await init(); if (TextUtil.isEmpty(key!)) return null; if (_useMmkv) return mmkv?.decodeBool(key, defaultValue: defaultValue); return _prefs?.getBool(key) ?? defaultValue; } Future setBool(String key, bool value) async { await init(); if (TextUtil.isEmpty(key)) return false; if (_useMmkv) return mmkv?.encodeBool(key, value); return await _prefs?.setBool(key, value); } Future?> getStringList(String key, [bool genNewKey = true]) async { await init(); if (TextUtil.isEmpty(key)) return null; if (_useMmkv) { var jsonS = mmkv?.decodeString(key); if (jsonS?.isNotEmpty == true) { return parseStringList(json.decode(jsonS!)); } return []; } return _prefs?.getStringList(key) ?? []; } /// Future setStringList(String key, List list, [bool genNewKey = true]) async { await init(); if (TextUtil.isEmpty(key)) return false; if (_useMmkv) { if (list.isEmpty) return mmkv?.encodeString(key, null); return mmkv?.encodeString(key, json.encode(list)); } return await _prefs?.setStringList(key, list); } }