Flutter第十二章(Flutter 中單例呻惕,shared_preferences 實現(xiàn)數(shù)據(jù)持久化荆责,`IO 文件讀寫操作數(shù)據(jù)持久化)

版權(quán)聲明:本文為作者原創(chuàng)書籍。轉(zhuǎn)載請注明作者和出處亚脆,未經(jīng)授權(quán),嚴禁私自轉(zhuǎn)載盲泛,侵權(quán)必究1舫帧!寺滚!

情感語錄: 不要去追一匹馬柑营,用追馬的時間種草,待到春暖花開時村视,就會有一群駿馬任你挑選官套;豐富自己,比取悅他人更有力量!

歡迎來到本章節(jié)奶赔,上一章節(jié)介紹了常用對話框和進度條的使用惋嚎,知識點回顧 戳這里 Flutter基礎(chǔ)第十一章

本章知識點主要介紹 Flutter 中的數(shù)據(jù)共享和持久化,很顯然站刑,本章知識點相當重要另伍,數(shù)據(jù)共享和持久化操作基本是每個應(yīng)用的必備操作。比如:一個應(yīng)用里有很多界面都會用到我的的用戶信息绞旅,而我的用戶信息一開始就在登錄時就返回了摆尝,我們把用戶的基本信息存儲在一個全局的變量里即可實現(xiàn)數(shù)據(jù)共享,讓其他視圖直接引用該變量值即可 因悲。但是這樣的存儲只能達到內(nèi)存級別的堕汞,應(yīng)用關(guān)閉后這些數(shù)據(jù)就被丟失了。要實現(xiàn)關(guān)閉應(yīng)用后再次重啟應(yīng)用我的用戶信息而不丟失那該怎么辦呢晃琳? 這就是本章節(jié)的重點了------ 持久化

本章簡要:

1讯检、Flutter 中單例介紹

2、shared_preferences 實現(xiàn)數(shù)據(jù)持久化

3蝎土、IO 文件讀寫操作數(shù)據(jù)持久化

一视哑、全局靜態(tài)變量

Flutter 中的 Dart 語言如同 Java 語言一樣,存在很多地方的相似性誊涯。在通過使用 static 關(guān)鍵字 為一個變量開辟內(nèi)存地址后挡毅,而其他需要引用到該變量值時, 只需要將內(nèi)存地址指向該變量即可實現(xiàn)數(shù)據(jù)共享暴构。

如: 我定義了一個 Global 類來管理全局的的靜態(tài)資源:

class Global{

  //定義一個靜態(tài)屬性 name 
  static String name = "張三";

}

在需要使用到該變量值的地方 通過 Global.name 方式獲取該變量的值跪呈。

二、單例

單例模式是日常開發(fā)中最常用的設(shè)計模式之一取逾。Dart 是單線程模型耗绿,因此在Flutter 中實現(xiàn)單例 不需要像 Java 中加雙重檢查鎖去考慮多線程的問題。

Dart中的單例也有兩種模式:

1砾隅、餓漢式

餓漢式比較好理解:在類加載時误阻,就進行實例的創(chuàng)建。加載時獲取實例速度較慢晴埂,運行時速度較快究反。通俗的講:"我管你吃不吃包子,反正我先把這坨包子蒸好放在這兒"

實例代碼:

class UserHelper{
  
    // 單例公開訪問點
    factory UserHelper() =>_userInstance();
  
    static UserHelper get instance => _userInstance();
  
    // 靜態(tài)私有成員儒洛,沒有初始化
    static UserHelper _instance = UserHelper._();
  
    // 私有構(gòu)造函數(shù)
    UserHelper._() {
      // 具體初始化代碼
      print("------>初始化");
    }
  
    // 靜態(tài)精耐、同步、私有訪問點
    static UserHelper _userInstance() {
  
      return _instance;
    }
  
    String getUserName(){
  
      return "張三";
    }
  
  }

餓漢式單例測試:

  void main(){

    var userHelper = UserHelper();
    var userHelper1 = UserHelper();
    var userHelper2 =  UserHelper.instance;

    print("------------> 對象:'${userHelper.hashCode} 相等  '+${identical(userHelper, userHelper1)}"); //true
    print("------------> 對象:'${userHelper1.hashCode} 相等  '+${identical(userHelper, userHelper2)}"); //true
    print("------------> 對象:'${userHelper2.hashCode} 相等  '+${identical(userHelper1, userHelper2)}"); //true

  }

控制臺輸出:

    I/flutter: ------>初始化
    I/flutter: ------------> 對象:'1070008279 相等  '+true
    I/flutter: ------------> 對象:'1070008279 相等  '+true
    I/flutter: ------------> 對象:'1070008279 相等  '+true

可以看出 UserHelper 類有且只初始化了一次琅锻,且 hashCode 值也都相等卦停,證明后面的無論 new UserHelper() 多少次拿到的都是同一對象向胡。

2、懶漢式

在類加載時惊完,不創(chuàng)建實例僵芹。加載時速度較快,運行時獲取實例速度較慢专执。通俗的講:“我管你吃不吃包子淮捆,等你問我了我才開始給你做包子”

實例代碼:

  class UserHelper{

    // 單例公開訪問點
    factory UserHelper() =>_userInstance();

    static UserHelper get instance => _userInstance();

    // 靜態(tài)私有成員,沒有初始化
    static UserHelper _instance;

    // 私有構(gòu)造函數(shù)
    UserHelper._() {
      // 具體初始化代碼
      print("------>初始化");
    }

    // 靜態(tài)本股、同步攀痊、私有訪問點
    static UserHelper _userInstance() {
      if (_instance == null) {
        _instance = UserHelper._();
      }
      return _instance;
    }


  }

懶漢式單例測試:

  void main(){

    var userHelper = UserHelper();
    var userHelper1 = UserHelper();
    var userHelper2 =  UserHelper.instance;

    print("------------> 對象:'${userHelper.hashCode} 相等  '+${identical(userHelper, userHelper1)}"); //true
    print("------------> 對象:'${userHelper1.hashCode} 相等  '+${identical(userHelper, userHelper2)}"); //true
    print("------------> 對象:'${userHelper2.hashCode} 相等  '+${identical(userHelper1, userHelper2)}"); //true

  }

控制臺輸出:

    I/flutter ( 8120): ------>初始化
    I/flutter ( 8120): ------------> 對象:'537698073 相等  '+true
    I/flutter ( 8120): ------------> 對象:'537698073 相等  '+true
    I/flutter ( 8120): ------------> 對象:'537698073 相等  '+true

無論是通過餓漢式 還是懶漢式方式實現(xiàn)的單例始終拿到的都是同一對象,而同一對象中的屬性值肯定是相等的拄显,那么上面介紹的第一點通過靜態(tài)實現(xiàn)全局共享 也可以換成單例的方式實現(xiàn)苟径。

下面列舉下使用單例的場景和好處:

1、對象需要頻繁的實例化和銷毀躬审,此時考慮使用單例可以大幅度提高性能棘街。

2、控制資源的使用承边。

3遭殉、控制實例產(chǎn)生的數(shù)量,達到節(jié)約資源的目的博助。

4险污、作為通信媒介使用,也就是數(shù)據(jù)共享富岳。

三蛔糯、shared_preferences 數(shù)據(jù)持久化

shared_preferences 是 Flutter 提供的 key-value 存儲插件,它通過Android和iOS平臺提供的機制來實現(xiàn)數(shù)據(jù)持久化到磁盤中窖式。在 iOS 上封裝的是 NSUserDefault(后綴 .plist的文件中)蚁飒, 在 android 上封裝的是 SharedPreferences(后綴.xml文件中)。在使用上也是如同原生一樣簡單萝喘。

為工程添加 shared_preferences 插件:
1淮逻、在pubspec.yaml文件中添加依賴
    dependencies:
      fluttertoast: ^3.0.3
      flutter:
        sdk: flutter
      #添加持久化插件 sp
      shared_preferences: ^0.5.3+1

在 pubspec.yaml 添加依賴時特別需要注意縮進,多一個或少一個空格可能都將添加不上阁簸。

2弦蹂、安裝依賴庫

執(zhí)行 flutter packages get 命令;AS 開發(fā)工具直接右上角 packages get也可强窖。

3、在使用的文件中導入該庫
import 'package:shared_preferences/shared_preferences.dart';
shared_preferences 源碼分析:
    class SharedPreferences {
      SharedPreferences._(this._preferenceCache);

      static const String _prefix = 'flutter.';
      static SharedPreferences _instance;
      static Future<SharedPreferences> getInstance() async {
        if (_instance == null) {
          final Map<String, Object> preferencesMap =
              await _getSharedPreferencesMap();
          _instance = SharedPreferences._(preferencesMap);
        }
        return _instance;
      }

      /// The cache that holds all preferences.
      ///
      /// It is instantiated to the current state of the SharedPreferences or
      /// NSUserDefaults object and then kept in sync via setter methods in this
      /// class.
      ///
      /// It is NOT guaranteed that this cache and the device prefs will remain
      /// in sync since the setter method might fail for any reason.
      final Map<String, Object> _preferenceCache;

      /// Returns all keys in the persistent storage.
      Set<String> getKeys() => Set<String>.from(_preferenceCache.keys);

      /// Reads a value of any type from persistent storage.
      dynamic get(String key) => _preferenceCache[key];

      /// Reads a value from persistent storage, throwing an exception if it's not a
      /// bool.
      bool getBool(String key) => _preferenceCache[key];

      /// Reads a value from persistent storage, throwing an exception if it's not
      /// an int.
      int getInt(String key) => _preferenceCache[key];

      /// Reads a value from persistent storage, throwing an exception if it's not a
      /// double.
      double getDouble(String key) => _preferenceCache[key];

      /// Reads a value from persistent storage, throwing an exception if it's not a
      /// String.
      String getString(String key) => _preferenceCache[key];

      /// Returns true if persistent storage the contains the given [key].
      bool containsKey(String key) => _preferenceCache.containsKey(key);

      /// Reads a set of string values from persistent storage, throwing an
      /// exception if it's not a string set.
      List<String> getStringList(String key) {
        List<Object> list = _preferenceCache[key];
        if (list != null && list is! List<String>) {
          list = list.cast<String>().toList();
          _preferenceCache[key] = list;
        }
        // Make a copy of the list so that later mutations won't propagate
        return list?.toList();
      }

      /// Saves a boolean [value] to persistent storage in the background.
      ///
      /// If [value] is null, this is equivalent to calling [remove()] on the [key].
      Future<bool> setBool(String key, bool value) => _setValue('Bool', key, value);

      /// Saves an integer [value] to persistent storage in the background.
      ///
      /// If [value] is null, this is equivalent to calling [remove()] on the [key].
      Future<bool> setInt(String key, int value) => _setValue('Int', key, value);

      /// Saves a double [value] to persistent storage in the background.
      ///
      /// Android doesn't support storing doubles, so it will be stored as a float.
      ///
      /// If [value] is null, this is equivalent to calling [remove()] on the [key].
      Future<bool> setDouble(String key, double value) =>
          _setValue('Double', key, value);

      /// Saves a string [value] to persistent storage in the background.
      ///
      /// If [value] is null, this is equivalent to calling [remove()] on the [key].
      Future<bool> setString(String key, String value) =>
          _setValue('String', key, value);

      /// Saves a list of strings [value] to persistent storage in the background.
      ///
      /// If [value] is null, this is equivalent to calling [remove()] on the [key].
      Future<bool> setStringList(String key, List<String> value) =>
          _setValue('StringList', key, value);

      /// Removes an entry from persistent storage.
      Future<bool> remove(String key) => _setValue(null, key, null);

      Future<bool> _setValue(String valueType, String key, Object value) {
        final Map<String, dynamic> params = <String, dynamic>{
          'key': '$_prefix$key',
        };
        if (value == null) {
          _preferenceCache.remove(key);
          return _kChannel
              .invokeMethod<bool>('remove', params)
              .then<bool>((dynamic result) => result);
        } else {
          if (value is List<String>) {
            // Make a copy of the list so that later mutations won't propagate
            _preferenceCache[key] = value.toList();
          } else {
            _preferenceCache[key] = value;
          }
          params['value'] = value;
          return _kChannel
              .invokeMethod<bool>('set$valueType', params)
              .then<bool>((dynamic result) => result);
        }
      }

      /// Always returns true.
      /// On iOS, synchronize is marked deprecated. On Android, we commit every set.
      @deprecated
      Future<bool> commit() async => await _kChannel.invokeMethod<bool>('commit');

      /// Completes with true once the user preferences for the app has been cleared.
      Future<bool> clear() async {
        _preferenceCache.clear();
        return await _kChannel.invokeMethod<bool>('clear');
      }

      /// Fetches the latest values from the host platform.
      ///
      /// Use this method to observe modifications that were made in native code
      /// (without using the plugin) while the app is running.
      Future<void> reload() async {
        final Map<String, Object> preferences =
            await SharedPreferences._getSharedPreferencesMap();
        _preferenceCache.clear();
        _preferenceCache.addAll(preferences);
      }

      static Future<Map<String, Object>> _getSharedPreferencesMap() async {
        final Map<String, Object> fromSystem =
            await _kChannel.invokeMapMethod<String, Object>('getAll');
        assert(fromSystem != null);
        // Strip the flutter. prefix from the returned preferences.
        final Map<String, Object> preferencesMap = <String, Object>{};
        for (String key in fromSystem.keys) {
          assert(key.startsWith(_prefix));
          preferencesMap[key.substring(_prefix.length)] = fromSystem[key];
        }
        return preferencesMap;
      }

      /// Initializes the shared preferences with mock values for testing.
      ///
      /// If the singleton instance has been initialized already, it is automatically reloaded.
      @visibleForTesting
      static void setMockInitialValues(Map<String, dynamic> values) {
        _kChannel.setMockMethodCallHandler((MethodCall methodCall) async {
          if (methodCall.method == 'getAll') {
            return values;
          }
          return null;
        });
        _instance?.reload();
      }
    }

從源碼上看非常簡單削祈,首先 SharedPreferences 使用的一個單例模式翅溺,且是異步的脑漫。它在數(shù)據(jù)存儲上提供了:setInt、setBool咙崎、setString 和 setStringList 方法來設(shè)置特定類型的數(shù)據(jù)优幸。在數(shù)據(jù)讀取上提供了:getString 、getInt 褪猛、 getDouble网杆、getStringList方法來獲取數(shù)據(jù)。此外還提供了如下幾個工具API:

1伊滋、是否包含有該 key 值從存儲:containsKey(String key)

2碳却、移除指定的 key 值存儲:remove(String key)

3、清除全部的持久化數(shù)據(jù):clear()

4笑旺、提交存儲數(shù)據(jù):commit()昼浦,該方法已被廢棄。

綜合運用:

首先我們在 全局類 Global 中初始化 SharedPreferences 筒主,使用靜態(tài)方式方便其他地方使用关噪。

import 'package:shared_preferences/shared_preferences.dart';

class Global{

  //定義一個全局的 sp
  static SharedPreferences preferences;

  //初始化
  static void initPreferences() async{
    preferences= await SharedPreferences.getInstance();
  }

}

特別注意:SharedPreferences單例的獲取是一個異步方法所以需要 await 、async 關(guān)鍵字乌妙。

其次:在程序啟動時就初始化

 void main(){

    runApp(MyApp());

    //初始化 sp
    Global.initPreferences();
  }

下面就可以在使用的地方直接引用即可使兔,例子:

  import 'package:flutter/material.dart';
  import 'package:flutter_learn/util/Global.dart';
  import 'package:flutter_learn/util/ToastUtil.dart';
  import 'package:shared_preferences/shared_preferences.dart';
  
  //使用初始化好的靜態(tài)資源
  SharedPreferences sharedPreferences = Global.preferences;
  //定義一個內(nèi)存級別的變量
  int count = 0;
  
  class PersistenPage extends StatefulWidget {
    PersistenPage({Key key}) : super(key: key);
  
    _PersistenPageState createState() => _PersistenPageState();
  }
  
  class _PersistenPageState extends State<PersistenPage> {
    @override
    Widget build(BuildContext context) {
      
      count = sharedPreferences.getInt("count") == null
          ? 0
          : sharedPreferences.getInt("count");
  
      return Scaffold(
          appBar: AppBar(
            title: Text("持久化"),
          ),
          body: Container(
            color: Colors.blue,
            width: double.infinity,
            child: Column(
              children: <Widget>[
                SizedBox(height: 20),
                Text('ShardPreferences方式持久化',
                    style: TextStyle(color: Colors.white)),
                SizedBox(height: 20),
                Text("當前累計數(shù)據(jù):$count"),
                Row(
                  children: <Widget>[
                    SizedBox(width: 5),
                    RaisedButton(
                      color: Colors.deepPurple,
                      elevation: 20,
                      focusElevation: 40,
                      child: Text('自增', style: TextStyle(color: Colors.white)),
                      onPressed: () {
                        setState(() {
                          count++;
                          sharedPreferences?.setInt("count", count);
                        });
                      },
                    ),
                    SizedBox(width: 5),
                    RaisedButton(
                      color: Colors.deepPurple,
                      elevation: 20,
                      focusElevation: 40,
                      child: Text('清除', style: TextStyle(color: Colors.white)),
                      onPressed: () {
                        setState(() {
                          count = 0;
                          sharedPreferences.clear();
                          // sharedPreferences.remove("count");
                        });
                      },
                    ),
                    SizedBox(width: 5),
                    RaisedButton(
                      color: Colors.deepPurple,
                      elevation: 20,
                      focusElevation: 40,
                      child: Text('是否包含', style: TextStyle(color: Colors.white)),
                      onPressed: () {
                        setState(() {
                          count = 0;
                          bool flag =  sharedPreferences.containsKey("count");
                          ToastUtil.show("是否包含: $flag");
                        });
                      },
                    ),
                    SizedBox(width: 5),
                  ],
                ),
                SizedBox(height: 10),
              ],
            ),
          ));
    }
  }

案例效果圖:

SharedPreferences.gif

從上面可以看出,無論是退出當前界面藤韵,還是關(guān)閉應(yīng)用后虐沥,再次回到持久化界面任然可以讀取到存儲到文件中的值,也就是實現(xiàn)了數(shù)據(jù)本地持久化荠察。

四置蜀、IO 文件讀寫操作數(shù)據(jù)持久化

Flutter 同原生一樣支持對文件的讀和寫。Flutter 中 IO 其實是 Dart 中的一部分悉盆,由于Dart VM是運行在PC或服務(wù)器操作系統(tǒng)下盯荤,而Flutter是運行在移動操作系統(tǒng)中,他們的文件系統(tǒng)所以會有一些差異焕盟。為此 PathProvider 插件提供了一種平臺透明的方式來訪問設(shè)備文件系統(tǒng)上的常用位置秋秤。該類當前支持訪問兩個文件系統(tǒng)位置:

1、臨時文件夾:可以使用 getTemporaryDirectory() 來獲取臨時目錄脚翘;在 iOS 上對用的是 NSCachesDirectory 在 Android 對用的是 getCacheDir()灼卢。

2、應(yīng)用的Documents 目錄:可以使用 getApplicationDocumentsDirectory 來獲取 Documents 目錄来农;在 iOS 對應(yīng)的是 NSDocumentDirectory 鞋真,在 Android 上對應(yīng)的是 AppData 目錄。

【特別注意:】臨時文件夾在執(zhí)行系統(tǒng)的清空緩存時會清空該文件夾沃于,documents 目錄只有在刪除應(yīng)用時才會清空涩咖。

PathProvider和 SharedPreferences 一樣需要引入海诲;具體步驟如下:
1、在pubspec.yaml 文件中添加聲明:
    dependencies:
      fluttertoast: ^3.0.3
      flutter:
        sdk: flutter
      #添加持久化插件 sp
      shared_preferences: ^0.5.3+1
      #添加文件庫
      path_provider: ^1.2.0
2檩互、安裝依賴庫

執(zhí)行 flutter packages get 命令特幔;AS 開發(fā)工具直接右上角 packages get也可。

3闸昨、在使用的文件中導入該庫
import 'package:path_provider/path_provider.dart';
File類的代碼比較多蚯斯,這里就不貼內(nèi)部源碼了,下面只列出開發(fā)中常用的 API :

1饵较、create() 創(chuàng)建文件,多級目錄 recursive 為真則遞歸創(chuàng)建
2拍嵌、createSync() 同步方式創(chuàng)建文件

3、rename() 文件重命名
4告抄、renameSync() 同步設(shè)置文件重命名

5撰茎、copy() 復制文件
6、copySync() 同步復制文件

7打洼、length() 獲取文件長度
8龄糊、lengthSync() 同步獲取文件長度

9、absolute 獲取絕對路徑文件

10募疮、lastAccessed() 獲取最后一次的訪問時間
11炫惩、lastAccessedSync() 同步獲取最后一次的訪問時間

12、setLastAccessed() 設(shè)置最后一次的訪問時間
13阿浓、setLastAccessedSync() 同步設(shè)置最后一次的訪問時間

14他嚷、lastModified() 獲取最后一次修改時間
15、lastModifiedSync() 同步獲取最后一次修改時間

16芭毙、setLastModified() 設(shè)置最后一次修改時間
17筋蓖、setLastModifiedSync() 同步設(shè)置最后一次修改時間

18、open() 打開文件有多種方式和文件的訪問模式退敦,詳情參閱源碼

19粘咖、readAsBytes() 讀取字節(jié)
20、readAsBytesSync() 同步讀取字節(jié)

21侈百、readAsString() 讀取字符串
22瓮下、readAsStringSync() 同步讀取字符串

23、readAsLines() 讀取一行
24钝域、readAsLinesSync() 同步讀取一行

25讽坏、writeAsBytes() 寫出字節(jié)數(shù)組
26、writeAsBytesSync() 同步寫出字節(jié)數(shù)組

27例证、writeAsString() 寫出字符串
28路呜、writeAsStringSync() 同步寫出字符串

對 File 的操作有太多 API 了 ,更多內(nèi)容只有自己去查閱源碼了织咧。下面我們來將之前的計數(shù)器實例來改造下拣宰,使用文件的方式党涕。

首先我們在 全局類 Global 中創(chuàng)建文件 ,使用靜態(tài)方式方便其他地方使用巡社。

import 'dart:io';

import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

class Global{

  //定義一個全局的 sp
  static SharedPreferences preferences;

  static File file;

  //初始化
  static void initPreferences() async{
    preferences= await SharedPreferences.getInstance();
  }

  //初始化一個文件,方便使用
  static void initFile() async{

    final directory = await getApplicationDocumentsDirectory();
    if (!(file is File)) {
      final String path = directory.path;
      file = File('$path/myInfo.txt');
     if( !file.existsSync()){
       // 不存在則創(chuàng)建文件
       file.createSync(recursive: true);
     }
    }
  }

}

其次:在程序啟動時就初始化

  void main(){

    runApp(MyApp());

    //初始化 sp
    Global.initPreferences();

    //初始化文件
    Global.initFile();
  }

下面就可以在使用的地方直接引用即可手趣,例子:

  import 'dart:io';

  import 'package:flutter/material.dart';
  import 'package:flutter_learn/util/Global.dart';
  import 'package:flutter_learn/util/ToastUtil.dart';
  import 'package:shared_preferences/shared_preferences.dart';

  //使用初始化好的靜態(tài)資源
  SharedPreferences sharedPreferences = Global.preferences;

  //使用初始化好的本地文件
  File localFile = Global.file;

  class PersistenPage extends StatefulWidget {
    PersistenPage({Key key}) : super(key: key);

    _PersistenPageState createState() => _PersistenPageState();
  }

  class _PersistenPageState extends State<PersistenPage> {
    @override
    Widget build(BuildContext context) {

      return Scaffold(
          appBar: AppBar(
            title: Text("持久化"),
          ),
          body: SingleChildScrollView(
            child: Container(
                color: Colors.blue,
                height: 1000,
                child: Column(
                  children: <Widget>[

                   // shardPreferences(),

                    IoReadWirte()
                  ],
                ),
            ),
          ));
    }


    //文件的讀寫
    Widget IoReadWirte(){
      //定義一個內(nèi)存級別的變量
      String info = localFile.readAsStringSync();
      int count =int.parse(info??0);
      return Column(
        children: <Widget>[
          SizedBox(height: 20),
          Text('文件操作方式持久化',
              style: TextStyle(color: Colors.white)),
          SizedBox(height: 20),
          Text("當前累計數(shù)據(jù):$count"),
          Row(
            children: <Widget>[
              SizedBox(width: 5),
              RaisedButton(
                color: Colors.deepPurple,
                elevation: 20,
                focusElevation: 40,
                child: Text('自增', style: TextStyle(color: Colors.white)),
                onPressed: () {
                  setState(() {
                    count++;
                    localFile.writeAsStringSync(count.toString());

                  });
                },
              ),
              SizedBox(width: 5),
              RaisedButton(
                color: Colors.deepPurple,
                elevation: 20,
                focusElevation: 40,
                child: Text('自減', style: TextStyle(color: Colors.white)),
                onPressed: () {
                  setState(() {
                    count--;
                    localFile.writeAsStringSync(count.toString());
                  });
                },
              ),
            ],
          ),
          SizedBox(height: 10),
        ],
      );
    }
  }

為了方便起見和減少文章篇幅晌该,這里只貼出了 File 操作的全部代碼,shardPreferences 中的代碼暫時刪除绿渣,文末的工程實例中擁有全部代碼朝群。

效果如下:

IO操作.gif

無論是用 SharedPreferences 還是文件操作 方式實現(xiàn)數(shù)據(jù)持久化都是非常簡單,在開發(fā)中 如果只是存儲一些簡單數(shù)據(jù) 其實用 SharedPreferences 就非常好了中符,如果涉及到一些文件(如:圖片姜胖,文檔等)才會去使用 File。

上面兩點都只是對一些單一數(shù)據(jù)的持久化淀散,而要持久化關(guān)系型數(shù)據(jù)就顯現(xiàn)的捉衿見肘了右莱,對于有關(guān)系的數(shù)據(jù)我們首先想到的肯定是通過數(shù)據(jù)庫去存儲,而數(shù)據(jù)庫在開發(fā)中也是重中之中档插,涉及到篇幅問題這里就不介紹了慢蜓,這也是為下一篇文章做鋪墊吧

本章中很多都是通過靜態(tài)的方式去存值,其實在開發(fā)中應(yīng)該減少這種方式的使用 即在需要使用的時候才去獲取郭膛,避免不必要的內(nèi)存消耗晨抡!我這里只是為了方便演示才這樣寫。

好了本章節(jié)到此結(jié)束则剃,又到了說再見的時候了耘柱,如果你喜歡請留下你的小紅星,創(chuàng)作真心也不容易棍现;你們的支持才是創(chuàng)作的動力调煎,如有錯誤,請熱心的你留言指正, 謝謝大家觀看轴咱,下章再會 O(∩_∩)O

實例源碼地址: https://github.com/zhengzaihong/flutter_learn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汛蝙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子朴肺,更是在濱河造成了極大的恐慌窖剑,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戈稿,死亡現(xiàn)場離奇詭異西土,居然都是意外死亡,警方通過查閱死者的電腦和手機鞍盗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門需了,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跳昼,“玉大人,你說我怎么就攤上這事肋乍《旒眨” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵墓造,是天一觀的道長堪伍。 經(jīng)常有香客問我,道長觅闽,這世上最難降的妖魔是什么帝雇? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蛉拙,結(jié)果婚禮上尸闸,老公的妹妹穿的比我還像新娘。我一直安慰自己孕锄,他們只是感情好吮廉,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硫惕,像睡著了一般茧痕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恼除,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天踪旷,我揣著相機與錄音,去河邊找鬼豁辉。 笑死令野,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的徽级。 我是一名探鬼主播气破,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼餐抢!你這毒婦竟也來了现使?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤旷痕,失蹤者是張志新(化名)和其女友劉穎碳锈,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欺抗,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡售碳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贸人。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡间景,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艺智,到底是詐尸還是另有隱情倘要,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布力惯,位于F島的核電站碗誉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏父晶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一弄跌、第九天 我趴在偏房一處隱蔽的房頂上張望甲喝。 院中可真熱鬧,春花似錦铛只、人聲如沸埠胖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽直撤。三九已至,卻和暖如春蜕着,著一層夾襖步出監(jiān)牢的瞬間谋竖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工承匣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蓖乘,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓韧骗,卻偏偏與公主長得像嘉抒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子袍暴,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容