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&lt;void&gt; init() async {
    _prefs ??= await SharedPreferences.getInstance();
  }

  SharedPreferences get prefs {
    if (_prefs == null) {
      throw StateError('StorageService not initialized');
    }
    return _prefs!;
  }
}

4.2 基础读写操作

实现完整的存储服务,支持所有数据类型:

class StorageService {
  // ... (初始化代码)

  // ========== 字符串操作 ==========
  Future&lt;void&gt; 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&lt;void&gt; 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&lt;void&gt; 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&lt;void&gt; 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&lt;void&gt; setStringList(String key, List&lt;String&gt; value) async {
    await prefs.setStringList(key, value);
  }

  List&lt;String&gt;? getStringList(String key) {
    return prefs.getStringList(key);
  }

  List&lt;String&gt; getStringListOrDefault(String key, List&lt;String&gt; defaultValue) {
    return prefs.getStringList(key) ?? defaultValue;
  }

  // ========== 删除和清空操作 ==========
  Future&lt;void&gt; remove(String key) async {
    await prefs.remove(key);
  }

  Future&lt;void&gt; clear() async {
    await prefs.clear();
  }

  // ========== 检查键是否存在 ==========
  bool containsKey(String key) {
    return prefs.containsKey(key);
  }

  // ========== 获取所有键 ==========
  Set&lt;String&gt; 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&lt;void&gt; 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&lt;void&gt; setLanguage(String language) async {
    await _storage.setString(StorageKeys.language, language);
  }

  // ========== 首次启动 ==========
  bool isFirstLaunch() {
    return _storage.getBoolOrDefault(StorageKeys.firstLaunch, true);
  }

  Future&lt;void&gt; setFirstLaunchComplete() async {
    await _storage.setBool(StorageKeys.firstLaunch, false);
  }

  // ========== 用户认证 ==========
  bool isAuthenticated() {
    return _storage.getString(StorageKeys.authToken) != null;
  }

  String? getAuthToken() {
    return _storage.getString(StorageKeys.authToken);
  }

  Future&lt;void&gt; setAuthToken(String token) async {
    await _storage.setString(StorageKeys.authToken, token);
  }

  String? getRefreshToken() {
    return _storage.getString(StorageKeys.refreshToken);
  }

  Future&lt;void&gt; setRefreshToken(String token) async {
    await _storage.setString(StorageKeys.refreshToken, token);
  }

  String? getUserId() {
    return _storage.getString(StorageKeys.userId);
  }

  Future&lt;void&gt; setUserId(String id) async {
    await _storage.setString(StorageKeys.userId, id);
  }

  // ========== 用户信息 ==========
  Future&lt;void&gt; 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&lt;String, dynamic&gt; 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&lt;void&gt; setNotificationsEnabled(bool enabled) async {
    await _storage.setBool(StorageKeys.notificationsEnabled, enabled);
  }

  // ========== 搜索历史 ==========
  List&lt;String&gt; getRecentSearches() {
    return _storage.getStringListOrDefault(StorageKeys.recentSearches, []);
  }

  Future&lt;void&gt; addSearchQuery(String query) async {
    final searches = getRecentSearches();
    searches.remove(query);
    searches.insert(0, query);
    if (searches.length &gt; 10) {
      searches.removeLast();
    }
    await _storage.setStringList(StorageKeys.recentSearches, searches);
  }

  Future&lt;void&gt; clearSearchHistory() async {
    await _storage.remove(StorageKeys.recentSearches);
  }

