从零开始用flutter写一个完整应用⑼:持久化储存1--SQL
说明
持久化储存,在应用开发中也是个很重要的功能,有些数据是需要在应用进程退出,再次启动时还能知道前一次的数据的,这种情况就需要持久化储存,最常见的就是设置的一些状态信息等。持久化储存在flutter里,可大致分3种:1,数据库SQLite;2,文件形式;3,键值对形式。本文先对数据库SQLite进行说明,后面再对后2种一一说明
SQLite 数据持久化
如果您正在编写一个需要持久化且查询大量本地设备数据的 app,可考虑采用数据库,而不是本地文件夹或关键值库。总的来说,相比于其他本地持久化方案来说,数据库能够提供更为迅速的插入、更新、查询功能。Flutter应用程序中可以通过
sqflite
package 来使用 SQLite 数据库
添加依赖
为了使用 SQLite 数据库,首先需要导入 sqflite 和 path 这两个 package。
1,sqflite 提供了丰富的类和方法,以便你能便捷实用 SQLite 数据库。
2,path 提供了大量方法,以便你能正确的定义数据库在磁盘上的存储位置。
//增加依赖
dependencies:
flutter:
sdk: flutter
sqflite:
path:
//确保你已将 packages 导入要使用的文件中。
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
定义数据模型
数据模型,也就是实体数据类。示例如下
class Dog {
final int id;
final String name;
final int age;
const Dog({
required this.id,
required this.name,
required this.age,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'age': age,
};
}
@override
String toString() {
return 'Dog{id: $id, name: $name, age: $age}';
}
}
打开数据库
在你准备读写数据库的数据之前,你要先打开这个数据库。打开一个数据库有以下两个步骤:
使用 sqflite package 里的 getDatabasesPath 方法并配合 path package里的 join 方法定义数据库的路径。
使用 sqflite package 里的 openDatabase 方法打开数据库。
WidgetsFlutterBinding.ensureInitialized();
final database = openDatabase(
join(await getDatabasesPath(), 'doggie_database.db'),
);
创建表
使用SQLite 语句创建表,示例如下
final database = openDatabase(
join(await getDatabasesPath(), 'doggie_database.db'),
onCreate: (db, version) {
// 创建表
return db.execute(
'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
},
//版本号
version: 1,
);
插入数据
使用SQLite 语句创建表,示例如下
分以下两步:1. 把Dog
转换成一个Map
数据类型;2. 使用insert() 方法把
Map保存到
dogs` 数据表中。
Future<void> insertDog(Dog dog) async {
final db = await database;
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
查询列表
分为以下两步:
1,调用 dogs 表对像的 query 方法。这将返回一个List <Map>。
2,将 List<Map> 转换成 List<Dog> 数据类型。
Future<List<Dog>> dogs() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query('dogs');
return List.generate(maps.length, (i) {
return Dog(
id: maps[i]['id'],
name: maps[i]['name'],
age: maps[i]['age'],
);
});
}
修改数据
修改数据操作包含以下两步:
1,将一条狗狗的数据转换成 Map 数据类型;
2,使用 where 语句定位到具体将要被修改的数据。
Future<void> updateDog(Dog dog) async {
final db = await database;
await db.update(
'dogs',
dog.toMap(),
where: 'id = ?',
whereArgs: [dog.id],
);
}
注意
使用 whereArgs 将参数传递给 where 语句。有助于防止 SQL 注入攻击。
这里不要使用字符串模板,比如: where: "id = ${dog.id}"!
删除数据
Future<void> deleteDog(int id) async {
// 获得数据库引用
final db = await database;
await db.delete(
'dogs',
where: 'id = ?',
whereArgs: [id],
);
}
完整示例如下
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final database = openDatabase(
// 设置数据库的路径。注意:使用 `path` 包中的 `join` 方法是
// 确保在多平台上路径都正确的最佳实践。
join(await getDatabasesPath(), 'doggie_database.db'),
// 当数据库第一次被创建的时候,创建一个数据表,用以存储狗狗们的数据。
onCreate: (db, version) {
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;
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 获取数据
Future<List<Dog>> dogs() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query('dogs');
// 将 List<Map<String, dynamic> 转换成 List<Dog> 数据类型
return List.generate(maps.length, (i) {
return Dog(
id: maps[i]['id'],
name: maps[i]['name'],
age: maps[i]['age'],
);
});
}
//更新数据
Future<void> updateDog(Dog dog) async {
// 获得数据库引用
final db = await database;
// 修改给定的狗狗的数据
await db.update(
'dogs',
dog.toMap(),
where: 'id = ?',
whereArgs: [dog.id],
);
}
//删除数据
Future<void> deleteDog(int id) async {
// 获得数据库引用
final db = await database;
// 将狗狗从数据库移除
await db.delete(
'dogs',
where: 'id = ?',
whereArgs: [id],
);
}
// 测试插入数据
var fido = const Dog(
id: 0,
name: 'Fido',
age: 35,
);
await insertDog(fido);
print(await dogs());
// 测试更新数据
fido = Dog(
id: fido.id,
name: fido.name,
age: fido.age + 7,
);
await updateDog(fido);
print(await dogs());
// 测试删除数据
await deleteDog(fido.id);
// 打印一个列表的狗狗们 (这里已经空了
print(await dogs());
}
class Dog {
final int id;
final String name;
final int age;
const Dog({
required this.id,
required this.name,
required this.age,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'age': age,
};
}
// 重写 toString 方法,以便使用 print 方法查看每个狗狗信息的时候能更清晰。
@override
String toString() {
return 'Dog{id: $id, name: $name, age: $age}';
}
}