Flutter 跨平台实战:OpenHarmony 健康管理应用 Day6|基于 SharedPreferences 的数据本地持久化实现

📅 2026/7/12 17:15:24 👁️ 阅读次数 📝 编程学习
Flutter 跨平台实战:OpenHarmony 健康管理应用 Day6|基于 SharedPreferences 的数据本地持久化实现

🎯Flutter 跨平台实战:OpenHarmony 健康管理应用 Day6|基于 SharedPreferences 的数据本地持久化实现

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

🚀 前言

大家好,本篇是我真实完成Flutter + OpenHarmony 健康管理应用的 Day6 开发笔记,基于 Day1-Day5 已实现的多页面数据闭环,继续实现数据本地持久化功能,通过引入第三方库shared_preferences完成用户信息与健康数据的本地存储与读取,实现关闭应用数据不丢失的完整功能,全程只用 DevEco Studio 开发,一步不漏、全部写细,新手照着做就能一次成功!

我会把第三方库集成、数据存储与读取逻辑、跨平台适配的全过程记录得特别详细,照着做就能实现 Flutter 应用本地数据持久化,为后续完整应用开发打下坚实基础。

💥 本文你能学到!!!!!!全部都是干货!!!!!!

  1. Flutter 第三方库shared_preferences的完整集成方法
  2. 数据本地持久化的实现逻辑(存储 / 读取 / 更新)
  3. 应用启动自动读取本地数据的方法
  4. 表单数据提交与本地存储的联动逻辑
  5. 适配 OpenHarmony 的数据存储规范写法
  6. Web 端本地存储调试与踩坑解决方案
  7. 跨平台应用数据持久化的通用实现思路

🥝 开发环境与项目准备

1. 开发环境

  • 开发工具:DevEco Studio(全程仅使用这一个工具,不使用其他编辑器)
  • 开发语言:Dart
  • 框架:Flutter(OpenHarmony 适配版本)
  • 调试方式:Web 浏览器端调试(避开模拟器配置繁琐问题,完整验证数据持久化功能)
  • 后续适配:OpenHarmony 模拟器及真机,当前数据存储代码可直接兼容.hap打包发布

2. 项目准备

基于 Day5 已实现多页面数据闭环的项目,无需额外新建工程,直接在原项目中迭代开发,确保:

  1. 已完成用户信息录入、表单校验、多页面数据传递功能(Day1-Day5)
  2. 项目依赖完整、无报错,可正常启动运行
  3. 保留完整 ohos 跨平台适配目录,不破坏原有工程结构
  4. 已添加shared_preferences第三方依赖并安装成功

本项目全程基于 Flutter for OpenHarmony 开发,后续会集成更多第三方库完成数据可视化、用户管理等高级功能,本篇为数据持久化开发篇。

📝 核心功能:数据本地持久化实现

1. 功能说明

本次 Day6 开发,通过shared_preferences实现以下核心功能:

  • 数据本地存储:用户信息与健康数据提交时,同步保存到本地
  • 自动数据读取:应用启动时,自动读取本地存储的数据并填充到表单
  • 数据持久化:关闭应用重新打开,之前填写的数据不会丢失
  • 表单联动:数据读取、存储与表单校验、页面跳转无缝联动

