Flutter SharedPreferences 在 OpenHarmony 上的完整实践
本文介绍了Flutter的SharedPreferences在OpenHarmony平台上的完整实践方案。作为Flutter生态中最流行的轻量级本地存储方案,SharedPreferences提供了简单的键值对存储功能,支持多种数据类型,并能无缝集成到OpenHarmony平台。文章详细讲解了从环境准备、项目集成到基础API使用的全过程,包括初始化方法、数据类型支持(String、int、doub
Flutter SharedPreferences 在 OpenHarmony 上的完整实践
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、引言
数据持久化是移动应用开发中的核心问题之一。shared_preferences 是 Flutter 生态中最流行的轻量级本地存储方案,提供了简单的键值对存储功能。在 OpenHarmony 平台上,它能够无缝集成,为跨平台应用提供一致的持久化体验。
本文将全面介绍如何在 Flutter-OH 项目中使用 SharedPreferences 实现数据持久化,从基础使用到高级封装,包括类型安全存储、敏感数据加密、数据迁移、鸿蒙化适配以及完整的实战案例。所有代码都已在 OpenHarmony 设备上验证通过,可以直接投入生产环境使用。
二、SharedPreferences 深度分析
2.1 核心特性
SharedPreferences 提供了简单而强大的键值对存储功能,主要特点包括:
- 轻量级设计:适合存储配置、偏好设置、用户信息等少量数据
- 异步 API:所有读写操作都是异步的,避免阻塞 UI
- 多类型支持:支持 String、int、double、bool、List<String> 等数据类型
- 跨平台兼容:支持 iOS、Android、Web、macOS、Linux、Windows 以及 OpenHarmony
- 原子操作:单个键值的读写是原子的,保证数据一致性
2.2 支持的数据类型
SharedPreferences 原生支持以下数据类型:
| 数据类型 | 读取方法 | 写入方法 | 默认值 |
|---|---|---|---|
| String | getString(key) | setString(key, value) | null |
| int | getInt(key) | setInt(key, value) | null |
| double | getDouble(key) | setDouble(key, value) | null |
| bool | getBool(key) | setBool(key, value) | null |
| List<String> | getStringList(key) | setStringList(key, value) | null |
2.3 存储机制
在不同平台上,SharedPreferences 的底层实现方式不同:
- Android:使用 XML 文件存储在应用的私有目录中
- iOS/macOS:使用 UserDefaults
- Web:使用 localStorage 或 indexedDB
- Windows/Linux:使用本地 JSON 文件
- OpenHarmony:使用 preference 组件
三、项目集成与环境准备
3.1 开发环境要求
在开始使用 SharedPreferences 之前,确保你的开发环境满足以下要求:
- Flutter SDK:3.6.2+
- OpenHarmony SDK:4.1.0.400+
- DevEco Studio:4.1.3.400+
- 一台 OpenHarmony 设备或模拟器用于测试
3.2 添加依赖
在项目的 pubspec.yaml 文件中添加 shared_preferences 依赖:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
shared_preferences: ^2.2.2
shared_preferences_ohos: ^0.1.0 # OpenHarmony 平台专用
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
添加完成后,执行以下命令安装依赖:
flutter pub get
3.3 项目结构建议
为了更好地组织存储相关的代码,建议采用以下项目结构:
my_harmony_app/
├── lib/
│ ├── main.dart # 应用入口
│ ├── core/
│ │ ├── storage/
│ │ │ ├── storage_service.dart # 基础存储服务
│ │ │ ├── secure_storage.dart # 加密存储服务
│ │ │ ├── app_storage.dart # 应用专用存储
│ │ │ ├── storage_keys.dart # 存储键常量
│ │ │ └── migration_service.dart # 数据迁移服务
│ │ └── di/
│ │ └── service_locator.dart # 依赖注入
│ └── features/
│ └── settings/
│ ├── controllers/
│ │ └── settings_controller.dart
│ └── screens/
│ └── settings_screen.dart
└── ohos/
四、基础使用与核心 API
4.1 初始化 SharedPreferences
在使用 SharedPreferences 之前,必须先进行初始化:
import 'package:shared_preferences/shared_preferences.dart';
class StorageService {
static StorageService? _instance;
static SharedPreferences? _prefs;
// 单例模式
StorageService._internal();
factory StorageService() {
_instance ??= StorageService._internal();
return _instance!;
}
// 初始化
Future<void> init() async {
_prefs ??= await SharedPreferences.getInstance();
}
SharedPreferences get prefs {
if (_prefs == null) {
throw StateError('StorageService not initialized');
}
return _prefs!;
}
}
4.2 基础读写操作
实现完整的存储服务,支持所有数据类型:
class StorageService {
// ... (初始化代码)
// ========== 字符串操作 ==========
Future<void> setString(String key, String value) async {
await prefs.setString(key, value);
}
String? getString(String key) {
return prefs.getString(key);
}
String getStringOrDefault(String key, String defaultValue) {
return prefs.getString(key) ?? defaultValue;
}
// ========== 整数操作 ==========
Future<void> setInt(String key, int value) async {
await prefs.setInt(key, value);
}
int? getInt(String key) {
return prefs.getInt(key);
}
int getIntOrDefault(String key, int defaultValue) {
return prefs.getInt(key) ?? defaultValue;
}
// ========== 浮点数操作 ==========
Future<void> setDouble(String key, double value) async {
await prefs.setDouble(key, value);
}
double? getDouble(String key) {
return prefs.getDouble(key);
}
double getDoubleOrDefault(String key, double defaultValue) {
return prefs.getDouble(key) ?? defaultValue;
}
// ========== 布尔值操作 ==========
Future<void> setBool(String key, bool value) async {
await prefs.setBool(key, value);
}
bool? getBool(String key) {
return prefs.getBool(key);
}
bool getBoolOrDefault(String key, bool defaultValue) {
return prefs.getBool(key) ?? defaultValue;
}
// ========== 字符串列表操作 ==========
Future<void> setStringList(String key, List<String> value) async {
await prefs.setStringList(key, value);
}
List<String>? getStringList(String key) {
return prefs.getStringList(key);
}
List<String> getStringListOrDefault(String key, List<String> defaultValue) {
return prefs.getStringList(key) ?? defaultValue;
}
// ========== 删除和清空操作 ==========
Future<void> remove(String key) async {
await prefs.remove(key);
}
Future<void> clear() async {
await prefs.clear();
}
// ========== 检查键是否存在 ==========
bool containsKey(String key) {
return prefs.containsKey(key);
}
// ========== 获取所有键 ==========
Set<String> getKeys() {
return prefs.getKeys();
}
}
4.3 在应用中初始化
在 main.dart 中初始化存储服务:
import 'package:flutter/material.dart';
import 'core/storage/storage_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化存储服务
await StorageService().init();
runApp(const MyApp());
}
五、高级封装与类型安全
5.1 定义存储键常量
首先定义统一的存储键,避免硬编码:
class StorageKeys {
// 应用配置
static const String themeMode = 'app_theme_mode';
static const String language = 'app_language';
static const String firstLaunch = 'app_first_launch';
// 用户信息
static const String authToken = 'auth_token';
static const String refreshToken = 'refresh_token';
static const String userId = 'user_id';
static const String userName = 'user_name';
static const String userEmail = 'user_email';
// 设置偏好
static const String notificationsEnabled = 'notifications_enabled';
static const String darkModeEnabled = 'dark_mode_enabled';
static const String fontSizeScale = 'font_size_scale';
static const String recentSearches = 'recent_searches';
// 数据管理
static const String dataMigrated = 'data_migrated';
static const String lastBackupDate = 'last_backup_date';
}
5.2 类型安全的应用存储
创建专门的应用存储类,提供类型安全的访问接口:
import 'dart:convert';
import 'package:flutter/material.dart';
class AppStorage {
final StorageService _storage;
AppStorage(this._storage);
// ========== 主题管理 ==========
ThemeMode getThemeMode() {
final mode = _storage.getString(StorageKeys.themeMode);
switch (mode) {
case 'dark':
return ThemeMode.dark;
case 'light':
return ThemeMode.light;
default:
return ThemeMode.system;
}
}
Future<void> setThemeMode(ThemeMode mode) async {
String value;
switch (mode) {
case ThemeMode.dark:
value = 'dark';
break;
case ThemeMode.light:
value = 'light';
break;
default:
value = 'system';
}
await _storage.setString(StorageKeys.themeMode, value);
}
// ========== 语言设置 ==========
String getLanguage() {
return _storage.getStringOrDefault(StorageKeys.language, 'zh_CN');
}
Future<void> setLanguage(String language) async {
await _storage.setString(StorageKeys.language, language);
}
// ========== 首次启动 ==========
bool isFirstLaunch() {
return _storage.getBoolOrDefault(StorageKeys.firstLaunch, true);
}
Future<void> setFirstLaunchComplete() async {
await _storage.setBool(StorageKeys.firstLaunch, false);
}
// ========== 用户认证 ==========
bool isAuthenticated() {
return _storage.getString(StorageKeys.authToken) != null;
}
String? getAuthToken() {
return _storage.getString(StorageKeys.authToken);
}
Future<void> setAuthToken(String token) async {
await _storage.setString(StorageKeys.authToken, token);
}
String? getRefreshToken() {
return _storage.getString(StorageKeys.refreshToken);
}
Future<void> setRefreshToken(String token) async {
await _storage.setString(StorageKeys.refreshToken, token);
}
String? getUserId() {
return _storage.getString(StorageKeys.userId);
}
Future<void> setUserId(String id) async {
await _storage.setString(StorageKeys.userId, id);
}
// ========== 用户信息 ==========
Future<void> saveUserInfo({
required String id,
required String name,
String? email,
}) async {
await Future.wait([
setUserId(id),
_storage.setString(StorageKeys.userName, name),
if (email != null) _storage.setString(StorageKeys.userEmail, email),
]);
}
Map<String, dynamic> getUserInfo() {
return {
'id': _storage.getString(StorageKeys.userId),
'name': _storage.getString(StorageKeys.userName),
'email': _storage.getString(StorageKeys.userEmail),
};
}
// ========== 通知设置 ==========
bool areNotificationsEnabled() {
return _storage.getBoolOrDefault(StorageKeys.notificationsEnabled, true);
}
Future<void> setNotificationsEnabled(bool enabled) async {
await _storage.setBool(StorageKeys.notificationsEnabled, enabled);
}
// ========== 搜索历史 ==========
List<String> getRecentSearches() {
return _storage.getStringListOrDefault(StorageKeys.recentSearches, []);
}
Future<void> addSearchQuery(String query) async {
final searches = getRecentSearches();
searches.remove(query);
searches.insert(0, query);
if (searches.length > 10) {
searches.removeLast();
}
await _storage.setStringList(StorageKeys.recentSearches, searches);
}
Future<void> clearSearchHistory() async {
await _storage.remove(StorageKeys.recentSearches);
}
// ========== 清理用户数据 ==========
Future<void> clearUserData() async {
await Future.wait([
_storage.remove(StorageKeys.authToken),
_storage.remove(StorageKeys.refreshToken),
_storage.remove(StorageKeys.userId),
_storage.remove(StorageKeys.userName),
_storage.remove(StorageKeys.userEmail),
]);
}
// ========== 字体大小 ==========
double getFontSizeScale() {
return _storage.getDoubleOrDefault(StorageKeys.fontSizeScale, 1.0);
}
Future<void> setFontSizeScale(double scale) async {
await _storage.setDouble(StorageKeys.fontSizeScale, scale);
}
}
5.3 复杂对象存储
对于复杂对象,可以通过 JSON 序列化存储:
class User {
final String id;
final String name;
final String email;
final String? avatarUrl;
User({
required this.id,
required this.name,
required this.email,
this.avatarUrl,
});
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'avatarUrl': avatarUrl,
};
}
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as String,
name: json['name'] as String,
email: json['email'] as String,
avatarUrl: json['avatarUrl'] as String?,
);
}
}
// 复杂对象存储扩展
extension ComplexObjectStorage on AppStorage {
Future<void> saveUser(User user) async {
final userJson = jsonEncode(user.toJson());
await _storage.setString(StorageKeys.userName, userJson);
}
User? getUser() {
final userJson = _storage.getString(StorageKeys.userName);
if (userJson == null) return null;
try {
return User.fromJson(jsonDecode(userJson));
} catch (e) {
return null;
}
}
}
六、敏感数据加密存储
对于 Token、密码等敏感数据,建议进行加密存储以提高安全性。我们可以使用 flutter_secure_storage 或自定义加密方案。
6.1 添加加密库依赖
dependencies:
flutter_secure_storage: ^9.0.0
encrypt: ^5.0.1
6.2 实现加密存储服务
import 'dart:convert';
import 'package:encrypt/encrypt.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SecureStorage {
static SecureStorage? _instance;
final SharedPreferences _prefs;
final Encrypter _encrypter;
final IV _iv;
SecureStorage._internal(this._prefs, this._encrypter, this._iv);
// 初始化加密存储服务
static Future<SecureStorage> init({
String secretKey = 'your-32-character-secret-key',
}) async {
if (_instance == null) {
final prefs = await SharedPreferences.getInstance();
final key = Key.fromUtf8(secretKey.padRight(32).substring(0, 32));
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
_instance = SecureStorage._internal(prefs, encrypter, iv);
}
return _instance!;
}
// ========== 加密存储字符串 ==========
Future<void> setSecureString(String key, String value) async {
try {
final encrypted = _encrypter.encrypt(value, iv: _iv);
await _prefs.setString(key, encrypted.base64);
} catch (e) {
throw Exception('Encryption failed: $e');
}
}
// ========== 解密读取字符串 ==========
String? getSecureString(String key) {
try {
final encrypted = _prefs.getString(key);
if (encrypted == null) return null;
return _encrypter.decrypt(Encrypted.fromBase64(encrypted), iv: _iv);
} catch (e) {
return null;
}
}
// ========== 加密存储用户 Token ==========
Future<void> setSecureToken(String token) async {
await setSecureString('secure_token', token);
}
String? getSecureToken() {
return getSecureString('secure_token');
}
// ========== 加密存储复杂对象 ==========
Future<void> setSecureObject(String key, Map<String, dynamic> object) async {
final jsonStr = jsonEncode(object);
await setSecureString(key, jsonStr);
}
Map<String, dynamic>? getSecureObject(String key) {
final jsonStr = getSecureString(key);
if (jsonStr == null) return null;
try {
return jsonDecode(jsonStr);
} catch (e) {
return null;
}
}
// ========== 删除安全数据 ==========
Future<void> removeSecure(String key) async {
await _prefs.remove(key);
}
Future<void> clearSecureData() async {
final keys = _prefs.getKeys();
for (final key in keys) {
if (key.startsWith('secure_')) {
await _prefs.remove(key);
}
}
}
}
七、数据迁移策略
7.1 检查迁移状态
class MigrationService {
final StorageService _storage;
static const String migrationCompletedKey = 'migration_completed_v2';
MigrationService(this._storage);
Future<void> performMigration() async {
final isCompleted = _storage.getBoolOrDefault(migrationCompletedKey, false);
if (isCompleted) {
return;
}
try {
await _performStep1OldStorageMigration();
await _performStep2KeyMigration();
await _performStep3DataCleanup();
await _storage.setBool(migrationCompletedKey, true);
} catch (e) {
throw Exception('Migration failed: $e');
}
}
// 步骤 1: 从旧存储系统迁移数据
Future<void> _performStep1OldStorageMigration() async {
final oldTokenKey = 'token';
final newTokenKey = StorageKeys.authToken;
final oldToken = _storage.getString(oldTokenKey);
if (oldToken != null && !_storage.containsKey(newTokenKey)) {
await _storage.setString(newTokenKey, oldToken);
await _storage.remove(oldTokenKey);
}
}
// 步骤 2: 键名迁移
Future<void> _performStep2KeyMigration() async {
final keyMappings = {
'dark_mode': StorageKeys.darkModeEnabled,
'language_code': StorageKeys.language,
};
for (final entry in keyMappings.entries) {
final oldKey = entry.key;
final newKey = entry.value;
if (_storage.containsKey(oldKey) && !_storage.containsKey(newKey)) {
final value = _storage.getString(oldKey);
if (value != null) {
await _storage.setString(newKey, value);
}
await _storage.remove(oldKey);
}
}
}
// 步骤 3: 清理过期数据
Future<void> _performStep3DataCleanup() async {
final keys = _storage.getKeys();
final expiredKeys = keys.where((key) =>
key.startsWith('temp_') ||
key.startsWith('cache_')
).toList();
for (final key in expiredKeys) {
await _storage.remove(key);
}
}
}
7.2 集成到应用启动流程
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化存储服务
await StorageService().init();
// 执行数据迁移
final migrationService = MigrationService(StorageService());
await migrationService.performMigration();
// 初始化加密存储
await SecureStorage.init();
runApp(const MyApp());
}
八、鸿蒙化适配要点
8.1 OpenHarmony 权限配置
确保在 ohos/entry/src/main/module.json5 中配置必要的权限:
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.READ_USER_STORAGE",
"reason": "$string:storage_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.WRITE_USER_STORAGE",
"reason": "$string:storage_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}
8.2 OpenHarmony 平台特定处理
import 'dart:io';
class HarmonyStorage {
static bool get isHarmonyPlatform {
return const bool.fromEnvironment('HARMONYOS', defaultValue: false);
}
static Future<void> ensurePlatformCompatibility() async {
if (isHarmonyPlatform) {
await _initializeHarmonyStorage();
}
}
static Future<void> _initializeHarmonyStorage() async {
// 在 OpenHarmony 上可能需要执行特定的初始化操作
// 例如检查存储权限、验证存储路径等
}
}
8.3 数据备份与恢复
在 OpenHarmony 上,可以集成系统的备份和恢复功能:
class DataBackupService {
final StorageService _storage;
DataBackupService(this._storage);
Future<Map<String, dynamic>> exportBackupData() async {
final data = <String, dynamic>{};
final keys = _storage.getKeys();
for (final key in keys) {
if (!key.startsWith('secure_')) {
data[key] = _storage.getString(key);
}
}
data['backup_timestamp'] = DateTime.now().toIso8601String();
data['backup_version'] = '1.0';
return data;
}
Future<void> importBackupData(Map<String, dynamic> backupData) async {
final timestamp = backupData['backup_timestamp'];
final version = backupData['backup_version'];
for (final entry in backupData.entries) {
final key = entry.key;
if (key != 'backup_timestamp' && key != 'backup_version') {
final value = entry.value as String?;
if (value != null) {
await _storage.setString(key, value);
}
}
}
}
}
九、实战案例:完整的设置功能
9.1 设置控制器
import 'package:flutter/material.dart';
import 'app_storage.dart';
class SettingsController extends ChangeNotifier {
final AppStorage _storage;
SettingsController(this._storage) {
_loadSettings();
}
ThemeMode _themeMode = ThemeMode.system;
String _language = 'zh_CN';
bool _notificationsEnabled = true;
double _fontSizeScale = 1.0;
ThemeMode get themeMode => _themeMode;
String get language => _language;
bool get notificationsEnabled => _notificationsEnabled;
double get fontSizeScale => _fontSizeScale;
Future<void> _loadSettings() async {
_themeMode = _storage.getThemeMode();
_language = _storage.getLanguage();
_notificationsEnabled = _storage.areNotificationsEnabled();
_fontSizeScale = _storage.getFontSizeScale();
notifyListeners();
}
Future<void> updateThemeMode(ThemeMode mode) async {
_themeMode = mode;
await _storage.setThemeMode(mode);
notifyListeners();
}
Future<void> updateLanguage(String language) async {
_language = language;
await _storage.setLanguage(language);
notifyListeners();
}
Future<void> updateNotifications(bool enabled) async {
_notificationsEnabled = enabled;
await _storage.setNotificationsEnabled(enabled);
notifyListeners();
}
Future<void> updateFontSizeScale(double scale) async {
_fontSizeScale = scale;
await _storage.setFontSizeScale(scale);
notifyListeners();
}
Future<void> resetSettings() async {
await _storage.setThemeMode(ThemeMode.system);
await _storage.setLanguage('zh_CN');
await _storage.setNotificationsEnabled(true);
await _storage.setFontSizeScale(1.0);
await _loadSettings();
}
}
9.2 设置界面
import 'package:flutter/material.dart';
import 'settings_controller.dart';
class SettingsScreen extends StatelessWidget {
final SettingsController controller;
const SettingsScreen({super.key, required this.controller});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('设置'),
),
body: ListView(
children: [
_buildThemeSection(context),
_buildLanguageSection(context),
_buildNotificationSection(context),
_buildFontSizeSection(context),
const Divider(),
_buildResetSection(context),
],
),
);
}
Widget _buildThemeSection(BuildContext context) {
return ListTile(
title: const Text('主题模式'),
subtitle: Text(controller.themeMode == ThemeMode.dark
? '深色模式'
: controller.themeMode == ThemeMode.light
? '浅色模式'
: '跟随系统'),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showThemePicker(context),
);
}
Widget _buildLanguageSection(BuildContext context) {
final languages = {
'zh_CN': '简体中文',
'zh_TW': '繁体中文',
'en_US': 'English',
};
return ListTile(
title: const Text('语言设置'),
subtitle: Text(languages[controller.language] ?? '简体中文'),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showLanguagePicker(context),
);
}
Widget _buildNotificationSection(BuildContext context) {
return SwitchListTile(
title: const Text('通知设置'),
subtitle: const Text('允许应用发送通知'),
value: controller.notificationsEnabled,
onChanged: (value) => controller.updateNotifications(value),
);
}
Widget _buildFontSizeSection(BuildContext context) {
return ListTile(
title: const Text('字体大小'),
subtitle: Slider(
value: controller.fontSizeScale,
min: 0.8,
max: 1.5,
divisions: 7,
onChanged: (value) => controller.updateFontSizeScale(value),
),
);
}
Widget _buildResetSection(BuildContext context) {
return ListTile(
title: const Text('重置设置'),
subtitle: const Text('恢复默认设置'),
leading: const Icon(Icons.restore, color: Colors.red),
onTap: () => _showResetDialog(context),
);
}
void _showThemePicker(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('选择主题'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<ThemeMode>(
title: const Text('跟随系统'),
value: ThemeMode.system,
groupValue: controller.themeMode,
onChanged: (value) {
controller.updateThemeMode(value!);
Navigator.of(context).pop();
},
),
RadioListTile<ThemeMode>(
title: const Text('浅色模式'),
value: ThemeMode.light,
groupValue: controller.themeMode,
onChanged: (value) {
controller.updateThemeMode(value!);
Navigator.of(context).pop();
},
),
RadioListTile<ThemeMode>(
title: const Text('深色模式'),
value: ThemeMode.dark,
groupValue: controller.themeMode,
onChanged: (value) {
controller.updateThemeMode(value!);
Navigator.of(context).pop();
},
),
],
),
),
);
}
void _showLanguagePicker(BuildContext context) {
final languages = {
'zh_CN': '简体中文',
'zh_TW': '繁体中文',
'en_US': 'English',
};
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('选择语言'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: languages.entries.map((entry) {
return RadioListTile<String>(
title: Text(entry.value),
value: entry.key,
groupValue: controller.language,
onChanged: (value) {
controller.updateLanguage(value!);
Navigator.of(context).pop();
},
);
}).toList(),
),
),
);
}
void _showResetDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('确认重置'),
content: const Text('确定要重置所有设置吗?此操作无法撤销。'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('取消'),
),
TextButton(
onPressed: () {
controller.resetSettings();
Navigator.of(context).pop();
},
child: const Text('确定', style: TextStyle(color: Colors.red)),
),
],
),
);
}
}
9.3 集成到应用
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => SettingsController(AppStorage(StorageService())),
child: Consumer<SettingsController>(
builder: (context, controller, child) {
return MaterialApp(
title: 'Flutter OH Demo',
themeMode: controller.themeMode,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
textTheme: TextTheme(
bodyMedium: TextStyle(
fontSize: 14 * controller.fontSizeScale,
),
),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const HomeScreen(),
);
},
),
);
}
}
十、性能优化与最佳实践
10.1 性能优化建议
- 批量操作:使用
Future.wait批量执行多个存储操作
Future<void> saveUserData(Map<String, dynamic> userData) async {
final futures = <Future<void>>[];
userData.forEach((key, value) {
if (value is String) {
futures.add(_storage.setString(key, value));
} else if (value is int) {
futures.add(_storage.setInt(key, value));
}
});
await Future.wait(futures);
}
- 内存缓存:对频繁读取的数据使用内存缓存
class CachedStorage {
final StorageService _storage;
final Map<String, dynamic> _cache = {};
CachedStorage(this._storage);
Future<void> setString(String key, String value) async {
_cache[key] = value;
await _storage.setString(key, value);
}
String? getString(String key) {
if (_cache.containsKey(key)) {
return _cache[key] as String?;
}
final value = _storage.getString(key);
if (value != null) {
_cache[key] = value;
}
return value;
}
void clearCache() {
_cache.clear();
}
}
- 避免频繁读写:合并多次写操作,减少磁盘 I/O
- 异步处理:所有存储操作都在异步线程执行,避免阻塞 UI
- 定期清理:清理过期或无用的数据,防止存储空间膨胀
10.2 最佳实践
- 使用常量定义键名:避免硬编码字符串,便于管理和重构
- 分层存储:将普通数据和敏感数据分开存储
- 错误处理:对存储操作进行适当的错误处理
- 数据备份:重要数据提供备份和恢复功能
- 数据验证:在读取和写入数据时进行验证
- 文档记录:记录存储数据的用途和格式
十一、常见问题与解决方案
11.1 数据丢失问题
问题:应用重启后数据丢失。
解决方案:
- 确保所有存储操作都使用
await等待完成 - 检查是否在正确的时机调用了
clear()方法 - 验证存储路径是否有写入权限
try {
await _storage.setString(key, value);
print('数据保存成功');
} catch (e) {
print('数据保存失败: $e');
}
11.2 性能问题
问题:大量数据存储导致应用性能下降。
解决方案:
- 对于大量数据,考虑使用 SQLite 数据库(sqflite)
- 实现数据分页和懒加载
- 定期清理缓存数据
11.3 权限问题
问题:在某些设备上无法写入数据。
解决方案:
- 确保在 module.json5 中正确配置了存储权限
- 对于 OpenHarmony,检查应用的权限设置
- 实现权限请求逻辑
import 'package:permission_handler/permission_handler.dart';
Future<void> requestStoragePermissions() async {
final status = await Permission.storage.status;
if (status != PermissionStatus.granted) {
await Permission.storage.request();
}
}
11.4 加密密钥安全
问题:如何安全地存储加密密钥。
解决方案:
- 在 OpenHarmony 上,使用 flutter_secure_storage 存储密钥
- 不要在代码中硬编码密钥
- 考虑使用设备唯一标识作为密钥的一部分
十二、测试策略
12.1 单元测试
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'storage_service.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('StorageService Tests', () {
setUp(() {
SharedPreferences.setMockInitialValues({});
});
test('存储和读取字符串', () async {
final storage = StorageService();
await storage.init();
await storage.setString('test_key', 'test_value');
expect(storage.getString('test_key'), equals('test_value'));
});
test('存储和读取整数', () async {
final storage = StorageService();
await storage.init();
await storage.setInt('test_int', 42);
expect(storage.getInt('test_int'), equals(42));
});
test('删除键', () async {
final storage = StorageService();
await storage.init();
await storage.setString('test_key', 'value');
expect(storage.containsKey('test_key'), isTrue);
await storage.remove('test_key');
expect(storage.containsKey('test_key'), isFalse);
});
test('清空所有数据', () async {
final storage = StorageService();
await storage.init();
await storage.setString('key1', 'value1');
await storage.setInt('key2', 2);
await storage.clear();
expect(storage.getKeys(), isEmpty);
});
});
}
12.2 Widget 测试
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'settings_screen.dart';
import 'settings_controller.dart';
void main() {
testWidgets('设置界面显示正确', (tester) async {
await tester.pumpWidget(
ChangeNotifierProvider(
create: (context) => SettingsController(AppStorage(StorageService())),
child: const MaterialApp(home: SettingsScreen()),
),
);
expect(find.text('主题模式'), findsOneWidget);
expect(find.text('语言设置'), findsOneWidget);
expect(find.text('通知设置'), findsOneWidget);
});
testWidgets('切换主题模式', (tester) async {
await tester.pumpWidget(
ChangeNotifierProvider(
create: (context) => SettingsController(AppStorage(StorageService())),
child: const MaterialApp(home: SettingsScreen()),
),
);
await tester.tap(find.text('主题模式'));
await tester.pumpAndSettle();
await tester.tap(find.text('深色模式'));
await tester.pumpAndSettle();
expect(find.text('深色模式'), findsOneWidget);
});
}
十三、总结与展望
SharedPreferences 是 Flutter 生态中最成熟的轻量级数据持久化方案。在 OpenHarmony 平台上,通过合理的封装、加密和适配,可以构建出安全、高效、可靠的数据存储功能。
通过本文的学习,你应该已经掌握了以下内容:
- SharedPreferences 的基础使用和核心 API
- 类型安全的存储封装方法
- 敏感数据的加密存储
- 数据迁移和备份策略
- 鸿蒙化适配要点
- 完整的设置功能实现
- 性能优化和最佳实践
- 测试策略和方法
随着 OpenHarmony 生态的不断发展,数据持久化将变得更加重要。开发者应积极参与开源鸿蒙跨平台社区建设,共同推动跨平台开发在鸿蒙生态中的发展。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
本文所有代码均已通过验证,可在 OpenHarmony 设备上正常运行。
更多推荐
所有评论(0)