美文网首页
关于flutter中的状态管理(二)(InheritedWidg

关于flutter中的状态管理(二)(InheritedWidg

作者: 晨曦中的花豹 | 来源:发表于2022-04-01 17:31 被阅读0次

我觉得ChangeNotifier的管理方法是最容易理解的了,因为他的流程是你在敲代码的时候就可以直观的看到的,哪里需要就在哪里监听,变化的时候来个setState搞定

1. ChangeNotifier

使用的时候需要继承自这个类比如叫A,我全局全局初始化了一个A实例a

class A extends ChangeNotifier {
  int count = 0;
  void add() {
    count++;
    notifyListeners();
  }
}
final a = A();

然后在需要的地方添加a的监听回调

@override
  void initState() {
    // TODO: implement initState
    super.initState();
    a.addListener(() {
      setState(() {

      });
    });
  }

addListener这个方法会将回调添加到数组中,当调用notifyListeners时,遍历数组调用回调函数,这个时候就会执行setState函数,刷新页面.
这里可以优化, setState本质是调用markNeedsBuild

@override
  void initState() {
    // TODO: implement initState
    super.initState();
    a.addListener(() {
      Element element = context as Element;
      element.markNeedsBuild();
    });
  }

是不是很好理解,这种逻辑自己写10分钟应该用不了吧.

2. InheritedWidget + ChangeNotifier

这种方式可以有两种思路:

  • 通过ChangeNotifier.listen去更新
  • 通过InheritedWidget去更新

可能大家比较晕,下面具体阐述一下,第一种是以ChangeNotifier为主, InheritedWidget用来将ChangeNotifier在Widget数中传递,上一篇已经说明了,而第二种是以InheritedWidget为主,使用他的状态管理方法,而ChangeNotifier只是用来更新InheritedWidget用的,因为只有InheritedWidget重新创建,才能调用状态变更的方法,如图调用栈,然后接上篇文章


WechatIMG33.jpeg

2.1 ChangeNotifier为主

InheritedWidget使用同样需要继承,我定义为B,实例为b

class B extends InheritedWidget {
  
  final A? a;
  
  const B({required Widget child,Key? key,this.a}) : super(child: child,key: key);

  static A of(BuildContext context) {
    final inheritedElement = context.getElementForInheritedWidgetOfExactType<B>();
    B value = inheritedElement?.widget as B;
    return value.a!;
  }

  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    return false;
  }
}

有了InheritedWidget,可以把a作为b的参数,看,这里我们updateShouldNotify返回的是false,因为我们压根没有用到InheritedWidget的状态管理,只是用它来传参罢了,真正起到关键作用的还是ChangeNotifier

void main() {
  // Isolate.spawn(doSth, "Hi");
  runApp(
    B(
      a: A(),
      child: const MyApp(),
    )
  );
}

调用
@override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
     B.of(context).addListener(() {
       Element element = context as Element;
       element.markNeedsBuild();
     });
    });
  }

通过context找到b,然后就能给a添加回调了,然后就能对页面进行刷新了
但是这里出现了问题,如果我要跟踪a,initState代码是入侵的并且局限性很大每次需要监听a的时候就要写这么一坨,有没有好的方法呢?这里我们可以优化一下of函数

static A of(BuildContext context,{bool listen = true}) {

    final inheritedElement = context.getElementForInheritedWidgetOfExactType<B>();

    B value = inheritedElement?.widget as B;
    A a = value.a!;
    if (listen) {
      Element element = context as Element;
      a.addListener(() {
        element.markNeedsBuild();
      });
    }
    return a;
  }

如果是需要监听的,我们调用addListener,这个时候initState就可以丢掉啦,但是随之出现的问题是build会重新创建Text,导致重复addListener,目前这种还无法找到突破口

2.2 InheritedWidget为主

首先是我们主要成员

class MyInheritedWidget extends InheritedWidget {

  StateChangeNotifier notifier;

