簡(jiǎn)單的數(shù)據(jù)持久化
保存數(shù)據(jù)到本地磁盤是應(yīng)用程序常用功能之一,比如保存用戶登錄信息、用戶配置信息等。而保存這些信息通常使用 shared_preferences
,它保存數(shù)據(jù)的形式為 Key-Value
(鍵值對(duì)),支持 Android 和 iOS。shared_preferences
是一個(gè)第三方插件,在 Android 中使用 SharedPreferences
,在 iOS中使用 NSUserDefaults
。
shared_preferences
持久化保存數(shù)據(jù),但在一下情況下會(huì)刪除數(shù)據(jù):
- 卸載應(yīng)用程序。
- 在設(shè)置中清除應(yīng)用數(shù)據(jù)。
安裝
添加依賴
在項(xiàng)目的 pubspec.yaml
文件中添加依賴
dependencies:
shared_preferences: ^2.1.1
安裝
flutter pub get
安裝成功后可以在pubspec.lock
文件中找到對(duì)應(yīng)的設(shè)置
保存/讀取數(shù)據(jù)
shared_preferences
支持的數(shù)據(jù)類型有 int
、double
、bool
、string
、stringList
。
int類型
// 初始化
var prefs = await SharedPreferences.getInstance();
// 存儲(chǔ)
prefs.setInt('key_int', 20);
// 讀取
var res = prefs.getInt('key_int');
print("數(shù)據(jù)是$res");
其他類型的操作調(diào)用對(duì)應(yīng)的方法即可
在這里遇到了個(gè)問(wèn)題
提示[ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: PlatformException(channel-error, Unable to establish connection on channel., null, null)
查了好久最后發(fā)現(xiàn)是sdk版本的問(wèn)題,下載了一個(gè)Android 9就解決了。
刪除數(shù)據(jù)
刪除指定的key
Future<bool> _deleteData() async {
var prefs = await SharedPreferences.getInstance();
prefs.remove('Key');
}
清除所有數(shù)據(jù)(謹(jǐn)慎使用)
Future<bool> _deleteData() async {
var prefs = await SharedPreferences.getInstance();
prefs.remove('Key');
}
key相關(guān)操作
獲取所有key
Future<Set<String>> _getKeys() async {
var prefs = await SharedPreferences.getInstance();
var keys = prefs.getKeys();
return keys ?? [];
}
檢測(cè)key是否存在
Future<bool> _containsKey() async {
var prefs = await SharedPreferences.getInstance();
return prefs.containsKey('Key') ?? false;
}
大量復(fù)雜數(shù)據(jù)持久化
SharedPreferences
只能存儲(chǔ)少量簡(jiǎn)單的數(shù)據(jù),如果需要存儲(chǔ)大量并且復(fù)雜的數(shù)據(jù)可以使用SQLite
Flutter中的SQLite是一個(gè)輕量級(jí)的本地?cái)?shù)據(jù)庫(kù),它可以在移動(dòng)應(yīng)用程序中存儲(chǔ)和管理數(shù)據(jù)。SQLite是一種關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),它使用SQL語(yǔ)言進(jìn)行數(shù)據(jù)操作。在Flutter中,可以使用sqflite插件來(lái)訪問(wèn)SQLite數(shù)據(jù)庫(kù)。該插件提供了一組API,可以執(zhí)行SQL查詢、插入、更新和刪除操作。使用SQLite可以在應(yīng)用程序中存儲(chǔ)和管理大量數(shù)據(jù),例如用戶信息、設(shè)置、日志等。SQLite還可以在離線模式下使用,這使得應(yīng)用程序可以在沒(méi)有網(wǎng)絡(luò)連接的情況下繼續(xù)工作。
安裝
添加依賴
dependencies:
sqflite: ^2.0.1
path_provider: ^2.0.11
使用 SQLite 創(chuàng)建數(shù)據(jù)庫(kù)的時(shí)候需要本地路徑做為參數(shù),所以添加path_provider
插件獲取本地路徑。sqflite的版本過(guò)高則需要對(duì)應(yīng)的dart sdk版本
安裝
flutter pub get
單例模式創(chuàng)建SQLite 訪問(wèn)
使用 SQLite 并不是一定要使用單例模式,單例模式是為了保證整個(gè)應(yīng)用程序僅有一個(gè)數(shù)據(jù)庫(kù)實(shí)例和全局訪問(wèn)。
下面是一個(gè)簡(jiǎn)單的新增、刪除、查詢?nèi)康牟僮?,SQLite的其他操作可以自行百度
數(shù)據(jù)庫(kù)操作
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
// 單例模式創(chuàng)建 SQLite 訪問(wèn)
class DBProvider {
// 創(chuàng)建一個(gè)實(shí)例
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
// 命名構(gòu)造函數(shù),因?yàn)開(kāi)internal是私有的,因此只有_singleton和DBProvider()可以被外部調(diào)用,這樣保證了只會(huì)有一個(gè)類的實(shí)例存在
DBProvider._internal();
// 數(shù)據(jù)庫(kù)對(duì)象
static Database? _db;
// 獲取數(shù)據(jù)庫(kù)對(duì)象,F(xiàn)uture表示異步操作類型
Future<Database?> get db async {
if (_db != null) {
return _db;
}
// 初始化數(shù)據(jù)庫(kù)
_db = await _initDB();
return _db;
}
// 初始化數(shù)據(jù)庫(kù)
Future<Database> _initDB() async {
// 獲取應(yīng)用程序文檔目錄
Directory documentsDirectory = await getApplicationDocumentsDirectory();
// 使用join函數(shù)拼接路徑
String path = '${documentsDirectory.path}dbName';
return await openDatabase(path,
version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
}
// 創(chuàng)建表
Future _onCreate(Database db, int version) async {
// 創(chuàng)建一張用戶表,表列有 id(唯一標(biāo)識(shí))、name(姓名)、age(年齡)、sex(性別)
// execute參數(shù)是sql語(yǔ)句
return await db.execute("CREATE TABLE User ("
"id integer primary key AUTOINCREMENT,"
"name TEXT,"
"age TEXT,"
"sex integer"
")");
}
// 更新表
Future _onUpgrade(Database db, int oldVersion, int newVersion) async {}
// 新增數(shù)據(jù)
Future saveDate(User user) async {
// 這里是異步操作,確保能拿到db
var db = await this.db;
// 表名,一個(gè)map對(duì)象
return await db?.insert('User', user.toJson());
}
// 根據(jù)id刪除數(shù)據(jù)
Future delete(int id) async {
var db = await this.db;
return db?.delete('User', where: 'id=?', whereArgs: [id]);
}
// 查詢所有數(shù)據(jù)
Future findAll() async {
var db = await this.db;
List<Map<String, Object?>>? result = await db?.query('User');
if (result != null && result.isNotEmpty) {
return result.map((e) {
return User.fromJson(e);
}).toList();
}
return [];
}
}
// 創(chuàng)建一個(gè)User的Model類,用于數(shù)據(jù)的保存
class User {
late int id;
late String name;
late int age;
late int sex;
// 構(gòu)造函數(shù)
User(
{required this.id,
required this.name,
required this.age,
required this.sex});
// 將JSON對(duì)象轉(zhuǎn)為User對(duì)象(命名構(gòu)造函數(shù))
User.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
age = int.parse(json['age']);
sex = json['sex'];
}
// 將User對(duì)象轉(zhuǎn)為JSON對(duì)象
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
// 在函數(shù)中因?yàn)闆](méi)有id變量,所以這里的id代表的類的成員變量;如果函數(shù)中有id,那么必須使用this.id來(lái)區(qū)分是函數(shù)內(nèi)的變量還是類的成員變量
data['id'] = id;
data['name'] = name;
data['age'] = age;
data['sex'] = sex;
return data;
}
}
功能頁(yè)面
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'db/db_provider.dart';
//使用箭頭函數(shù)簡(jiǎn)寫
main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
//創(chuàng)建widget的唯一標(biāo)識(shí)
const MyApp({Key? key}) : super(key: key);
//重寫build方法
Widget build(BuildContext context) {
//返回一個(gè)material類型的app
return const MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('zh', 'CN'),
Locale('en', 'US'),
],
//指定顯示哪一個(gè)頁(yè)面
home: YcHomePage(),
);
}
}
//app的主頁(yè)面
class YcHomePage extends StatelessWidget {
const YcHomePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('數(shù)據(jù)持久化'),
),
body: const YcHomeBody(),
);
}
}
class YcHomeBody extends StatefulWidget {
const YcHomeBody({Key? key}) : super(key: key);
State<YcHomeBody> createState() => _YcHomeBodyState();
}
class _YcHomeBodyState extends State<YcHomeBody> {
// 表格數(shù)據(jù)
List<User> _list = [];
final List<String> _nameList = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j'
];
// 加在數(shù)據(jù)的方法
_loadData() async {
_list = await DBProvider().findAll();
setState(() {});
}
void initState() {
// 初始化狀態(tài)
super.initState();
_loadData();
}
Widget build(BuildContext context) {
return Column(
children: [
// 頂部操作按鈕
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () async {
User user = User(
id: DateTime.now().microsecondsSinceEpoch,
name: _nameList[_list.length],
age: Random().nextInt(11) + 10,
sex: Random().nextInt(2));
int res = await DBProvider().saveDate(user);
if (res > 0) {
print("新增成功:$res");
// 刷新數(shù)據(jù)
_loadData();
} else {
print("新增失?。?/span>$res");
}
},
child: const Text("新增一條數(shù)據(jù)")),
ElevatedButton(
onPressed: () async {
if (_list.isNotEmpty) {
await DBProvider().delete(_list[0].id);
_loadData();
}
},
child: const Text("刪除第一條數(shù)據(jù)"))
],
),
//占位
const SizedBox(
height: 20,
),
// 底部表格
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Table(
border: TableBorder.all(
width: 1,
color: Colors.grey,
style: BorderStyle.solid,
),
columnWidths: const {
0: FixedColumnWidth(100),
1: FixedColumnWidth(100),
2: FixedColumnWidth(100),
3: FixedColumnWidth(100)
},
children: [
const TableRow(children: [
TableCell(
child: Text(
'id',
textAlign: TextAlign.center,
)),
TableCell(child: Text('姓名', textAlign: TextAlign.center)),
TableCell(child: Text('年齡', textAlign: TextAlign.center)),
TableCell(child: Text('性別', textAlign: TextAlign.center)),
]),
..._list.map((user) {
return TableRow(children: [
TableCell(
child: Text('${user.id}', textAlign: TextAlign.center)),
TableCell(
child: Text(user.name, textAlign: TextAlign.center)),
TableCell(
child:
Text('${user.age}', textAlign: TextAlign.center)),
TableCell(
child: Text(user.sex == 0 ? '男' : '女',
textAlign: TextAlign.center)),
]);
}).toList()
],
)
],
)
],
);
}
}
優(yōu)化
正常請(qǐng)求創(chuàng)建數(shù)據(jù)庫(kù)與表的相關(guān)操作需要分離,這樣便于維護(hù)。下面是一個(gè)例子
創(chuàng)建數(shù)據(jù)庫(kù)
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
// 單例模式創(chuàng)建 SQLite 訪問(wèn)
class DatabaseHelper {
// 定義一個(gè)實(shí)例
static final DatabaseHelper _instance = DatabaseHelper.internal();
//使用Flutter的工廠構(gòu)造函數(shù)語(yǔ)法來(lái)創(chuàng)建一個(gè)單例對(duì)象。確保只有一個(gè)DatabaseHelper對(duì)象存在,并且可以在整個(gè)應(yīng)用程序中共享該對(duì)象
factory DatabaseHelper() => _instance;
// 創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)實(shí)例
static Database? _database;
// 獲取數(shù)據(jù)庫(kù)實(shí)例
Future<Database?> get database async {
if (_database == null) {
_database = await initDatabase();
}
return _database;
}
// 私有的命名構(gòu)造函數(shù),防止在類外部直接實(shí)例化
DatabaseHelper.internal();
// 初始化數(shù)據(jù)庫(kù)
Future<Database> initDatabase() async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'my_database.db');
// 打開(kāi)或創(chuàng)建表
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE IF NOT EXISTS search_history ('
'id INTEGER PRIMARY KEY AUTOINCREMENT,' // 主鍵
'name VARCHAR(50),' // 搜索的關(guān)鍵字
'type VARCHAR(50),' // 搜索的類型
'gmtCreate DATETIME' // 創(chuàng)建時(shí)間
')');
// 創(chuàng)建其他表
});
return database;
}
}
使用,操作數(shù)據(jù)
import 'package:sqflite/sqflite.dart';
import 'package:spider/store/database_helper.dart';
import 'package:spider/models/SearchHistoryModel.dart';
/**
* 搜索歷史持久化操作方法
*/
class HistoryStore {
// 保存搜索歷史
Future<int> saveData(SearchHistoryModel historyModel) async {
// 獲取數(shù)據(jù)庫(kù)對(duì)象
Database database = await DatabaseHelper().database as Database;
var existingData = await findByName(historyModel.name!);
if (existingData == null) {
return await database.insert('search_history', historyModel.toJson());
}
return 0;
}
// 查詢所有的搜索歷史
Future<List<SearchHistoryModel>> findAll() async {
Database database = await DatabaseHelper().database as Database;
List result = await database.query('search_history');
if (result.isNotEmpty) {
return result.map((e) {
return SearchHistoryModel.fromJson(e);
}).toList();
}
return [];
}
// 根據(jù)名字查詢搜索歷史
Future findByName(String name) async {
Database database = await DatabaseHelper().database as Database;
List result = await database.query('search_history',
where: 'name = ?', whereArgs: [name], limit: 1);
if (result.isNotEmpty) {
return result.first;
}
return null;
}
// 刪除所有搜索歷史
Future deleteAll() async {
Database database = await DatabaseHelper().database as Database;
await database.delete('search_history');
}
}
補(bǔ)充
查看數(shù)據(jù)庫(kù)數(shù)據(jù)
db文件一般在data/data/應(yīng)用包名/databases
路徑下
右鍵保存到本地。然后使用數(shù)據(jù)庫(kù)查看工具進(jìn)行打開(kāi),我使用的是DBeaver
,就依次為例
1、點(diǎn)擊數(shù)據(jù)庫(kù)選擇新建數(shù)據(jù)庫(kù)鏈接
2、選擇sqlite
數(shù)據(jù)庫(kù)
3、打開(kāi)導(dǎo)出的db
文件文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-486138.html
下面就可以正常查看數(shù)據(jù)了文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-486138.html
到了這里,關(guān)于flutter:數(shù)據(jù)持久化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!