相信做过原生开发对数据存储并不陌生,在原生 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 还有一些其他的用法,能够依据业务需求去扩展。