Flutter应用:如何保存按钮位置与绘图至手机内存并恢复状态?
解决Flutter应用状态持久化:保存自定义按钮位置与绘图内容
为什么SharedPreferences不适用?
SharedPreferences仅适合存储简单键值对(字符串、布尔值、基础数值),但你的需求涉及复杂对象(按钮位置数组)和绘图内容(路径/像素数据),用它会面临序列化繁琐、存储效率低的问题,因此需要更适配的方案。
方案一:本地文件存储(自定义序列化)
直接将数据序列化为JSON或二进制写入本地文件,适合结构化数据和中等大小内容:
1. 处理按钮位置(List)
先把Offset列表转成可序列化的JSON结构:
// 序列化按钮位置 List<Map<String, double>> serializeButtons(List<Offset> positions) { return positions.map((offset) => { 'dx': offset.dx, 'dy': offset.dy, }).toList(); } // 反序列化恢复按钮位置 List<Offset> deserializeButtons(List<dynamic> jsonList) { return jsonList.map((json) => Offset( json['dx'] as double, json['dy'] as double, )).toList(); }
2. 处理绘图内容(以Path集合为例)
序列化Path的控制点,还原时重建路径:
// 序列化Path(简化版,复杂路径需扩展指令解析逻辑) List<Map<String, dynamic>> serializePaths(List<Path> paths) { return paths.map((path) { final metrics = path.computeMetrics(); final segments = <Map<String, dynamic>>[]; for (final metric in metrics) { final segment = metric.extractPath(0, metric.length); segments.add({'points': segment.toJson()}); // 实际需根据Path指令自定义序列化 } return {'segments': segments}; }).toList(); } // 反序列化恢复Path List<Path> deserializePaths(List<dynamic> jsonList) { return jsonList.map((json) { final path = Path(); final segments = json['segments'] as List; for (final seg in segments) { path.addPath(Path.fromJson(seg['points']), Offset.zero); } return path; }).toList(); }
3. 写入/读取本地文件
结合path_provider获取应用私有目录,用dart:io操作文件:
import 'dart:convert'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; // 获取应用文档目录路径 Future<String> get _localDirPath async { final dir = await getApplicationDocumentsDirectory(); return dir.path; } Future<File> get _stateFile async { final path = await _localDirPath; return File('$path/app_state.json'); } // 保存应用状态 Future<File> saveAppState(List<Offset> buttons, List<Path> drawings) async { final file = await _stateFile; final data = { 'buttons': serializeButtons(buttons), 'drawings': serializePaths(drawings), }; return file.writeAsString(jsonEncode(data)); } // 加载应用状态 Future<Map<String, dynamic>?> loadAppState() async { try { final file = await _stateFile; final contents = await file.readAsString(); final json = jsonDecode(contents); return { 'buttons': deserializeButtons(json['buttons']), 'drawings': deserializePaths(json['drawings']), }; } catch (e) { // 文件不存在或读取失败,返回null return null; } }
方案二:使用Hive轻量数据库
Hive是NoSQL本地数据库,支持自定义对象存储,无需手动写大量序列化代码:
1. 配置依赖
在pubspec.yaml中添加:
dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0 dev_dependencies: hive_generator: ^1.1.5 build_runner: ^2.4.6
2. 注册自定义类型适配器
为Offset生成适配器,让Hive能直接存储该类型:
// 初始化Hive void initHive() async { await Hive.initFlutter(); Hive.registerAdapter(OffsetAdapter()); await Hive.openBox('app_state'); } // Offset适配器实现 class OffsetAdapter extends TypeAdapter<Offset> { @override final typeId = 0; @override Offset read(BinaryReader reader) { return Offset(reader.readDouble(), reader.readDouble()); } @override void write(BinaryWriter writer, Offset obj) { writer.writeDouble(obj.dx); writer.writeDouble(obj.dy); } }
3. 保存与加载状态
// 保存状态到Hive void saveStateToHive(List<Offset> buttons, List<Path> drawings) { final box = Hive.box('app_state'); box.put('buttons', buttons); // Path可转成字节存储,还原时再转回Path box.put('drawings', drawings.map((p) => p.toBytes()).toList()); } // 从Hive加载状态 Map<String, dynamic> loadStateFromHive() { final box = Hive.box('app_state'); final buttons = box.get('buttons', defaultValue: <Offset>[]); final drawings = box.get('drawings', defaultValue: <List<int>>[]) .map((bytes) => Path.fromBytes(bytes)) .toList(); return {'buttons': buttons, 'drawings': drawings}; }
关键注意事项
- 如果绘图是像素位图,建议直接保存为图片文件,在状态中存储图片路径即可,避免序列化大体积数据。
- 建议在应用退出时(如
dispose方法、WillPopScope监听返回事件)触发状态保存,防止数据丢失。 - 大体积绘图数据优先用文件存储,而非JSON或数据库,避免性能问题。
内容的提问来源于stack exchange,提问作者alicandari
相关产品推荐
相关产品推荐

