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

2021-03-09 15:12 更新

應(yīng)用程序中通常會包含一些貫穿 APP 生命周期的變量信息,這些信息在 APP 大多數(shù)地方可能都會被用到,比如當前用戶信息、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 的全局變量,定義如下:

  1. // 提供五套可選主題色
  2. const _themes = <MaterialColor>[
  3. Colors.blue,
  4. Colors.cyan,
  5. Colors.teal,
  6. Colors.green,
  7. Colors.red,
  8. ];
  9. class Global {
  10. static SharedPreferences _prefs;
  11. static Profile profile = Profile();
  12. // 網(wǎng)絡(luò)緩存對象
  13. static NetCache netCache = NetCache();
  14. // 可選的主題列表
  15. static List<MaterialColor> get themes => _themes;
  16. // 是否為release版
  17. static bool get isRelease => bool.fromEnvironment("dart.vm.product");
  18. //初始化全局信息,會在APP啟動時執(zhí)行
  19. static Future init() async {
  20. _prefs = await SharedPreferences.getInstance();
  21. var _profile = _prefs.getString("profile");
  22. if (_profile != null) {
  23. try {
  24. profile = Profile.fromJson(jsonDecode(_profile));
  25. } catch (e) {
  26. print(e);
  27. }
  28. }
  29. // 如果沒有緩存策略,設(shè)置默認緩存策略
  30. profile.cache = profile.cache ?? CacheConfig()
  31. ..enable = true
  32. ..maxAge = 3600
  33. ..maxCount = 100;
  34. //初始化網(wǎng)絡(luò)請求相關(guān)配置
  35. Git.init();
  36. }
  37. // 持久化Profile信息
  38. static saveProfile() =>
  39. _prefs.setString("profile", jsonEncode(profile.toJson()));
  40. }

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

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

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

#15.4.2 共享狀態(tài)

有了全局變量,我們還需要考慮如何跨組件共享狀態(tài)。當然,如果我們將要共享的狀態(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定義如下:

  1. class ProfileChangeNotifier extends ChangeNotifier {
  2. Profile get _profile => Global.profile;
  3. @override
  4. void notifyListeners() {
  5. Global.saveProfile(); //保存Profile變更
  6. super.notifyListeners(); //通知依賴的Widget更新
  7. }
  8. }

#用戶狀態(tài)

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

  1. class UserModel extends ProfileChangeNotifier {
  2. User get user => _profile.user;
  3. // APP是否登錄(如果有用戶信息,則證明登錄過)
  4. bool get isLogin => user != null;
  5. //用戶信息發(fā)生變化,更新用戶信息并通知依賴它的子孫Widgets更新
  6. set user(User user) {
  7. if (user?.login != _profile.user?.login) {
  8. _profile.lastLogin = _profile.user?.login;
  9. _profile.user = user;
  10. notifyListeners();
  11. }
  12. }
  13. }

#APP主題狀態(tài)

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

  1. class ThemeModel extends ProfileChangeNotifier {
  2. // 獲取當前主題,如果為設(shè)置主題,則默認使用藍色主題
  3. ColorSwatch get theme => Global.themes
  4. .firstWhere((e) => e.value == _profile.theme, orElse: () => Colors.blue);
  5. // 主題改變后,通知其依賴項,新主題會立即生效
  6. set theme(ColorSwatch color) {
  7. if (color != theme) {
  8. _profile.theme = color[500].value;
  9. notifyListeners();
  10. }
  11. }
  12. }

#APP語言狀態(tài)

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

  1. class LocaleModel extends ProfileChangeNotifier {
  2. // 獲取當前用戶的APP語言配置Locale類,如果為null,則語言跟隨系統(tǒng)語言
  3. Locale getLocale() {
  4. if (_profile.locale == null) return null;
  5. var t = _profile.locale.split("_");
  6. return Locale(t[0], t[1]);
  7. }
  8. // 獲取當前Locale的字符串表示
  9. String get locale => _profile.locale;
  10. // 用戶改變APP語言后,通知依賴項更新,新語言會立即生效
  11. set locale(String locale) {
  12. if (locale != _profile.locale) {
  13. _profile.locale = locale;
  14. notifyListeners();
  15. }
  16. }
  17. }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號