  // ========== 清理用户数据 ==========
  Future&lt;void&gt; 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&lt;void&gt; 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&lt;String, dynamic&gt; toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'avatarUrl': avatarUrl,
    };
  }

  factory User.fromJson(Map&lt;String, dynamic&gt; 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&lt;void&gt; 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&lt;SecureStorage&gt; 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&lt;void&gt; 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&lt;void&gt; setSecureToken(String token) async {
    await setSecureString('secure_token', token);
  }

  String? getSecureToken() {
    return getSecureString('secure_token');
  }

  // ========== 加密存储复杂对象 ==========
  Future&lt;void&gt; setSecureObject(String key, Map&lt;String, dynamic&gt; object) async {
    final jsonStr = jsonEncode(object);
    await setSecureString(key, jsonStr);
  }

  Map&lt;String, dynamic&gt;? getSecureObject(String key) {
    final jsonStr = getSecureString(key);
    if (jsonStr == null) return null;
    try {
      return jsonDecode(jsonStr);
    } catch (e) {
      return null;
    }
  }

  // ========== 删除安全数据 ==========
  Future&lt;void&gt; removeSecure(String key) async {
    await _prefs.remove(key);
  }

  Future&lt;void&gt; 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&lt;void&gt; 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&lt;void&gt; _performStep1OldStorageMigration() async {
    final oldTokenKey = 'token';
    final newTokenKey = StorageKeys.authToken;
    
    final oldToken = _storage.getString(oldTokenKey);
    if (oldToken != null &amp;&amp; !_storage.containsKey(newTokenKey)) {
      await _storage.setString(newTokenKey, oldToken);
      await _storage.remove(oldTokenKey);
    }
  }

  // 步骤 2: 键名迁移
  Future&lt;void&gt; _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) &amp;&amp; !_storage.containsKey(newKey)) {
        final value = _storage.getString(oldKey);
        if (value != null) {
          await _storage.setString(newKey, value);
        }
        await _storage.remove(oldKey);
      }
    }
  }

  // 步骤 3: 清理过期数据
  Future&lt;void&gt; _performStep3DataCleanup() async {
    final keys = _storage.getKeys();
    final expiredKeys = keys.where((key) =&gt; 
      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&lt;void&gt; ensurePlatformCompatibility() async {
    if (isHarmonyPlatform) {
      await _initializeHarmonyStorage();
    }
  }

  static Future&lt;void&gt; _initializeHarmonyStorage() async {
    // 在 OpenHarmony 上可能需要执行特定的初始化操作
    // 例如检查存储权限、验证存储路径等
  }
}

8.3 数据备份与恢复

在 OpenHarmony 上,可以集成系统的备份和恢复功能:

class DataBackupService {
  final StorageService _storage;

  DataBackupService(this._storage);

  Future&lt;Map&lt;String, dynamic&gt;&gt; exportBackupData() async {
    final data = &lt;String, dynamic&gt;{};
    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&lt;void&gt; importBackupData(Map&lt;String, dynamic&gt; 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' &amp;&amp; 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 =&gt; _themeMode;
  String get language =&gt; _language;
  bool get notificationsEnabled =&gt; _notificationsEnabled;
  double get fontSizeScale =&gt; _fontSizeScale;

  Future&lt;void&gt; _loadSettings() async {
    _themeMode = _storage.getThemeMode();
    _language = _storage.getLanguage();
    _notificationsEnabled = _storage.areNotificationsEnabled();
    _fontSizeScale = _storage.getFontSizeScale();
    notifyListeners();
  }

  Future&lt;void&gt; updateThemeMode(ThemeMode mode) async {
    _themeMode = mode;
    await _storage.setThemeMode(mode);
    notifyListeners();
  }

  Future&lt;void&gt; updateLanguage(String language) async {
    _language = language;
    await _storage.setLanguage(language);
    notifyListeners();
  }

  Future&lt;void&gt; updateNotifications(bool enabled) async {
    _notificationsEnabled = enabled;
    await _storage.setNotificationsEnabled(enabled);
    notifyListeners();
  }

  Future&lt;void&gt; updateFontSizeScale(double scale) async {
    _fontSizeScale = scale;
    await _storage.setFontSizeScale(scale);
    notifyListeners();
  }

  Future&lt;void&gt; 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: () =&gt; _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: () =&gt; _showLanguagePicker(context),
    );
  }

  Widget _buildNotificationSection(BuildContext context) {
    return SwitchListTile(
      title: const Text('通知设置'),
      subtitle: const Text('允许应用发送通知'),
      value: controller.notificationsEnabled,
      onChanged: (value) =&gt; 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) =&gt; controller.updateFontSizeScale(value),
      ),
    );
  }

  Widget _buildResetSection(BuildContext context) {
    return ListTile(
      title: const Text('重置设置'),
      subtitle: const Text('恢复默认设置'),
      leading: const Icon(Icons.restore, color: Colors.red),
      onTap: () =&gt; _showResetDialog(context),
    );
  }

  void _showThemePicker(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) =&gt; AlertDialog(
        title: const Text('选择主题'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            RadioListTile&lt;ThemeMode&gt;(
              title: const Text('跟随系统'),
              value: ThemeMode.system,
              groupValue: controller.themeMode,
              onChanged: (value) {
                controller.updateThemeMode(value!);
                Navigator.of(context).pop();
              },
            ),
            RadioListTile&lt;ThemeMode&gt;(
              title: const Text('浅色模式'),
              value: ThemeMode.light,
              groupValue: controller.themeMode,
              onChanged: (value) {
                controller.updateThemeMode(value!);
                Navigator.of(context).pop();
              },
            ),
            RadioListTile&lt;ThemeMode&gt;(
              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) =&gt; AlertDialog(
        title: const Text('选择语言'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: languages.entries.map((entry) {
            return RadioListTile&lt;String&gt;(
              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) =&gt; AlertDialog(
        title: const Text('确认重置'),
        content: const Text('确定要重置所有设置吗?此操作无法撤销。'),
        actions: [
          TextButton(
            onPressed: () =&gt; 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) =&gt; SettingsController(AppStorage(StorageService())),
      child: Consumer&lt;SettingsController&gt;(
        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 性能优化建议

  1. 批量操作:使用 Future.wait 批量执行多个存储操作
Future&lt;void&gt; saveUserData(Map&lt;String, dynamic&gt; userData) async {
  final futures = &lt;Future&lt;void&gt;&gt;[];
  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);
}
  1. 内存缓存:对频繁读取的数据使用内存缓存
class CachedStorage {
  final StorageService _storage;
  final Map&lt;String, dynamic&gt; _cache = {};

  CachedStorage(this._storage);

  Future&lt;void&gt; 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();
  }
}
  1. 避免频繁读写:合并多次写操作,减少磁盘 I/O
  2. 异步处理:所有存储操作都在异步线程执行,避免阻塞 UI
  3. 定期清理:清理过期或无用的数据,防止存储空间膨胀

10.2 最佳实践

  1. 使用常量定义键名:避免硬编码字符串,便于管理和重构
  2. 分层存储:将普通数据和敏感数据分开存储
  3. 错误处理:对存储操作进行适当的错误处理
  4. 数据备份:重要数据提供备份和恢复功能
  5. 数据验证:在读取和写入数据时进行验证
  6. 文档记录:记录存储数据的用途和格式

十一、常见问题与解决方案

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&lt;void&gt; 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) =&gt; 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) =&gt; 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 设备上正常运行。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