Flutter實戰(zhàn) 全局變量及共享狀態(tài)

2021-03-09 15:12 更新

應(yīng)用程序中通常會包含一些貫穿 APP 生命周期的變量信息,這些信息在 APP 大多數(shù)地方可能都會被用到,比如當(dāng)前用戶信息、Local 信息等。在 Flutter 中我們把需要全局共享的信息分為兩類:全局變量和共享狀態(tài)。全局變量就是單純指會貫穿整個 APP 生命周期的變量,用于單純的保存一些信息,或者封裝一些全局工具和方法的對象。而共享狀態(tài)則是指哪些需要跨組件或跨路由共享的信息,這些信息通常也是全局變量,而共享狀態(tài)和全局變量的不同在于前者發(fā)生改變時需要通知所有使用該狀態(tài)的組件,而后者不需要。為此,我們將全局變量和共享狀態(tài)分開單獨管理。

#15.4.1 全局變量-Global類

我們在“l(fā)ib/common”目錄下創(chuàng)建一個Global類,它主要管理 APP 的全局變量,定義如下:

// 提供五套可選主題色
const _themes = <MaterialColor>[
  Colors.blue,
  Colors.cyan,
  Colors.teal,
  Colors.green,
  Colors.red,
];


class Global {
  static SharedPreferences _prefs;
  static Profile profile = Profile();
  // 網(wǎng)絡(luò)緩存對象
  static NetCache netCache = NetCache();


  // 可選的主題列表
  static List<MaterialColor> get themes => _themes;


  // 是否為release版
  static bool get isRelease => bool.fromEnvironment("dart.vm.product");


  //初始化全局信息,會在APP啟動時執(zhí)行
  static Future init() async {
    _prefs = await SharedPreferences.getInstance();
    var _profile = _prefs.getString("profile");
    if (_profile != null) {
      try {
        profile = Profile.fromJson(jsonDecode(_profile));
      } catch (e) {
        print(e);
      }
    }


    // 如果沒有緩存策略,設(shè)置默認緩存策略
    profile.cache = profile.cache ?? CacheConfig()
      ..enable = true
      ..maxAge = 3600
      ..maxCount = 100;


    //初始化網(wǎng)絡(luò)請求相關(guān)配置
    Git.init();
  }


  // 持久化Profile信息
  static saveProfile() =>
      _prefs.setString("profile", jsonEncode(profile.toJson()));
}

Global 類的各個字段的意義都有注釋,在此不再贅述,需要注意的是init()需要在 App 啟動時就要執(zhí)行,所以應(yīng)用的main方法如下:

void main() => Global.init().then((e) => runApp(MyApp()));

在此,一定要確保Global.init()方法不能拋出異常,否則 runApp(MyApp())根本執(zhí)行不到。

#15.4.2 共享狀態(tài)

有了全局變量,我們還需要考慮如何跨組件共享狀態(tài)。當(dāng)然,如果我們將要共享的狀態(tài)全部用全局變量替代也是可以的,但是這在 Flutter 開發(fā)中并不是一個好主意,因為組件的狀態(tài)是和 UI 相關(guān),而在狀態(tài)改變時我們會期望依賴該狀態(tài)的 UI 組件會自動更新,如果使用全局變量,那么我們必須得去手動處理狀態(tài)變動通知、接收機制以及變量和組件依賴關(guān)系。因此,本實例中,我們使用前面介紹過的 Provider 包來實現(xiàn)跨組件狀態(tài)共享,因此我們需要定義相關(guān)的 Provider。在本實例中,需要共享的狀態(tài)有登錄用戶信息、APP 主題信息、APP 語言信息。由于這些信息改變后都要立即通知其它依賴的該信息的 Widget 更新,所以我們應(yīng)該使用ChangeNotifierProvider,另外,這些信息改變后都是需要更新 Profile 信息并進行持久化的。綜上所述,我們可以定義一個ProfileChangeNotifier基類,然后讓需要共享的 Model 繼承自該類即可,ProfileChangeNotifier定義如下:

class ProfileChangeNotifier extends ChangeNotifier {
  Profile get _profile => Global.profile;


  @override
  void notifyListeners() {
    Global.saveProfile(); //保存Profile變更
    super.notifyListeners(); //通知依賴的Widget更新
  }
}

#用戶狀態(tài)

用戶狀態(tài)在登錄狀態(tài)發(fā)生變化時更新、通知其依賴項,我們定義如下:

class UserModel extends ProfileChangeNotifier {
  User get user => _profile.user;


  // APP是否登錄(如果有用戶信息,則證明登錄過)
  bool get isLogin => user != null;


  //用戶信息發(fā)生變化,更新用戶信息并通知依賴它的子孫Widgets更新
  set user(User user) {
    if (user?.login != _profile.user?.login) {
      _profile.lastLogin = _profile.user?.login;
      _profile.user = user;
      notifyListeners();
    }
  }
}

#APP主題狀態(tài)

主題狀態(tài)在用戶更換 APP 主題時更新、通知其依賴項,定義如下:

class ThemeModel extends ProfileChangeNotifier {
  // 獲取當(dāng)前主題,如果為設(shè)置主題,則默認使用藍色主題
  ColorSwatch get theme => Global.themes
      .firstWhere((e) => e.value == _profile.theme, orElse: () => Colors.blue);


  // 主題改變后,通知其依賴項,新主題會立即生效
  set theme(ColorSwatch color) {
    if (color != theme) {
      _profile.theme = color[500].value;
      notifyListeners();
    }
  }
}

#APP語言狀態(tài)

當(dāng) APP 語言選為跟隨系統(tǒng)(Auto)時,在系通語言改變時,APP 語言會更新;當(dāng)用戶在 APP 中選定了具體語言時(美國英語或中文簡體),則 APP 便會一直使用用戶選定的語言,不會再隨系統(tǒng)語言而變。語言狀態(tài)類定義如下:

class LocaleModel extends ProfileChangeNotifier {
  // 獲取當(dāng)前用戶的APP語言配置Locale類,如果為null,則語言跟隨系統(tǒng)語言
  Locale getLocale() {
    if (_profile.locale == null) return null;
    var t = _profile.locale.split("_");
    return Locale(t[0], t[1]);
  }


  // 獲取當(dāng)前Locale的字符串表示
  String get locale => _profile.locale;


  // 用戶改變APP語言后,通知依賴項更新,新語言會立即生效
  set locale(String locale) {
    if (locale != _profile.locale) {
      _profile.locale = locale;
      notifyListeners();
    }
  }
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號