Flutter入門進(jìn)階之旅(十二)Flutter 數(shù)據(jù)存儲(chǔ)

前言

之前的章節(jié)我們基本上把Flutter中基礎(chǔ)部分的東西都做了簡(jiǎn)單的講解,通過前面章節(jié)的循序?qū)W習(xí)讀者也基本能完成一些簡(jiǎn)單的UI繪制并能利用Flutter處理一些簡(jiǎn)單的用戶交互,讀者可能也留意到,我們之前的章節(jié)中所學(xué)習(xí)到的內(nèi)容并沒有涉及到數(shù)據(jù)存儲(chǔ)方面的操作,或者說,我們到現(xiàn)在為止并不知道在Flutter中數(shù)據(jù)應(yīng)該怎么存,存在哪。本篇博文中筆者將會(huì)為大家解決這一疑惑。

關(guān)于Flutter中的數(shù)據(jù)存儲(chǔ)

相信做過原生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ǔ)體系,下面我們就一直來了解下上述提到的這3中本地存儲(chǔ)方式在Flutter中使用。

1.SharedPreferences

在Flutter中本身并沒有內(nèi)置SharedPreferences存儲(chǔ),但是官方給我們提供了第三方的組件來實(shí)現(xiàn)這一存儲(chǔ)方式。我們可以通過pubspec.yaml文件引入,關(guān)于pubspec.yaml的使用我們?cè)?a href="http://www.itdecent.cn/p/f20877589072" target="_blank">Flutter入門進(jìn)階之旅(五)Image Widget,這一章節(jié)提到過,只不過在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)掌握了上述操作,可跳過準(zhǔn)備工作直接到下面的部分。

繼續(xù)上面的內(nèi)容,我們先來體驗(yàn)一下sharedPreference,貼個(gè)圖大家放松一下。


sharedPreference

從上圖中我們看到我們使用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也是通過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ù)類型的存取。

SharedPreferences

取數(shù)據(jù)

/**
   * 獲取存在SharedPreferences中的數(shù)據(jù)
   */
  Future getString() async {
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    setState(() {
      _storageString = sharedPreferences.get(STORAGE_KEY);
    });
  }

上述操作邏輯中我們通過_textFieldController獲取在TextField中的值,在按下存儲(chǔ)按鈕的同時(shí)我們把數(shù)據(jù)寫入sp中,當(dāng)按下獲取值的時(shí)候我們通過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ǔ)方式,不過好在官方給我們提供了三方的支持庫,不知道后續(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è)獲取文件路徑的方法我這里說明一下,做過原生Android開發(fā)的讀者可能對(duì)此不陌生,但是ios或者初學(xué)者可能并不了解這個(gè)概念,所以我想提出來說明一下。

在path_provider中有三個(gè)獲取文件路徑的方法:

  • getTemporaryDirectory()//獲取應(yīng)用緩存目錄,等同IOS的NSTemporaryDirectory()和Android的getCacheDir() 方法
  • getApplicationDocumentsDirectory()獲取應(yīng)用文件目錄類似于Ios的NSDocumentDirectory和Android上的 AppData目錄
  • getExternalStorageDirectory()//這個(gè)是存儲(chǔ)卡,僅僅在Android平臺(tái)可以使用

來看下操作文件的效果圖

文件存儲(chǔ)

借用了SharedPreferences存儲(chǔ)的邏輯,只是把存儲(chǔ)的代碼放在了file.text中,代碼里有詳盡的注釋,我就不多做解釋說明了,讀者可自行嘗試對(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ù)庫叫Sqflite跟原生安卓的Sqlite叫法不一樣。我們來看下Sqflite官方對(duì)它的解釋說明:

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是一個(gè)同時(shí)支持Android跟Ios平臺(tái)的數(shù)據(jù)庫,并且支持標(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ù)庫中最后一條數(shù)據(jù)顯示在下方Text上,并且顯示出當(dāng)前數(shù)據(jù)庫中一共有多少條數(shù)據(jù),以及數(shù)據(jù)庫的存儲(chǔ)路徑。

效果圖

Sqflite

上述描述樣式代碼

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ù)庫存儲(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ù)庫中的數(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ù)庫中的最后一條數(shù)據(jù)
        _storageString = lists[listSize - 1]['name'] +
            "\n現(xiàn)在數(shù)據(jù)庫中一共有${listSize}條數(shù)據(jù)" +
            "\n數(shù)據(jù)庫的存儲(chǔ)路徑為${dbPath}";
      });
    });
  }

  /**
   * 初始化數(shù)據(jù)庫存儲(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ù)庫對(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ù)庫存儲(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ù)庫中獲取的值為  $_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é)差別。

下一篇:Flutter入門進(jìn)階之旅(十三)Flutter 路由

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容