Skip to content

本地存储

为什么要使用本地存储

移动应用在运行过程中经常需要持久化数据,本地存储是解决这一需求的核心手段。使用本地存储的主要理由:

  • 离线可用:网络不可用时,应用仍能读取本地缓存的数据,保证基本功能可用
  • 提升性能:将频繁访问的数据缓存到本地,避免重复的网络请求,减少加载时间
  • 保存用户偏好:主题、语言、字体大小等个性化设置需要在应用重启后保留
  • 减少流量消耗:已获取的数据本地缓存后,不必每次都从服务器拉取
  • 提升用户体验:启动时立即展示本地缓存内容(骨架屏/占位数据),再异步刷新,避免白屏等待

常见使用场景

场景说明推荐方案
用户设置主题色、语言、字体大小、通知开关等shared_preferences / get_storage
登录状态Token、是否首次启动、是否同意协议等shared_preferences / get_storage
表单草稿用户未提交的表单数据暂存,防止丢失get_storage / hive
列表缓存首页列表数据缓存,实现离线浏览hive / sqflite
聊天记录消息的本地持久化与历史查询sqflite / isar
购物车商品增删改查、事务一致性sqflite
收藏/点赞离线操作队列,网络恢复后同步sqflite / isar
搜索历史最近搜索关键词记录shared_preferences / get_storage
文件缓存下载的图片、PDF 等文件资源文件读写(path_provider

本地存储方案对比

方案适合存储性能备注
shared_preferences键值对、用户设置官方维护,最通用
get_storage键值对、简单配置优秀同步 API,最简单
sqflite关系型数据、大量数据中文社区最常用的 SQLite 方案
isarNoSQL 数据库优秀维护状态不稳定,谨慎选用
hive结构化对象优秀原生 Dart,作者已转向 Isar
path_provider + 文件读写JSON、自定义文件最灵活,需手动管理

SharedPreferences(键值对存储)

适合存储简单配置、用户偏好等。

安装

bash
flutter pub add shared_preferences

基本用法

dart
import 'package:shared_preferences/shared_preferences.dart';

// 写入
Future<void> saveSettings() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('username', 'Tom');
  await prefs.setInt('age', 25);
  await prefs.setBool('isDarkMode', true);
  await prefs.setDouble('fontSize', 16.0);
  await prefs.setStringList('tags', ['flutter', 'dart']);
}

// 读取
Future<void> loadSettings() async {
  final prefs = await SharedPreferences.getInstance();
  final username = prefs.getString('username') ?? 'Guest';
  final age = prefs.getInt('age') ?? 0;
  final isDarkMode = prefs.getBool('isDarkMode') ?? false;
  final fontSize = prefs.getDouble('fontSize') ?? 14.0;
  final tags = prefs.getStringList('tags') ?? [];
}

// 删除
await prefs.remove('username');        // 删除单个
await prefs.clear();                    // 清除所有

GetStorage(最简单的键值对存储)

来自 GetX 生态,中文社区使用广泛。相比 shared_preferences,API 更简洁,读写同步无需 await

安装

bash
flutter pub add get_storage

基本用法

dart
import 'package:get_storage/get_storage.dart';

// 初始化(main 中调用一次)
await GetStorage.init();

final box = GetStorage();

// 写入(同步,无需 await)
box.write('username', 'Tom');
box.write('age', 25);
box.write('isDarkMode', true);

// 读取(同步)
final username = box.read('username') ?? 'Guest';
final age = box.read<int>('age') ?? 0;

// 删除
box.remove('username');
box.erase(); // 清除所有

监听变化

dart
box.listen(() {
  print('数据已变化');
});

box.listenKey('username', (value) {
  print('username 变为: $value');
});

适用场景:简单配置、用户偏好、小型缓存。不适合大量数据或复杂查询。

sqflite(关系型数据库)

中文社区最常用的 SQLite 方案,适合存储关系型数据和大量结构化数据,支持完整的 SQL 语法。

