likes
comments
collection
share

Flutter:从ValueListenableBuilder进化到MultiValueListenableBuilder

作者站长头像
站长
· 阅读数 44

前言

看本文的前提是你对ValueListenableBuilder与ValueNotifier有基础的了解和使用,如果不了解请移步下面的往期文章,特别是第一篇。

往期文章:

之前在写的文章中,我运用过ValueListenableBuilder来减少使用setState的使用,通过改变定义的ValueNotifier中的值,进行局部刷新。

使用ValueListenableBuilder的使用时,我特别强调一般情况下,ValueListenableBuilder一对一的绑定关系是比较好控制与书写代码的,如果涉及到多对一的绑定,ValueListenableBuilder会陷入嵌套地狱。

并且建议使用Provider中的Selector与Consumer组件进行多变量与一个组件的绑定关系的构建。

如果你仔细看Selector与Consumer组件的源码,会发现它有从1个到6个变量与一个组件绑定的类,我尝试阅读其代码并模仿,将ValueListenableBuilder向MultiValueListenableBuilder的扩展与延伸。

文章大部分内容是堆砌代码和例子哈。

原始思路

先上一段代码:

typedef MultiValueWidgetBuilder<T1, T2> = Widget Function(
    BuildContext context, T1 value1, T2 value2, Widget child1, Widget child2);

class MultiValueListenable2<T1, T2> extends StatelessWidget {
  const MultiValueListenable({
    Key key,
    @required this.value1Listenable,
    @required this.value2Listenable,
    @required this.builder,
    this.child,
  })  : assert(value1Listenable != null),
        assert(value2Listenable != null),
        assert(builder != null),
        super(key: key);

  final MultiValueWidgetBuilder<T1, T2> builder;
  final Widget child;
  final ValueNotifier<T1> value1Listenable;
  final ValueNotifier<T2> value2Listenable;

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<T1>(
      builder: (context1, T1 value1, widget1) {
        return ValueListenableBuilder<T2>(
          builder: (context2, T2 value2, widget2) {
            return builder(context2, value1, value2, widget1, widget2);
          },
          valueListenable: value2Listenable,
        );
      },
      valueListenable: value1Listenable,
    );
  }
}

这段代码无非就是在初始化的时候初始化两个ValueNotifier,并在 Widget build(BuildContext context)方法中嵌套两层ValueListenableBuilder达到效果。

虽然原始,不过能实现需求。

优化版本

/// 拓展思路
class MultiValueListenableBuilder2<T1, T2> extends ValueListenableBuilder<T1> {
  MultiValueListenableBuilder2({
    Key key,
    @required this.value1Listenable,
    @required this.value2Listenable,
    @required this.twoValueBuilder,
    Widget child,
  })  : assert(value1Listenable != null),
        assert(value2Listenable != null),
        assert(twoValueBuilder != null),
        super(
          key: key,
          valueListenable: value1Listenable,
          builder: (context, T1 value1, _) {
            return ValueListenableBuilder(
              valueListenable: value2Listenable,
              builder: (context2, T2 value2, child) {
                return twoValueBuilder(context2, value1, value2, child);
              },
            );
          },
        );

  final ValueNotifier<T1> value1Listenable;

  final ValueNotifier<T2> value2Listenable;

  final Widget Function(
      BuildContext context, T1 value1, T2 value2, Widget child) twoValueBuilder;
}

我将MultiValueListenableBuilder2的继承于StatelessWidget改为继承于ValueListenableBuilder,这样一来我就可以在其初始化方法里面搞事情了,哈哈。

这一点和Selector组件中Selector2继承于Selector0如出一辙。那么后面的MultiValueListenableBuilder3到MultiValueListenableBuilder6的组件构建思路也就和Selector3到Selector6的构建思路一致了。

继承、初始化方法中进行不断包裹,甚至可以理解为某种递归。

