【Flutter for OpenHarmony 】第三方库实战:本地存储 SharedPreferences 完整实现(记住登录状态)✨
本文介绍了如何在Flutter for OpenHarmony项目中实现本地存储功能,重点讲解了使用shared_preferences库来保存用户登录状态。主要内容包括: 项目环境配置:使用Flutter 3.16.0-ohos鸿蒙适配版和shared_preferences 2.2.2库 核心功能设计:实现记住登录状态、自动登录、退出登录清除数据等功能 技术实现: 封装StorageServi
📱 Flutter for OpenHarmony 第三方库实战:本地存储 SharedPreferences 完整实现(记住登录状态)✨
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
写在前面
哈喽大家好!我还是那个正在学习 Flutter for OpenHarmony 的大一学生~
上两篇文章,我们已经完整实现了:
- Provider 状态管理(用户/商品/购物车数据统一管理)
- 下拉刷新 + 上拉加载更多(列表分页加载)
今天我们继续完成任务清单里的 第2个任务:本地存储(SharedPreferences),实现 App 开发中最常用的「记住登录状态」功能,让用户下次打开应用时不用重复登录,直接进入首页!
整篇文章依旧是小白友好、步骤超详细、代码可直接复制、鸿蒙真机验证通过,完全符合征文规范,写完就能直接发布~
一、本篇你能学到什么?🎯
- 如何在 Flutter 项目中引入
shared_preferences依赖 - 封装通用本地存储服务,统一管理所有存储逻辑
- 实现「记住我」登录状态持久化,重启应用自动登录
- 结合 Provider 状态管理,实现数据与存储的联动
- 鸿蒙平台本地存储权限与适配注意事项
- 完整的错误处理与状态恢复逻辑
- 可直接复用到你的电商/社区/工具类鸿蒙项目中
二、技术栈与环境说明 🧰
- Flutter 版本:
3.16.0-ohos(鸿蒙适配稳定版) - OpenHarmony SDK:API 10
- 状态管理:
provider: ^6.1.2(上篇已集成) - 本地存储:
shared_preferences: ^2.2.2(已兼容鸿蒙) - 开发工具:VS Code + DevEco Studio 4.0
- 测试设备:OpenHarmony 开发板 / 模拟器
三、为什么要做本地存储?🤔
很多刚入门的同学会觉得:“登录状态存内存里不就行了?”
但真实场景下,这样会有很多问题:
- App 重启后登录状态丢失,用户每次打开都要重新登录,体验极差
- 应用被系统后台杀死后,状态无法恢复
- 无法实现「记住我」「自动登录」这类用户友好的功能
- 无法存储用户偏好设置(如主题、语言、上次浏览位置)
而 shared_preferences 是 Flutter 生态中最常用的轻量级本地存储方案,完美适配 OpenHarmony 平台,专门用来存储简单的键值对数据,比如登录状态、用户偏好、配置项等,对新手非常友好!
四、功能整体设计思路 📝
我们要实现一个完整的「记住登录状态」流程:
- 首次登录时,根据用户勾选的「记住我」选项,决定是否保存登录信息
- 应用启动时,读取本地存储的登录状态,如果存在且勾选了「记住我」,则自动恢复登录
- 用户退出登录时,清除本地存储的登录信息
- 所有存储逻辑封装成统一的
StorageService,和业务代码解耦 - 结合 Provider,实现状态变化与本地存储的自动同步
五、完整代码实现(超详细,逐行讲解)🚀
第一步:添加 shared_preferences 依赖
打开项目根目录的 pubspec.yaml 文件,在 dependencies 中添加依赖:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.2 # 上篇已添加的状态管理库
dio: ^5.4.0 # 网络请求库
shared_preferences: ^2.2.2 # 鸿蒙兼容的本地存储库
添加完成后,在终端执行依赖拉取命令:
flutter pub get
💡 鸿蒙适配注意:shared_preferences: ^2.2.2 及以上版本已通过 OpenHarmony TPC 社区适配验证,可直接使用,无需额外修改平台代码。
第二步:封装通用本地存储服务
在 lib/ 目录下新建 services 文件夹,创建 storage_service.dart 文件,统一封装所有本地存储逻辑:
import 'package:shared_preferences/shared_preferences.dart';
/// 本地存储服务封装类
/// 统一管理所有键值对存储逻辑,避免业务代码直接操作 SharedPreferences
class StorageService {
// 单例模式,全局唯一实例
static final StorageService _instance = StorageService._internal();
factory StorageService() => _instance;
StorageService._internal();
late SharedPreferences _prefs;
/// 初始化存储服务,应用启动时必须调用
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
// ------------------- 登录状态相关存储 -------------------
static const String _keyIsLoggedIn = "is_logged_in";
static const String _keyRememberMe = "remember_me";
static const String _keyUsername = "username";
static const String _keyUserId = "user_id";
/// 保存登录状态
Future<void> saveLoginState({
required bool isLoggedIn,
required bool rememberMe,
required String username,
required String userId,
}) async {
await _prefs.setBool(_keyIsLoggedIn, isLoggedIn);
await _prefs.setBool(_keyRememberMe, rememberMe);
await _prefs.setString(_keyUsername, username);
await _prefs.setString(_keyUserId, userId);
}
/// 清除登录状态(退出登录时调用)
Future<void> clearLoginState() async {
await _prefs.remove(_keyIsLoggedIn);
await _prefs.remove(_keyRememberMe);
await _prefs.remove(_keyUsername);
await _prefs.remove(_keyUserId);
}
/// 获取保存的登录状态
bool get isLoggedIn => _prefs.getBool(_keyIsLoggedIn) ?? false;
/// 获取是否勾选了「记住我」
bool get rememberMe => _prefs.getBool(_keyRememberMe) ?? false;
/// 获取保存的用户名
String get savedUsername => _prefs.getString(_keyUsername) ?? "";
/// 获取保存的用户ID
String get savedUserId => _prefs.getString(_keyUserId) ?? "";
// ------------------- 其他通用存储方法 -------------------
/// 保存字符串
Future<void> setString(String key, String value) async {
await _prefs.setString(key, value);
}
/// 获取字符串
String? getString(String key) {
return _prefs.getString(key);
}
/// 保存布尔值
Future<void> setBool(String key, bool value) async {
await _prefs.setBool(key, value);
}
/// 获取布尔值
bool? getBool(String key) {
return _prefs.getBool(key);
}
/// 删除指定键
Future<void> remove(String key) async {
await _prefs.remove(key);
}
/// 清空所有存储数据
Future<void> clearAll() async {
await _prefs.clear();
}
}
💡 设计亮点:
- 采用单例模式,全局唯一存储实例,避免重复初始化
- 统一封装键名,避免业务代码中出现魔法字符串
- 所有存储操作封装成方法,业务代码直接调用,后续如果更换存储方案,只需修改这里即可
- 预留了通用存储方法,后续可以扩展存储用户偏好、设置等数据
第三步:修改 UserProvider,支持登录状态持久化
打开 lib/providers/user_provider.dart 文件,修改用户状态管理类,加入本地存储联动逻辑:
import 'package:flutter/foundation.dart';
import '../services/storage_service.dart';
// 用户模型类
class User {
final String id;
final String username;
final String avatar;
User({
required this.id,
required this.username,
required this.avatar,
});
}
class UserProvider extends ChangeNotifier {
User? _currentUser;
bool _isLoggedIn = false;
bool _rememberMe = false; // 记住我选项状态
User? get currentUser => _currentUser;
bool get isLoggedIn => _isLoggedIn;
bool get rememberMe => _rememberMe;
/// 应用启动时恢复登录状态
Future<void> restoreLoginState() async {
final storage = StorageService();
// 如果勾选了记住我,且保存了登录状态
if (storage.rememberMe && storage.isLoggedIn) {
_currentUser = User(
id: storage.savedUserId,
username: storage.savedUsername,
avatar: "https://picsum.photos/200",
);
_isLoggedIn = true;
_rememberMe = true;
notifyListeners();
}
}
/// 登录方法,支持记住我选项
Future<bool> login(String username, String password, bool rememberMe) async {
try {
// 模拟网络请求延迟
await Future.delayed(const Duration(seconds: 1));
// 测试账号:test / 123456
if (username == "test" && password == "123456") {
_currentUser = User(
id: "1001",
username: username,
avatar: "https://picsum.photos/200",
);
_isLoggedIn = true;
_rememberMe = rememberMe;
// 如果勾选了记住我,保存登录状态
if (rememberMe) {
await StorageService().saveLoginState(
isLoggedIn: true,
rememberMe: true,
username: username,
userId: "1001",
);
} else {
// 不勾选记住我,清除之前的保存状态
await StorageService().clearLoginState();
}
notifyListeners();
return true;
} else {
return false;
}
} catch (e) {
if (kDebugMode) {
print("登录出错:$e");
}
return false;
}
}
/// 退出登录方法,清除本地存储状态
Future<void> logout() async {
_currentUser = null;
_isLoggedIn = false;
_rememberMe = false;
// 清除本地存储的登录信息
await StorageService().clearLoginState();
notifyListeners();
}
/// 更新记住我选项状态(未登录时勾选/取消勾选)
void setRememberMe(bool value) {
_rememberMe = value;
notifyListeners();
}
}
💡 关键修改说明:
- 新增
restoreLoginState()方法,应用启动时从本地存储恢复登录状态 - 修改
login()方法,增加rememberMe参数,根据用户选择决定是否保存登录信息 - 修改
logout()方法,退出登录时清除本地存储的登录状态 - 新增
setRememberMe()方法,支持用户在登录页面修改记住我选项
第四步:修改 main.dart,初始化存储服务
打开 lib/main.dart 文件,在应用启动时初始化存储服务:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/user_provider.dart';
import 'providers/product_provider.dart';
import 'providers/cart_provider.dart';
import 'services/storage_service.dart';
import 'pages/home_page.dart';
void main() async {
// 确保 Flutter 绑定初始化完成,再调用异步方法
WidgetsFlutterBinding.ensureInitialized();
// 初始化本地存储服务
await StorageService().init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserProvider()),
ChangeNotifierProvider(create: (_) => ProductProvider()),
ChangeNotifierProvider(create: (_) => CartProvider()),
],
child: MaterialApp(
title: '鸿蒙电商 Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
debugShowCheckedModeBanner: false,
),
);
}
}
💡 注意:WidgetsFlutterBinding.ensureInitialized() 必须在 main() 开头调用,否则无法在 main() 中执行异步初始化方法。
第五步:修改登录页面,加入记住我选项
打开 lib/pages/home_page.dart 文件,修改登录页面,添加「记住我」复选框:
// 在登录页面的 build 方法中,添加记住我复选框
Column(
children: [
// 用户名输入框
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: '用户名',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
// 密码输入框
TextField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: '密码',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
// 记住我复选框
Consumer<UserProvider>(
builder: (context, userProvider, child) {
return Row(
children: [
Checkbox(
value: userProvider.rememberMe,
onChanged: (value) {
userProvider.setRememberMe(value ?? false);
},
),
const Text('记住我(下次自动登录)'),
],
);
},
),
const SizedBox(height: 16),
// 登录按钮
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isLoading
? null
: () async {
setState(() {
_isLoading = true;
});
// 调用登录方法,传入记住我选项
final success = await context.read<UserProvider>().login(
_usernameController.text,
_passwordController.text,
context.read<UserProvider>().rememberMe,
);
setState(() {
_isLoading = false;
});
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('登录成功!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('用户名或密码错误,请用 test / 123456 测试')),
);
}
},
child: _isLoading
? const CircularProgressIndicator(color: Colors.white)
: const Text('登录'),
),
),
],
)
同时,在 HomePage 的 initState 中,添加恢复登录状态的逻辑:
void initState() {
super.initState();
// 页面初始化时,恢复登录状态
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<UserProvider>().restoreLoginState();
});
}
第六步:鸿蒙平台适配注意事项
shared_preferences 在 OpenHarmony 平台上的适配非常简单,无需额外配置权限,即可正常使用。但有两个小细节需要注意:
- 数据存储位置:在鸿蒙设备上,
shared_preferences的数据会存储在应用私有目录下,其他应用无法访问,安全可靠 - 数据持久化:应用卸载时,本地存储的数据会被自动清除,符合鸿蒙系统的安全规范
- 模拟器与真机一致性:鸿蒙模拟器和真机上的存储行为完全一致,无需额外适配
六、运行与真机测试 ✅
-
执行命令拉取依赖
flutter pub get -
连接鸿蒙开发板 / 打开模拟器
-
运行项目
flutter run
-
测试完整流程:
- ✅ 首次登录:输入
test/123456,勾选「记住我」,点击登录,登录成功 - ✅ 退出应用:完全关闭应用,重新打开,直接显示已登录状态,无需重新输入账号密码
- ✅ 退出登录:点击退出登录,本地存储的登录状态被清除,下次打开需要重新登录
- ✅ 不勾选记住我:登录后关闭应用,重新打开需要重新登录
- ✅ 鸿蒙真机运行:存储状态稳定,重启设备后依然能恢复登录
- ✅ 首次登录:输入
七、核心原理通俗讲解(小白必看)💡
很多同学会好奇:shared_preferences 到底是怎么实现本地存储的?
其实原理非常简单:
shared_preferences会把数据以键值对的形式,写入设备本地的 XML 文件中- 应用启动时,读取这个 XML 文件,把数据加载到内存中
- 我们通过封装好的
setBool、getString等方法,读写这些键值对 - 数据会持久化保存在设备上,直到应用被卸载或主动清除数据
而我们实现的「记住登录状态」流程,就是把用户的登录信息和记住我选项,通过 shared_preferences 保存到本地,应用启动时再读取出来,自动恢复登录状态。
八、我踩过的巨坑(帮你少走弯路)⚠️
坑1:main() 中初始化存储服务报错
- 表现:运行时红屏报错
ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized - 原因:
main()中没有调用WidgetsFlutterBinding.ensureInitialized(),就执行了异步初始化方法 - 解决:在
main()开头添加WidgetsFlutterBinding.ensureInitialized();
坑2:登录状态保存后,重启应用无法恢复
- 表现:勾选记住我登录后,重启应用还是未登录状态
- 原因:
restoreLoginState()方法没有调用,或者调用时机不对 - 解决:在
HomePage的initState中,用addPostFrameCallback包裹调用,确保页面渲染完成后再恢复状态
坑3:退出登录后,下次打开还是登录状态
- 表现:点击退出登录,状态变成未登录,但重启应用又恢复了登录状态
- 原因:
logout()方法中没有调用clearLoginState()清除本地存储 - 解决:在
logout()方法中,添加await StorageService().clearLoginState();
坑4:鸿蒙设备上存储数据丢失
- 表现:模拟器上正常,真机上重启应用数据丢失
- 原因:部分低版本鸿蒙系统,应用后台杀死后会清除临时数据
- 解决:使用
shared_preferences: ^2.2.2及以上版本,该版本已修复鸿蒙真机存储丢失的问题
九、功能扩展方向(进阶必学)🚀
如果你想做得更完善,还可以继续扩展:
- 存储用户设置:比如主题模式、语言设置、消息推送开关
- 存储用户浏览历史:记录用户上次浏览的商品、页面位置
- 存储购物车数据:用户退出应用后,购物车商品不会丢失
- 存储本地缓存数据:比如商品列表数据,无网络时也能查看
- 实现数据加密:敏感数据(如密码、token)加密后再存储,提升安全性
十、总结 🎉
这篇文章我们完整实现了 Flutter + OpenHarmony 平台下的本地存储功能,完成了 App 开发中最常用的「记住登录状态」场景:
- ✅ 引入并适配鸿蒙的
shared_preferences依赖 - ✅ 封装通用本地存储服务,统一管理所有存储逻辑
- ✅ 结合 Provider 状态管理,实现登录状态的持久化
- ✅ 鸿蒙真机完美运行,重启应用自动登录
- ✅ 完整的错误处理与状态恢复逻辑
整个代码结构清晰、易于维护,完全可以直接用在你的鸿蒙征文、课程设计、大创项目、实战 Demo 中。
作为一名正在学习 Flutter 鸿蒙开发的大一学生,我深刻感受到:Flutter 生态真的太完善了,几乎所有常见的功能,都有成熟的三方库和方案,适配鸿蒙也非常简单,学习成本低、产出效率高。
下一篇预告
接下来我还会继续完成任务清单里的其他任务:
- 路由管理 go_router(规范化页面跳转)
- 图片缓存 cached_network_image(优化鸿蒙性能)
- 依赖注入 get_it(解耦业务逻辑)
- 购物车完整功能实现
想看哪个内容,欢迎在评论区告诉我~
更多推荐

所有评论(0)