Flutter
提供了三种数据持久化方法,即文件、SharedPreferences 与数据库
。
文件
Flutter
提供了两种文件存储的目录,即临时(Temporary
)目录与文档(Documents
)目录:
-
临时目录是操作系统可以随时清除的目录,通常被用来存放一些不重要的临时缓存数据。这个目录在
iOS
上对应着NSTemporaryDirectory
返回的值(网上搜集的信息,对 iOS 不熟悉故不确定对错)
,而在Android
上则对应着getCacheDir
返回的值。 -
文档目录则是只有在删除应用程序时才会被清除的目录,通常被用来存放应用产生的重要数据文件。在
iOS
上,这个目录对应着NSDocumentDirectory (网上搜集的信息,对iOS不熟悉故不确定对错)
,而在Android
上则对应着AppData
目录。
如何在Flutter
中获取上述两个目录地址呢?这里使用到的是 path_provider库
先在pubspec.yaml
中添加依赖,然后点击AndroidStudio
右上角的 packages get
dependencies:
path_provider: ^1.6.5
接下来就可以获取到以上两个目录路径了:
// 获取临时目录
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
// 获取文档目录
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;
接下来我们通过示例来看一下效果。
示例
创建文件并写入数据,随后将数据读取出来。由于文件读写一般比较耗时且容易出错,为了保证程序的健壮性,需要异步
Futures
以及try catch
使用。
首先在文档目录创建文件:
Future<File> get _localFile async {
// 存在文档目录
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
// 使用绝对路径
return File('$path/content.txt');
}
接着在文件中写入数据:
Future<File> writeContent(String content) async {
// 获取到文件
final file = await _localFile;
// 执行写入
return file.writeAsString(content);
}
最后将上一步写入的数据读取出来:
Future<String> readContent() async {
try {
// 获取到文件
final file = await _localFile;
// 执行读取
String contents = await file.readAsString();
return contents;
} catch (e) {
return "";
}
}
效果图:
SharedPreferences
文件比较适合大量的、有序的数据持久化,如果我们只是需要缓存少量的键值对信息(比如记录用户是否阅读了公告,或是简单的计数),则可以使用 SharedPreferences。
接下来我们通过示例来看一下效果。
示例
首先引入库:shared_preferences
dependencies:
shared_preferences: ^0.5.6+3
然后往 SP 中写入数据:
// 获得实例
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
// 写入数据
_prefs.then((sp) => {
sp.setString('hello', '你好 SharedPreferences')
});
最后将之前写入的数据读取出来:
// 获得实例
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
// 取出数据
_prefs.then((sp) {
setState(() {
mContent = sp.getString('hello');// 通过可 key 取出 value
});
});
封装之后使用,源码地址:shared_preferences.dart
先看示例效果:
跳转网页那块有点慢,录制 gif 的时候直接跳过了,所以在详情页面会短暂显示空白
使用流程:
- 先获取到
SharedPrefernces
对象, - 然后再点击搜索列表的
item
然后将数据存入到SharedPrefernces
中, - 最后在历史面板展示
SharedPrefernces
中的数据。
具体使用可查看 Flutter-WanAndroid 项目
数据库 sqlite
SharedPrefernces
的使用固然方便,但这种方式只适用于持久化少量数据的场景,我们并不能用它来存储大量数据。原因是在Android
中使用SharedPrefernces
存储的文件过大会导致读取速度变慢还可能出现内存抖动问题,iOS
不熟悉这块也没法描述。为了解决以上问题,通常会选用 sqlite 数据库来本地化存储稍大量的数据。
示例
往数据库中添加三个学生的数据,然后读取出学生的姓名。
1. 首先引入库:sqflite
dependencies:
...
sqflite: ^1.3.0
2. 创建数据库,并调用初始化方法,我这里是在initState()
调用:
join
方法可能会爆红,在顶部引入path
库即可
import 'package:path/path.dart';
Future<void> initDataBase() async {
dataBase = await openDatabase(
join(await getDatabasesPath(), 'students_database.db'),
onCreate: (db, version) => db.execute(
"CREATE TABLE students(id TEXT PRIMARY KEY, name TEXT, score INTEGER)"),
onUpgrade: (db, oldVersion, newVersion) {
//dosth for migration
print("old:$oldVersion,new:$newVersion");
},
version: 1,
);
print("database:$dataBase");
}
// 调用初始化
@override
void initState() {
initDataBase();
super.initState();
}
3. 存入数据:
// 创建 Student 对象
class Student {
String id;
String name;
int score;
Student({
this.id,
this.name,
this.score,
});
factory Student.fromJson(Map<String, dynamic> parsedJson) {
return Student(
id: parsedJson['id'],
name: parsedJson['name'],
score: parsedJson['score'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'score': score,
};
}
}
// 插入数据库 中的 students 表
Future<void> insertStudent(Student std) async {
await dataBase.insert(
'students',
std.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 调用插入数据方法
insertData() async {
var student1 = Student(id: '0', name: '张三', score: 90);
var student2 = Student(id: '1', name: '李四', score: 80);
var student3 = Student(id: '2', name: '王五', score: 85);
// Insert a dog into the database.
await insertStudent(student1);
await insertStudent(student2);
await insertStudent(student3);
}
4. 取出数据:
// 查询 students 表的数据
Future<List<Student>> students() async {
final List<Map<String, dynamic>> maps = await dataBase.query('students');
return List.generate(maps.length, (i) => Student.fromJson(maps[i]));
}
// 转为 string 输出
getStudents() async {
await students()
.then((list) => list.forEach((s) => sb.writeln(s.toJson().toString())));
setState(() {
mContext = sb.toString();
});
}
5. 关闭数据库:
@override
void dispose() {
dataBase.close();
super.dispose();
}
以上就是数据库的基本使用,使用步骤略有繁琐,我们可以封装了之后再使用。
参考 flutter-go
封装之后使用,源码地址:
先看示例效果:
本地收藏数据库实现。跳转网页那块有点慢,录制 gif 的时候直接跳过了,所以在详情页面会短暂显示空白。
以下文件请在 Flutter-WanAndroid 项目中查找
使用流程:
- 初始化数据库对象,这里在
main.dart
文件中初始化。具体的初始化逻辑在utils/provider.dart
文件中
// 数据库初始化
await provider.init(true);
- 然后我们将常见的数据操作(增删改查)放在
utils/sql.dart
文件中,便于操作数据库。
- 接着就是具体的使用了。如收藏操作,在
model/collect.dart
文件中,先创建到Collection
对象和在数据库中创建表格,并在里面实现model
的数据库逻辑操作。
// 例如:插入新收藏
Future insert(Collection collection) {
var result =
sql.insert({'id': collection.id, 'title': collection.title, 'link':collection.link});
return result;
}
- 定义好逻辑操作之后,接着就是在合理的地方响应用户的输入了。这里是在
详情页面
用户点击收藏图标时调用model/collect.dart 中的 insert
方法插入数据库。
// 插入操作
_collectionControl
.insert(Collection(
id: widget.id.toString(),
title: widget.title,
link: widget.link))
.then((result) {
if (this.mounted) {
setState(() {
_hasCollected = true;
});
print("收藏成功");
// 发送事件给 收藏页面,显示数据
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _link, false));
}
}
});
- 最后在
views/collection_page.dart
页面展示收藏的数据。
void _getList() {
_collectionList.clear();
_collectionControl.getAllCollection().then((resultList) {
resultList.forEach((item) {
_collectionList.add(item);
});
if (this.mounted) {
setState(() {
_collectionList = _collectionList;
});
}
});
}
具体使用可查看 Flutter-WanAndroid 项目。