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 关联的小部件会被重新构建,然后改写界面。