在 Flutter 應用開發中,本地存儲是必不可少的功能。無論是保存用户登錄信息、應用配置,還是存儲結構化的業務數據,都需要選擇合適的本地存儲方案。Flutter 提供了多種本地存儲方式,涵蓋輕量級鍵值對存儲、NoSQL 數據庫和關係型數據庫等,滿足不同場景的需求。本文將詳細解析 Flutter 中主流的本地存儲方案,包括其特點、用法和適用場景。

一、Flutter 本地存儲的常見場景與分類

Flutter 本地存儲主要用於以下場景:

  • 保存輕量級配置信息(如主題模式、語言設置);
  • 緩存網絡數據,減少重複請求;
  • 存儲結構化業務數據(如本地訂單、收藏列表);
  • 持久化用户登錄狀態、個人信息等。

根據存儲數據的類型和結構,Flutter 本地存儲可分為三類:

  1. 鍵值對存儲:適合存儲簡單的鍵值型數據,如SharedPreferencesHive(輕量 NoSQL,也支持鍵值對);
  2. NoSQL 數據庫:適合存儲非結構化或半結構化數據,如HiveObjectBox
  3. 關係型數據庫:適合存儲結構化、關聯度高的數據,如SQFliteDrift

二、SharedPreferences:輕量級鍵值對存儲

SharedPreferences是 Flutter 中最常用的輕量級存儲方案,本質上是對 Android 的SharedPreferences和 iOS 的NSUserDefaults的封裝,適用於存儲簡單的鍵值對數據(如字符串、數字、布爾值)。

1. 集成與基本使用

首先在pubspec.yaml中添加依賴:

dependencies:
  shared_preferences: ^2.2.2

核心操作包括存、取、刪、清

import 'package:shared_preferences/shared_preferences.dart';

// 存儲數據
Future<void> saveData() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setString('username', 'FlutterDev'); // 存儲字符串
  await prefs.setInt('age', 25); // 存儲整數
  await prefs.setBool('isLogin', true); // 存儲布爾值
  await prefs.setStringList('hobbies', ['coding', 'reading']); // 存儲字符串列表
}

// 獲取數據
Future<void> getData() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  String? username = prefs.getString('username');
  int? age = prefs.getInt('age');
  bool? isLogin = prefs.getBool('isLogin');
  List<String>? hobbies = prefs.getStringList('hobbies');
  
  print("用户名:$username,年齡:$age,是否登錄:$isLogin,愛好:$hobbies");
}

// 刪除數據
Future<void> removeData() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.remove('username'); // 刪除指定鍵的數據
}

// 清空所有數據
Future<void> clearData() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.clear();
}

2. 適用場景與侷限性

適用場景:存儲應用配置、用户偏好設置、簡單的狀態標記(如是否首次啓動)。侷限性

  • 僅支持基本數據類型,無法存儲複雜對象;
  • 存儲容量有限,不適合存儲大量數據;
  • 性能一般,頻繁讀寫可能導致卡頓。

三、Hive:高性能 NoSQL 鍵值數據庫

Hive是專為 Flutter 設計的輕量級 NoSQL 數據庫,基於鍵值對存儲,支持複雜對象序列化,性能遠超SharedPreferences,且無需原生依賴,跨平台兼容性好。

1. 集成與初始化

添加依賴並配置:

dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0

dev_dependencies:
  hive_generator: ^1.1.5
  build_runner: ^2.4.6

初始化 Hive:

import 'package:hive_flutter/hive_flutter.dart';

void initHive() async {
  await Hive.initFlutter(); // 初始化Hive
  await Hive.openBox('userBox'); // 打開名為userBox的盒子(類似數據庫表)
}

2. 基本操作

Hive 的核心操作圍繞 Box(盒子) 展開,一個 Box 相當於一個數據集:

// 存儲數據
void saveToHive() {
  final box = Hive.box('userBox');
  box.put('user', {'name': 'FlutterDev', 'age': 25}); // 存儲Map
  box.put('scores', [90, 85, 95]); // 存儲列表
}

// 獲取數據
void getFromHive() {
  final box = Hive.box('userBox');
  var user = box.get('user');
  var scores = box.get('scores');
  print("用户信息:$user,分數:$scores");
}

// 自定義對象存儲(需序列化)
// 1. 創建實體類並添加註解
import 'package:hive/hive.dart';

part 'person.g.dart'; // 生成的序列化代碼

@HiveType(typeId: 0)
class Person {
  @HiveField(0)
  final String name;

  @HiveField(1)
  final int age;

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

// 2. 運行構建命令生成序列化代碼:flutter pub run build_runner build

// 3. 註冊適配器並存儲對象
void saveCustomObject() async {
  Hive.registerAdapter(PersonAdapter()); // 註冊適配器
  final box = Hive.box('userBox');
  box.put('person', Person(name: 'FlutterDev', age: 25));
  
  Person? person = box.get('person');
  print("自定義對象:${person?.name},${person?.age}");
}

3. 適用場景與優勢

適用場景:存儲複雜對象、中等規模的本地數據、需要高性能讀寫的場景(如緩存列表數據)。優勢

  • 支持複雜對象序列化,無需手動轉換;
  • 純 Dart 實現,跨平台無原生依賴;
  • 讀寫速度快,性能優於 SQFlite;
  • 支持加密存儲,保護敏感數據。

四、SQFlite:SQLite 的 Flutter 封裝

SQFlite是 Flutter 對 SQLite 數據庫的封裝,適用於存儲結構化、關聯度高的關係型數據,如電商訂單、本地通訊錄等。

1. 集成與數據庫初始化

添加依賴:

dependencies:
  sqflite: ^2.3.0
  path: ^1.8.3

初始化數據庫並創建表:

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

class DatabaseHelper {
  static final DatabaseHelper instance = DatabaseHelper._init();
  static Database? _database;