class MultiValueListenableBuilder3<T1, T2, T3>
    extends MultiValueListenableBuilder2<T1, T2> {
  MultiValueListenableBuilder3({
    Key key,
    @required this.value1Listenable,
    @required this.value2Listenable,
    @required this.value3Listenable,
    @required this.threeValueBuilder,
    Widget child,
  })  : assert(value1Listenable != null),
        assert(value2Listenable != null),
        assert(value3Listenable != null),
        assert(threeValueBuilder != null),
        super(
          key: key,
          value1Listenable: value1Listenable,
          value2Listenable: value2Listenable,
          twoValueBuilder: (context, T1 value1, T2 value2, _) {
            return ValueListenableBuilder(
              valueListenable: value3Listenable,
              builder: (context3, T3 value3, child) {
                return threeValueBuilder(
                    context3, value1, value2, value3, child);
              },
            );
          },
        );

  final ValueNotifier<T1> value1Listenable;

  final ValueNotifier<T2> value2Listenable;

  final ValueNotifier<T3> value3Listenable;

  final Widget Function(
          BuildContext context, T1 value1, T2 value2, T3 value3, Widget child)
      threeValueBuilder;
}

class MultiValueListenableBuilder4<T1, T2, T3, T4>
    extends MultiValueListenableBuilder3<T1, T2, T3> {
  MultiValueListenableBuilder4({
    Key key,
    @required this.value1Listenable,
    @required this.value2Listenable,
    @required this.value3Listenable,
    @required this.value4Listenable,
    @required this.fourValueBuilder,
    Widget child,
  })  : assert(value1Listenable != null),
        assert(value2Listenable != null),
        assert(value3Listenable != null),
        assert(value4Listenable != null),
        assert(fourValueBuilder != null),
        super(
          key: key,
          value1Listenable: value1Listenable,
          value2Listenable: value2Listenable,
          value3Listenable: value3Listenable,
          threeValueBuilder: (context, T1 value1, T2 value2, T3 value3, _) {
            return ValueListenableBuilder(
              valueListenable: value4Listenable,
              builder: (context4, T4 value4, child) {
                return fourValueBuilder(
                    context4, value1, value2, value3, value4, child);
              },
            );
          },
        );

  final ValueNotifier<T1> value1Listenable;

  final ValueNotifier<T2> value2Listenable;

  final ValueNotifier<T3> value3Listenable;

  final ValueNotifier<T4> value4Listenable;

  final Widget Function(BuildContext context, T1 value1, T2 value2, T3 value3,
      T4 value4, Widget child) fourValueBuilder;
}

class MultiValueListenableBuilder5<T1, T2, T3, T4, T5>
    extends MultiValueListenableBuilder4<T1, T2, T3, T4> {
  MultiValueListenableBuilder5({
    Key key,
    @required this.value1Listenable,
    @required this.value2Listenable,
    @required this.value3Listenable,
    @required this.value4Listenable,
    @required this.value5Listenable,
    @required this.fiveValueBuilder,
    Widget child,
  })  : assert(value1Listenable != null),
        assert(value2Listenable != null),
        assert(value3Listenable != null),
        assert(value4Listenable != null),
        assert(value5Listenable != null),
        assert(fiveValueBuilder != null),
        super(
          key: key,
          value1Listenable: value1Listenable,
          value2Listenable: value2Listenable,
          value3Listenable: value3Listenable,
          value4Listenable: value4Listenable,
          fourValueBuilder:
              (context, T1 value1, T2 value2, T3 value3, T4 value4, _) {
            return ValueListenableBuilder(
              valueListenable: value5Listenable,
              builder: (context5, T5 value5, child) {
                return fiveValueBuilder(
                    context5, value1, value2, value3, value4, value5, child);
              },
            );
          },
        );

  final ValueNotifier<T1> value1Listenable;

  final ValueNotifier<T2> value2Listenable;

  final ValueNotifier<T3> value3Listenable;

  final ValueNotifier<T4> value4Listenable;

  final ValueNotifier<T5> value5Listenable;

  final Widget Function(BuildContext context, T1 value1, T2 value2, T3 value3,
      T4 value4, T5 value5, Widget child) fiveValueBuilder;
}