安装

bash
flutter pub add sqflite
flutter pub add path  # 辅助拼接数据库路径

基本用法

1. 打开数据库

dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

Future<Database> openMyDatabase() async {
  final dbPath = await getDatabasesPath();
  return openDatabase(
    join(dbPath, 'app.db'),
    version: 1,
    onCreate: (db, version) async {
      // 首次创建时执行建表
      await db.execute('''
        CREATE TABLE users (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          name TEXT NOT NULL,
          age INTEGER,
          email TEXT
        )
      ''');
      await db.execute('''
        CREATE TABLE orders (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          userId INTEGER,
          product TEXT,
          price REAL,
          createdAt TEXT,
          FOREIGN KEY (userId) REFERENCES users(id)
        )
      ''');
    },
    onUpgrade: (db, oldVersion, newVersion) async {
      // 版本升级时执行迁移
      if (oldVersion < 2) {
        await db.execute('ALTER TABLE users ADD COLUMN phone TEXT');
      }
    },
  );
}

2. 插入数据

dart
final db = await openMyDatabase();

// 插入单条
int id = await db.insert('users', {
  'name': 'Tom',
  'age': 25,
  'email': 'tom@example.com',
});

// 原始 SQL 插入
await db.rawInsert(
  'INSERT INTO users(name, age, email) VALUES(?, ?, ?)',
  ['Jerry', 30, 'jerry@example.com'],
);

// 批量插入(事务)
await db.transaction((txn) async {
  await txn.insert('users', {'name': 'Alice', 'age': 22});
  await txn.insert('users', {'name': 'Bob', 'age': 28});
});

3. 查询数据

dart
// 查询所有
List<Map<String, dynamic>> users = await db.query('users');

// 条件查询
List<Map<String, dynamic>> results = await db.query(
  'users',
  columns: ['id', 'name', 'age'],
  where: 'age > ?',
  whereArgs: [20],
  orderBy: 'age DESC',
  limit: 10,
);

// 原始 SQL 查询
List<Map<String, dynamic>> results = await db.rawQuery(
  'SELECT * FROM users WHERE age > ? ORDER BY age DESC LIMIT ?',
  [20, 10],
);

// 联表查询
List<Map<String, dynamic>> results = await db.rawQuery('''
  SELECT u.name, o.product, o.price
  FROM users u
  INNER JOIN orders o ON u.id = o.userId
  WHERE o.price > ?
  ORDER BY o.price DESC
''', [100.0]);

4. 更新数据

dart
// 更新
int count = await db.update(
  'users',
  {'age': 26, 'email': 'tom_new@example.com'},
  where: 'name = ?',
  whereArgs: ['Tom'],
);

// 原始 SQL 更新
int count = await db.rawUpdate(
  'UPDATE users SET age = ? WHERE name = ?',
  [26, 'Tom'],
);

5. 删除数据

dart
// 删除
int count = await db.delete(
  'users',
  where: 'age < ?',
  whereArgs: [18],
);

// 原始 SQL 删除
int count = await db.rawDelete(
  'DELETE FROM users WHERE age < ?',
  [18],
);

使用事务

dart
await db.transaction((txn) async {
  // 所有操作在同一个事务中,任一失败则全部回滚
  await txn.insert('orders', {
    'userId': 1,
    'product': 'Flutter Book',
    'price': 59.9,
    'createdAt': DateTime.now().toIso8601String(),
  });

  await txn.update(
    'users',
    {'age': 26},
    where: 'id = ?',
    whereArgs: [1],
  );
});

封装数据库帮助类

dart
class DatabaseHelper {
  static Database? _database;