  MyInheritedWidget({required this.notifier,Key? key, required Widget child })
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
}

class StateChangeNotifier extends ChangeNotifier {
  int count = 0;
  add() {
    count++;
    notifyListeners();
  }
}

然后这里巧妙的套一个StatelessWidget

class Provider extends StatelessWidget {
  Widget child;
  StateChangeNotifier notifier;
  Provider({Key? key,required this.child,required this.notifier}) : super(key: key);
  Element? _element;

  static StateChangeNotifier? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()?.notifier;
  }


  @override
  Widget build(BuildContext context) {
    if (_element == null) {
      _element = context as Element;
      notifier.addListener(() {
        _element?.markNeedsBuild();
      });
    }
    return MyInheritedWidget(notifier: notifier, child: child);
  }
}

这个StatelessWidget主要是用来更新InheritedWidget的,这里可以在build中添加监听,这样一来ChangeNotifier调用notifyListeners(), Provider 重新build, MyInheritedWidget被重新构建,所有使用过Provider.of(context)的组件就被重新build了
然后我们对Provider和InheritedWidget做一下泛型处理,这样我们就可以自定义ChangeNotifier了

class Provider<T extends ChangeNotifier> extends StatelessWidget {
  Widget child;
  T notifier;
  Provider({Key? key,required this.child,required this.notifier}) : super(key: key);
  Element? _element;

  static T? of<T>(BuildContext context) {
    MyInheritedWidget? inheritedWidget = context.dependOnInheritedWidgetOfExactType<MyInheritedWidget<T>>();
    return inheritedWidget?.notifier;
  }


  @override
  Widget build(BuildContext context) {
    if (_element == null) {
      _element = context as Element;
      notifier.addListener(() {
        _element?.markNeedsBuild();
      });
    }
    return MyInheritedWidget<T>(notifier: notifier, child: child);
  }
}


class MyInheritedWidget<T> extends InheritedWidget {

  T notifier;

  MyInheritedWidget({required this.notifier,Key? key, required Widget child })
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
    return true;
  }
}

最后一点就是我们在调用of的时候有的是不需要监听的,比如我的按钮,只是为了调用add函数,并没有监听什么数据,这个时候我们在优化一下of函数

static T? of<T>(BuildContext context,{bool listen = true}) {
    MyInheritedWidget? inheritedWidget;
    inheritedWidget = ((context.getElementForInheritedWidgetOfExactType<MyInheritedWidget<T>>())?.widget as MyInheritedWidget?);
    if (listen) {
      context.dependOnInheritedWidgetOfExactType<MyInheritedWidget<T>>();
    }

    return inheritedWidget?.notifier;
  }

这样就完美的实现了对状态的管理
provider三方库也是这样实现的状态管理,但是它的监听是放到了of函数,还有就是它修改了InheritedWidget调用流程,它屏蔽了系统的调用方式

class _InheritedProviderScope<T> extends InheritedWidget {
  const _InheritedProviderScope({
    required this.owner,
    required this.debugType,
    required Widget child,
  })  : assert(null is T),
        super(child: child);

  final InheritedProvider<T> owner;
  final String debugType;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }

  @override
  _InheritedProviderScopeElement<T> createElement() {
    return _InheritedProviderScopeElement<T>(this);
  }
}

然后自定义了InheritedElement,修改了build方法,直接在build中调用了notifyClients,感觉是很牛逼...

@override
  Widget build() {
    if (widget.owner._lazy == false) {
      value; // this will force the value to be computed.
    }
    _delegateState.build(
      isBuildFromExternalSources: _isBuildFromExternalSources,
    );
    _isBuildFromExternalSources = false;
    if (_shouldNotifyDependents) {
      _shouldNotifyDependents = false;
      notifyClients(widget);
    }
    return super.build();
  }

当然我的demo只是一些思路上的东西,具体代码的规范以及严谨性肯定是有漏洞的

相关文章

网友评论

      本文标题:关于flutter中的状态管理(二)(InheritedWidg

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