Provider使用

前言Provider有两个重要的状态

提供者:提供数据
消费者:消费数据

一、提供者

Provider为我们提供了非常多的提供者,总共有八种。但我们比较常用的是ChangeNotifierProvider、MultiProvider、ChangeNotifierProxyProvider,关于其他的提供者可根据自己的实际应用场景来。

1、Provider组件

1、创建模型
class UserModel {
  String name = "Jimi";
  void changeName() {
    name = "hello";
  }
}

2、应用程序入口设置

ProviderExample内部处理数据

return Provider<UserModel>(
  create: (_) => UserModel(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ProviderExample(),
  ),
);

3、使用共享数据

3.1 第一个Comsumer是用于读取模型的数据name
3.2 第二个Consumer用于改变模型的数据name

class ProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel>(
              builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel>(
              builder: (_, userModel, child) {
                return Padding(
                  padding: EdgeInsets.all(20),
                  child: ElevatedButton(
                    onPressed: (){
                      userModel.changeName();
                    },
                    child: Text("改变值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

2、ChangeNotifierProvider

重点:ChangeNotifierProvider跟Provider组件不同,ChangeNotifierProvider会监听模型对象的变化,而且当数据改变时,它也会重建Consumer(消费者)。

1、创建模型

这里定义模型主要有两处变化:
1.1 with混入了ChangeNotifier,
1.2 调用了notifyListeners(),model内部改变之后,调用监听

import 'package:flutter/material.dart';
class UserModel1 with ChangeNotifier {
  String name = "Jimi";
  void changeName() {
    name = "hello";
    notifyListeners();
  }
}
2、应用程序入口设置,使用ChangeNotifierProvider
return ChangeNotifierProvider<UserModel1>(
  create: (_) => UserModel1(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ChangeNotifierProviderExample(),
  ),
);
3、共享数据
class ChangeNotifierProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ChangeNotifierProvider"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel1>(
              builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel1>(
              builder: (_, userModel, child) {
                return Padding(
                  padding: EdgeInsets.all(20),
                  child: ElevatedButton(
                    onPressed: (){
                      userModel.changeName();
                    },
                    child: Text("改变值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

3、MultiProvider

前面的都只是返回了一个提供者,在实际开发过程中肯定会有多个提供者,我们虽然可以采用嵌套的方式来解决,但是这样无疑是混乱的,可读性级差。这个时候强大的MultiProvder就产生了,

1、创建两个模型
import 'package:flutter/material.dart';

class UserModel1 with ChangeNotifier {

  String name = "Jimi";

  void changeName() {
    name = "hello";
    notifyListeners();
  }
}

class UserModel4 with ChangeNotifier {

  String name = "Jimi";
  int age = 18;

  void changeName() {
    name = "hello";
    age = 20;
    notifyListeners();
  }
}
2、应用程序入口设置

2.1 使用笨方法,嵌套的方式,数据多的情况下会无限套娃

return ChangeNotifierProvider<UserModel1>( //第一次嵌套
  create: (_) => UserModel1(),
  child: ChangeNotifierProvider<UserModel4>( //第二次嵌套
    create: (_) => UserModel4(),
    child: MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MultiProviderExample(),
    ),
  ),
);

2.2 使用MultiProvider
返回MultiProvider,然后在providers属性中创建。

return MultiProvider(
  providers: [
    ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1()),
    ChangeNotifierProvider<UserModel4>(create: (_) => UserModel4()),
    /// 添加更多
  ],
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: MultiProviderExample(),
  ),
);
3 使用共享数据

通过Consumer去监听数据的改变,并渲染。

class MultiProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MultiProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel1>(
              builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel4>(
              builder: (_, userModel, child) {
                return Text(userModel.age.toString(),
                    style: TextStyle(
                        color: Colors.green,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer2<UserModel1, UserModel4>(
              builder: (_, userModel1, userModel4, child) {
                return Padding(
                  padding: EdgeInsets.all(20),
                  child: ElevatedButton(
                    onPressed: (){
                      userModel1.changeName();
                      userModel4.changeName();
                    },
                    child: Text("改变值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

二、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 实现这个功能。

示例:

  • Model
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(),
  ),
);

  • View
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 一样极大地缩小控件刷新范围。

参考链接:
jimi的provider详细讲解
链接:https://juejin.cn/post/7067356022272163847

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容