使用 SQLite 持久化数据
如果您正在编写一个需要在本地设备上持久化和查询大量数据的应用程序,请考虑使用数据库而不是本地文件或键值存储。一般来说,与其他本地持久化解决方案相比,数据库提供更快的插入、更新和查询速度。
Flutter 应用可以通过 pub.dev 上提供的 sqflite
插件使用 SQLite 数据库。本示例演示了使用 sqflite
插入、读取、更新和删除各种狗的数据的基础知识。
如果您不熟悉 SQLite 和 SQL 语句,请在完成本示例之前,查看SQLite 教程以了解基础知识。
本示例使用以下步骤:
- 添加依赖项。
- 定义
Dog
数据模型。 - 打开数据库。
- 创建
dogs
表。 - 将
Dog
插入数据库。 - 检索狗的列表。
- 更新数据库中的
Dog
。 - 从数据库中删除
Dog
。
1. 添加依赖项
#要使用 SQLite 数据库,请导入 sqflite
和 path
包。
sqflite
包提供与 SQLite 数据库交互的类和函数。path
包提供定义数据库在磁盘上存储位置的函数。
要添加包作为依赖项,请运行 flutter pub add
:
flutter pub add sqflite path
确保在您将要使用的文件中导入这些包。
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
2. 定义 Dog 数据模型
#在创建用于存储狗信息的表之前,请花一些时间定义需要存储的数据。对于此示例,定义一个包含三个数据片段的 Dog 类:唯一的 id
、name
和每条狗的 age
。
class Dog {
final int id;
final String name;
final int age;
const Dog({
required this.id,
required this.name,
required this.age,
});
}
3. 打开数据库
#在读取和写入数据库数据之前,请打开与数据库的连接。这包括两个步骤:
- 使用
sqflite
包中的getDatabasesPath()
函数以及path
包中的join
函数定义数据库文件的路径。 - 使用
sqflite
中的openDatabase()
函数打开数据库。
// 避免因 flutter upgrade 引起的错误。
// 需要导入 'package:flutter/widgets.dart'。
WidgetsFlutterBinding.ensureInitialized();
// 打开数据库并存储引用。
final database = openDatabase(
// 设置数据库的路径。注意:使用 `path` 包中的 `join` 函数是最佳实践,以确保为每个平台正确构造路径。
join(await getDatabasesPath(), 'doggie_database.db'),
);
4. 创建 dogs
表
#接下来,创建一个表来存储各种狗的信息。对于此示例,创建一个名为 dogs
的表,该表定义可以存储的数据。每个 Dog
包含一个 id
、name
和 age
。因此,这些在 dogs
表中表示为三列。
id
是一个 Dartint
,并存储为INTEGER
SQLite 数据类型。最好也使用id
作为表的主键,以提高查询和更新时间。name
是一个 DartString
,并存储为TEXT
SQLite 数据类型。age
也是一个 Dartint
,并存储为INTEGER
数据类型。
有关可以在 SQLite 数据库中存储的可用数据类型的更多信息,请参阅官方 SQLite 数据类型文档。
final database = openDatabase(
// 设置数据库的路径。注意:使用 `path` 包中的 `join` 函数是最佳实践,以确保为每个平台正确构造路径。
join(await getDatabasesPath(), 'doggie_database.db'),
// 当数据库第一次创建时,创建一个表来存储狗。
onCreate: (db, version) {
// 在数据库上运行 CREATE TABLE 语句。
return db.execute(
'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
},
// 设置版本。这将执行 onCreate 函数并提供执行数据库升级和降级的路径。
version: 1,
);
5. 将 Dog 插入数据库
#现在您有一个包含适合存储各种狗信息的表的数据库,现在该读取和写入数据了。
首先,将 Dog
插入 dogs
表。这包括两个步骤:
- 将
Dog
转换为Map
- 使用
insert()
方法将Map
存储在dogs
表中。
class Dog {
final int id;
final String name;
final int age;
Dog({
required this.id,
required this.name,
required this.age,
});
// 将 Dog 转换为 Map。键必须与数据库中列的名称相对应。
Map<String, Object?> toMap() {
return {
'id': id,
'name': name,
'age': age,
};
}
// 实现 toString 以便在使用 print 语句时更容易查看有关每条狗的信息。
@override
String toString() {
return 'Dog{id: $id, name: $name, age: $age}';
}
}
// 定义一个将狗插入数据库的函数
Future<void> insertDog(Dog dog) async {
// 获取数据库的引用。
final db = await database;
// 将 Dog 插入正确的表中。您也可以指定在两次插入相同的狗时使用的 `conflictAlgorithm`。
//
// 在这种情况下,替换任何以前的数据。
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 创建一条狗并将其添加到 dogs 表
var fido = Dog(
id: 0,
name: 'Fido',
age: 35,
);
await insertDog(fido);
6. 检索狗的列表
#现在数据库中已经存储了一条 Dog
,请查询数据库以获取特定狗或所有狗的列表。这包括两个步骤:
- 对
dogs
表运行query
。这将返回一个List<Map>
。 - 将
List<Map>
转换为List<Dog>
。
// 一个从 dogs 表检索所有狗的方法。
Future<List<Dog>> dogs() async {
// 获取数据库的引用。
final db = await database;
// 查询表中的所有狗。
final List<Map<String, Object?>> dogMaps = await db.query('dogs');
// 将每条狗的字段列表转换为 `Dog` 对象列表。
return [
for (final {
'id': id as int,
'name': name as String,
'age': age as int,
} in dogMaps)
Dog(id: id, name: name, age: age),
];
}
// 现在,使用上述方法检索所有狗。
print(await dogs()); // 打印包含 Fido 的列表。
7. 更新数据库中的 Dog
#在将信息插入数据库后,您可能希望稍后更新该信息。您可以通过使用 sqflite
库中的 update()
方法来实现。
这包括两个步骤:
- 将 Dog 转换为 Map。
- 使用
where
子句确保更新正确的 Dog。
Future<void> updateDog(Dog dog) async {
// 获取数据库的引用。
final db = await database;
// 更新给定的 Dog。
await db.update(
'dogs',
dog.toMap(),
// 确保 Dog 具有匹配的 id。
where: 'id = ?',
// 传递 Dog 的 id 作为 whereArg 以防止 SQL 注入。
whereArgs: [dog.id],
);
}
// 更新 Fido 的年龄并将其保存到数据库。
fido = Dog(
id: fido.id,
name: fido.name,
age: fido.age + 7,
);
await updateDog(fido);
// 打印更新的结果。
print(await dogs()); // 打印年龄为 42 的 Fido。
8. 从数据库中删除 Dog
#除了插入和更新有关狗的信息外,您还可以从数据库中删除狗。要删除数据,请使用 sqflite
库中的 delete()
方法。
在本节中,创建一个函数,该函数接受一个 id 并从数据库中删除具有匹配 id 的狗。要使其工作,您必须提供一个 where
子句来限制被删除的记录。
Future<void> deleteDog(int id) async {
// 获取数据库的引用。
final db = await database;
// 从数据库中删除 Dog。
await db.delete(
'dogs',
// 使用 `where` 子句删除特定的狗。
where: 'id = ?',
// 传递 Dog 的 id 作为 whereArg 以防止 SQL 注入。
whereArgs: [id],
);
}
示例
#要运行示例:
- 创建一个新的 Flutter 项目。
- 将
sqflite
和path
包添加到您的pubspec.yaml
。 - 将以下代码粘贴到名为
lib/db_test.dart
的新文件中。 - 使用
flutter run lib/db_test.dart
运行代码。
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
void main() async {
// 避免因 flutter upgrade 引起的错误。
// 需要导入 'package:flutter/widgets.dart'。
WidgetsFlutterBinding.ensureInitialized();
// 打开数据库并存储引用。
final database = openDatabase(
// 设置数据库的路径。注意:使用 `path` 包中的 `join` 函数是最佳实践,以确保为每个平台正确构造路径。
join(await getDatabasesPath(), 'doggie_database.db'),
// 当数据库第一次创建时,创建一个表来存储狗。
onCreate: (db, version) {
// 在数据库上运行 CREATE TABLE 语句。
return db.execute(
'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
},
// 设置版本。这将执行 onCreate 函数并提供执行数据库升级和降级的路径。
version: 1,
);
// 定义一个将狗插入数据库的函数
Future<void> insertDog(Dog dog) async {
// 获取数据库的引用。
final db = await database;
// 将 Dog 插入正确的表中。您也可以指定在两次插入相同的狗时使用的 `conflictAlgorithm`。
//
// 在这种情况下,替换任何以前的数据。
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 一个从 dogs 表检索所有狗的方法。
Future<List<Dog>> dogs() async {
// 获取数据库的引用。
final db = await database;
// 查询表中的所有狗。
final List<Map<String, Object?>> dogMaps = await db.query('dogs');
// 将每条狗的字段列表转换为 `Dog` 对象列表。
return [
for (final {
'id': id as int,
'name': name as String,
'age': age as int,
} in dogMaps)
Dog(id: id, name: name, age: age),
];
}
Future<void> updateDog(Dog dog) async {
// 获取数据库的引用。
final db = await database;
// 更新给定的 Dog。
await db.update(
'dogs',
dog.toMap(),
// 确保 Dog 具有匹配的 id。
where: 'id = ?',
// 传递 Dog 的 id 作为 whereArg 以防止 SQL 注入。
whereArgs: [dog.id],
);
}
Future<void> deleteDog(int id) async {
// 获取数据库的引用。
final db = await database;
// 从数据库中删除 Dog。
await db.delete(
'dogs',
// 使用 `where` 子句删除特定的狗。
where: 'id = ?',
// 传递 Dog 的 id 作为 whereArg 以防止 SQL 注入。
whereArgs: [id],
);
}
// 创建一条狗并将其添加到 dogs 表
var fido = Dog(
id: 0,
name: 'Fido',
age: 35,
);
await insertDog(fido);
// 现在,使用上述方法检索所有狗。
print(await dogs()); // 打印包含 Fido 的列表。
// 更新 Fido 的年龄并将其保存到数据库。
fido = Dog(
id: fido.id,
name: fido.name,
age: fido.age + 7,
);
await updateDog(fido);
// 打印更新的结果。
print(await dogs()); // 打印年龄为 42 的 Fido。
// 从数据库中删除 Fido。
await deleteDog(fido.id);
// 打印狗的列表(空)。
print(await dogs());
}
class Dog {
final int id;
final String name;
final int age;
Dog({
required this.id,
required this.name,
required this.age,
});
// 将 Dog 转换为 Map。键必须与数据库中列的名称相对应。
Map<String, Object?> toMap() {
return {
'id': id,
'name': name,
'age': age,
};
}
// 实现 toString 以便在使用 print 语句时更容易查看有关每条狗的信息。
@override
String toString() {
return 'Dog{id: $id, name: $name, age: $age}';
}
}
(这段内容本身就是链接和说明,不需要翻译成中文。 如果需要翻译的是前面内容中用到的这些链接对应的页面内容,请提供这些页面的内容。)
除非另有说明,否则本网站上的文档反映的是 Flutter 的最新稳定版本。页面最后更新于 2025-01-30。 查看源代码 或 报告问题。