2. 完整可运行核心代码(lib/main.dart

import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: '鸿蒙健康管理APP', debugShowCheckedModeBanner: false, theme: ThemeData(primarySwatch: Colors.blue), home: const UserInfoPage(), ); } } // ========================== // 页面1:用户信息录入(支持本地保存/读取) // ========================== class UserInfoPage extends StatefulWidget { const UserInfoPage({super.key}); @override State<UserInfoPage> createState() => _UserInfoPageState(); } class _UserInfoPageState extends State<UserInfoPage> { final _formKey = GlobalKey<FormState>(); final nameCtrl = TextEditingController(); final ageCtrl = TextEditingController(); String? gender; // 打开页面时:自动读取本地数据 @override void initState() { super.initState(); loadLocalData(); } // 读取本地保存的数据 Future<void> loadLocalData() async { final sp = await SharedPreferences.getInstance(); setState(() { nameCtrl.text = sp.getString("name") ?? ""; ageCtrl.text = sp.getString("age") ?? ""; gender = sp.getString("gender"); }); } // 保存数据到本地 Future<void> saveLocalData() async { final sp = await SharedPreferences.getInstance(); sp.setString("name", nameCtrl.text); sp.setString("age", ageCtrl.text); sp.setString("gender", gender!); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("用户信息录入"), centerTitle: true, ), body: Padding( padding: const EdgeInsets.all(20), child: Form( key: _formKey, child: SingleChildScrollView( child: Column( children: [ TextFormField( controller: nameCtrl, decoration: const InputDecoration( labelText: "姓名", border: OutlineInputBorder(), ), validator: (v) => v == null || v.isEmpty ? "请输入姓名" : null, ), const SizedBox(height: 15), TextFormField( controller: ageCtrl, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: "年龄", border: OutlineInputBorder(), ), validator: (v) => v == null || v.isEmpty ? "请输入年龄" : null, ), const SizedBox(height: 15), DropdownButtonFormField<String>( value: gender, decoration: const InputDecoration( labelText: "性别", border: OutlineInputBorder(), ), items: const [ DropdownMenuItem(value: "男", child: Text("男")), DropdownMenuItem(value: "女", child: Text("女")), ], onChanged: (val) { setState(() { gender = val; }); }, validator: (v) => v == null ? "请选择性别" : null, ), const SizedBox(height: 25), // 保存 + 下一步 ElevatedButton( onPressed: () async { if (_formKey.currentState!.validate()) { await saveLocalData(); // 保存到本地 Navigator.push( context, MaterialPageRoute( builder: (context) => HealthInputPage( name: nameCtrl.text, age: ageCtrl.text, gender: gender!, ), ), ); } }, child: const Text("保存并下一步 → 健康数据"), ), ], ), ), ), ), ); } } // ========================== // 页面2:健康数据录入 // ========================== class HealthInputPage extends StatefulWidget { final String name; final String age; final String gender; const HealthInputPage({ super.key, required this.name, required this.age, required this.gender, }); @override State<HealthInputPage> createState() => _HealthInputPageState(); } class _HealthInputPageState extends State<HealthInputPage> { final formKey = GlobalKey<FormState>(); final heightCtrl = TextEditingController(); final weightCtrl = TextEditingController(); final heartCtrl = TextEditingController(); @override void initState() { super.initState(); loadHealthData(); } // 读取健康数据 Future<void> loadHealthData() async { final sp = await SharedPreferences.getInstance(); setState(() { heightCtrl.text = sp.getString("height") ?? ""; weightCtrl.text = sp.getString("weight") ?? ""; heartCtrl.text = sp.getString("heart") ?? ""; }); } // 保存健康数据 Future<void> saveHealthData() async { final sp = await SharedPreferences.getInstance(); sp.setString("height", heightCtrl.text); sp.setString("weight", weightCtrl.text); sp.setString("heart", heartCtrl.text); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("健康数据录入"), centerTitle: true, ), body: Padding( padding: const EdgeInsets.all(20), child: Form( key: formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("用户:${widget.name}", style: const TextStyle(fontSize: 16, color: Colors.blue)), const SizedBox(height: 15), TextFormField( controller: heightCtrl, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: "身高(cm)", border: OutlineInputBorder(), ), validator: (v) => v == null || v.isEmpty ? "请输入身高" : null, ), const SizedBox(height: 15), TextFormField( controller: weightCtrl, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: "体重(kg)", border: OutlineInputBorder(), ), validator: (v) => v == null || v.isEmpty ? "请输入体重" : null, ), const SizedBox(height: 15), TextFormField( controller: heartCtrl, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: "心率", border: OutlineInputBorder(), ), validator: (v) => v == null || v.isEmpty ? "请输入心率" : null, ), const SizedBox(height: 30), Center( child: ElevatedButton( onPressed: () async { if (formKey.currentState!.validate()) { await saveHealthData(); Navigator.push( context, MaterialPageRoute( builder: (context) => DataShowPage( name: widget.name, age: widget.age, gender: widget.gender, height: heightCtrl.text, weight: weightCtrl.text, heart: heartCtrl.text, ), ), ); } }, child: const Text("提交并查看数据"), ), ), ], ), ), ), ), ); } } // ========================== // 页面3:数据总览页(已修正语法错误) // ========================== class DataShowPage extends StatelessWidget { final String name; final String age; final String gender; final String height; final String weight; final String heart; const DataShowPage({ super.key, required this.name, required this.age, required this.gender, required this.height, required this.weight, required this.heart, }); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("健康数据总览"), centerTitle: true, ), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "✅ 用户信息", style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), ), const SizedBox(height: 10), Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("姓名:$name", style: const TextStyle(fontSize: 16)), Text("年龄:$age", style: const TextStyle(fontSize: 16)), Text("性别:$gender", style: const TextStyle(fontSize: 16)), ], ), ), ), const Divider(height: 40), const Text( "✅ 健康数据", style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), ), const SizedBox(height: 10), Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("身高:$height cm", style: const TextStyle(fontSize: 16)), Text("体重:$weight kg", style: const TextStyle(fontSize: 16)), Text("心率:$heart 次/分", style: const TextStyle(fontSize: 16)), ], ), ), ), const SizedBox(height: 40), const Center( child: Text( "🎉 数据已保存到本地!", style: TextStyle(fontSize: 20, color: Colors.blue, fontWeight: FontWeight.bold), ), ), ], ), ), ); } }

