4.3 持久化存储 (Persistence Storage)

基础知识

在移动应用程序中,持久化存储是必不可少的功能,它允许应用程序在关闭后仍然保留数据。Flutter 提供了多种持久化存储方案,可以根据数据的类型、大小和访问需求选择合适的方案。

1. shared_preferences (键值对存储)

shared_preferences 是一个轻量级的键值对存储插件,适用于存储简单的配置信息、用户偏好设置、登录状态等。它在 Android 上使用 SharedPreferences,在 iOS 上使用 NSUserDefaults

  • 添加依赖:在 pubspec.yaml 中添加 shared_preferences 依赖。
    yaml
    复制代码

dependencies:
shared_preferences: ^2.2.2 # 使用最新版本
```
运行 flutter pub get

  • 基本用法
    dart
    复制代码
    import 'package:shared_preferences/shared_preferences.dart';
    
    Future<void> saveSettings() async {
      final prefs = await SharedPreferences.getInstance();
    
      // 存储数据
      await prefs.setBool('isDarkMode', true);
      await prefs.setInt('fontSize', 16);
      await prefs.setString('username', 'Alice');
      await prefs.setStringList('recentSearches', ['Flutter', 'Dart', 'Widgets']);
    
      print('设置已保存');
    }
    
    Future<void> loadSettings() async {
      final prefs = await SharedPreferences.getInstance();
    
      // 读取数据
      final bool? isDarkMode = prefs.getBool('isDarkMode');
      final int? fontSize = prefs.getInt('fontSize');
      final String? username = prefs.getString('username');
      final List<String>? recentSearches = prefs.getStringList('recentSearches');
    
      print('isDarkMode: $isDarkMode');
      print('fontSize: $fontSize');
      print('username: $username');
      print('recentSearches: $recentSearches');
    }
    
    Future<void> removeSetting() async {
      final prefs = await SharedPreferences.getInstance();
      await prefs.remove('username'); // 移除某个键值对
      print('username 已移除');
    }
    
    Future<void> clearAllSettings() async {
      final prefs = await SharedPreferences.getInstance();
      await prefs.clear(); // 清除所有键值对
      print('所有设置已清除');
    }
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized(); // 确保 Flutter 绑定已初始化
      await saveSettings();
      await loadSettings();
      await removeSetting();
      await loadSettings();
      await clearAllSettings();
      await loadSettings();
    }

2. path_provider (文件存储)

path_provider 插件用于获取设备上常用目录的路径,例如应用程序的文档目录、临时目录等。结合 Dart 的 dart:io 库,可以实现文件的读写操作。

  • 添加依赖:在 pubspec.yaml 中添加 path_provider 依赖。
    yaml
    复制代码

dependencies:
path_provider: ^2.1.1 # 使用最新版本
```
运行 flutter pub get

  • 基本用法
    dart
    复制代码
    import 'dart:io';
    import 'package:path_provider/path_provider.dart';
    
    Future<String> _getLocalPath() async {
      final directory = await getApplicationDocumentsDirectory();
      return directory.path;
    }
    
    Future<File> _getLocalFile() async {
      final path = await _getLocalPath();
      return File('$path/my_data.txt');
    }
    
    Future<File> writeData(String data) async {
      final file = await _getLocalFile();
      return file.writeAsString(data);
    }
    
    Future<String> readData() async {
      try {
        final file = await _getLocalFile();
        final contents = await file.readAsString();
        return contents;
      } catch (e) {
        return '读取失败: $e';
      }
    }
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await writeData('Hello, Flutter File Storage!');
      print('写入数据: ${await readData()}');
    }

3. sqflite (SQLite 数据库)

sqflite 是一个 Flutter 插件,提供了 SQLite 数据库的接口。它适用于存储结构化数据,例如用户列表、商品信息、聊天记录等。对于需要复杂查询和大量数据的场景,SQLite 是一个很好的选择。

  • 添加依赖:在 pubspec.yaml 中添加 sqflite 依赖。
    yaml
    复制代码

