前言
之前的章節(jié)我們基本上把Flutter中基礎(chǔ)部分的東西都做了簡(jiǎn)單的講解,通過(guò)前面章節(jié)的循序?qū)W習(xí)讀者也基本能完成一些簡(jiǎn)單的UI繪制并能利用Flutter處理一些簡(jiǎn)單的用戶交互,讀者可能也留意到埃篓,我們之前的章節(jié)中所學(xué)習(xí)到的內(nèi)容并沒有涉及到數(shù)據(jù)存儲(chǔ)方面的操作,或者說(shuō)贩毕,我們到現(xiàn)在為止并不知道在Flutter中數(shù)據(jù)應(yīng)該怎么存些己,存在哪。本篇博文中筆者將會(huì)為大家解決這一疑惑潜必。
關(guān)于Flutter中的數(shù)據(jù)存儲(chǔ)
相信做過(guò)原生Android開發(fā)的讀者對(duì)數(shù)據(jù)存儲(chǔ)并不陌生,在原生Android中我們會(huì)把一些輕量級(jí)的數(shù)據(jù)(如用戶信息沃但、APP配置信息等)寫入SharedPreferences
做存儲(chǔ)磁滚,把需要長(zhǎng)期存儲(chǔ)的數(shù)據(jù)寫入本地文件或者Sqlite3
,當(dāng)然Flutter中也同樣用一套完整的本地?cái)?shù)據(jù)存儲(chǔ)體系宵晚,下面我們就一直來(lái)了解下上述提到的這3中本地存儲(chǔ)方式在Flutter中使用垂攘。
1.SharedPreferences
在Flutter中本身并沒有內(nèi)置SharedPreferences存儲(chǔ),但是官方給我們提供了第三方的組件來(lái)實(shí)現(xiàn)這一存儲(chǔ)方式淤刃。我們可以通過(guò)pubspec.yaml
文件引入晒他,關(guān)于pubspec.yaml
的使用我們?cè)?a href="http://www.reibang.com/p/f20877589072" target="_blank">Flutter入門進(jìn)階之旅(五)Image Widget,這一章節(jié)提到過(guò),只不過(guò)在Image使用中我們引入的是assets
文件依賴逸贾。
如下我們?cè)赿ependencies節(jié)點(diǎn)下引入SharedPreferences的依賴陨仅,讀者在pubspec.yaml引入依賴時(shí)一定要注意代碼縮進(jìn)格式,否則在在執(zhí)行flutter packages get時(shí)很可能會(huì)報(bào)錯(cuò)
铝侵。
dependencies:
flutter:
sdk: flutter
# 添加sharedPreference依賴
shared_preferences: ^0.5.0
dev_dependencies:
flutter_test:
sdk: flutter
# 引入本地資源圖片
assets:
- images/a.png
- images/aaa.png
然后命令行執(zhí)行flutter packages get
把遠(yuǎn)程依賴同步到本地灼伤,在此筆者寫文章的時(shí)候sharedPreference的最新版本是0.5.0,讀者可自行去https://pub.dartlang.org/flutter上獲取最新版本咪鲜,同時(shí)也可以在上面找到其他需要引入的資源依賴包狐赡。
筆者的話
啰里啰嗦的準(zhǔn)備工作總算是講完了,主要是今天的課程涉及到了包依賴管理疟丙,可能對(duì)于有些初學(xué)者有點(diǎn)懵颖侄,所以我就借助sharedPreference把依賴引入廢話扯了一大通鸟雏,如果讀者已經(jīng)掌握了上述操作,可跳過(guò)準(zhǔn)備工作直接到下面的部分览祖。
繼續(xù)上面的內(nèi)容崔慧,我們先來(lái)體驗(yàn)一下sharedPreference,貼個(gè)圖大家放松一下穴墅。
從上圖中我們看到我們使用sharedPreference
做了簡(jiǎn)單存儲(chǔ)跟獲取的操作惶室,其實(shí)sharedPreference
好像也就這么點(diǎn)左右,不是存就是取玄货。讀者在自行操作時(shí)一定不要忘記導(dǎo)入sharedPreference
的包
import 'package:shared_preferences/shared_preferences.dart';
存數(shù)據(jù)
跟原生Android一樣皇钞,F(xiàn)lutter中操作sp也是通過(guò)key-value的方式存取數(shù)據(jù)
/**
* 利用SharedPreferences存儲(chǔ)數(shù)據(jù)
*/
Future saveString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString(
STORAGE_KEY, _textFieldController.value.text.toString());
}
SharedPreferences
中為我們提供了String、bool松捉、Double夹界、Int、StringList數(shù)據(jù)類型的存取隘世。
取數(shù)據(jù)
/**
* 獲取存在SharedPreferences中的數(shù)據(jù)
*/
Future getString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
setState(() {
_storageString = sharedPreferences.get(STORAGE_KEY);
});
}
上述操作邏輯中我們通過(guò)_textFieldController
獲取在TextField
中的值可柿,在按下存儲(chǔ)按鈕的同時(shí)我們把數(shù)據(jù)寫入sp中,當(dāng)按下獲取值的時(shí)候我們通過(guò)setState
把從sp中獲取的值同步更新到下面的Text上顯示丙者。
完整代碼:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class StoragePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => StorageState();
}
class StorageState extends State {
var _textFieldController = new TextEditingController();
var _storageString = '';
final STORAGE_KEY = 'storage_key';
/**
* 利用SharedPreferences存儲(chǔ)數(shù)據(jù)
*/
Future saveString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString(
STORAGE_KEY, _textFieldController.value.text.toString());
}
/**
* 獲取存在SharedPreferences中的數(shù)據(jù)
*/
Future getString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
setState(() {
_storageString = sharedPreferences.get(STORAGE_KEY);
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('數(shù)據(jù)存儲(chǔ)'),
),
body: new Column(
children: <Widget>[
Text("shared_preferences存儲(chǔ)", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: new Text("存儲(chǔ)"),
color: Colors.pink,
),
MaterialButton(
onPressed: getString,
child: new Text("獲取"),
color: Colors.lightGreen,
),
Text('shared_preferences存儲(chǔ)的值為 $_storageString'),
],
),
);
}
}
2.文件存儲(chǔ)
雖然我們今天內(nèi)容是Flutter的數(shù)據(jù)存儲(chǔ)复斥,尷尬的是Flutter本身都沒有內(nèi)置提到的這三種存儲(chǔ)方式,不過(guò)好在官方給我們提供了三方的支持庫(kù)械媒,不知道后續(xù)的Flutter版本中會(huì)不會(huì)對(duì)此做改進(jìn)目锭。操作文件同樣我們也需要像SharedPreferences一樣,需要在pubspec.yaml
引入纷捞。在 Flutter 里實(shí)現(xiàn)文件讀寫痢虹,需要使用 path_provider 和 dart 的 io 模塊。path_provider 負(fù)責(zé)查找 iOS/Android 的目錄文件主儡,IO 模塊負(fù)責(zé)對(duì)文件進(jìn)行讀寫
# 添加文件依賴
path_provider: ^0.5.0
筆者在此引入的最新版本是0.5.0奖唯,讀者可自行去https://pub.dartlang.org/flutter上獲取最新版本。
由于整個(gè)操作演示邏輯跟SharedPreferences一樣糜值,我就不詳細(xì)講解文件存儲(chǔ)中關(guān)于存取數(shù)據(jù)的具體操作了丰捷,稍微我貼上源代碼,讀者自行查閱代碼對(duì)比即可臀玄,關(guān)于文件存儲(chǔ)的三個(gè)獲取文件路徑的方法我這里說(shuō)明一下瓢阴,做過(guò)原生Android開發(fā)的讀者可能對(duì)此不陌生畅蹂,但是ios或者初學(xué)者可能并不了解這個(gè)概念健无,所以我想提出來(lái)說(shuō)明一下。
在path_provider中有三個(gè)獲取文件路徑的方法:
getTemporaryDirectory()//獲取應(yīng)用緩存目錄液斜,等同IOS的NSTemporaryDirectory()和Android的getCacheDir() 方法
getApplicationDocumentsDirectory()獲取應(yīng)用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
getExternalStorageDirectory()//這個(gè)是存儲(chǔ)卡累贤,僅僅在Android平臺(tái)可以使用
來(lái)看下操作文件的效果圖:
借用了SharedPreferences存儲(chǔ)的邏輯叠穆,只是把存儲(chǔ)的代碼放在了file.text
中,代碼里有詳盡的注釋臼膏,我就不多做解釋說(shuō)明了硼被,讀者可自行嘗試對(duì)比跟SharedPreferences
的差別
樣例代碼
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
class StoragePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => StorageState();
}
class StorageState extends State {
var _textFieldController = new TextEditingController();
var _storageString = '';
/**
* 利用文件存儲(chǔ)數(shù)據(jù)
*/
saveString() async {
final file = await getFile('file.text');
//寫入字符串
file.writeAsString(_textFieldController.value.text.toString());
}
/**
* 獲取存在文件中的數(shù)據(jù)
*/
Future getString() async {
final file = await getFile('file.text');
var filePath = file.path;
setState(() {
file.readAsString().then((String value) {
_storageString = value +'\n文件存儲(chǔ)路徑:'+filePath;
});
});
}
/**
* 初始化文件路徑
*/
Future<File> getFile(String fileName) async {
//獲取應(yīng)用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
final fileDirectory = await getApplicationDocumentsDirectory();
//獲取存儲(chǔ)路徑
final filePath = fileDirectory.path;
//或者file對(duì)象(操作文件記得導(dǎo)入import 'dart:io')
return new File(filePath + "/"+fileName);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('數(shù)據(jù)存儲(chǔ)'),
),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("文件存儲(chǔ)", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: new Text("存儲(chǔ)"),
color: Colors.cyan,
),
MaterialButton(
onPressed: getString,
child: new Text("獲取"),
color: Colors.deepOrange,
),
Text('從文件存儲(chǔ)中獲取的值為 $_storageString'),
],
),
);
}
}
3.Sqflite
在Flutter中的數(shù)據(jù)庫(kù)叫Sqflite
跟原生安卓的Sqlite
叫法不一樣。我們來(lái)看下Sqflite
官方對(duì)它的解釋說(shuō)明:
SQLite plugin for Flutter. Supports both iOS and Android.
Support transactions and batches
Automatic version managment during open
Helpers for insert/query/update/delete queries
DB operation executed in a background thread on iOS and Android
通過(guò)上面的描述渗磅,我們了解到Sqflite
是一個(gè)同時(shí)支持Android跟Ios平臺(tái)的數(shù)據(jù)庫(kù)嚷硫,并且支持標(biāo)準(zhǔn)的CURD
操作,下面我們還是用上面操作文件跟sp的代碼邏輯是一塊體驗(yàn)一下Sqflite
始鱼。
同樣需要引入依賴:
#添加Sqflite依賴
sqflite: ^1.0.0
模擬場(chǎng)景:
利用Sqflite創(chuàng)建一張user表仔掸,其中user表中id設(shè)置為主鍵id,且為自增長(zhǎng)医清,name字段為text類型起暮,用戶按下存儲(chǔ)按鈕后,把TextFile輸入框里的內(nèi)容插入到user表中会烙,當(dāng)按下獲取按鈕時(shí)负懦,取出數(shù)據(jù)庫(kù)中最后一條數(shù)據(jù)顯示在下方Text上,并且顯示出當(dāng)前數(shù)據(jù)庫(kù)中一共有多少條數(shù)據(jù)柏腻,以及數(shù)據(jù)庫(kù)的存儲(chǔ)路徑纸厉。
效果圖
上述描述樣式代碼
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:sqflite/sqflite.dart';
class StoragePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => StorageState();
}
class StorageState extends State {
var _textFieldController = new TextEditingController();
var _storageString = '';
/**
* 利用Sqflite數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)
*/
saveString() async {
final db = await getDataBase('my_db.db');
//寫入字符串
db.transaction((trx) {
trx.rawInsert(
'INSERT INTO user(name) VALUES("${_textFieldController.value.text.toString()}")');
});
}
/**
* 獲取存在Sqflite數(shù)據(jù)庫(kù)中的數(shù)據(jù)
*/
Future getString() async {
final db = await getDataBase('my_db.db');
var dbPath = db.path;
setState(() {
db.rawQuery('SELECT * FROM user').then((List<Map> lists) {
print('----------------$lists');
var listSize = lists.length;
//獲取數(shù)據(jù)庫(kù)中的最后一條數(shù)據(jù)
_storageString = lists[listSize - 1]['name'] +
"\n現(xiàn)在數(shù)據(jù)庫(kù)中一共有${listSize}條數(shù)據(jù)" +
"\n數(shù)據(jù)庫(kù)的存儲(chǔ)路徑為${dbPath}";
});
});
}
/**
* 初始化數(shù)據(jù)庫(kù)存儲(chǔ)路徑
*/
Future<Database> getDataBase(String dbName) async {
//獲取應(yīng)用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
final fileDirectory = await getApplicationDocumentsDirectory();
//獲取存儲(chǔ)路徑
final dbPath = fileDirectory.path;
//構(gòu)建數(shù)據(jù)庫(kù)對(duì)象
Database database = await openDatabase(dbPath + "/" + dbName, version: 1,
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE user (id INTEGER PRIMARY KEY, name TEXT)");
});
return database;
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('數(shù)據(jù)存儲(chǔ)'),
),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Sqflite數(shù)據(jù)庫(kù)存儲(chǔ)", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: new Text("存儲(chǔ)"),
color: Colors.cyan,
),
MaterialButton(
onPressed: getString,
child: new Text("獲取"),
color: Colors.deepOrange,
),
Text('從Sqflite數(shù)據(jù)庫(kù)中獲取的值為 $_storageString'),
],
),
);
}
}
至此,關(guān)于Flutter的本地存儲(chǔ)相關(guān)的內(nèi)容就全部講解完了五嫂,在本文章中残腌,我為了清晰代碼結(jié)構(gòu)跟業(yè)務(wù)邏輯,復(fù)用的都是同一個(gè)存儲(chǔ)業(yè)務(wù)邏輯跟UI便于大家結(jié)合代碼做對(duì)比贫导,讀者可結(jié)合代碼自行對(duì)比三種存儲(chǔ)方式的細(xì)節(jié)差別抛猫。