likes
comments
collection
share

懂 Vue、React 就懂 Flutter 数据存储

作者站长头像
站长
· 阅读数 2

在Flutter的世界里,有三个特别的储物柜:SharedPreferences、SQLite数据库和文件存储。SharedPreferences像是你的随身小本子,你可以在其中记下一些键值对,比如你的喜好设置,它非常轻便,适合存储少量的数据。SQLite数据库就像是你的大书柜,你可以在其中存储大量的数据并进行复杂查询,它将信息安排得井井有条。而文件存储就像你的仓库,你可以在其中存储大量的数据,如图片或视频。这其实和前端存储很类似,在浏览器中,少量数据数据我们可以存在LocalStorage中, 大量数据可以存在IndexedDB。

SharedPreferences

SharedPreferences 与 Web 的 LocalStorage 类似,都是以键值对的形式存储数据。如果你正在开发一个音乐播放器应用,你可能需要存储用户的播放设置,比如播放模式(顺序播放、随机播放等)、音量大小等。在这种情况下,你可以使用SharedPreferences来存储这些用户偏好。

首先,获取 SharedPreferences 的实例,然后使用 putString 和 putFloat 方法来存储数据,如下所示:

SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('play_mode', 'random');
prefs.setFloat('volume', 0.7);

在这个例子中,play_modevolume 都是键,random0.7 分别是要存储的值,表示播放模式和音量大小。当你需要获取这个值时,可以使用 getStringgetFloat 方法,如下所示:

SharedPreferences prefs = await SharedPreferences.getInstance();
String playMode = prefs.getString('play_mode');
float volume = prefs.getFloat('volume');

请注意,使用时我们需要安装依赖 shared_preferences

flutter pub add shared_preferences

虽然SharedPreferences和LocalStorage在功能上相似,但SharedPreferences在数据类型的支持上更为丰富。SharedPreferences可以存储的数据类型包括字符串、整数、布尔值和浮点数。而LocalStorage主要存储的是字符串类型的数据。如果你尝试在 LocalStorage 中存储非字符串类型的数据,如数字或对象,它会被自动转换为字符串,可能很多人都遇到过这个 Bug

localStorage.setItem("data", {hello: world})
localStorage.getItem("data")
'[object Object]'

SQLite

对于更复杂的数据存储需求,如需要存储大量数据或需要进行复杂查询的情况,开发者则可能会选择数据库存储。Flutter支持SQLite数据库,开发者可以使用SQLite进行本地数据的存储。

同样的,我们需要先添加依赖

flutter pub add sqflite path

并导入

import 'dart:async';

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

接下来我们就可以通过 SQL 语句建表了

await db.execute( 'CREATE TABLE Test (id INTEGER PRIMARY KEY, value TEXT)');

这行代码在SQLite数据库中创建了一个名为 Test 的表。该表有两列,id 和 value。 id 列是整数类型,被设定为主键,这意味着每一个 Test 表中的记录都应有一个唯一的 id。value 列是文本(字符串)类型,可以存储任意形式的文本信息。

然后,我们可以像这样进行写入操作:

await db.transaction((txn) async {
  int id1 = await txn.rawInsert(
      'INSERT INTO Test(value) VALUES("flutter sqlite")');
});

我们首先开始一个数据库事务,然后在该事务中执行一个 rawInsert 操作。在这个操作中,我们向名为 Test 的表中插入一行数据,该行数据的 value 字段的值为 flutter sqlite。这个操作是异步的,所以我们用await关键字等待它完成。这个操作完成后,它会返回新插入行的id,我们将这个id保存在变量id1中。完成所有操作后,事务会自动提交。

这和前端的 IndexedDB 一样,都是一个事务型数据库系,如同SQL-based RDBMS。我们可以通过 window.indexedDB.open 打开一个数据库后进行正删改查操作

var db;
var request = window.indexedDB.open("TestDB", 1);
request.onerror = function(event) {
  console.log("error: ");
};
request.onsuccess = function(event) {
  db = request.result;
  console.log("success: "+ db);
};

TestDB 如果不存在则会自动创建,后面那个 1 是版本号的意思。然后我们在CRUD 时也会看到 transaction 这个关键字

var request = db.transaction(["Test"], "readwrite") 
              .request.objectStore("Test") 
              .add({ id: "01", name: "John", age: 30, email: "john@example.com" });

这段代码是在IndexedDB中添加数据的例子。首先,通过db.transaction(["Test"], "readwrite")开启一个读写事务。参数"Test"表示要操作的对象存储空间名称,"readwrite"表示这是一个可以进行读写操作的事务。

然后,通过.request.objectStore("Test")获取名为"Test"的对象存储空间。

接着,调用.add({ id: "01", name: "John", age: 30, email: "john@example.com" })向对象存储空间中添加一个对象。这个对象的id为"01",name为"John",age为30,email为"john@example.com"。

如果这个操作成功,IndexedDB会存储这个对象。如果对象存储空间中已经存在一个id为"01"的对象,这个操作会失败,并触发错误事件。

文件存储

文件存储稍微复杂有一点点,不是因为 API 复杂,是因为稍微需要懂一点点原生开发的知识,首先我们要搞清楚文件存储是存到哪里?但其实我们只要用 path_provider 这个封装好的包帮我们处理跨平台的事情,我们不需要搞清楚 IOS 和 Android 存储系统的差异,我们直接找到 2个移动端开发常用的存储位置 应用目录 和 临时目录。

应用目录是用于存储应用产生的文件,这些文件仅对应用自身可见,且会随着应用的卸载而删除。下面是获取应用文档目录的代码示例:

import 'package:path_provider/path_provider.dart';

void getApplicationDocumentsDirectoryPath() async {
  final directory = await getApplicationDocumentsDirectory();
  print('Application Documents Directory: ${directory.path}');
}

临时目录是用于存储临时文件的,这些文件可以随时被系统清理。下面是获取临时目录的代码示例:

import 'package:path_provider/path_provider.dart';

void getTemporaryDirectoryPath() async {
  final directory = await getTemporaryDirectory();
  print('Temporary Directory: ${directory.path}');
}

获取到文件存储的路径后,我们可以使用Dart的 dart:io 库来进行文件读写。首先,我们需要创建一个File 对象,然后调用它的 writeAsString 方法来写入数据,或者调用 readAsString 方法来读取数据。下面是一个简单的示例:

import 'dart:io';

void writeFile(String path, String text) async {
  final file = File(path);
  await file.writeAsString(text);
}

void readFile(String path) async {
  final file = File(path);
  String text = await file.readAsString();
}