【Flutter for OpenHarmony】Flutter三方库心情数据持久化功能的鸿蒙化适配与实战指南
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、为什么我要做数据持久化?
我是 IntMainJhy,大一计算机学生。说起数据持久化,我真的被坑了好几次。
最开始我写的心情记录功能,每次关闭 App 再打开,数据全没了!我当时就很懵:数据去哪了?
后来才知道,我之前只是把数据存在内存里了,App 一关,内存一清,数据自然就没了。
所以数据持久化就是:把数据保存到硬盘上,这样 App 关闭再打开,数据还在。
二、Flutter 数据持久化方案
Flutter 中常用的持久化方案:
| 方案 | 适用场景 | 特点 |
|---|---|---|
| SharedPreferences | 简单配置 | key-value,存字符串/数字 |
| Hive | 结构化数据 | NoSQL,存对象 |
| SQLite | 复杂查询 | 关系型数据库 |
我用的是SharedPreferences,因为心情记录的数据量不大,用它足够了。
三、Provider 数据持久化实现
// lib/mental_health/providers/mood_provider.dartimport'dart:convert';import'package:flutter/material.dart';import'package:shared_preferences/shared_preferences.dart';import'../models/mood_model.dart';classMoodProviderextendsChangeNotifier{// 存储 keystaticconstString_moodEntriesKey='mood_entries';staticconstString_streakKey='mood_streak';staticconstString_lastRecordDateKey='last_record_date';// 状态List<MoodEntry>_entries=[];int _currentStreak=0;bool _isLoading=true;// GettersList<MoodEntry>getentries=>_entries;intgetcurrentStreak=>_currentStreak;boolgetisLoading=>_isLoading;/// 初始化 - 从本地加载数据Future<void>initialize()async{_isLoading=true;notifyListeners();try{finalprefs=awaitSharedPreferences.getInstance();// 加载记录列表finalentriesJson=prefs.getString(_moodEntriesKey);if(entriesJson!=null){finalList<dynamic>decoded=jsonDecode(entriesJson);_entries=decoded.map((e)=>MoodEntry.fromJson(e)).toList();}// 加载连续天数_currentStreak=prefs.getInt(_streakKey)??0;// 检查是否需要重置连续天数_checkStreak();}catch(e){debugPrint('加载心情数据失败:$e');}finally{_isLoading=false;notifyListeners();}}/// 添加心情记录Future<void>addMood(MoodTypemood,{String?note})async{finalentry=MoodEntry(id:DateTime.now().millisecondsSinceEpoch.toString(),mood:mood,note:note,date:DateTime.now(),);_entries.insert(0,entry);await_saveEntries();// 保存到本地await_updateStreak();// 更新连续天数notifyListeners();}/// 删除心情记录Future<void>deleteMood(Stringid)async{_entries.removeWhere((e)=>e.id==id);await_saveEntries();await_updateStreak();notifyListeners();}/// 保存记录到本地Future<void>_saveEntries()async{finalprefs=awaitSharedPreferences.getInstance();finaljsonList=_entries.map((e)=>e.toJson()).toList();awaitprefs.setString(_moodEntriesKey,jsonEncode(jsonList));}/// 更新连续打卡天数Future<void>_updateStreak()async{finalprefs=awaitSharedPreferences.getInstance();finalnow=DateTime.now();finaltoday=DateTime(now.year,now.month,now.day);// 检查今天是否有记录finalhasToday=todayEntries.isNotEmpty;finallastRecordStr=prefs.getString(_lastRecordDateKey);if(hasToday){if(lastRecordStr==null){_currentStreak=1;}else{finallastRecord=DateTime.parse(lastRecordStr);finallastDate=DateTime(lastRecord.year,lastRecord.month,lastRecord.day);finaldiff=today.difference(lastDate).inDays;if(diff==0){// 同一天,不更新}elseif(diff==1){_currentStreak++;}else{_currentStreak=1;}}awaitprefs.setString(_lastRecordDateKey,today.toIso8601String());awaitprefs.setInt(_streakKey,_currentStreak);}}/// 检查是否需要重置连续天数void_checkStreak(){if(_entries.isEmpty){_currentStreak=0;return;}finalsorted=List<MoodEntry>.from(_entries)..sort((a,b)=>b.date.compareTo(a.date));finallatest=sorted.first.date;finaltoday=DateTime.now();finaldiff=DateTime(today.year,today.month,today.day).difference(DateTime(latest.year,latest.month,latest.day)).inDays;if(diff>1){_currentStreak=0;}}}四、模型序列化
为了能保存到 SharedPreferences,需要把对象转成 JSON:
// lib/mental_health/models/mood_model.dartclassMoodEntry{finalStringid;finalMoodTypemood;finalString?note;finalDateTimedate;MoodEntry({requiredthis.id,requiredthis.mood,this.note,requiredthis.date,});/// 转为 JSONMap<String,dynamic>toJson(){return{'id':id,'mood':mood.value,'note':note,'date':date.toIso8601String(),};}/// 从 JSON 创建factoryMoodEntry.fromJson(Map<String,dynamic>json){returnMoodEntry(id:json['id'],mood:MoodType.fromValue(json['mood']),note:json['note'],date:DateTime.parse(json['date']),);}}五、鸿蒙平台专属适配
适配点1:使用鸿蒙专用包
# pubspec.yamldependencies:shared_preferences:^2.3.5shared_preferences_harmonyos:^0.0.1shared_preferences_harmonyos是鸿蒙专用版本,API 和标准版完全一样,但针对鸿蒙做了优化。
六、我的踩坑记录
坑1:JSON 序列化时 null 值问题
问题:笔记为 null 时,JSON 解析报错。
解决:在 toJson 中处理 null:
Map<String,dynamic>toJson(){return{'note':note??'',// null 转为空字符串};}坑2:日期比较时忽略时分秒
问题:同一天的记录被认为在不同日期。
解决:去掉时分秒再比较:
finaltoday=DateTime(now.year,now.month,now.day);finalentryDate=DateTime(entry.date.year,entry.date.month,entry.date.day);坑3:首次加载时数据还没回来
问题:UI 显示加载中,但数据已经加载完了。
解决:用_isLoading状态控制:
boolgetisLoading=>_isLoading;// 在 build 中if(provider.isLoading){returnconstCenter(child:CircularProgressIndicator());}七、大一学生真实学习总结
数据持久化这个知识点真的很重要!以前我总觉得数据存在变量里就行了,后来才明白:
- 变量存在内存里,App 关了就丢了
- 持久化存在硬盘上,App 关了还在
这个概念不只是 Flutter,在任何平台开发都是这样的。
好啦,这篇文章就到这里!
作者:IntMainJhy
创作时间:2026年5月