接着上一篇的 ScopedModel 来讲解 Provide。
有关Provide的题外话,Provide 是 ScopedModel 的进阶或者说是兄弟,为何这么说呢?因为这两个插件的内容重叠的太多,所以对于这两个插件存在争议。
有兴趣想要了解更多的可以看看。
Provide争议
Provide 介绍
It is designed as a replacement for ScopedModel that allows for more flexible handling of data types and data.
它被设计为ScopedModel的替代品,允许更灵活地处理数据类型和数据。
可以理解为在ScopedModel的基础上再次封装了一下,使用起来更加方便。
有需要的可以先了解一下 Flutter —— 状态管理 | ScopedModel
Provide 使用
第一步 创建model
///为了更好的理解,我创建了两个model
import 'package:flutter/material.dart';
//model1
class Counter with ChangeNotifier {
int _value;
int get value => _value;
Counter(this._value);
void increment() {
_value++;
notifyListeners();
}
}
//model2
import 'package:flutter/material.dart';
class PersionModel with ChangeNotifier {
List<String> persionNames = ['小明', '小红'];
PersionModel();
List<String> get getPersionNameList => persionNames;
void addName(String name) {
persionNames.insert(0, name);
notifyListeners();
}
}
第二步 将需要传递的model加入到providers中
void main() {
//1.创建 model
var counter =Counter(0);
var persionNameModel=PersionModel();
//2.创建 Providers
var providers=Providers();
providers
//3.将创建的 modle 加入到 providers 中
..provide(Provider.value(counter))
..provide(Provider.value(persionNameModel));
runApp(
//4.通过 ProviderNode 将树包裹
ProviderNode(child: MyApp(), providers: providers)
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FirstPage(),
);
}
}
第三步 构建小部件
方法一:通过 provide 构建小部件
方法二:StreamBuilder构建小部件
二者区别在于StreamBuilder可以操作stream流,做一些简单的操作。
///方法一: 通过 provide 构建小部件
Provide<Modle>(
builder: (context, child, modle) {
return Text('小部件' + modle.value);
},
),
///方法二: StreamBuilder<Model>构建小部件
StreamBuilder<Counter>(
initialData:Provide.value<Counter>(context) ,
stream: Provide.stream<Counter>(context).where((Counter counter)=> counter.value % 2 ==0),
builder: (context,snapshot){
return Text(
'通过StreamBuilder构建Widget->${snapshot.data.value}',
);
},
),
class FirstPage extends StatefulWidget {
@override
FirstPageState createState() => new FirstPageState();
}
class FirstPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('第一个界面'),
),
body: Column(
children: <Widget>[
//通过Provide构建一个 Text 小部件
Provide<PersionModel>(
builder: (context, child, persionNameModel) {
return Text('取第一个人的名字' + persionNameModel.getPersionNameList[0]);
},
),
Provide<Counter>(
builder: (context, child, counter) {
return Text('当前数值' + counter.value.toString());
},
),
//通过StreamBuilder构建一个 Text 小部件
StreamBuilder<Counter>(
initialData: Provide.value<Counter>(context),
//可以操作的 stream 流
stream: Provide.stream<Counter>(context)
.where((Counter counter) => counter.value % 2 == 0),
builder: (context, snapshot) {
return Text(
'通过StreamBuilder构建Widget->${snapshot.data.value}',
);
},
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => SecondPage()));
}),
);
}
}
至此就可以使用 Provide 了,另外还需要修改数据的方法,接下来继续走。
第四步 获取与调用 Model 中的方法
1.获取 model 的方法 Provide.value<Model>(context)
2.调用 model 中的数据 Provide.value<Model>(context).方法名
///例如在 SecondPage 界面中
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
///value值加一
Provide.value<Counter>(context).increment();
///往人名集合中添加人名
Provide.value<PersionModel>(context).addName(
'小紫' + Provide.value<Counter>(context).value.toString());
}),
Provide 特性
1.provide可以构建 Stream 流,通过流我们可以做一系列的变换,这是Provide的一大亮点。
2.Provide 可以添加多个Model
Provide 源码分析
内容点1:
providers
..provide(Provider.value(counter));
(1) Provider.value(counter)=>返回 Provider 对象
(2) providers.provide()=>
//将provide添加进map集合
Map<Type, Provider<dynamic>> _providersForScope(scope) =>
_providers[scope ?? defaultScope] ??= {};
(3) ProviderNode(child: MyApp(), providers: providers)
ProviderNode创建一个小部件,返回
_InheritedProviders(
child: child,
providers: providers,
parent: _InheritedProviders.of(context));
同样_InheritedProviders继承了InheritedWidget,通过InheritedWidget向下传递数据
内容点2:如何构建小部件,并且刷新数据
Provide<PersionModel>(
builder: (context, child, persionNameModel) {
return Text('取第一个人的名字' + persionNameModel.getPersionNameList[0]);
},
),
class Provide<T> extends StatelessWidget {
/// Called whenever there is a change.
final ValueBuilder<T> builder;
/// The part of the widget tree not rebuilt on change.
final Widget child;
/// The scope from which the type is requested
final ProviderScope scope;
/// Constructor.
const Provide({@required this.builder, this.child, this.scope});
/// 返回model
static T value<T>(BuildContext context, {ProviderScope scope}) {
final provider = _InheritedProviders.of(context).getValue<T>(scope: scope);
assert(provider != null);
return provider.get(context);
}
///返回一个stream
static Stream<T> stream<T>(BuildContext context, {ProviderScope scope}) {
final provider = _InheritedProviders.of(context).getValue<T>(scope: scope);
assert(provider != null);
return provider.stream(context);
}
@override
Widget build(BuildContext context) {
/// 从map集合中获取provider
final provider = _InheritedProviders.of(context).getValue<T>(scope: scope);
/// 返回model
final value = provider.get(context);
final listenable = _getListenable(provider, value);
///关键字is的意思 类型匹配 类似 java 的 instance of
if (provider is Listenable) {
return ListeningBuilder(
listenable: listenable,
child: child,
builder: (buildContext, child) =>
builder(buildContext, child, provider.get(context)),
);
} else if (value is Listenable) {
/// value 就是我们定义的model ,model with ChangeNotifier
/// ChangeNotifier implements Listenable
return ListeningBuilder(
listenable: listenable,
child: child,
builder: (buildContext, child) => builder(buildContext, child, value),
);
}
throw ArgumentError('Either the type or the provider of it must'
' implement listenable. To get a non-listenable value, use the static'
' Provide.value<T>.');
}
}
对于ListeningBuilder,有这么一段话
/// Widget that rebuilds part of the widget tree whenever
/// the [listenable] changes.
当listenable 发生改变时,widget 会rebuild 重新构建widget