Flutter-数据持久化的两种方式
相信做过原生开发对数据存储并不陌生,在原生 Android 中会把一些轻量级的数据(如用户登录信息、APP配置信息等)写入 SharedPreferences
做存储,在 iOS 中使用 NSUserDefaults
做存储。而对有大批量数据增、删、改、查的需求时,则会选择通过 Sqlite
进行实现。而在 Flutter 中也有官方维护的插件可以实现这些功能。
简单数据持久化
保存数据到本地磁盘是应用程序常用功能之一,比如保存用户登录信息、用户配置信息等。而保存这些信息通常使用 shared_preferences
,它保存数据的形式为 Key-Value(键值对),支持 Android 和 iOS。shared_preferences 是一个第三方插件,在 iOS 中使用 NSUserDefaults
,在 Android 中使用 SharedPreferences
。
添加依赖
在项目的 pubspec.yaml
文件中添加依赖:
dependencies:
shared_preferences: ^2.0.10
执行命令:
flutter pub get
shared_preferences 支持的数据类型有 int、double、bool、string、stringList。
示例代码1
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SaveDataPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => SaveDataState();
}
class SaveDataState extends State {
final _textFieldController = TextEditingController();
var _storageString = '';
final SAVE_KEY = 'storage_key';
//利用SharedPreferences存储数据
Future saveString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString(
SAVE_KEY, _textFieldController.value.text.toString());
}
//获取存在SharedPreferences中的数据
Future getString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
setState(() {
if(sharedPreferences.getString(SAVE_KEY) != null) {
_storageString = sharedPreferences.getString(SAVE_KEY)!;
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('数据存储'),
),
body: Column(
children: <Widget>[
const Text("shared_preferences存储", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: const Text("存储"),
color: Colors.pink,
),
MaterialButton(
onPressed: getString,
child: const Text("获取"),
color: Colors.lightGreen,
),
Text('shared_preferences存储的值为 $_storageString'),
],
),
);
}
}
实际效果
示例代码2
这里是封装一个类进行数据存储,包含保存、获取、删除、清理功能
。
import 'package:shared_preferences/shared_preferences.dart';
class Storage {
static Future<void> setString(key, value) async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.setString(key, value);
}
static Future<String?> getString(key) async{
SharedPreferences sp = await SharedPreferences.getInstance();
sp.getString(key);
}
static Future<void> remove(key) async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.remove(key);
}
static Future<void> clear() async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.clear();
}
}
这里使用这个封装的类,具体包含是这样的功能:
1、获取本地存储里面的数据 (searchList)
2、判断本地存储是否有数据
2.1、如果有数据
1、读取本地存储的数据
2、判断本地存储中有没有当前数据,
如果有不做操作、如果没有当前数据,本地存储的数据和当前数据拼接后重新写入
2.2、如果没有数据
直接把当前数据放在数组中写入到本地存储
import 'dart:convert';
import 'storage.dart';
class SearchServices {
static setHistoryData(keywords) async {
String? searchList = await Storage.getString('searchList');
if(searchList != null){
List searchListData = json.decode(searchList);
var hasData = searchListData.any((value){
return value == keywords;
});
if(!hasData){
searchListData.add(keywords);
await Storage.setString('searchList', json.encode(searchListData));
}
} else {
List tempList = [];
tempList.add(keywords);
await Storage.setString('searchList', json.encode(tempList));
}
}
static getHistoryList() async {
String? searchList = await Storage.getString('searchList');
if(searchList != null){
List searchListData = json.decode(searchList);
return searchListData;
}
return [];
}
static clearHistoryList() async {
await Storage.remove('searchList');
}
static removeHistoryData(keywords) async {
String? searchList = await Storage.getString('searchList');
if(searchList != null){
List searchListData = json.decode(searchList);
searchListData.remove(keywords);
await Storage.setString('searchList', searchList);
}
}
}
大量复杂数据持久化
对有大批量数据增、删、改、查的需求时,我们就想到了数据库 Sqlite。在Flutter中的数据库叫 Sqflite
跟原生的 Sqlite
叫法不一样,Sqflite
是一个同时支持 Android 跟 iOS 平台的数据库。
添加依赖
sqflite: ^2.0.1
执行命令:
flutter pub get
使用方法介绍
插入数据
插入数据有两种方法可以实现:
Future<int> insert(String table, Map<String, Object?> values,
{String? nullColumnHack, ConflictAlgorithm? conflictAlgorithm});
Future<int> rawInsert(String sql, [List<Object?>? arguments]);
insert
方法第一个参数为操作的表名,第二个参数map中是想要添加的字段名和对应字段值,第三个参数是发生冲突时解决方案。官方给的一个插入的示例:
var value = {
'age': 18,
'name': 'Candy'
};
int id = await db.insert(
'table',
value,
conflictAlgorithm: ConflictAlgorithm.replace,
);
rawInsert
方法第一个参数为一条插入sql
语句,第二个参数表示填充数据。官方给的一个插入的示例:
int id1 = await database.rawInsert('INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');
查询数据
查询数据提供了两种方法:
Future<List<Map<String, Object?>>> query(String table,
{bool? distinct,
List<String>? columns,
String? where,
List<Object?>? whereArgs,
String? groupBy,
String? having,
String? orderBy,
int? limit,
int? offset});
Future<List<Map<String, Object?>>> rawQuery(String sql,
[List<Object?>? arguments]);
query
方法第一个参数为操作的表名,后边的可选参数依次表示是否去重、查询字段、where子句、where子句占位符参数值、如何分组、包含哪些行组、如何排序、查询的条数、查询的偏移位。除了表名和查询字段其他都是非必传的。官方给出的查询示例:
List<Map> maps = await db.query(tableTodo,
columns: ['columnId', 'columnDone', 'columnTitle'],
where: 'columnId = ?',
whereArgs: [id]);
rawQuery
方法第一个参数是一条查询sql语句。官方给出的查询示例:
List<Map> list = await database.rawQuery('SELECT * FROM Test');
更新数据
更新数据库中的数据,返回修改了的数量,这里也是提供了两种方法,
Future<int> rawUpdate(String sql, [List<Object?>? arguments]);
Future<int> update(String table, Map<String, Object?> values,
{String? where,
List<Object?>? whereArgs,
ConflictAlgorithm? conflictAlgorithm});
rawUpdate
方法第一个参数为一条更新sql语句,第二个参数表示更新的数据。官方给出的查询示例:
int count = await database.rawUpdate(
'UPDATE Test SET name = ?, value = ? WHERE name = ?',
['updated name', '9876', 'some name']);
update
方法第一个参数为操作的表名,第二个参数为修改的字段和对应值,后面的参数依次是 where 语句,where 子句占位符参数值,冲突的解决方案。官方给出的查询示例:
int count = await db.update(tableTodo, todo.toMap(),
where: '$columnId = ?', whereArgs: [todo.id]);
删除数据
删除数据也有两种方法,返回删除的数量。
Future<int> rawDelete(String sql, [List<Object?>? arguments]);
Future<int> delete(String table, {String? where, List<Object?>? whereArgs});
rawDelete
方法第一个参数为一条删除sql语句,第二个参数表示填充数据。官方给出的查询示例:
int count = await database.rawDelete('DELETE FROM Test WHERE name = ?', ['another name']);
delete
方法第一个参数为操作的表名,后边的可选参数依次表示 where 子句、where 子句占位符参数值。官方给出的查询示例:
int count = await db.delete(tableTodo, where: 'columnId = ?', whereArgs: [id]);
举个例子
定义了一个 Person
对象,通过封装数据库定义一个类 DBProvider
实现增、删、改、查。
单例模式创建 SQLite
//创建单例模式SQLite
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
DBProvider._internal();
完整的创建数据库,包含增、删、改、查的代码
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DBProvider {
//创建单例模式SQLite
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
DBProvider._internal();
static Database? _db;
Future<Database> get db async {
if (_db != null) {
return _db!;
}
_db = await _initDB();
return _db!;
}
//初始化数据库
Future<Database> _initDB() async {
// 获取数据库文件的存储路径
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, 'demo.db');
//定义了数据库的版本
return await openDatabase(path,
version: 1, onCreate: _onCreate);
}
//创建数据库表
Future _onCreate(Database db, int version) async {
return await db.execute('''
CREATE TABLE $tablePerson (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT,
$columnSex TEXT,
$columnAge INTEGER,
''');
}
// 插入人员信息
Future<Person> insert(Person person) async {
person.id = await _db!.insert(tablePerson, person.toMap());
return person;
}
// 查找所有人员信息
Future<List<Person>?> queryAll() async {
List<Map> maps = await _db!.query(tablePerson, columns: [
columnId,
columnName,
columnSex,
columnAge
]);
if (maps.isEmpty) {
return null;
}
List<Person> books = [];
for (int i = 0; i < maps.length; i++) {
books.add(Person.fromMap(maps[i]));
}
return books;
}
// 根据ID查找个人信息
Future<Person?> getBook(int id) async {
List<Map> maps = await _db!.query(tablePerson,
columns: [
columnId,
columnName,
columnSex,
columnAge
],
where: '$columnId = ?',
whereArgs: [id]);
if (maps.isNotEmpty) {
return Person.fromMap(maps.first);
}
return null;
}
// 根据ID删除个人信息
Future<int> delete(int id) async {
return await _db!.delete(tablePerson, where: '$columnId = ?', whereArgs: [id]);
}
// 更新个人信息
Future<int> update(Person person) async {
return await _db!.update(tablePerson, person.toMap(),
where: '$columnId = ?', whereArgs: [person.id]);
}
}
Person类的代码
const String tablePerson = 'person';
const String columnId = '_id';
const String columnName = 'name';
const String columnSex = 'sex';
const String columnAge = 'age';
class Person {
int? id;
String? name;
String? sex;
int? age;
Person({required this.id, required this.name, required this.sex, required this.age});
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
columnName: name,
columnSex: sex,
columnAge: age
};
map[columnId] = id;
return map;
}
Person.fromMap(Map<dynamic, dynamic> map) {
id = map[columnId];
name = map[columnName];
sex = map[columnSex];
age = map[columnAge];
}
}
上面介绍了 SQLite 的基本用法,数据的增删改查是使用频率比较高的,SQLite 还有一些其他的用法,可以根据业务需求去扩展。
转载自:https://juejin.cn/post/7040986659533357087