Flutter 状态管理

2024-07-09 16:26:20 浏览数 (1)

我之前对 Flutter 的状态管理一直比较头大,最近看到了一篇Flutter state management for minimalists | by Suragch | Medium文章,感觉文章真的很好,把为什么要进行状态管理以及什么是状态管理说的很通透,推荐大家看原文,这里是自己总结一下。

<!--more-->

什么是状态管理

老实说,之前看 Flutter 相关的文章和视频的时候,有些上来就推荐用 Bloc 的就很懵,没有前因后果,就告诉我要用这个东西,这东西用了有什么好处?这东西上手难度如何,这东西是必备的吗?和其他的状态管理库(比如GetX)对比有什么优点,等等之类的都不清楚,所以一直很难深入学习。

来看下图:

我本人是 iOS APP 开发,所以对于 MVVM 很熟悉,对比上图,UI Layer 是 View,Service Layer是 Model,所以 State Management Layer 就是 ViewModel,这样解释对比,我就对状态管理有了直观的理解,只是换了个名称,其实就是 ViewModel。

为什么要状态管理

那为什么要状态管理呢?其实理解了上面,就知道这其实不是个问题。由于项目使用MVVM所以需要 ViewModel。 同理由于采用了UI LayerState Management LayerService Layer这种架构设计,所以需要状态管理,所以真正的问题是为什么采用这种架构?而这个问题就简单了,因为采用这种架构设计清晰、更容易实现数据分离、容易测试、代码更容易复用。

UI LayerState Management LayerService Layer

Flutter

UI Layer是绘制 UI,尽量不要把逻辑写作 UI Layer中,最多就是写一些If else判断显示不同的 UI,而 UI 要显示什么,显示成什么样,则是state控制的。

UI Layer是把state显示给用户。所以解析、格式化字符串、存储数据之类的逻辑也不应该写在这一层中。

State Management Layer是负责数据和 UI 交互的,一方面把数据处理后显示到 UI 上,另一方面处理响应 UI的事件,对数据进行计算变更。这个地方是大部分逻辑应该存在的地方。使用State Managerment Layer时需要注意,虽然这层负责和 UI 的交互,但是不应该把UI Layer和他混在一起,简单的说,可以这么理解UI Layer通过 State Management Layer获取数据,处理数据,但是State Management LayerUI Layer了解不多,这样做的好处是,当重构UI Layer时,State Management Layer不受影响。

Service Layer,虽然大部分的逻辑在State Management Layer中,但是还有一部分逻辑是在这一层处理的,这一层可以理解为封装好的统一的数据层,里面可能是一个本地数据库、或者网络请求的数据,或者其他存储的数据。然后统一封装为Service Layer对外调用。通常做法是把Service Layer定义为Protocol或者interface,然后再由具体的Service实现这些协议或者接口,对于State Management Layer来说,只知道有这些协议或者接口,至于具体是怎么实现的,State Management Layer并不清楚。这样做的好处是,通过定义协议或者接口,可以更方便的实现分离,更方便的测试,比如可以在服务端没好的时候,通过 Service来实现Mock Fake Data进行测试。

作者推崇的Service Locator模式

回归主题,如何实现状态管理,即如何设计State Management Layer

首先再来思考一下State Management Layer的作用:

  1. UI Layer能够引用State Management Layer
  2. State Management Layer能够响应UI Layer的事件
  3. UI Layer能够通过State Management Layer监听状态变化
  4. 状态变化能够自动刷新 UI

下面一条条来看,如何处理:

UI Layer能够引用State Management Layer

这一步,很简单,只需要在对应Widget中声明对应的 State Manager 即可,这里有两种方式,

一种是普通的

代码语言:Swift复制
class MyPage extends StatefulWidget {
  // ... 
} 

class _MyPageState extends State<MyPage> {
  final manager = MyPageManager(); 
  // ...
}

一种是使用GetIt,单例模式的

代码语言:Swift复制
class MyPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    final manager = getIt<MyPageManager>();
    // return ...
  }
}

