鸿蒙专家 发表于 2026-7-25 11:00:01

Flutter鸿蒙SharedPreferences踩坑指南

在Flutter鸿蒙开发中,本地存储是绕不开的基础能力。之前遇到一个尴尬场景:用户选择了深色模式,关掉应用再打开又变回浅色,原因是主题状态只存在内存里,没做持久化。本文结合实践,详细梳理Flutter鸿蒙下使用shared_preferences包进行轻量存储的完整流程、常见踩坑点以及最佳实践。


dependencies:
shared_preferences: ^2.3.3

添加依赖后执行flutter pub get即可。该包由Flutter官方维护,鸿蒙社区也有适配版本(如shared_preferences_ohos),纯Dart接口,无需额外原生端配置。

SharedPreferences支持五种基础类型:int、double、bool、String、List<String>。读取时若key不存在返回null,通常用??赋默认值:

final prefs = await SharedPreferences.getInstance();
final count = prefs.getInt('count') ?? 0;
final name = prefs.getString('name') ?? '匿名';
final tags = prefs.getStringList('tags') ?? [];

注意getInstance()是异步的,因其底层需读文件。不能在build方法中直接调用,应在initState里异步加载,用_loading状态控制UI:

bool _loading = true;

@override
void initState() {
super.initState();
_loadAll();
}

Future<void> _loadAll() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
    _count = prefs.getInt('count') ?? 0;
    _notes = prefs.getStringList('notes') ?? [];
    _notifyEnabled = prefs.getBool('notify') ?? true;
    _lang = prefs.getString('lang') ?? 'zh';
    _fontScale = prefs.getDouble('font_scale') ?? 1.0;
    _loading = false;
});
}


典型场景一:计数器持久化。写入时先更新内存状态再异步写文件:

Future<void> _saveCount(int v) async {
setState(() => _count = v);
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('count', v);
}

即使快速连续点击加减按钮,SharedPreferences内部有队列处理,后写覆盖先写,最终值正确。

典型场景二:记事本List<String>。用setStringList存列表,每次添加/删除都需重写整个列表,因为底层是将整个列表序列化为JSON字符串再存储,并非增量更新。数据量大时性能下降明显:

Future<void> _addNote() async {
final text = _noteCtrl.text.trim();
if (text.isEmpty) return;
setState(() => _notes = [..._notes, text]);
_noteCtrl.clear();
final prefs = await SharedPreferences.getInstance();
await prefs.setStringList('notes', _notes);
}

Future<void> _removeNote(int index) async {
setState(() => _notes.removeAt(index));
final prefs = await SharedPreferences.getInstance();
await prefs.setStringList('notes', _notes);
}

实践中,几百条数据尚可,上千条建议改用SQLite。

典型场景三:用户偏好设置。开关、语言、字体缩放等每次改动立刻写入:

Future<void> _setNotify(bool v) async {
setState(() => _notifyEnabled = v);
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('notify', v);
}

可额外记录“上次保存”时间戳,让用户感知存储生效。

高级用法:JSON序列化存储对象。SharedPreferences不支持直接存复杂对象,需将对象序列化为字符串:

class User {
final String name;
final int age;
final List<String> tags;
User({required this.name, required this.age, required this.tags});
factory User.fromJson(Map<String, dynamic> json) => User(
    name: json['name'] as String,
    age: json['age'] as int,
    tags: (json['tags'] as List).cast<String>(),
);
Map<String, dynamic> toJson() => {'name': name, 'age': age, 'tags': tags};
}
// 存
await prefs.setString('user', jsonEncode(user.toJson()));
// 取
final json = prefs.getString('user');
if (json != null) {
final user = User.fromJson(jsonDecode(json));
}

清除数据用remove('key')或clear(),但clear()会清除应用所有SharedPreferences数据(包括其他模块),生产环境应避免直接调用clear(),改为只删除自己管理的key。

踩坑总结:
1. getInstance()是异步的,不能在build方法中直接调用,必须在initState里await并用_loading状态控制UI。
2. 写入操作(setInt等)返回Future<bool>,写入并非即时生效,若写入后立刻读取可能读到旧值,需要确保await完成。
3. setStringList是覆盖写而非增量更新,大数据量时性能差,建议几百条内使用,超限改用SQLite或Hive。
4. key命名冲突:SharedPreferences的key全局共享,应使用模块前缀,如"user_name"、"settings_theme",避免用"count"等通用名。
5. clear()太粗暴:会清空所有key,可能影响其他模块。删除单个key或自己管理好的key集合。
6. 平台兼容:鸿蒙上需安装对应的平台适配包(如shared_preferences_ohos),否则会抛MissingPluginException。建议在Demo中加入try/catch降级处理,平台不支持时给出提示,不崩掉应用。

与鸿蒙ArkTS存储方案对比:ArkTS的AppStorage是同步读取且支持存取对象(无需手动JSON序列化),还有@StorageProp装饰器能自动绑定数据与UI。Flutter的SharedPreferences异步设计不阻塞UI,但每个操作需await,略显啰嗦。两者各有优劣,按需选用。

最佳实践建议:
- 主题持久化:将themeMode和seedColor存进SharedPreferences,应用启动时恢复。
- 首次启动引导:用getBool('first_launch') ?? true判断,引导后设为false。
- 数据迁移:在SharedPreferences中存版本号,启动时根据版本执行迁移逻辑。
- 若数据量大,可考虑sqflite(关系型)或Hive(NoSQL),这两个在鸿蒙上的适配情况需另行验证。

SharedPreferences是Flutter鸿蒙开发中最简单的轻量存储方案,覆盖int、double、bool、String、List<String>五种类型及JSON序列化对象,适合存放用户偏好、小量计数器等场景。掌握上述踩坑点,能避免绝大多数线上问题。

热心网友2 发表于 2026-7-25 11:10:00

Re: Flutter鸿蒙SharedPreferences踩坑指南

看了楼主的分享,对于刚接触Flutter鸿蒙开发的人来说太实用了。特别是把主题状态丢内存这个坑,我也踩过,后来才意识到持久化的重要性。想问下楼主,鸿蒙版shared_preferences是不是直接替换包名就能用,还是需要在鸿蒙原生侧做额外配置?还有如果同时存多个键值对,用commit和await是否有性能差异?希望楼主有空能再补充下。

热心网友2 发表于 2026-7-25 11:10:00

Re: Flutter鸿蒙SharedPreferences踩坑指南

感谢鸿蒙专家的详细分享,非常实用!我之前也踩过 `getInstance()` 直接在 build 里调用的坑,看了你的 `_loading` 状态控制思路瞬间明白了。对于 `List` 存储的性能问题,我上次存了不到 200 条感觉还能接受,再大确实得换 SQLite。还想请教一下,在鸿蒙上使用 `shared_preferences_ohos` 这个适配包时,是否需要像一些原生插件那样配置鸿蒙的 `module.json` 之类的文件?还是直接 `flutter pub get` 就能用了?期待后续更多坑点合集!

热心网友2 发表于 2026-7-25 11:10:00

Re: Flutter鸿蒙SharedPreferences踩坑指南

楼主总结得很详细,尤其是异步加载和内存优先更新的思路非常实用。我补充一个点:对于频繁写入的场景(比如快速点击按钮),可以配合 `debounce` 或延迟写入,避免短期内多次重复写文件,能稍微减少I/O压力。另外,用JSON存储对象时记得处理异常和版本兼容,否则升级后可能出现反序列化失败。感谢分享!
页: [1]
查看完整版本: Flutter鸿蒙SharedPreferences踩坑指南