Flutter技术文章

Provider

2022-07-02  本文已影响0人  woniu

Provider的三个消费者:
Provider第三方是基于InheritedWidget封装的:InheritedWidget
Provider.of、Consumer(会刷新不必要刷新的组件)r、Selector(更精细化)

1、Provider.of

InheritedWidget有个默认的约定:如果状态是希望暴露出的,应当提供一个 “of” 静态方法来获取其对象,开发者便可直接通过该方法来获取。

static T of<T>(BuildContext context, {bool listen = true})

其中 listen:默认true监听状态变化,false为不监听状态改变。

Provider.of<T>(context)Provider 为我们提供的静态方法,当我们使用该方法去获取值的时候会返回查找到的最近的 T 类型的 provider 给我们,且也不会遍历整个组件树。

2、Consumer

Provider 中使用比较频繁的消费者,查看源码:

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

...

@override
Widget buildWithChild(BuildContext context, Widget? child) {
  return builder(
    context,
    Provider.of<T>(context),
    child,
  );
}

发现它就是通过 Provider.of<T>(context) 来实现的。而且实际开发中使用 Provider.of<T>(context)Consumer 简单好用太多,那 Consumer有什么优势吗?

对比一下,我们发现 Consumer 有个 Widget? child,它非常重要,能够在复杂项目中,极大地缩小你的控件刷新范围。

我们通过一个简单的计数器示例来说明:

return Scaffold(
  body: Consumer(
    builder: (BuildContext context,CounterModel counterModel,Widget? child){
      return Column(
        children: [
          Text("${counterModel.count}"),
          ElevatedButton(
            onPressed: ()=> counterModel.increment(),
            child: const Text("点击加1"),
          ),
          Text("其他更多组件"),
          Text("其他更多组件"),
          Text("其他更多组件"),
          Text("其他更多组件"),
          Text("其他更多组件"),
        ],
      );
    },
  ),
);

在上述示例中,我们后面很多的 Text 组件没有用到模型数据且不需要更新状态的,但是因为被Consumer 包裹,导致每次数据改变都会重新构建!严重影响性能且不优雅!

解决以上问题,一方面我们可以尽可能调整 Consumer 的位置,在需要使用数据的组件包裹 Consumer ,但这也会存在一个问题,单独用大量 Consumer 包裹,也跟 Provider 诞生的理念背道而驰。这时候我们就可以使用到 Widget? child 了。我们针对上述示例优化一下:

return Scaffold(
  body: Consumer(
    builder: (BuildContext context,CounterModel counterModel,Widget? child){
      return Column(
        children: [
          Text("${counterModel.count}"),
          ElevatedButton(
            onPressed: ()=> counterModel.increment(),
            child: const Text("点击加1"),
          ),
          child!
        ],
      );
    },
    child: Column(
      children: [
        Text("其他更多组件"),
        Text("其他更多组件"),
        Text("其他更多组件"),
        Text("其他更多组件"),
        Text("其他更多组件"),
      ],
    ),
  ),
);

我们使用 Widget? child 将不需要更新状态的组件包裹起来,大大提升了性能。

3、Selector

Selector 也是一个消费者。与 Consumer 类似,只是对build调用Widget方法时提供更精细的控制。 Consumer 是监听一个 Provider 中所有数据的变化,Selector 则是监听某一个/多个值的变化。

比如用户模型 Person:有姓名、性别、年龄、身高、体重等信息,但是我们可能只会更新下年龄,其他的信息我们不希望重建,就可以使用 Selector 实现这个功能。

示例:

class Person with ChangeNotifier {
  String name = "小虎牙";
  int age = 18;
  double height = 180.0;

  // 年龄改变
  void increaseAge() {
    age ++;
    notifyListeners();
  }
}

return ChangeNotifierProvider(
  create: (ctx) => Person(),
  child: const MaterialApp(
    home: SelectorDemo(),
  ),
);

class SelectorDemo extends StatelessWidget {
  const SelectorDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Selector"),),
      body: Center(
        child: Selector<Person,int>(
          selector: (ctx,person) => person.age,
          builder: (ctx,age,child) {
            return Column(
              children: [
                Text("年龄为:$age"),
                child!
              ],
            );
          },
          child: Padding(
            padding: const EdgeInsets.only(top: 50),
            child: ElevatedButton(
              onPressed: () => Provider.of<Person>(context,listen: false).increaseAge(),
              child: const Text("点击改变年龄"),
            ),
          ),
        )
      )
    );
  }
}

显示 年龄为18,点击后年龄加1。

这里也使用到了 Widget? child,同 Consumer 一样极大地缩小控件刷新范围。

作者:RichTime
链接:https://juejin.cn/post/7067356022272163847
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇下一篇

猜你喜欢

热点阅读