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

场景

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

  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 loginListeners = [];
  List 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.addLoginListeners(() {
      _requestMessage();
    });
  }
  
  void addLogoutListers(Function func) {
    final userLoginStore = Provider.of();
    //注册退出登录订阅,退出登录后清空消息
    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。
这个时候UserLoginStore如何去通知MessageStore呢?容器登场。使用GetIt组件定义全局容器类,代码如下:

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

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

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

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

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

你可能感兴趣的:(使用容器和Provider实现全局状态与局部状态管理交互)