Flutter完整开发实践详解:全面理解state和provider
1.状态
1。什么是国家?
我们知道 Flutter 宇宙中的一切都是 Widget 和 WidgetWidget 是 ⓽ ⓽ im Widget 每个状态代表一个框架。
在此基础上,来自 这就是 Flutter 中StatefulWidget的State帮助我们实现了通过frame进行绘制‾‾‾‾‾‾‾‾‾‾‾ ,所以每次 当 Widget 时重绘,Stat用于重新提供Widget所需的绘图信息。 2。如何实现跨框架状态共享?
Widget 的实现原理。我们在上一章中介绍过。这里我们要讲两个概念: Widget在正常情况下需要将Element⓽转换为❝⓼对象才能实现绘制。 Element是BuildContext的实现类,Element包含♾‼Widget ,我们的方法构建.widget(BuildContext上下文){}在代码中称为Element 。
了解了这两个概念后,我们先看下图,在Flutter中创建一个Widget。首先,我们创建此 Widget 的 Element 。 事实上,State实现了各个帧之间的共享,将State保存在❀Element ❓Element中 每次通话时 ❓ 小部件构建(),通过testate.build(this)获得新的数据可以复用。 Widget;❙
国家是在哪里创建的?
如下图所示,来自 同时我们来看看方法 首先我们看一下问题代码如下:StatefulWidget的createState是通过构造方法❓❙创建的,这样保证了只要元素is 不会重新创建,并且 中,我们创建了 State 始终被重用。 更新。当创建新的 StatefulWidget 来更新 UI 时,会再次创建新的 widget。授予_state,这个设置也导致了一个新手经常忽略的问题。 DemoPage 并将变量 data 。 Demo页 创建时,创建状态是直接传递到PageState
_DemoPageState中,通过文本直接显示传入的数据。运行后可以看到没有问题吧? 但是当我们在4中点击 问题在于方法构造先前的setState时,发现3中的Text并没有发现任何变化,StatefulElement和更新:❙仅在状态StatefulElement当我们调用时在的set方法中创建设置状态运行update,我们执行_state.widget‽=_state‽_DemoPageState(this.data)传递导入的数据 时没有改变setState 在上传后执行楔子。 ♽ 方法 t.data 来自上面代码中的 3 个注释,因为 _state.widget = newWidget, State 小部件‼自然会待更新。
3。 setState 的作用是什么?
我们常说的setState实际上是调用了markNeedsBuild,会在内部标记元素脏了,然后 WidgetsBinding.drawFrame将在下一帧中绘制。还可以看出,setState并没有立即生效。
4。状态共享
前面我们讲了State在Flutter中的作用和运行原理。接下来我们看一个常见的对象:InheritedWidget。
static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
final _InheritedTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedTheme);
if (shadowThemeOnly) {
/// inheritedTheme 这个 Widget 内的 theme
/// theme 内有我们需要的 ThemeData
return inheritedTheme.theme.data;
}
···
}
复制代码这里需要注意的是,inheritsFromWidgetOfExactType这个方法是做什么的呢?
我们直接在Element中找到方法deditFromWidgetOfExactType的实现,关键代码如下:: _inheritedWidge 检查该类型是否ts InheritedElement 存在。。找到
_dependency,并且当前 Element 通过 ❙‶‶updateDependency 添加到 InheritedElement 来自 _dependencies 在这张地图上。 继承元素 中的 小部件。 @override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
/// 在共享 map _inheritedWidgets 中查找
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
/// 返回找到的 InheritedWidget ,同时添加当前 element 处理
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
/// 就是将当前 element(this) 添加到 _dependents 里
/// 也就是 InheritedElement 的 _dependents
/// _dependents[dependent] = value;
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@override
void notifyClients(InheritedWidget oldWidget) {
for (Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
复制代码这里关键是ancestor.updateDependency(this,spect);这个方法:
我们都知道,获取
一般需要继承BuildContext,比如 这是什么意思? 例如,当我们在 而当 将会‽‽― dependent– 实际分析从Provider开始。 为什么存在提供商? 由于 Flutter 和 React 技术相似,因此在 Flutter 中创建它们为 作为国家领导官方推荐的Flutter, 于是在经历了诸多坎坷后,在今年的Google I/O大会之后,provider成为了Flutter官方推荐的新的状态管理方式之一。 其特点是: 不复杂,容易理解。当代码量不大时,可以轻松地以刷新粒度组合和控制,而原来谷歌官方flutter-provide仓库的状态管理被GG宣布,provider成为了它的替代者。 ⚠️注意“provider”比“flutter-provide”多了一个“r”。 题外话:以前面试的时候,面试官有时会问我,“你的开源项目代码不多”。每次我都会笑笑不语,虽然代码量可以代表一些成果,但是我非常反对用代码量来衡量一个贡献的价值。这和用加班时间来衡量员工价值有什么区别? 如以下代码所示。实现点击计数器,其中: 所以在上面的代码中,我们将 以下是一些见解: ,因此可以更新 然后我们分析 。由于这是状态管理,因此必须调用 在Provider中,对 。 的状态肯定需要 那么我们可以直接使用 当然可以,但是你还记得我们之前介绍 即将推出的❀‼️呈现了这个上下文Widget T提供‾‾。在 所以 该库还提供组合 相信这样的设置,对于使用过BLoC模式的同学来说一定会感觉很贴心。正常用作 BLoC 时,每个 当然,如果你想直接使用 还有更多 整个提供商'你可以看到Theme.of(context),而BuildContext的实现是Element所以我们调用Elementcontext.inher itFromWidgetOfExactType , 表示的元素 已“注册”在 context将被添加到_dependents♾‼‽中。设置为 StatefulWidget 中调用 Theme.of(context).primaryColor 表示此 小部件。已“注册”到 _Dependents 位于 ❙InheriteDelement。 InheritedWidget更新时,如下代码所示,Element中的notifyDependent ,终于触发markNeedsBuild,因此当InheritedWidget更新时,‾文本.♝引用的地点也会被触发更新。 原因。 2。提供商
flutter_redux、xflutter_dva‼ 、 大致经历了这个过程: fish _flutter 大部分它们非常复杂,需要对框架概念有一定的了解。 scoped_model,由于其设计比较简单,有时不太适合复杂的场景。 0。演示代码
MultiProvider 用于: _ProviderPageState 以提供多个 支持提供程序❀ 计数器。 通过 消费者获得 位于 CountWidget♿‾ erPage 同时 AppBar 和 处于状态 文本 位于 显示CountWidgetclass _ProviderPageState extends State<ProviderPage> {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => ProviderModel()),
],
child: Scaffold(
appBar: AppBar(
title: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
var counter = Provider.of<ProviderModel>(context);
return new Text("Provider ${counter.count.toString()}");
},
)
),
body: CountWidget(),
),
);
}
}
class CountWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ProviderModel>(builder: (context, counter, _) {
return new Column(
children: <Widget>[
new Expanded(child: new Center(child: new Text(counter.count.toString()))),
new Center(
child: new FlatButton(
onPressed: () {
counter.add();
},
color: Colors.blue,
child: new Text("+")),
)
],
);
});
}
}
class ProviderModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void add() {
_count++;
notifyListeners();
}
}
复制代码ChangeNotifier(ProviderModel)与ChangeNotifierProvider结合起来,实现共享;我们使用提供者。和消费者获取共享状态计数器;通过调用 ChangeNotifier notifyListeners(); 触发更新。 DelegateWidget Provider 是 @override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
/// 在共享 map _inheritedWidgets 中查找
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
/// 返回找到的 InheritedWidget ,同时添加当前 element 处理
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
/// 就是将当前 element(this) 添加到 _dependents 里
/// 也就是 InheritedElement 的 _dependents
/// _dependents[dependent] = value;
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@override
void notifyClients(InheritedWidget oldWidget) {
for (Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
复制代码InheritedWidget 的 InheritedProvider 实现的。MultiProvider和Consumer包实现对组合和刷新的详细控制。 1并委托
StatefulWidget 和 setState。 StatefulWidget的一系列生命周期管理和更新都是通过上述不同的代理完成的ChangeNotifierProviderChangeNotifierProvider‽change‽ 并执行♾‼️。 addListener 添加监听器 监听器。 监听器调用方法StateDelegateStateSetter,该方法调用setState的方法ChangeNotifer notifyListeners MultiProvider我们使用的允许我们组合多个的 元素提供商。。如下代码所示,传入的。 Providers会按照倒序排列,最终组合成一个嵌套的widget树,方便我们添加多个Provider: :通过Delegate回调不同的生命周期如Disposer,也有助于我们外部的二次处理,减少外部的嵌套使用‼️‼️。2。共享 InheritedProvider
InheritedWidget,InheritedProvider 是 ♾id继承 , 的所有实现Provider 均使用 嵌套InheritedProvider.在由用户共享InheritedWidgetbuild方法中实现值的共享。 3。 Consumer
Consumer是Provider中比较有趣的东西。它本身StatelessWidget,刚刚通过程序集♽提供者。‼❀(上下文)‼值 final Widget Function(BuildContext context, T value, Widget child) builder;
@override
Widget build(BuildContext context) {
return builder(
context,
Provider.of<T>(context),
child,
);
}
复制代码Provider.of(context)来代替Consumer。没关系? InheritedWidget时说过的话:继承元素 中的 _dependents。 消费者作为独立的StatelessWidget是一个优势上下文中传递是消费者本人。 在本例中,我们使用 Consumer,我们需要使用 Provider.value 进行嵌套,并且 ♾‼‼❙ 已更新。 ,它不会更新整个页面,而是只更新 Consumer 这个 StatelessWidget。 消费者深思熟虑地封装了“注册逻辑”上下文获取‽‽到‽‽‽❓状态更改。 ,需要更新技巧。 Consumer2 ~ Consumer6。感受一下:
@override
Widget build(BuildContext context) {
return builder(
context,
Provider.of<A>(context),
Provider.of<B>(context),
Provider.of<C>(context),
Provider.of<D>(context),
Provider.of<E>(context),
Provider.of<F>(context),
child,
);
复制代码StreamBuilder snapShot 仅支持一种类型。当有多个时,要么将多个状态合并为一个实体,要么需要StreamBuilder的多次嵌套。 LayoutBuilder与Provider.of(context),也是可以的:❙ListenableProvider value Provider.of(context) FutureProvider、StreamProvider还有更多 @override
Widget build(BuildContext context) {
var tree = child;
for (final provider in providers.reversed) {
tree = provider.cloneWithChild(tree);
}
return tree;
}
/// Clones the current provider with a new [child].
/// Note for implementers: all other values, including [Key] must be
/// preserved.
@override
MultiProvider cloneWithChild(Widget child) {
return MultiProvider(
key: key,
providers: providers,
child: child,
);
}
复制代码 @override
Widget build(BuildContext context) {
var tree = child;
for (final provider in providers.reversed) {
tree = provider.cloneWithChild(tree);
}
return tree;
}
/// Clones the current provider with a new [child].
/// Note for implementers: all other values, including [Key] must be
/// preserved.
@override
MultiProvider cloneWithChild(Widget child) {
return MultiProvider(
key: key,
providers: providers,
child: child,
);
}
复制代码 Provider设计更接近Flutter原生功能,设计也更好理解并考虑到性能等问题。 ?商业转载请联系作者获取授权。非商业转载请注明出处。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网




发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。