1,调用setState方法刷新界面
使用 StatefulWidget 作为页面,StatefulWidget基类实现为如下:
less
abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
@protected
@factory
State createState();
}
通过createState方法创建state,调用state的build方法创建视图:
scala
//使用 StatefulElement element类型
class StatefulElement extends ComponentElement {
// state
State<StatefulWidget> get state => _state!;
//调用state的build方法创建视图
Widget build() => state.build(this);
}
调用state的setState方法刷新界面:
scala
class State<T extends StatefulWidget> .. {
//..
void setState(VoidCallback fn) {
_element!.markNeedsBuild();
}
///
}
我们看到就是调用 element的markNeedsBuild方法刷新界面 element:
scala
abstract class Element extends DiagnosticableTree implements BuildContext {
//标记Element 为dirty
void markNeedsBuild() {
if (dirty) {
return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
}
owner核心实现如下:
csharp
class BuildOwner {
//...
//
void scheduleBuildFor(Element element) {
_dirtyElements.add(element);
element._inDirtyList = true;
}
}
了解了这种调用原理,我们就可以在咱们实现的statefullwidget的state中,手动调用下面方法刷新界面:
kotlin
(context as Element).owner!.scheduleBuildFor(this);
效果是跟调用setState一样的。
2,provider简单实例
一个使用provider的简单页面代码如下:
scala
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类作为管理数据变化。
scala
class ProvdiderTestPage1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Counter(), // 创建Counter实例并提供给整个应用程序
child: Scaffold(body: ProvdierPage1()),
);
}
}
看下这个ChangeNotifierProvider实现:
scala
// 继承 ListenableProvider
class ChangeNotifierProvider<T extends ChangeNotifier?>
extends ListenableProvider<T> {
}
继续看父类:
scala
//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的
scala
abstract class SingleChildStatelessWidget extends StatelessWidget
implements SingleChildWidget {
@override
Widget build(BuildContext context) => buildWithChild(context, _child);
}
build为调用buildWithChild ,子类实现为:
scala
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:
scala
class _InheritedProviderScope<T> extends InheritedWidget {
}
abstract class InheritedWidget extends ProxyWidget {
}
abstract class ProxyWidget extends Widget {
}
相比于StatefulWidget+StatefulElement+ State 的管理机制,我们用的是 _InheritedProviderScopeElement类型的element对象:
scala
class _InheritedProviderScope<T> extends InheritedWidget {
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}
_InheritedProviderScopeElement实现如下:
scala
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 方法进行。
scala
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知观察者状态已更改
}
}
notifyListeners 是怎么走到markNeedsBuild呢?
先看下:
ini
Provider.of<Counter>(context);
因为使用ChangeNotifierProvider包裹,所以Provider.of(context);
调用create:
less
ChangeNotifierProvider(
create: (context) => Counter(), // 创建Counter实例并提供给整个应用程序
child: Scaffold(body: ProvdierPage1()),
);
_CreateInheritedProviderState
csharp
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
scala
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
scala
abstract class InheritedContext<T> extends BuildContext {
void markNeedsNotifyDependents();
}
_InheritedProviderScopeElement
scala
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
}
所以不难看出,counter对象状态改变,都将会通知到 InheritedContext 的markNeedsNotifyDependents 函数从而调用markNeedsBuild方法.
scala
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 关联的小部件会被重新构建,从而刷新界面。