Flutter学习(六)——持久化存储

Flutter 的数据持久化有三种方式:

  1. 键值对 shared_preferences
  2. 读写文件 path_provider
  3. 数据库 sqflite

预备知识

Dart 库中包含许多返回 Future 或 Stream 对象的函数. 这些函数在设置完耗时任务(例如 I/O 操作)后, 就立即返回了,不会等待耗任务完成。 使用 async 和 await 关键字实现异步编程。 可以让像编写同步代码一样实现异步操作。

1
2
3
4
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}

必须要在 异步函数,也就是 asycn 标记的函数中,才可以使用 awiat。 并且在一个异步函数中,可以多次使用 await。

在 await 表达式中, 表达式的值通常是一个 Future 对象; 如果不是,这是表达式的值会被自动包装成一个 Future 对象。 Future 对象指明返回一个对象的承诺(promise)。 await 表达式执行的结果为这个返回的对象。 await 表达式会阻塞代码的执行,直到需要的对象返回为止。

键值对 —— shared_preferences

一个简单、异步的持久化 key-value 存储系统。在 iOS上 包装 NSUserDefaures,在 Android 上包装 SharedPreferences。

数据可能会被异步持久化到磁盘上,并且不能保证写操作返回后数据必然持久化到磁盘上,所以这个插件不能用于存储关键数据。

支持的类型

  • int
  • double
  • bool
  • String
  • List

安装

  1. 在 pubspec.yaml 中增加依赖。然后执行 flutter pub get

    1
    2
    dependencies:
    shared_preferences: ^2.0.0
  2. 使用时,导入包

    1
    import 'package:shared_preferences/shared_preferences.dart'

使用

存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void set(String key, value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (value is String) {
prefs.setString(key, value);
} else if (value is num) {
prefs.setInt(key, value);
} else if (value is double) {
prefs.setDouble(key, value);
} else if (value is bool) {
prefs.setBool(key, value);
} else if (value is List) {
prefs.setStringList(key, value.cast<String>());
}
}

取值

1
2
3
4
5
Object? get(String key, [dynamic replace]) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var data = prefs.get(key);
return data ?? replace ?? null;
}

移除

1
2
3
4
5
6
7
8
9
void remove(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}

void removeAll() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.clear();
}

读写文件 —— path_provider & dart:io

path_provider 提供了一种平台透明的方式来访问设备文件系统上的常用存储位置。

dart:io 包含了文件读写的相关类,它属于 Dart 语法标准的一部分.

安装

  1. 在 pubspec.yaml 中增加依赖。然后执行 flutter pub get

    1
    2
    dependencies:
    path_provider: ^2.0.0
  2. 使用时,导入包

    1
    import 'package:shared_preferences/shared_preferences.dart'

使用

文件夹目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// 获取临时目录路径
Future<String> _getLocalTemporaryPath() async {
Directory dir = await getTemporaryDirectory();
return dir.path;
}

/// 获取应用程序目录路径
Future<String> _getLocalSupportPath() async {
Directory dir = await getApplicationSupportDirectory();
return dir.path;
}

/// 获取文档目录路径
Future<String> _getLocalDocumentPath() async {
Directory dir = await getApplicationDocumentsDirectory();
return dir.path;
}

/// 获取 Library 路径
Future<String> _getLocalLibraryPath() async {
Directory dir = await getLibraryDirectory();
return dir.path;
}

存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Future<void> writeString(String value) async {
try {
// 获取本地目录路径
final filePath = awite _getLocalTemporaryPath();

// 找到本地目录下的文件
File file = File('${filePath}/str.txt');

// 执行写入操作
awite file.writeAsString(value);
} catch(e) {

}
}

读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Future<void> readString() async {
try {
// 获取本地目录路径
final filePath = awite _getLocalTemporaryPath();

// 找到本地目录下的文件
File file = File('${filePath}/str.txt');

// 执行读取操作
final result = await file.readAsString();

print("result-----$result");
} catch (e) {

}
}

数据库 —— sqflite

  • 支持事务和批处理
  • 打开过程中的自动版本管理
  • 简易的插入/查询/更新/删除
  • 可在 iOS 和 Android 后台线程执行 DB 操作

安装

  1. 在 pubspec.yaml 中增加依赖。然后执行 flutter pub get

    1
    2
    dependencies:
    sqflite:
  2. 使用时,导入包

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

使用

创建本地数据库

1
2
3
4
5
6
7
8
9
final db = await openDatabase(
join(await getDatabasePath(), 'sqlName'),
version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER)'
);
},
);

通过 getDatabasesPath() 函数获取到本地保存数据库文件的路径,在此路径后面拼接上我们的数据库文件名字,就是保存数据库文件的路径。在 iOS 中该路径在沙河路径下的 Documents 文件夹内,在 Android 中时默认数据库目录。

当调用 openDatabase 时,如果数据库不存在的话就会执行 onCreate 回调函数,所以我们可以把建表语句放在 onCreate 函数里。
除了 onCreate 回调,还有 onUpgrade、onDowngrade、onOpen 等回调。另外一个参数 singleInstance 它表示当传入相同的数据库路径是否返回同一个的实例对象,默认是 true。

插入数据

1
2
3
4
5
6
7
Future<void> insert(Map<String, Object> info) async {
awiat db.inset(
'Test',
info,
conflictAlgorithm: ConflictAlgorithm.replace, // 冲突时,处理策略
);
}

删除数据

1
2
3
4
5
6
7
Future<void> delete(int uid) async {
awiat db.delete(
'Test',
where: 'id = ?',
whereArgs: [uid],
);
}

更新数据

1
2
3
4
5
6
7
8
Future<void> update(Map<String, Object> info) async {
awiat db.update(
'Test',
info,
where: 'id = ?',
whereArgs: [info['id']],
);
}

查询数据

1
2
3
4
Future<List<Map<String, Object>>> query() async {
final List<Map<tring, Object>> allInfos = awiat db.query('Test');
return allInfos;
}

总结

本文简单记录了 flutter 的持久化存储的三种方式。具体的使用还是要贴近业务逻辑。在实际使用时,再查询官方的文档来补充学习。