1,调用setState办法改写界面
运用 StatefulWidget 作为页面,StatefulWidget基类完成为如下:
abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
@protected
@factory
State createState();
}
经过createState办法创立state,调用state的build办法创立视图:
//运用 StatefulElement element类型
class StatefulElement extends ComponentElement {
// state
State<StatefulWidget> get state => _state!;
//调用state的build办法创立视图
Widget build() => state.build(this);
}
调用state的setState办法改写界面:
class State<T extends StatefulWidget> .. {
//..
void setState(VoidCallback fn) {
_element!.markNeedsBuild();
}
///
}
咱们看到便是调用 element的markNeedsBuild办法改写界面
element:
abstract class Element extends DiagnosticableTree implements BuildContext {
//标记Element 为dirty
void markNeedsBuild() {
if (dirty) {
return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
}
owner中心完成如下:
class BuildOwner {
//...
//
void scheduleBuildFor(Element element) {
_dirtyElements.add(element);
element._inDirtyList = true;
}
}
了解了这种调用原理,咱们就可以在咱们完成的statefullwidget的state中,手动调用下面办法改写界面:
(context as Element).owner!.scheduleBuildFor(this);
作用是跟调用setState相同的。
2,provider简略实例
一个运用provider的简略页面代码如下:
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 告诉观察者状况已更改
}
}
class ProvdiderTestPage1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Counter(), // 创立Counter实例并提供应整个应用程序
child: Scaffold(body: ProvdierPage1()),
);
}
}
class ProvdierPage1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 运用Provider.of获取Counter实例
final counter = Provider.of<Counter>(context);
return Scaffold(
appBar: AppBar(
title: Text('Provider 示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'计数器值:',
style: TextStyle(fontSize: 20),
),
Text(
'${counter.count}',
style: TextStyle(fontSize: 40),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 调用Counter实例的increment办法
counter.increment();
},
child: Icon(Icons.add),
),
);
}
}
经过上面页面,咱们发现provider的调用为如下:
- ChangeNotifierProvider
- Provider.of(context);
- counter.increment();
- notifyListeners(); // 告诉观察者状况已更改
3,运用provider改写界面
下面咱们看一下provider原理
provider没有运用StatefulWidget 和State
中心区别便是没有运用State类作为办理数据改变。
class ProvdiderTestPage1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Counter(), // 创立Counter实例并提供应整个应用程序
child: Scaffold(body: ProvdierPage1()),
);
}
}
看下这个ChangeNotifierProvider完成:
// 承继 ListenableProvider
class ChangeNotifierProvider<T extends ChangeNotifier?>
extends ListenableProvider<T> {
}
持续看父类:
//ChangeNotifierProvider承继ListenableProvider
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
}
//ListenableProvider承继InheritedProvider:
class InheritedProvider<T> extends SingleChildStatelessWidget {
}
abstract class SingleChildStatelessWidget extends StatelessWidget
implements SingleChildWidget {
}
所以咱们的页面widget实际上是一个StatelessWidget类型的。
那么这是怎样做到数据改写页面改写的呢?
先看页面怎样build的
abstract class SingleChildStatelessWidget extends StatelessWidget
implements SingleChildWidget {
@override
Widget build(BuildContext context) => buildWithChild(context, _child);
}
build为调用buildWithChild ,子类完成为:
class InheritedProvider<T> extends SingleChildStatelessWidget {
@override
Widget buildWithChild(BuildContext context, Widget? child) {
return _InheritedProviderScope<T?>(
owner: this,
debugType: kDebugMode ? '$runtimeType' : '',
child: builder != null
? Builder(
builder: (context) => builder!(context, child),
)
: child!,
);
}
}
咱们创立 ChangeNotifierProvider 时的 build 便是在这里被调用。
经过组合包装,在SingleChildStatelessWidget类中 build返回的一个 _InheritedProviderScope 类型的widget:
class _InheritedProviderScope<T> extends InheritedWidget {
}
abstract class InheritedWidget extends ProxyWidget {
}
abstract class ProxyWidget extends Widget {
}
比较于StatefulWidget+StatefulElement+ State 的办理机制,咱们用的是 _InheritedProviderScopeElement类型的element目标:
class _InheritedProviderScope<T> extends InheritedWidget {
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}
_InheritedProviderScopeElement完成如下:
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
@override
_InheritedProviderScope<T> get widget =>
super.widget as _InheritedProviderScope<T>;
@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
}
看到这里,跟StatefullWidget相同,也是经过markNeedsBuild办法 来标记dirty来完成界面改写的
回到一开始,改写数据咱们是经过 notifyListeners 办法进行。
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 告诉观察者状况已更改
}
}
notifyListeners 是怎样走到markNeedsBuild呢?
先看下:
Provider.of<Counter>(context);
因为运用ChangeNotifierProvider包裹,所以Provider.of(context);
调用create:
ChangeNotifierProvider(
create: (context) => Counter(), // 创立Counter实例并提供应整个应用程序
child: Scaffold(body: ProvdierPage1()),
);
_CreateInheritedProviderState
class _CreateInheritedProviderState<T>
extends _DelegateState<T, _CreateInheritedProvider<T>> {
T get value {
if (!_didInitValue) {
_didInitValue = true;
if (delegate.create != null) {
_value = delegate.create!(element!); //创立 Counter 目标
}
_removeListener ??= delegate.startListening?.call(element!, _value as T); //Counter添加监听函数 counter数据改动经过notifyListeners告诉监听函数
return _value as T;
}
}
ListenableProvider
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
static VoidCallback _startListening(
InheritedContext<Listenable?> e,
Listenable? value,
) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}
InheritedContext
e.markNeedsNotifyDependents
abstract class InheritedContext<T> extends BuildContext {
void markNeedsNotifyDependents();
}
_InheritedProviderScopeElement
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
}
所以不难看出,counter目标状况改动,都将会告诉到 InheritedContext 的markNeedsNotifyDependents 函数然后调用markNeedsBuild办法.
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T>
class InheritedElement extends ProxyElement
abstract class ProxyElement extends ComponentElement
abstract class ComponentElement extends Element
abstract class Element extends DiagnosticableTree implements BuildContext
BuildContext:
void markNeedsBuild() {
owner!.scheduleBuildFor(this);
}
总结
provider运用
provider 是Flutter中一种常用的状况办理库,它经过运用 ChangeNotifier 或其他相似的可监听目标,使得在状况改动时可以告诉相关的观察者进行界面改写。下面是 provider 更新界面的基本机制:
-
ChangeNotifier: ChangeNotifier 是 provider 的中心。它是一个轻量级的类,用于表明应用程序状况。当状况发生改变时,ChangeNotifier 会调用 notifyListeners() 办法。
-
Provider: Provider 是一个用于办理和获取状况的类。它接收一个 ChangeNotifier 的实例,并将它提供应整个应用程序的小部件树。
-
Consumer Widget: Consumer 是一个小部件,它订阅 ChangeNotifier 的改变。当 ChangeNotifier 调用 notifyListeners() 时,与 Consumer 关联的小部件会被重新构建,然后改写界面。