美文网首页Flutter圈子Android开发Flutter中文社区
使用容器和Provider实现全局状态与局部状态管理交互

使用容器和Provider实现全局状态与局部状态管理交互

作者: 岛上码农 | 来源:发表于2020-05-27 21:36 被阅读0次

    场景

    在实际中存在全局状态需要与局部状态进行交互,例如用户登录成功后需要通知业务模块更新数据(比如后台上传位置,开始刷新拉取数据)。传统的方式中需要在局部状态向全局状态主动订阅消息,当全局状态改变时再通知局部状态处理业务。这样存在如下缺陷:

    1. 未使用局部状态前也需要创建局部状态实例(因需要发起全局状态消息订阅)
    2. 局部状态需要调用全局状态的消息订阅方法,出现了耦合。

    主动订阅方式

    以之前的主动订阅方式,使用用户登录为例。这里定义两个状态管理,一个是用户登录退出全局状态UserLoginStore,一个是消息接收局部状态MessageStore。在用户登录和用户退出的时候需要MessageStore进行登录成功和退出登录后的业务处理。原先的两个类定义如下:


    基本类图

    代码实现上,以Provider为例,首先是在顶层widget注册全局状态管理UserLoginStore,同时还需要将MessageStore实例化(或者也注册为全局状态)。

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

    UsreLoginStore的实现如下,在登录成功和退出登录时,通过调用对应listeners的Function对象,执行相应的函数进行响应。

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

    MessageStore的实现方法如下:

    class MessageStore with ChangeNotifier {
    
      void _requestMessage() async {
        //省略后台接口请求消息代码
        notifyListeners();
      }
      
      void _clearMessage() async {
        //省略将消息列表清空方法
        notifyListeners();
      }
      
      void registerLoginListener() {
        final userLoginStore = Provider.of<UserLoginStore>();
        //注册登录成功订阅,成功后请求消息
        userLoginStore.addLoginListeners(() {
          _requestMessage();
        });
      }
      
      void addLogoutListers(Function func) {
        final userLoginStore = Provider.of<UserLoginStore>();
        //注册退出登录订阅,退出登录后清空消息
        userLoginStore.addLogoutListeners(() {
          _clearMessage();
        });
      }
    }
    

    从代码可以看出UserLoginStore的addLoginListeners和addLogoutListeners暴露给了MessageStore,增加了耦合。而且因为是主动订阅,会需要提前实例化MessageStore,哪怕是一开始没有任何用户信息。

    使用容器和通过接口解耦

    为了对UserLoginStore和MessageStore进行解耦,更改一下类图。


    引入抽象类解耦

    增加UserLoginService抽象类,定义登录成功和登录失败的处理接口方法。此时MessageStore需要实现UserLoginService的loginHandler和logoutHandler方法。代码修改如下:

    class MessageStore with ChangeNotifier, UserLoginService {
    
      void _requestMessage() async {
        //省略后台接口请求消息代码
        notifyListeners();
      }
      
      void _clearMessage() async {
        //省略将消息列表清空方法
        notifyListeners();
      }
      
      void loginHandler() {
        _requestMessage();
      }
      
      void logoutHandler() {
        _clearMessage();
      }
    }
    

    从代码里看,已经完全不依赖于UserLoginStore。<br />这个时候UserLoginStore如何去通知MessageStore呢?容器登场。使用GetIt组件定义全局容器类,代码如下:

    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>();
      }
    }
    

    代码也很简单,首先为了保证在需要的时候拿到MessageStore的实例,通过registerLazySingleton<MessageStore>注册一个懒加载的MessageStore。然后考虑UserLoginStore可能与多个局部Store关联,一次注册一个List<UserLoginService>,注意这里使用的是抽象类UserLoginService了,也是懒加载的方式返回一个List,这个List其实就是在用户登录成功或退出登录需要通知的对象。这个List里通过getService<MessageStore>()返回了UserLoginStore需要通知的MessageStore示例。<br />再来看UserLoginStore的实现代码。

    class UserLoginStore with ChangeNotifier {
      void login() async {
        //省略调用登录接口代码
        //从容器取出需要通知的listeners对象
        List<UserLoginService> listners = GlobalServiceRepository.getService<List<UserLoginService>>();
        listners.forEach((listener) {
           listener.loginHandler();
        });
      }
      
      void logout() async {
        //省略调用退出登录接口代码
        List<UserLoginService> listners = GlobalServiceRepository.getService<List<UserLoginService>>();
        listners.forEach((listener) {
           listener.logoutHandler();
        });
      }
    }
    

    从代码里可以看到,UserLoginStore只需要关心容器里是否有需要通知的listeners即可,任何注册到容器的UserLoginService的实现类都可以接收到登录和退出登录的消息。

    相关文章

      网友评论

        本文标题:使用容器和Provider实现全局状态与局部状态管理交互

        本文链接:https://www.haomeiwen.com/subject/hgogahtx.html