dependencies:
sqflite: ^2.3.0 # 使用最新版本
path: ^1.8.3 # 用于处理路径
```
运行 flutter pub get

  • 基本用法 (概念性)
    dart
    复制代码
    import 'package:sqflite/sqflite.dart';
    import 'package:path/path.dart';
    
    class DatabaseHelper {
      static Database? _database;
      static const String _databaseName = 'my_database.db';
      static const int _databaseVersion = 1;
    
      static const String tableName = 'todos';
      static const String columnId = 'id';
      static const String columnTitle = 'title';
      static const String columnIsCompleted = 'isCompleted';
    
      Future<Database> get database async {
        if (_database != null) return _database!;
        _database = await _initDatabase();
        return _database!;
      }
    
      _initDatabase() async {
        String path = join(await getDatabasesPath(), _databaseName);
        return await openDatabase(path,
            version: _databaseVersion,
            onCreate: _onCreate);
      }
    
      Future _onCreate(Database db, int version) async {
        await db.execute('''
          CREATE TABLE $tableName (
            $columnId INTEGER PRIMARY KEY AUTOINCREMENT,
            $columnTitle TEXT NOT NULL,
            $columnIsCompleted INTEGER NOT NULL
          )
          ''');
      }
    
      Future<int> insertTodo(Map<String, dynamic> row) async {
        Database db = await database;
        return await db.insert(tableName, row);
      }
    
      Future<List<Map<String, dynamic>>> queryAllTodos() async {
        Database db = await database;
        return await db.query(tableName);
      }
    
      Future<int> updateTodo(Map<String, dynamic> row) async {
        Database db = await database;
        int id = row[columnId];
        return await db.update(tableName, row,
            where: '$columnId = ?', whereArgs: [id]);
      }
    
      Future<int> deleteTodo(int id) async {
        Database db = await database;
        return await db.delete(tableName, where: '$columnId = ?', whereArgs: [id]);
      }
    }

官方文档链接

Flutter 开发中的应用案例

持久化存储是几乎所有实际应用都需要的核心功能。选择合适的存储方案取决于数据的特性和应用的需求。

案例:一个简单的用户设置管理应用 (使用 shared_preferences)

我们将创建一个应用,允许用户设置主题模式(亮/暗)和通知开关,并将这些设置持久化存储,以便下次打开应用时能够恢复。

dart
复制代码
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class UserSettingsScreen extends StatefulWidget {
  const UserSettingsScreen({super.key});

  @override
  State<UserSettingsScreen> createState() => _UserSettingsScreenState();
}

class _UserSettingsScreenState extends State<UserSettingsScreen> {
  bool _isDarkMode = false;
  bool _enableNotifications = true;

  @override
  void initState() {
    super.initState();
    _loadSettings();
  }

  // 加载用户设置
  Future<void> _loadSettings() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _isDarkMode = prefs.getBool('isDarkMode') ?? false;
      _enableNotifications = prefs.getBool('enableNotifications') ?? true;
    });
  }

  // 保存主题模式设置
  Future<void> _saveDarkMode(bool value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('isDarkMode', value);
    setState(() {
      _isDarkMode = value;
    });
  }

  // 保存通知设置
  Future<void> _saveNotifications(bool value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('enableNotifications', value);
    setState(() {
      _enableNotifications = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('用户设置'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: <Widget>[
            SwitchListTile(
              title: const Text('深色模式'),
              value: _isDarkMode,
              onChanged: _saveDarkMode,
            ),
            SwitchListTile(
              title: const Text('启用通知'),
              value: _enableNotifications,
              onChanged: _saveNotifications,
            ),
            const Divider(),
            ElevatedButton(
              onPressed: () async {
                final prefs = await SharedPreferences.getInstance();
                await prefs.clear();
                _loadSettings(); // 重新加载以反映清除后的默认值
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('所有设置已重置')), 
                );
              },
              child: const Text('重置所有设置'),
            ),
          ],
        ),
      ),
    );
  }
}

void main() {
  // 确保 Flutter 绑定已初始化,因为 SharedPreferences 需要访问平台服务
  WidgetsFlutterBinding.ensureInitialized(); 
  runApp(MaterialApp(
    home: const UserSettingsScreen(),
    theme: ThemeData(
      brightness: Brightness.light, // 默认亮色模式
    ),
    darkTheme: ThemeData(
      brightness: Brightness.dark, // 深色模式
    ),
    themeMode: ThemeMode.system, // 默认跟随系统主题
    // 实际应用中,这里可以根据 _isDarkMode 动态设置 themeMode
    // 例如: themeMode: _isDarkMode ? ThemeMode.dark : ThemeMode.light,
  ));
}

案例分析:

  • WidgetsFlutterBinding.ensureInitialized():在 main() 函数中调用此方法非常重要,它确保 Flutter 框架的绑定已初始化,这是 shared_preferences 等插件能够正常工作的前提。
  • SharedPreferences.getInstance():这是一个异步方法,用于获取 SharedPreferences 的单例实例。由于它是异步的,所以需要使用 await
  • _loadSettings():在 initState 中调用,用于在屏幕加载时从 shared_preferences 中读取之前保存的设置。prefs.getBool('key') ?? false 使用了空安全操作符 ??,如果读取不到值(例如第一次运行应用),则提供一个默认值。
  • _saveDarkMode()_saveNotifications():当用户切换开关时,这些方法会被调用。它们将新的设置值保存到 shared_preferences 中,并更新 _isDarkMode_enableNotifications 状态,从而触发 UI 重建。
  • prefs.clear():用于清除 shared_preferences 中存储的所有数据。

这个案例清晰地展示了如何使用 shared_preferences 在 Flutter 应用中进行轻量级的持久化存储。它非常适合存储用户偏好、应用配置等简单数据。对于更复杂的数据结构或大量数据,则需要考虑文件存储或数据库解决方案。