3. 关键逻辑与加分点解析

  • 第三方库集成:引入shared_preferences第三方库,实现数据本地存储,完全符合课程要求。
  • 自动数据读取:通过initState钩子,在页面初始化时自动读取本地数据并填充表单,用户体验流畅。
  • 提交同步存储:表单校验通过、页面跳转前,同步将数据保存到本地,确保数据不会丢失。
  • 持久化效果验证:关闭应用重新打开,表单自动填充之前的数据,实现真正的持久化存储。
  • 跨平台兼容shared_preferences是 Flutter 官方推荐的轻量级存储方案,可直接兼容 OpenHarmony 平台。

📱 调试与运行完整流程

  1. 安装第三方依赖:在项目根目录执行flutter pub get,确保shared_preferences依赖安装成功。
  2. 启动调试服务:在 DevEco Studio 内置终端输入flutter run -d web-server,启动本地调试服务。
  3. 访问应用首页:复制终端输出的本地地址,在浏览器中打开,进入用户信息录入页面。
  4. 测试数据存储:填写用户信息,点击 “保存并下一步”,数据同步保存到本地。
  5. 测试数据持久化:关闭浏览器并重新打开,表单会自动填充之前填写的数据,验证持久化效果。
  6. 全流程功能测试:页面跳转、表单校验、数据存储与读取均正常运行,无报错。

🔐 跨平台适配说明

本次开发的数据持久化代码,依托shared_preferences第三方库的跨平台特性,一套代码可支持多端运行:

平台运行方式适配说明
Chrome 浏览器端flutter run -d web-server调试便捷,可直接验证数据存储与读取逻辑
OpenHarmony 模拟器 / 真机配置 OH 环境后直接编译依赖无兼容问题,可直接打包生成.hap安装包

🧩 超全错误排查与解决方案

错误场景现象解决方案
依赖安装失败执行flutter pub get报错检查pubspec.yaml中依赖版本是否正确,网络环境是否正常
数据无法保存提交数据后本地数据为空检查保存逻辑是否正确调用,SharedPreferences.getInstance()是否获取成功
页面空白报错运行后页面无法加载检查pubspec.yaml依赖是否安装成功,执行flutter clean清理缓存后重新运行
数据读取为空应用启动表单无数据检查initState中读取数据的逻辑是否正确,键名是否与保存时一致

🎨 项目后续规划

Day6 已经完成数据本地持久化功能,后续依次开发:

  • Day7:数据可视化展示与页面样式优化
  • Day8+:对接 OpenHarmony 适配、项目整理与打包

📌 项目总结

本篇笔记完整记录了在 DevEco Studio 中实现 Flutter 数据本地持久化的全过程,从第三方库集成、数据存储读取逻辑到调试运行,全程只用一款开发工具,熟悉了shared_preferences第三方库的用法、跨平台数据存储方案与 Web 端调试方式,同时和 Day1-Day5 的跨平台适配逻辑无缝衔接,为后续健康数据管理模块的开发打下了坚实基础。

✅ 结尾小贴士

  • 文中所有代码可以直接复制替换到自己项目中使用
  • 不用额外安装其他编辑器,全程 DevEco Studio 就能完成所有开发
  • 点赞 + 收藏,后续 Day7 数据可视化开发笔记更新不迷路!