Szhangbiao's blog

记录一些让自己可以回忆的东西

0%

Flutter中数据存储

在 Flutter 的项目实践中数据存储的选型上主要考虑易用性和跨平台。这里罗列出了 Flutter 中常用的本地数据存储方案。

数据存储之 Shared Preferences

简单来说 Shared Preferences 是以键值对的形式存储数据在本地文件中,适合存储少量简单的数据。支持的 Dart 数据类型有 int、double、bool、String、ListString 等,支持全平台。
写入数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 将shared_preferences包添加到您的pubspec.yaml文件中。
import 'package:shared_preferences/shared_preferences.dart';

// Obtain shared preferences.
final SharedPreferences prefs = await SharedPreferences.getInstance();

// Save an integer value to 'counter' key.
await prefs.setInt('counter', 10);
// Save an boolean value to 'repeat' key.
await prefs.setBool('repeat', true);
// Save an double value to 'decimal' key.
await prefs.setDouble('decimal', 1.5);
// Save an String value to 'action' key.
await prefs.setString('action', 'Start');
// Save an list of strings to 'items' key.
await prefs.setStringList('items', <String>['Earth', 'Moon', 'Sun']);

读取数据:

1
2
3
4
5
6
7
8
9
10
// Try reading data from the 'counter' key. If it doesn't exist, returns null.
final int? counter = prefs.getInt('counter');
// Try reading data from the 'repeat' key. If it doesn't exist, returns null.
final bool? repeat = prefs.getBool('repeat');
// Try reading data from the 'decimal' key. If it doesn't exist, returns null.
final double? decimal = prefs.getDouble('decimal');
// Try reading data from the 'action' key. If it doesn't exist, returns null.
final String? action = prefs.getString('action');
// Try reading data from the 'items' key. If it doesn't exist, returns null.
final List<String>? items = prefs.getStringList('items');

通过 Key 删除数据:

1
2
// Remove data for the 'counter' key.
await prefs.remove('counter');

存储的位置根据平台不同而不同,如

平台 存储位置
Android SharedPreferences
iOS NSUserDefaults
Linux XDG_DATA_HOME directory
Windows In the roaming AppData directory
macOS NSUserDefaults
Web LocalStorage

数据存储之 Hive

Hive 是一个轻量级和快速的键值数据库解决方案, 纯 Dart 编写,没有原生依赖性且跨平台。除了 Dart 的基本类型外还支持自定义对象,不过需要添加对应的 TypeAdapter,支持数据加密且使用简单。

写入数据:

1
2
3
4
5
var box = await Hive.openBox('testBox');
box.put('name', 'David');
// 也支持直接 Add,这时的Key是自动递增整数
box.add('Linda'); // index 0, key 0
box.add('Dan'); // index 1, key 1

读取数据:

1
2
3
4
var name = box.get('name');
// 自动递增的值
box.getAt(0);
box.getAt(1);

更新数据:

1
2
3
4
// 直接使用相同的Key覆盖旧值
box.put('name', 'Mike');
// 使用自动递增的值添加
box.putAt(0, 'Jenifer');

删除数据:

1
2
3
4
// 直接通过Key来使用delete() 方法。
box.delete('name');
// 使用自动递增的值添加
box.deleteAt(0);

使用 TypeAdapter 存储自定义对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@JsonSerializable()
class Person {
final String name;
final String country;

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

factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);

Map<String, dynamic> toJson() => _$PersonToJson(this);
}

这里使用了 JsonSerializable,它是 Flutter 中的一个开源库,用于序列化和反序列化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'package:hive/hive.dart';

class PersonAdapter extends TypeAdapter<Person> {
@override
int get typeId => 0; // typeId不能重复,不然会报错
@override
Person read(BinaryReader reader) {
String personString = reader.readString();
Map<String, dynamic> personJson = json.decode(personString);
return Person.fromJson(personJson);
}

@override
void write(BinaryWriter writer, Person obj) {
String personString = json.encode(obj.toJson());
writer.writeString(personString);
}
}

不过 TypeAdapter 需要在openBox之前注册

1
2
3
4
5
6
7
8
9
10
main() async {
// Initialize hive
await Hive.initFlutter();
// Registering the adapter
Hive.registerAdapter(PersonAdapter());
// Opening the box
await Hive.openBox('testBox');

runApp(MyApp());
}

数据存储之文件存储

支持除了 Web 平台以外的所有平台,通过path_provider包获取文件存储的目录路径。
写入数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import 'package:path_provider/path_provider.dart';

Future<String> getFilePath() async {
final directory = await getApplicationDocumentsDirectory();
return File('${directory.path}/test_file.txt').path;
}

Future<void> writeToFile(String data) async {
final file = File(await getFilePath());
await file.writeAsString(data);
}

// 从文件中读取数据
Future<String> readFromFile() async {
final file = File(await getFilePath());
return file.readAsString();

写入数据前可以把自定义对象转换成 json,读取数据时再把 json 转换成自定义对象

数据存储之 SQLite 数据库

SqLite 是一个轻量级的关系型数据库解决方案,支持多种数据库类型,支持多种数据存储类型。也是支持除了 Web 平台以外的所有平台,在 Flutter 中通过sqflite包来与 SQLite 数据库交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 将 sqflite 包添加到 pubspec.yaml 文件中。
import 'package:sqflite/sqflite.dart';

// 初始化数据库
Future<Database> initDatabase() async {
final path = join(await getDatabasesPath(), 'test_database.db');
return openDatabase(path, onCreate: (db, version) {
return db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
}, version: 1);
}

// 将数据插入数据库
Future<void> insertData(String name) async {
final db = await initDatabase();
await db.insert('users', {'name': name});
}

// 从数据库中查询数据
Future<List<Map<String, dynamic>>?> fetchData() async {
final db = await initDatabase();
return db.query('users');
}

数据库表的字段的更改都要涉及数据库版本的更新,这里不做详细介绍。