前言
之前的章節我們基本上把Flutter中基礎部分的東西都做了簡單的講解,通過前面章節的循序學習讀者也基本能完成一些簡單的UI繪制并能利用Flutter處理一些簡單的用戶交互,讀者可能也留意到,我們之前的章節中所學習到的內容并沒有涉及到數據存儲方面的操作,或者說,我們到現在為止并不知道在Flutter中數據應該怎么存,存在哪。本篇博文中筆者將會為大家解決這一疑惑。
關于Flutter中的數據存儲
相信做過原生Android開發的讀者對數據存儲并不陌生,在原生Android中我們會把一些輕量級的數據(如用戶信息、APP配置信息等)寫入SharedPreferences
做存儲,把需要長期存儲的數據寫入本地文件或者Sqlite3
,當然Flutter中也同樣用一套完整的本地數據存儲體系,下面我們就一直來了解下上述提到的這3中本地存儲方式在Flutter中使用。
1.SharedPreferences
在Flutter中本身并沒有內置SharedPreferences存儲,但是官方給我們提供了第三方的組件來實現這一存儲方式。我們可以通過pubspec.yaml
文件引入,關于pubspec.yaml
的使用我們在Flutter入門進階之旅(五)Image Widget,這一章節提到過,只不過在Image使用中我們引入的是assets
文件依賴。
如下我們在dependencies節點下引入SharedPreferences的依賴,讀者在pubspec.yaml引入依賴時一定要注意代碼縮進格式,否則在在執行flutter packages get時很可能會報錯
。
dependencies:
flutter:
sdk: flutter
# 添加sharedPreference依賴
shared_preferences: ^0.5.0
dev_dependencies:
flutter_test:
sdk: flutter
# 引入本地資源圖片
assets:
- images/a.png
- images/aaa.png
然后命令行執行flutter packages get
把遠程依賴同步到本地,在此筆者寫文章的時候sharedPreference的最新版本是0.5.0,讀者可自行去https://pub.dartlang.org/flutter上獲取最新版本,同時也可以在上面找到其他需要引入的資源依賴包。
筆者的話
啰里啰嗦的準備工作總算是講完了,主要是今天的課程涉及到了包依賴管理,可能對于有些初學者有點懵,所以我就借助sharedPreference把依賴引入廢話扯了一大通,如果讀者已經掌握了上述操作,可跳過準備工作直接到下面的部分。
繼續上面的內容,我們先來體驗一下sharedPreference,貼個圖大家放松一下。
從上圖中我們看到我們使用sharedPreference
做了簡單存儲跟獲取的操作,其實sharedPreference
好像也就這么點左右,不是存就是取。讀者在自行操作時一定不要忘記導入sharedPreference
的包
import 'package:shared_preferences/shared_preferences.dart';
存數據
跟原生Android一樣,Flutter中操作sp也是通過key-value的方式存取數據
/**
* 利用SharedPreferences存儲數據
*/
Future saveString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString(
STORAGE_KEY, _textFieldController.value.text.toString());
}
SharedPreferences
中為我們提供了String、bool、Double、Int、StringList數據類型的存取。
取數據
/**
* 獲取存在SharedPreferences中的數據
*/
Future getString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
setState(() {
_storageString = sharedPreferences.get(STORAGE_KEY);
});
}
上述操作邏輯中我們通過_textFieldController
獲取在TextField
中的值,在按下存儲按鈕的同時我們把數據寫入sp中,當按下獲取值的時候我們通過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存儲數據
*/
Future saveString() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString(
STORAGE_KEY, _textFieldController.value.text.toString());
}
/**
* 獲取存在SharedPreferences中的數據
*/
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('數據存儲'),
),
body: new Column(
children: <Widget>[
Text("shared_preferences存儲", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: new Text("存儲"),
color: Colors.pink,
),
MaterialButton(
onPressed: getString,
child: new Text("獲取"),
color: Colors.lightGreen,
),
Text('shared_preferences存儲的值為 $_storageString'),
],
),
);
}
}
2.文件存儲
雖然我們今天內容是Flutter的數據存儲,尷尬的是Flutter本身都沒有內置提到的這三種存儲方式,不過好在官方給我們提供了三方的支持庫,不知道后續的Flutter版本中會不會對此做改進。操作文件同樣我們也需要像SharedPreferences一樣,需要在pubspec.yaml
引入。在 Flutter 里實現文件讀寫,需要使用 path_provider 和 dart 的 io 模塊。path_provider 負責查找 iOS/Android 的目錄文件,IO 模塊負責對文件進行讀寫
# 添加文件依賴
path_provider: ^0.5.0
筆者在此引入的最新版本是0.5.0,讀者可自行去https://pub.dartlang.org/flutter上獲取最新版本。
由于整個操作演示邏輯跟SharedPreferences一樣,我就不詳細講解文件存儲中關于存取數據的具體操作了,稍微我貼上源代碼,讀者自行查閱代碼對比即可,關于文件存儲的三個獲取文件路徑的方法我這里說明一下,做過原生Android開發的讀者可能對此不陌生,但是ios或者初學者可能并不了解這個概念,所以我想提出來說明一下。
在path_provider中有三個獲取文件路徑的方法:
getTemporaryDirectory()//獲取應用緩存目錄,等同IOS的NSTemporaryDirectory()和Android的getCacheDir() 方法
getApplicationDocumentsDirectory()獲取應用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
getExternalStorageDirectory()//這個是存儲卡,僅僅在Android平臺可以使用
來看下操作文件的效果圖:
借用了SharedPreferences存儲的邏輯,只是把存儲的代碼放在了file.text
中,代碼里有詳盡的注釋,我就不多做解釋說明了,讀者可自行嘗試對比跟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 = '';
/**
* 利用文件存儲數據
*/
saveString() async {
final file = await getFile('file.text');
//寫入字符串
file.writeAsString(_textFieldController.value.text.toString());
}
/**
* 獲取存在文件中的數據
*/
Future getString() async {
final file = await getFile('file.text');
var filePath = file.path;
setState(() {
file.readAsString().then((String value) {
_storageString = value +'\n文件存儲路徑:'+filePath;
});
});
}
/**
* 初始化文件路徑
*/
Future<File> getFile(String fileName) async {
//獲取應用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
final fileDirectory = await getApplicationDocumentsDirectory();
//獲取存儲路徑
final filePath = fileDirectory.path;
//或者file對象(操作文件記得導入import 'dart:io')
return new File(filePath + "/"+fileName);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('數據存儲'),
),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("文件存儲", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: new Text("存儲"),
color: Colors.cyan,
),
MaterialButton(
onPressed: getString,
child: new Text("獲取"),
color: Colors.deepOrange,
),
Text('從文件存儲中獲取的值為 $_storageString'),
],
),
);
}
}
3.Sqflite
在Flutter中的數據庫叫Sqflite
跟原生安卓的Sqlite
叫法不一樣。我們來看下Sqflite
官方對它的解釋說明:
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
通過上面的描述,我們了解到Sqflite
是一個同時支持Android跟Ios平臺的數據庫,并且支持標準的CURD
操作,下面我們還是用上面操作文件跟sp的代碼邏輯是一塊體驗一下Sqflite
。
同樣需要引入依賴:
#添加Sqflite依賴
sqflite: ^1.0.0
模擬場景:
利用Sqflite創建一張user表,其中user表中id設置為主鍵id,且為自增長,name字段為text類型,用戶按下存儲按鈕后,把TextFile輸入框里的內容插入到user表中,當按下獲取按鈕時,取出數據庫中最后一條數據顯示在下方Text上,并且顯示出當前數據庫中一共有多少條數據,以及數據庫的存儲路徑。
效果圖
上述描述樣式代碼
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數據庫存儲數據
*/
saveString() async {
final db = await getDataBase('my_db.db');
//寫入字符串
db.transaction((trx) {
trx.rawInsert(
'INSERT INTO user(name) VALUES("${_textFieldController.value.text.toString()}")');
});
}
/**
* 獲取存在Sqflite數據庫中的數據
*/
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;
//獲取數據庫中的最后一條數據
_storageString = lists[listSize - 1]['name'] +
"\n現在數據庫中一共有${listSize}條數據" +
"\n數據庫的存儲路徑為${dbPath}";
});
});
}
/**
* 初始化數據庫存儲路徑
*/
Future<Database> getDataBase(String dbName) async {
//獲取應用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
final fileDirectory = await getApplicationDocumentsDirectory();
//獲取存儲路徑
final dbPath = fileDirectory.path;
//構建數據庫對象
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('數據存儲'),
),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Sqflite數據庫存儲", textAlign: TextAlign.center),
TextField(
controller: _textFieldController,
),
MaterialButton(
onPressed: saveString,
child: new Text("存儲"),
color: Colors.cyan,
),
MaterialButton(
onPressed: getString,
child: new Text("獲取"),
color: Colors.deepOrange,
),
Text('從Sqflite數據庫中獲取的值為 $_storageString'),
],
),
);
}
}
至此,關于Flutter的本地存儲相關的內容就全部講解完了,在本文章中,我為了清晰代碼結構跟業務邏輯,復用的都是同一個存儲業務邏輯跟UI便于大家結合代碼做對比,讀者可結合代碼自行對比三種存儲方式的細節差別。