  static Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  static Future<Database> _initDatabase() async {
    final dbPath = await getDatabasesPath();
    return openDatabase(
      join(dbPath, 'app.db'),
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            age INTEGER,
            email TEXT
          )
        ''');
      },
    );
  }

  // CRUD 封装示例
  static Future<int> insertUser(Map<String, dynamic> user) async {
    final db = await database;
    return db.insert('users', user);
  }

  static Future<List<Map<String, dynamic>>> getUsers({int? minAge}) async {
    final db = await database;
    if (minAge != null) {
      return db.query('users', where: 'age > ?', whereArgs: [minAge]);
    }
    return db.query('users');
  }

  static Future<int> updateUser(int id, Map<String, dynamic> values) async {
    final db = await database;
    return db.update('users', values, where: 'id = ?', whereArgs: [id]);
  }

  static Future<int> deleteUser(int id) async {
    final db = await database;
    return db.delete('users', where: 'id = ?', whereArgs: [id]);
  }
}

删除数据库

dart
final dbPath = await getDatabasesPath();
await deleteDatabase(join(dbPath, 'app.db'));

适用场景:需要复杂查询、多表关联、事务支持的场景,如购物车、订单管理、消息记录等。

Isar(NoSQL 数据库)

注意:Isar 维护状态不稳定,生产项目请谨慎选用。如需稳定方案,优先考虑 sqflite

高性能 NoSQL 数据库,支持全文搜索、索引、查询等,API 设计现代。

安装

bash
flutter pub add isar
flutter pub add isar_flutter_libs
flutter pub add build_runner
flutter pub add isar_generator

基本用法

dart
import 'package:isar/isar.dart';

// 1. 定义模型
@collection
class User {
  Id id = Isar.autoIncrement;

  String name;

  int age;

  User({required this.name, required this.age});
}

// 2. 打开数据库
final isar = await Isar.open(
  [UserSchema],
  directory: (await getApplicationDocumentsDirectory()).path,
);

// 3. 写入
await isar.writeTxn(() async {
  await isar.users.put(User(name: 'Tom', age: 25));
});

// 4. 查询
final users = await isar.users.where().findAll();
final adult = await isar.users.filter().ageGreaterThan(18).findAll();

// 5. 删除
await isar.writeTxn(() async {
  await isar.users.delete(1);
});

适用场景:需要高性能 NoSQL 存储、全文搜索的场景。因维护状态不稳定,建议评估风险后使用。

Hive(高性能键值存储)

注意:Hive 原作者已转向 Isar 开发,Hive 维护频率降低。新项目如需轻量键值存储优先考虑 get_storage,需复杂 NoSQL 优先考虑 isar(但需注意其维护状态)。仅在已有 Hive 项目或特殊需求时选用。

安装

bash
flutter pub add hive
flutter pub add hive_flutter

基本用法

dart
import 'package:hive_flutter/hive_flutter.dart';

// 初始化
await Hive.initFlutter();

// 打开盒子
var box = await Hive.openBox('settings');

// 写入
box.put('username', 'Tom');
box.put('age', 25);
box.putAll({'key1': 'value1', 'key2': 'value2'});

// 读取
var username = box.get('username', defaultValue: 'Guest');

// 删除
box.delete('username');
await box.close();

Hive 对象存储

dart
// 1. 定义模型(加注解)
@HiveType(typeId: 0)
class User extends HiveObject {
  @HiveField(0)
  String name;

  @HiveField(1)
  int age;

  User({required this.name, required this.age});
}

// 2. 生成适配器(运行命令)
// flutter pub run build_runner build

// 3. 注册适配器
Hive.registerAdapter(UserAdapter());

// 4. 使用
var box = await Hive.openBox<User>('users');
box.put('tom', User(name: 'Tom', age: 25));
var user = box.get('tom');

文件读写

dart
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'dart:convert';

// 获取应用文档目录
final directory = await getApplicationDocumentsDirectory();

// 写入
final file = File('${directory.path}/data.json');
final jsonString = jsonEncode({'key': 'value'});
await file.writeAsString(jsonString);

// 读取
if (await file.exists()) {
  final content = await file.readAsString();
}

基于 Flutter 官方文档整理