provider

可以进行依赖注入和状态管理,使用widget创建,适用于widget。
它是故意设计成使用widget来进行依赖注入和状态管理的,而不是纯使用dart类,像是stream这些。因为widget简单且健壮可伸缩。
使用weidget来进行状态管理可以保证。

  1. 维护性。
  2. 可测试和兼容性。
  3. 健壮性。

    使用

    暴露一个值

    暴露一个对象实例(object instance)

    除了暴露一个值的可访问性,provider还包括这个值的创建,监听,销毁。
    为了暴露一个新创建的对象,可以使用provider的默认构造函数。不要使用.value命名构造函数类创建一个值对象,不然可能会造成其他不期望影响。
  • 可做create中创建一个对象。

Provider(
create:(_)=>new MyModel(),
child:…
)

  • 不可做使用Provider.value命名构造函数创建一个对象。

ChangeNotifierProvider.value(
value:new MyModel(),
child:…
)
如果创建了一个对象,它使用了为了可能变更的变量做参数,那么考虑使用ProxyProvider:
int count;

ProxyProvider0(
update:L(,_)=> new MyModel(count),
child:…
)

复用一个已经存在的对象实例

如果有个对象实例,你想把其暴露在其他地方使用,那么你应该使用Provider的.value命名构造函数。
不这么做可能会造成在该对象还在使用时被dispose。

  • 可做使用ChangeNotifierProvider.value来提供一个已经存在的ChangeNotifier。

MyChangeNotifier varibale;

ChangeNotifierProvider.value(
value:variable,
child:…
)

  • 不可做在默认构造函数中重用CahangeNotifier。

MyChangeNotifier variable;

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

读取一个值(获取暴露的值)

获取一个值最简单的方法是使用静态方法Provider.of(BuildContext context)。
这个方法会从当前的context在widget树中向根widget方向查找符合类型T的最近的值。(如果没有找到就throw)。
除了Provider.of方法我们也可以使用Consumer和Selector两个widget。这对于高效的组织代码以及难以获取BuildContext的情况比较有帮助。

多个Provider的情况(嵌套)/MultiProvider

当在一个较大的应用中注入较多的数据时,Provider会飞快地嵌套多层。
Provider(
create:()=>Something(),
child: Provider(
create:(
)=>SomethingElse(),
child:Provider(
create:()=>AnotherTing(),
child:someWidget,
)
)
)
可以这样写
MultiProvider(
Providers:[
Provider(create:(
)=>Something()),
Provider(create:()=>SomethingElse()),
Provider(create:(
)=>AnotherTthing()),
],
child:someWidget
)
上面代码的结果是严格的一样的。MultiProvider仅仅是改变了代码的形式。

ProxyProvider

从版本3.0.0开始增加了一个新的Provider:ProxyProvider。
ProxyProvider本身是一个Provider,它把其他多个provider的数据结合成一个新的对象,并且把这个结果发送一个一个Provider。
被结合的的这些provider中的任何一个数据更新了,这个新的对象都会更新。
下面这个例子使用了ProxyProvider,他把其他provider中的counter做了个中转。
Widget build(BuildContext context){
return MultiProvider(
providers:[
ChangeNotifierProvider(create:()=>Counter()),
ProxyProvider(
create:(
,counter,__)=>Translations(clunter.value),
),
child:Foo()
]
)
}

class Translations{
const Translations(this._value);

final init _value;

String get title=>’You clicked: $_value times’;
}
ProxyProvider有很多种变体,例如:

  • ProxyProvidervsProxyProvider2vsProxyProvider3…

类名后的数字是指ProxyProvider依赖其他Provider的数量。

  • ProxyProvidervsChangeNotifierProxyProvidervs ListenableProxyProvider,…他们的工作方式是类似的,相对于发送结果给一个Provider,一个ChangeNotifierProxyProvider会发送给一个ChangeNotifierProxyProvider。

    问答

    在InitState中获取Provider发生了错误,怎么办?

    这个错误是因为你想监听一个在其生命周期中不会被再次调用的provider。
    这说明你不会再使用其他的生命周期(didChangeDependencies/build),或者你不在乎数据更新。不要这么做
    initState(){
    super.initState();
    print(Provider.of(context).value);
    }
    你可以这么做
    Value value;

didChangeDependencies(){
super.didChangeDependencies();
final value = Provider.of(context).value;
if(value != this.value){
this.value = value;
print(value);
}
}
每当值发生了变化,都会被打印。也可以这么做
initState(){
super.initState();
print(Provider.of(context,listen:false).value);
}
这样只会打印value一次,不再更新。

我使用了ChangeNotifier,当我更新数据时发生了错误,发生了什么?

这个经常发生在widget树正在构建时,你对ChangeNotifier进行了更改操作。
一个典型的情景是,发起了一个http请求,然后该future被保存在了notifer中。
initState(){
super.initState();
Provider.of(context).fetchSomething();
}
这样是禁止的,因为更改必须是立即的。
这意味着有些widget可能在变动之前build,然而其他的在变动之后build。这可能会造成你的ui发生冲突,所以是禁止的。
相比,你可以在整个widget树都同步之后(渲染前/选然后?)进行变动。

  • 直接在你的模型之内进行创建:

class Mymodel width ChangeNotifier{
MyModel(){
_fetchSomething();
}

Future<void> _fetchSomething()async {}
}
这个适用于没有额外参数的情况。

  • 一部发生在最后一帧:

initState(){
super.initState();
Future.microtash(()=>{
Provider.of(context).fetchSomething(someValue);
})
}
这个多少是不太理想的,但是允许传入参数进行变更。

对于复杂的状态我是否必须使用ChangeNotifier?

不是。
你可以使用任何对象来呈现状态。例如其他可用的方式是Provider.value()结合一个StatefulWidget使用。
这里有个计数的例子,使用了这个方法:
class Example extends StatefulWidget{
const Example({Key key, this.child}):super(key key);

final Widget child;

@override
ExampleState createState()=> ExampleState();
}

class ExampleState extends State<Example>{
int _count;

void increment(){
setState((){
_count++;
})
}

@override
Widget build(BuildContext context){
return Provider.value(
value:_count,
child:Provider.value(
value:this,
child:widget.child
)
)
}
}
可以如此读取数据:
return Text(Provider.of(context).toString());
如此更改数据:
return FloatingActionButton(
onPress:Provider.of(context).increment,
child:Icon(Icons.plus_one),
);
此外,你也可以创建自己的provider。

我制作一个自己的Provider吗?

当然,provider暴露了所有的小的组件,这些制作了一个简陋的provider。
包括:

名字 说明
Provider provider的最基本形式,可以添加和暴露任何形式的值
ListenableProvider 使用Listenable对象的特殊provider,ListenableProvider会监听对象,并且在监听器在任何时候调用时要求widget重构。
ChangeNotifierProvider ChangeNotifier规格的ListenableProvider,他会在必要时自动调用ChangeNotifier.dispose.
ValueListenableProvider 监听一个ValueListenable并且只暴露ValueListenable.value。
StreamProvider 监听Stream并且暴露最新的emitted的值。
FutureProvider 添加一个Future,并且在future完成时更新附从。

原文
https://segmentfault.com/a/1190000021953500