使用容器和Provider實(shí)現(xiàn)全局狀態(tài)與局部狀態(tài)管理交互

場(chǎng)景

在實(shí)際中存在全局狀態(tài)需要與局部狀態(tài)進(jìn)行交互,例如用戶(hù)登錄成功后需要通知業(yè)務(wù)模塊更新數(shù)據(jù)(比如后臺(tái)上傳位置,開(kāi)始刷新拉取數(shù)據(jù))。傳統(tǒng)的方式中需要在局部狀態(tài)向全局狀態(tài)主動(dòng)訂閱消息,當(dāng)全局狀態(tài)改變時(shí)再通知局部狀態(tài)處理業(yè)務(wù)。這樣存在如下缺陷:

  1. 未使用局部狀態(tài)前也需要?jiǎng)?chuàng)建局部狀態(tài)實(shí)例(因需要發(fā)起全局狀態(tài)消息訂閱)
  2. 局部狀態(tài)需要調(diào)用全局狀態(tài)的消息訂閱方法,出現(xiàn)了耦合。

主動(dòng)訂閱方式

以之前的主動(dòng)訂閱方式,使用用戶(hù)登錄為例。這里定義兩個(gè)狀態(tài)管理,一個(gè)是用戶(hù)登錄退出全局狀態(tài)UserLoginStore,一個(gè)是消息接收局部狀態(tài)MessageStore。在用戶(hù)登錄和用戶(hù)退出的時(shí)候需要MessageStore進(jìn)行登錄成功和退出登錄后的業(yè)務(wù)處理。原先的兩個(gè)類(lèi)定義如下:


基本類(lèi)圖

代碼實(shí)現(xiàn)上,以Provider為例,首先是在頂層widget注冊(cè)全局狀態(tài)管理UserLoginStore,同時(shí)還需要將MessageStore實(shí)例化(或者也注冊(cè)為全局狀態(tài))。

Widget myApp = MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (context) => UserLoginStore(),
        lazy: false,
      ),
    ],
    child: MyApp(),
  );

UsreLoginStore的實(shí)現(xiàn)如下,在登錄成功和退出登錄時(shí),通過(guò)調(diào)用對(duì)應(yīng)listeners的Function對(duì)象,執(zhí)行相應(yīng)的函數(shù)進(jìn)行響應(yīng)。

class UserLoginStore with ChangeNotifier {
  List<Function()> loginListeners = [];
  List<Function()> logoutListeners = [];
  
  void login() async {
    //省略調(diào)用登錄接口代碼
    if (loginListeners != null) {
      loginListeners.forEach((func) {
        func();
      })
    }
  }
  
  void logout() async {
    //省略調(diào)用退出登錄接口代碼
    if (logoutListeners != null) {
      logoutListeners.forEach((func) {
        func();
      })
    }
  }
  
  void addLoginListeners(Function func) {
    loginListeners.add(func);
  }
  
  void addLogoutListeners(Function func) {
    logoutListeners.add(func);
  }
}

MessageStore的實(shí)現(xiàn)方法如下:

class MessageStore with ChangeNotifier {

  void _requestMessage() async {
    //省略后臺(tái)接口請(qǐng)求消息代碼
    notifyListeners();
  }
  
  void _clearMessage() async {
    //省略將消息列表清空方法
    notifyListeners();
  }
  
  void registerLoginListener() {
    final userLoginStore = Provider.of<UserLoginStore>();
    //注冊(cè)登錄成功訂閱,成功后請(qǐng)求消息
    userLoginStore.addLoginListeners(() {
      _requestMessage();
    });
  }
  
  void addLogoutListers(Function func) {
    final userLoginStore = Provider.of<UserLoginStore>();
    //注冊(cè)退出登錄訂閱,退出登錄后清空消息
    userLoginStore.addLogoutListeners(() {
      _clearMessage();
    });
  }
}

從代碼可以看出UserLoginStore的addLoginListeners和addLogoutListeners暴露給了MessageStore,增加了耦合。而且因?yàn)槭侵鲃?dòng)訂閱,會(huì)需要提前實(shí)例化MessageStore,哪怕是一開(kāi)始沒(méi)有任何用戶(hù)信息。

使用容器和通過(guò)接口解耦

為了對(duì)UserLoginStore和MessageStore進(jìn)行解耦,更改一下類(lèi)圖。


引入抽象類(lèi)解耦

增加UserLoginService抽象類(lèi),定義登錄成功和登錄失敗的處理接口方法。此時(shí)MessageStore需要實(shí)現(xiàn)UserLoginService的loginHandler和logoutHandler方法。代碼修改如下:

class MessageStore with ChangeNotifier, UserLoginService {

  void _requestMessage() async {
    //省略后臺(tái)接口請(qǐng)求消息代碼
    notifyListeners();
  }
  
  void _clearMessage() async {
    //省略將消息列表清空方法
    notifyListeners();
  }
  
  void loginHandler() {
    _requestMessage();
  }
  
  void logoutHandler() {
    _clearMessage();
  }
}

從代碼里看,已經(jīng)完全不依賴(lài)于UserLoginStore。<br />這個(gè)時(shí)候UserLoginStore如何去通知MessageStore呢?容器登場(chǎng)。使用GetIt組件定義全局容器類(lèi),代碼如下:

class GlobalServiceRepository {
  static void resisterServices() {
    GetIt getIt = GetIt.instance;
    getIt.registerLazySingleton<MessageStore>(() => MessageStore());
    getIt.registerLazySingleton<List<UserLoginService>>(() {
      return [getService<MessageStore>()];
    });
  }

  static T getService<T>() {
    GetIt getIt = GetIt.instance;
    return getIt<T>();
  }
}

代碼也很簡(jiǎn)單,首先為了保證在需要的時(shí)候拿到MessageStore的實(shí)例,通過(guò)registerLazySingleton<MessageStore>注冊(cè)一個(gè)懶加載的MessageStore。然后考慮UserLoginStore可能與多個(gè)局部Store關(guān)聯(lián),一次注冊(cè)一個(gè)List<UserLoginService>,注意這里使用的是抽象類(lèi)UserLoginService了,也是懶加載的方式返回一個(gè)List,這個(gè)List其實(shí)就是在用戶(hù)登錄成功或退出登錄需要通知的對(duì)象。這個(gè)List里通過(guò)getService<MessageStore>()返回了UserLoginStore需要通知的MessageStore示例。<br />再來(lái)看UserLoginStore的實(shí)現(xiàn)代碼。

class UserLoginStore with ChangeNotifier {
  void login() async {
    //省略調(diào)用登錄接口代碼
    //從容器取出需要通知的listeners對(duì)象
    List<UserLoginService> listners = GlobalServiceRepository.getService<List<UserLoginService>>();
    listners.forEach((listener) {
       listener.loginHandler();
    });
  }
  
  void logout() async {
    //省略調(diào)用退出登錄接口代碼
    List<UserLoginService> listners = GlobalServiceRepository.getService<List<UserLoginService>>();
    listners.forEach((listener) {
       listener.logoutHandler();
    });
  }
}

從代碼里可以看到,UserLoginStore只需要關(guān)心容器里是否有需要通知的listeners即可,任何注冊(cè)到容器的UserLoginService的實(shí)現(xiàn)類(lèi)都可以接收到登錄和退出登錄的消息。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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