使用GetItService Locator模式,可以参考How the GetIt service locator package works in Swift。这里感觉还是有些难以接受,因为之前开发 APP 时,被教导的是尽量少用单例,因为单例全局可访问,不可控,容易造成难以测试,难以维护。但是这里作者提出的是,在遵循下面规则的情况下,可以使用单例,来进行状态管理。

  • 不要直接通过UI Layer修改State Management中的State。要通过调用State Management Layer中的方法来修改。
  • 针对测试的问题,GetIt提供测试的方法,参考GetIt provides a way to test these classes

State Management Layer能够响应UI Layer的事件

UI Layer中的事件响应,直接通过调用State Management Layer中的方法来实现,如下:

代码语言:Swift复制
loginPageManager.submitUserInfo(username, password);

如果有需要在页面打开时初始化的逻辑,可以放在initState中来初始化。如下:

代码语言:Swift复制
@override 
void initState() { 
  super.initState(); 
  manager.doSomething(); 
}

UI Layer能够通过State Management Layer监听状态变化

这一步是比较麻烦的,直接使用Flutter自带的ValueNotifier或者ChangeNotifier来实现。使用方式如下:

代码语言:Swift复制
final myStateNotifier = ValueNotifer<int>(1);

myStateNotifier.value = 2;

也可以声明一个类继承自ValueNotifier,如下:

代码语言:Swift复制
class FavoriteNotifier extends ValueNotfier<bool> {
  // set initial value to false
  FavoriteNotfier(): super(false);

  // get reference to service layer
  final _storageService = getIt<StorageService>();

  // a method to call from the outside
  void toggleFavoriteStatus(Song song) {
    value = !value;
    _storageService.updateFavoriteSong(song, value);
  }
}

从上面的代码可以看出,所有的逻辑都封装在 Notifier中,对外暴露的是只一个调用方法。在State Management类中,只需要创建这个Notifier,然后调用Notifier中的方法即可。从而把复杂的逻辑进一步细化到具体的Notifier中,而不是直接成堆的混在State Management Layer中。

状态变化能够自动刷新 UI

最后,如果使用了ValueNotifier或者ChangeNotifier,那么UI的显示也需要做对应的调整。比如使用了ValueNotifier则需要用对应的ValueListenableBuilder Widget 来接收并响应对应Value的改变。

比如想要接收FavoriteNotifer的变化刷新 UI,则需要如下实现:

代码语言:Swift复制
class FavoriteButton extends StatelessWidget { 
  const FavoriteButton({Key? key}) : super(key: key); 
  @override Widget build(BuildContext context) { 
    final playPage = getIt<PlayPageManager>(); // 1 
    return ValueListenableBuilder<bool>( // 2 
      valueListenable: playPage.favoriteNotifier, // 3 
      builder: (context, value, child) { // 4, 5, 6, 7 
        return IconButton( 
          icon: Icon( 
            (value) 
            ? Icons.favorite 
            : Icons.favorite_border, 
	      ), 
	      onPressed: playPage.onFavoritePressed, 
	    ); 
      }, 
    ); 
  } 
}

总结

Flutter中的状态管理,可以简单理解为MVVMVM的实现方式。其目的都是为了架起数据和 UI 的桥梁,不同的状态管理方式,本质上是不同的传递数据和事件的方式。

针对不复杂的项目,可以采用service locator的模式,通过GetIt把对应的State Management Layer声明为单例,然后在UI Layer中通过GetIt直接获取。这种模式把项目架构总体分为三层:

  • UI Layer:尽量不要包含逻辑,只负责渲染State的数据;
  • State Management Layer - 响应UI Layer的事件 - 读取Service Layer的数据,处理后传递给UI Layer - 调用Service Layer方法,处理数据 - 把逻辑尽量拆分细,不同模块的逻辑不要混在一起,建议抽取相同模块的逻辑,封装为Notifier,然后在State Management Layer中调用Notifier的实现。
  • Service Layer - 一个项目可能用多个不同的Service - Service定义为interface或者protocol,具体的实现,通过集成自interface的类来实现。

参考

  • GitHub - suragch/minimalist_state_management_timer_app
  • Flutter state management for minimalists | by Suragch | Medium
  • How the GetIt service locator package works in Swift
  • GetIt provides a way to test these classes

0 人点赞