class MultiValueListenableBuilder6<T1, T2, T3, T4, T5, T6>
    extends MultiValueListenableBuilder5<T1, T2, T3, T4, T5> {
  MultiValueListenableBuilder6({
    Key key,
    @required this.value1Listenable,
    @required this.value2Listenable,
    @required this.value3Listenable,
    @required this.value4Listenable,
    @required this.value5Listenable,
    @required this.value6Listenable,
    @required this.sixValueBuilder,
    Widget child,
  })  : assert(value1Listenable != null),
        assert(value2Listenable != null),
        assert(value3Listenable != null),
        assert(value4Listenable != null),
        assert(value5Listenable != null),
        assert(value6Listenable != null),
        assert(sixValueBuilder != null),
        super(
          key: key,
          value1Listenable: value1Listenable,
          value2Listenable: value2Listenable,
          value3Listenable: value3Listenable,
          value4Listenable: value4Listenable,
          value5Listenable: value5Listenable,
          fiveValueBuilder: (context, T1 value1, T2 value2, T3 value3,
              T4 value4, T5 value5, _) {
            return ValueListenableBuilder(
              valueListenable: value6Listenable,
              builder: (context6, T6 value6, child) {
                return sixValueBuilder(context6, value1, value2, value3, value4,
                    value5, value6, child);
              },
            );
          },
        );

  final ValueNotifier<T1> value1Listenable;

  final ValueNotifier<T2> value2Listenable;

  final ValueNotifier<T3> value3Listenable;

  final ValueNotifier<T4> value4Listenable;

  final ValueNotifier<T5> value5Listenable;

  final ValueNotifier<T6> value6Listenable;

  final Widget Function(BuildContext context, T1 value1, T2 value2, T3 value3,
      T4 value4, T5 value5, T6 value6, Widget child) sixValueBuilder;
}

和Selector组件一样,扩展到6个变量绑定一个组件我也就不写,其实可以这样不断的扩展下去,我甚至考虑这里为什么不使用迭代器来构建一个无限变量绑定一个组件的终极方案,然后折腾数组,序列,然后发现要是可以这样Selector早就这么干了,是Dart语言无法显示所以才最终是机械性的进行迭代吧。

来个例子

六个输入框,第1个输入框输入1个字符串,第2个输入框输入2个字符串......第6个输入框输入6个字符串,底部的按钮才会高亮。

相当于6个变量绑定在1一个按钮上,我们用封装好的MultiValueListenableBuilder6来试试吧。

先放一张效果图:

Flutter:从ValueListenableBuilder进化到MultiValueListenableBuilder

怎么样效果还不错吧,只是如果你看图层的话,会发现那真是叫一个套娃:

Flutter:从ValueListenableBuilder进化到MultiValueListenableBuilder

这样就做到了6个变量绑定一个1组件了,可以肯定的是Selector与Consumer同样可以做到这件事情。 既然有已经可以用的轮子为何还要自己写呢?

有的时候,往往看起来强大又简单的组件其实就是很简单的组件堆砌积累而成,授人以鱼不如授人以渔,只有自己深入理解,才不至于完完全全成为API工程师。

由于Demo的例子逻辑并不是很复杂,这里就不再过多展示了,放上关键代码,避免重复造车:

MultiValueListenableBuilder6<bool, bool, bool, bool, bool, bool>(
    value1Listenable: value1Notifier,
    value2Listenable: value2Notifier,
    value3Listenable: value3Notifier,
    value4Listenable: value4Notifier,
    value5Listenable: value5Notifier,
    value6Listenable: value6Notifier,
    sixValueBuilder: (context, value1, value2, value3, value4,
        value5, value6, child) {
      bool isAllRight =
          value1 && value2 && value3 && value4 && value5 && value6;
      return Container(
        margin: const EdgeInsets.fromLTRB(0, 60, 0, 0),
        child: RaisedButton(
          color: _buttonColor(isAllRight),
          shape: BeveledRectangleBorder(
            borderRadius: BorderRadius.circular(0),
          ),
          child: Container(
            child: Center(
              child: Text(
                "这是一个MultiValueListenableBuilder6构造的确认修改",
                style: TextStyle(
                  fontSize: 16,
                  color: isAllRight
                      ? Colors.white
                      : DSColor.colorA3A4A4,
                ),
              ),
            ),
            height: 48,
          ),
          onPressed: () {
            if (isAllRight) {}
          },
        ),
      );
    },
 ),
转载自:https://juejin.cn/post/6953828576323960863
评论
请登录