  DatabaseHelper._init();

  // 獲取數據庫實例
  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDB('notes.db');
    return _database!;
  }

  // 初始化數據庫
  Future<Database> _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);

    return await openDatabase(path, version: 1, onCreate: _createDB);
  }

  // 創建表
  Future<void> _createDB(Database db, int version) async {
    await db.execute('''
      CREATE TABLE notes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        content TEXT,
        create_time DATETIME DEFAULT CURRENT_TIMESTAMP
      )
    ''');
  }
}

2. CRUD 操作

// 插入數據
Future<int> insertNote(Map<String, dynamic> note) async {
  final db = await DatabaseHelper.instance.database;
  return await db.insert('notes', note);
}

// 查詢所有數據
Future<List<Map<String, dynamic>>> queryAllNotes() async {
  final db = await DatabaseHelper.instance.database;
  return await db.query('notes');
}

// 更新數據
Future<int> updateNote(Map<String, dynamic> note) async {
  final db = await DatabaseHelper.instance.database;
  int id = note['id'];
  return await db.update('notes', note, where: 'id = ?', whereArgs: [id]);
}

// 刪除數據
Future<int> deleteNote(int id) async {
  final db = await DatabaseHelper.instance.database;
  return await db.delete('notes', where: 'id = ?', whereArgs: [id]);
}

3. 適用場景與侷限性

適用場景:存儲結構化、有複雜關聯關係的數據,如多表關聯的業務數據。侷限性

  • 需要編寫 SQL 語句,學習成本較高;
  • 性能略低於 Hive,適合數據量中等的場景;
  • 跨平台需適配不同系統的 SQLite 版本。

五、Drift:類型安全的 SQL 數據庫

Drift(原 Moor)是基於 SQLite 的高級封裝,採用 Dart 代碼生成的方式實現類型安全的數據庫操作,無需手動編寫 SQL 語句,開發效率更高。

1. 集成與基本使用

添加依賴:

dependencies:
  drift: ^2.12.0
  sqlite3_flutter_libs: ^0.5.16
  path_provider: ^2.1.1
  path: ^1.8.3

dev_dependencies:
  drift_dev: ^2.12.0
  build_runner: ^2.4.6

定義數據庫表和操作:

import 'package:drift/drift.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:drift/native.dart';
import 'dart:io';

// 定義表
class Notes extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get title => text().notNull()();
  TextColumn get content => text().nullable()();
  DateTimeColumn get createTime => dateTime().withDefault(currentDateAndTime)();
}

// 定義數據庫類
part 'database.g.dart';

@DriftDatabase(tables: [Notes])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  @override
  int get schemaVersion => 1;

  // 插入筆記
  Future<int> insertNote(NotesCompanion note) => into(notes).insert(note);

  // 查詢所有筆記
  Future<List<Note>> getAllNotes() => select(notes).get();

  // 更新筆記
  Future<int> updateNote(Note note) => update(notes).replace(note);

  // 刪除筆記
  Future<int> deleteNote(Note note) => delete(notes).delete(note);
}

// 打開數據庫連接
LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'notes.db'));
    return NativeDatabase(file);
  });
}

運行構建命令生成代碼:flutter pub run build_runner build,之後即可調用數據庫方法:

void testDrift() async {
  final db = AppDatabase();
  // 插入數據
  await db.insertNote(NotesCompanion(title: Value('Flutter筆記'), content: Value('Drift數據庫學習')));
  // 查詢數據
  List<Note> notes = await db.getAllNotes();
  print(notes);
}

2. 優勢與適用場景

優勢

  • 類型安全,編譯期檢查 SQL 錯誤;
  • 支持 Dart 語法代替 SQL 語句,開發更便捷;
  • 支持流查詢(watch方法),數據變化時自動刷新 UI;
  • 兼容 SQLite 所有特性,支持複雜查詢和事務。

適用場景:需要類型安全、複雜查詢的關係型數據存儲場景,適合中大型 Flutter 應用。

六、Flutter 本地存儲方案的選擇建議

  1. 簡單鍵值對數據:優先選擇SharedPreferences,開發成本低;若需要存儲複雜對象或追求高性能,選擇Hive
  2. 非結構化 / 半結構化數據:選擇Hive,兼顧性能和易用性;
  3. 結構化、關聯度高的數據
  • 若熟悉 SQL 且數據量較小,選擇SQFlite
  • 若追求類型安全和開發效率,選擇Drift
  1. 大數據量、高性能要求:考慮ObjectBox(另一個高性能 NoSQL 數據庫,適合海量數據存儲)。

七、本地存儲的注意事項

  1. 數據加密:敏感數據(如用户密碼、token)需加密存儲,可使用encrypt包結合 Hive/SQFlite 實現;
  2. 數據清理:定期清理過期緩存數據,避免佔用過多設備存儲;
  3. 事務管理:批量操作數據時使用事務,確保數據一致性;
  4. 錯誤處理:捕獲存儲操作中的異常(如數據庫打開失敗、讀寫權限不足),避免程序崩潰;
  5. 適配不同平台:注意 Android 和 iOS 的存儲路徑、權限差異,確保跨平台兼容性。

八、總結

        Flutter 提供了豐富的本地存儲方案,從輕量級的SharedPreferences到高性能的Hive,再到關係型的SQFliteDrift,每種方案都有其適用場景。在實際開發中,需根據數據類型、規模和性能需求選擇合適的存儲方式,並遵循最佳實踐確保數據安全和應用穩定性。掌握這些本地存儲技術,能讓 Flutter 應用在離線場景下也能提供流暢的用户體驗。