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所以我们调用Element
context.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
添加监听器 监听器
。 监听器
调用方法StateDelegate
StateSetter
,该方法调用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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。