Flutter 撸一个简单的状态管理

329 阅读4分钟

Flutter 撸一个简单的状态管理

序言

好久没写Flutter了,转眼都3.7了,这段时间捡起来发现竟然没忘记语法(也许是前一段时间写Compose的原因,它俩可太像了)

看过 GetX 源码的都应该知道,GetX中的状态管理的核心其实就是对StatefulWidget的最小封装;也就是说 GetX 就是 setState((){})

ObxWidget.png

本文参照它的 最小StatefulWidget 来撸一个简单的状态管理

本文不会深剖 GetX 的源码;

文章内容不复杂,也不难理解,代码也没考虑到多层次关系,更多的还是偏小白向,学的还是思想嘛[dog]。

那么!开撸!

封装最小可变的 StatefulWidget

老生常谈的东西了,与它同辈的还有一个StatelessWidget无状态的,这两个也是Flutter中对于Widget最基本的实现。

还是计数器的例子,官方 New Flutter Project 生成的模板代码就般了(不然就有水文的嫌疑了)。

按照Flutter中一般的状态管理框架,一定会有一个Controller作为视图控制器,将视图管理与数据进行关联(类似于MVP的设计思想),接口代码:

/// state_interfaces.dart/// 顶层接口
/// Controller
abstract class IStateController {
  // setState()
  void refresh();
}
​
/// View
abstract class IStateView {
  // setState()
  void refresh();
}

这里为 IStateController 写一个抽象类StateController

因为dart中没有 interfac 关键字的实现,接口的定义也采用abstract,区分接口和抽象类的区别在于implementsextends两个关键字。

/// state_controller.dartimport 'state_interfaces.dart';
​
abstract class StateController implements IStateController {
  @override
  void refresh() {
    //刷新视图的逻辑, 下文补齐
  }
}

然后就是IStateView的实现类StateView,需要注意的是StateView作为可变组件的最小主体,它还应该继承至StatefulWidget

又由于StateView作为最小的可变组件,因此这里将它定义为一个容器,这个容器内的所有Widget就是可变的,可以仔细品味下面的代码:

/// state_view.dartimport 'package:flutter/material.dart';
import 'package:test_state_view/state/state_controller.dart';
import 'state_interfaces.dart';
​
typedef StateViewBuilder<C extends StateController> = Widget Function(BuildContext context, C controller);
​
class StateView<C extends StateController> extends StatefulWidget {
  const StateView({
    Key? key,
    required this.controller,
    required this.builder,
  }) : super(key: key);
​
  final C controller;
  final StateViewBuilder<C> builder;
​
  @override
  State<StateView> createState() => _StateViewState<C>();
}
​
class _StateViewState<C extends StateController> extends State<StateView<C>> implements IStateView {
  @override
  Widget build(BuildContext context) {
    return widget.builder(
      context,
      widget.controller,
    );
  }
​
  @override
  void refresh() => setState(() {});
}

如此一来,Widget(这里命名为View) 便持有了 Controller 了;

接来下就是对Controller的设计,让他能够顺利的操作到State,达到setState((){})的效果;

这里需要做一个 多Widget 的考虑,可不能将 Controller 与某一个State 做死绑定;

为什么这么说呢?因为一个应用中是存在多个路由页面的,当需要在第二个页面中刷新第一个页面中的内容,如文章开头的图示,一旦ControllerState绑死,那第二个路由页对于第一个路由页Controller的获取就要复杂很多了,外加上Flutter中不允许反射,甚至可能无法获取。

因此,ControllerState 之间的关系为:一对多关系,这里就需要第三方介入来存储它们之间的关系了;

我们定义一个名为 StateManager 的管理类,让它来持有 ControllerState

/// state_manager.dart
​
import 'state_interfaces.dart';
​
class StateManager {
  const StateManager._();
​
  static final Map<IStateController, List<IStateView>> _states = {};
​
  static void putState(IStateController controller, IStateView state) {
    //如果该Controller未被记录
    if (!_states.containsKey(controller)) {
      _states[controller] = [state];
      return;
    }
​
    //如果该State不存在
    if (!_states[controller]!.contains(state)) {
      _states[controller]!.add(state);
    }
  }
​
  static void removeState(IStateController controller, IStateView state) {
    //如果该Controller未被记录
    if (!_states.containsKey(controller)) return;
​
    //如果该State不存在
    if (_states[controller] == null || !_states[controller]!.contains(state)) return;
​
    _states[controller]!.remove(state);
  }
​
  static void releaseState(IStateController controller) {
    //如果该Controller未被记录
    if (!_states.containsKey(controller)) return;
​
    _states[controller]?.clear();
    _states.remove(controller);
  }
​
  static void clear() {
    _states.clear();
  }
​
  static List<IStateView> getStates(IStateController controller) {
    return _states[controller] ?? [];
  }
}
​

如此一来,就只需要将 ContollerState 交由它管理,就可以实现一对多的效果;

我们修改一下 _StateViewState 的代码,分别增加 initState()dispose() 方法,将 ControllerState 关联起来。

class _StateViewState<C extends StateController> extends State<StateView<C>> implements IStateView {
  @override
  void initState() {
    super.initState();
    StateManager.putState(widget.controller, this);
  }
​
  @override
  Widget build(BuildContext context) {
    return widget.builder(
      context,
      widget.controller,
    );
  }
​
  @override
  void dispose() {
    super.dispose();
    StateManager.removeState(widget.controller, this);
  }
​
  @override
  void refresh() => setState(() {});
}

然后,就是 Controller 中的 refresh() 方法了,我们来补齐上文的刷新功能

/// state_controller.dartimport 'state_interfaces.dart';
import 'state_manager.dart';
​
abstract class StateController implements IStateController {
  @override
  void refresh() {
    //刷新视图的逻辑
    StateManager.getStates(this).forEach((state) {
      state.refresh();
    });
  }
}

至此,最小 StatefulWidget 的封装结束。

这里的逻辑有些粗糙,只允许对应的Controller有一个实例对象,否则这一套封装将没有意义了;

当然了,出于程序方面考虑,应用中某个的Controller也应该是单例的。

测试计数器

Running.gif

逻辑不复杂,代码很简单,测试Demo以及实现代码GitHub待会放评论区。