flutter的Provider数据共享

437 阅读4分钟

Provider简介

通过使用 provider 去取代手写 InheritedWidget, 你将得到:

  • s资源的简化分配/处置
  • 懒加载
  • 通过每次创建class类的方式,极大的减少了代码体积
  • devtool很方便,很容易查看
  • 一个通用的方式消费这些InheritedWidgets (通过 Provider.of/Consumer/Selector)
  • 使用复杂性呈指数增长的侦听机制提高类的可扩展性 (例如 ChangeNotifier, 用于发送通知).

2.使用方法

2.1 暴露一个值

2.2 暴露一个实例对象

Providers不仅允许你暴露一个值,而且他还允许你创建/监听/销毁它

注:使用 .value 是将已经存在的对象实例暴露出来,如果是新的模型对象,务必使用 create 。

  • DO create a new object inside create.
Provider(
  create: (_) => MyModel(),
  child: ...
)
  • DON'T use Provider.value to create your object.
ChangeNotifierProvider.value(
  value: MyModel(),
  child: ...
)
  • DON'T create your object from variables that can change over time.

    该情况下, 当值改变时你的对象将不会更新.

int count;

Provider(
  create: (_) => MyModel(count),
  child: ...
)

使用 ProxyProvider解决上面值改变的问题:

int count;

ProxyProvider0(
  update: (_, __) => MyModel(count),
  child: ...
)

注意

在使用提供者的create/update回调时,值得注意的是,这个回调默认是惰性调用的。

这意味着在至少一次请求该值之前,不会调用create/update回调。

如果您想使用以下参数预先计算一些逻辑,则可以禁用此lazy行为:

MyProvider(
  create: (_) => Something(),
  lazy: false,
)

2.3 复用现有对象实例:

如果您已经有一个对象实例并且想要公开它,最好使用.value提供者的构造函数。

不这样做可能会在您dispose仍在使用时调用您的对象方法。

MyChangeNotifier variable;

ChangeNotifierProvider.value(
  value: variable,
  child: ...
)
MyChangeNotifier variable;

ChangeNotifierProvider(
  create: (_) => variable,
  child: ...
)

2.4 读取值

读取值的最简单方法是使用 [BuildContext] 上的扩展方法:

  • context.watch<T>(),widget监听T的变化
  • context.read<T>(),返回T的值,不监听
  • context.select<T, R>(R cb(T value)),它允许一个widget只监听T的部分变化.
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      // Don't forget to pass the type of the object you want to obtain to `watch`!
      context.watch<String>(),
    );
  }
}

也可以使用静态方法Provider.of<T>(context),其行为类似于watch. 当listen参数设置为false(如Provider.of<T>(context, listen: false))时,其行为类似于read

值得注意的是,context.read<T>()当值更改并且无法在StatelessWidget.build/State.build内部调用时,不会重建widget。另一方面,它可以在这些方法之外自由调用。

context.watch<Model>()  // 没找到会抛错
context.watch<Model?>()  // 传出空值

多层Provider嵌套

Provider<Something>(
  create: (_) => Something(),
  child: Provider<SomethingElse>(
    create: (_) => SomethingElse(),
    child: Provider<AnotherThing>(
      create: (_) => AnotherThing(),
      child: someWidget,
    ),
  ),
),

简洁方式

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
    Provider<AnotherThing>(create: (_) => AnotherThing()),
  ],
  child: someWidget,
)

proxy Provider的使用

根据Provider(Counter)的count值,生成新的Provider(Translations)

Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => Counter()),
      ProxyProvider<Counter, Translations>(
        update: (_, counter, __) => Translations(counter.value),
      ),
    ],
    child: Foo(),
  );
}

class Translations {
  const Translations(this._value);

  final int _value;

  String get title => 'You clicked $_value times';
}

使用Consumer方式

class ProviderDemo extends StatelessWidget {
  const ProviderDemo({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Consumer<Person>( /// 在程序任何地方都可以拿到person对象,读取数据
            builder: (_,person,child){
              return Text(person.name);
            }
        ),
      ),
    );
  }
}

ChangeNotifierProvider

监听模型对象的变化,而且当数据改变时,它也会重建Consumer,更新UI。

  • 继承、混入ChangeNotifier
  • 调用了notifyListeners()

它不会重复实例化模型,除非在个别场景下。如果该实例已经不会再被调用,ChangeNotifierProvider 也会自动调用模型的dispose()方法。

  • ChangeNotifier

它类似于一个Observable,继承自Listenable,内部维护了一个 ObserverList _listeners,提供添加和删除listener的方法,有一个notify方法,用来通知所有的观察者(在Provider或者称为消费者Consumer)。

示例:

  • Model
class Person with ChangeNotifier{
  String name = "ChangeNotifierProvider";
  /// 提供一个改变name的方法
  void changName({required String newName}){
    name = newName;
    notifyListeners();
  }
}
  • 程序入口设置
return ChangeNotifierProvider<Person>(
  create: (ctx)=> Person(),
  child: const MaterialApp(
    home: ChangeNotifierProviderDemo(),
  ),
);
  • View
class ChangeNotifierProviderDemo extends StatelessWidget {
  const ChangeNotifierProviderDemo({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("ChangeNotifierProvider"),),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            /// 拿到person对象,读取数据
            Consumer<Person>(builder: (ctx,person,child) =>  Text(person.name),),
            /// 拿到person对象,调用方法
            Consumer<Person>(builder: (ctx,person,child){
              return ElevatedButton( /// 点击按钮,调用方法更新
                onPressed: () => person.changName(newName: "ChangeNotifierProvider更新了"),
                child: const Text("点击更新"),
              );
            },),
          ],
        ),
      ),
    );
  }
}
  • 结果 显示数据;点击按钮数据成功修改。

更多操作