Un artículo para comprender el mecanismo de actualización local de InheritedWidget

La diferencia entre InheritedWidget y StatefulWidget

En primer lugar,  la cadena de herencia  de InheritedWidget y  es diferente, y la comparación es la siguiente.StatefulWidget

 InheritedWidgetHeredado de  ProxyWidget, después  WidgetStatefulWidget directamente heredado  Widget. La segunda es que la clase de elemento de representación creada es diferente, InheritedWidget el  createElement retorno es InheritedElementStatefulWidget el  createElement retorno es StatefulElement.

Como ya sabemos en el artículo anterior, el control de renderizado real lo  Element realizan las clases, el Widget método real createElement es pasar el  Widget objeto al  Element objeto, y el  Element objeto decide cómo renderizar de acuerdo con  Widget la configuración del componente.

InhretiedWidget La definición es simple como sigue:

abstract class InheritedWidget extends ProxyWidget {
  const InheritedWidget({Key? key, required Widget child})
      : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

updateShouldNotifyMétodo para  InheritedWidget implementaciones de subclases que han decidido si notificar o no a sus subcomponentes ( widget). Por ejemplo, si los datos no han cambiado (por lo general, como una extracción para actualizar sin datos nuevos), entonces puede regresar  false, lo que elimina la necesidad de actualizar los subcomponentes y reduce los costos de rendimiento. En nuestro  ModelBinding ejemplo anterior, devolvió directamente  true, es decir, cada vez que ocurra un cambio, se notificará al componente secundario. A continuación veremos  InheritedElement la  StatefulElement diferencia.

La diferencia entre InheritedElement y StatefulElement

Ya lo hemos analizado en el artículo anterior  StatefulElement , y más adelante  setState llamará al método de reconstrucción  performRebuild. performRebuild El método se implementa en la clase padre Component . El núcleo es que cuando el  Widget árbol cambia, el  método se Widget llama de acuerdo con el nuevo árbol  updateChild para actualizar los elementos secundarios.

Cuando se  ModelBinding llama  al artículo anterior setState , porque es uno  StatefulWidget, no hay duda de que también llamará updateChildpara actualizar los elementos secundarios. A juzgar por los resultados de la ejecución, dado  ModelBinding que no existe el caso de reconstruir  Widget el árbol en el ejemplo anterior, el procesamiento updateChild anterior diferente. El  método updateChild del componente  build se llama antes para obtener el nuevo  Widget árbol. ¿Es diferente aquí? Sigue mirando hacia abajo.

InheritedWidget En consecuencia, hay una  InheritedElementcapa adicional de herencia arriba, es decir  ProxyElement. Y precisamente  ProxyElement encontramos un build camino. Por el contrario  StatefulElement, el  build método aquí no llama al método del  Widget objeto  correspondiente build , sino que devuelve directamente  widget.child.

// ProxyElement的 build 方法
@override
Widget build() => widget.child;

// StatefulElement 的 build 方法
@override
Widget build() => state.build(this);

// StatelessElement 的 build方法
@override
Widget build() => widget.build(this);

A partir de esto, sabemos por qué  InheritedWidgetel árbol de subcomponentes no se reconstruye cuando se actualiza el estado. Esto se debe ProxyElementa que el árbol de subcomponentes que se ha creado se devuelve directamente en lugar de reconstruirse. ¿Crees que la verdad está fuera? Bueno , ¿qué hay de llegar al fondo de esto? ProxyElement ¿No deberíamos preguntarnos cómo se percibe si el árbol de subcomponentes ha cambiado ? Por ejemplo, cuando se inserta un nuevo elemento o cambian los parámetros de representación de un elemento (color, fuente, contenido, etc.), ¿cómo lo sabe la capa de representación? ¡Sigue adelante! 

Cómo percibe InheritedElement los cambios en el árbol de componentes

Echemos un vistazo a la estructura de clases de InheritedElement.

 

Desde el punto de vista de la estructura de clases, no es complicado, porque la mayor parte de la gestión de representación se ha completado en la  ComponentElement suma  Element de la clase principal. build Ya hemos hablado sobre el método, centrémonos en  el proceso después InheritedWidget de llamar al componente principal  setState . Cuando el componente secundario necesita obtener la gestión del estado, el método que utilizamos es:

ModelBindingV2.of<FaceEmotion>(context)

Lo que este método realmente llama es:

_ModelBindingScope<T> scope =
  context.dependOnInheritedWidgetOfExactType(aspect: _ModelBindingScope);

Los dependOnInheritedWidgetOfExactTypemétodos  aquí están BuildContextdefinidos, pero realmente Element implementados. Esto accederá a un HashMap objeto _inheritedWidgetsy encontrará el tipo correspondiente de la matriz InheritedElement.

@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor,
    {Object? aspect}) {
  assert(ancestor != null);
  _dependencies ??= HashSet<InheritedElement>();
  _dependencies!.add(ancestor);
  ancestor.updateDependencies(this, aspect);
  return ancestor.widget;
}

@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>(
    {Object? aspect}) {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement? ancestor =
      _inheritedWidgets == null ? null : _inheritedWidgets![T];
  if (ancestor != null) {
    assert(ancestor is InheritedElement);
    return dependOnInheritedElement(ancestor, aspect: aspect) as T;
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}

Esta matriz en realidad se inicializa en la  mount llamada al método _updateInheritance . Y  este método InheritedElement está sobrecargado en  .  Es decir , cuando  Elementse cree  ,  se   asociará con el tipo de tiempo de ejecución del componente correspondiente.InheritedWidgetmountInheritedElement

@override
void _updateInheritance() {
  assert(_lifecycleState == _ElementLifecycle.active);
  final Map<Type, InheritedElement>? incomingWidgets =
      _parent?._inheritedWidgets;
  if (incomingWidgets != null)
    _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
  else
    _inheritedWidgets = HashMap<Type, InheritedElement>();
  _inheritedWidgets![widget.runtimeType] = this;
}

Primero, este método continuará con todos los InheritedWidgets del padre y luego se almacenará (InheritedElement) en este HashMap para que el elemento se pueda encontrar más tarde.

Por lo tanto, cuando se usa en un componente secundario, dependOnInheritedWidgetOfExactTypeel método se ejecuta realmente  dependOnInheritedElement y los parámetros pasados ​​son el  elemento encontrado por tipo y el  parámetro de tipo  InheritedElement especificado  , que es el nuestro aquí , y luego el elemento de representación actual (subclase de elementos) en lugar de vincular, decirle  al objeto que este componente dependerá de él . Podemos ver a partir de los resultados de la depuración que  dicho objeto existe en . De esta forma  se establece una conexión con el elemento de renderizado correspondiente al componente.InheritedWidgetaspect_ModeBindScope<T>InheritedElementInheritedWidget_dependentsInheritedElement

El siguiente paso es ver  setState cómo obtener un nuevo árbol de componentes y actualizar los componentes. Ya sabemos que el  método  setState se llamará cuando  ,  el  método  se llamará  en  , y ahora veamos qué  hace. en realidad   llamará al  método:performRebuildperformRebuildElementupdateChildInheritedElementupdateChildupdateChildchild.update(newWidget)

 else if (hasSameSuperclass &&
      Widget.canUpdate(child.widget, newWidget)) {
    if (child.slot != newSlot) updateSlotForChild(child, newSlot);
    child.update(newWidget);
    //...
    newChild = child;
 }

// ...

return newChild;

En  ProxyElement , el método se anula  update .

@override
void update(ProxyWidget newWidget) {
  final ProxyWidget oldWidget = widget;
  assert(widget != null);
  assert(widget != newWidget);
  super.update(newWidget);
  assert(widget == newWidget);
  updated(oldWidget);
  _dirty = true;
  rebuild();
}

El newWidget aquí es una nueva configuración de componente creada cuando se establece setState, por lo que no es lo mismo que oldWidget. Para InheritedWidget, primero llamará a updated(oldWidget), este método en realidad es para notificar a los componentes que dependen de InheirtedWidget para actualizar:

@protected
void updated(covariant ProxyWidget oldWidget) {
  notifyClients(oldWidget);
}

// InheritedElement类
@override
void updated(InheritedWidget oldWidget) {
  if (widget.updateShouldNotify(oldWidget)) super.updated(oldWidget);
}

@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
  dependent.didChangeDependencies();
}

@override
void notifyClients(InheritedWidget oldWidget) {
  assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
  for (final Element dependent in _dependents.keys) {
      assert(() {
        // check that it really is our descendant
        Element? ancestor = dependent._parent;
        while (ancestor != this && ancestor != null)
          ancestor = ancestor._parent;
        return ancestor == this;
      }());
      // check that it really depends on us
      assert(dependent._dependencies!.contains(this));
      notifyDependent(oldWidget, dependent);
    }
  }
}

De hecho, finalmente se llama al método didChangeDependencies que se basa en el elemento de representación del componente InheritedWidget Vamos a imprimirlo en este método. 

 En el elemento  didChangeDependencies , se llamará  markNeedsBuildpara marcar el elemento como que necesita ser actualizado, y luego el proceso posterior será  StatefulElement el mismo. Y para elementos sin estado dependiente, por no estar en _dependent , no se actualizarán. ModelBinding el componente es  StatelessWidget, por lo que el árbol de configuración original  Widget no cambiará una vez creado, y solo hay dos casos si se necesita cambiar el árbol de subcomponentes: 1. El subcomponente es  StatefulWidget, al setState cambiar, no pertenece a la categoría de InheritedWidget, en cambio, se realiza a través del método de actualización de StatefulWidget; por supuesto, no se recomienda este enfoque. 2. ¿El cambio del árbol de componentes del subcomponente depende del estado? Entonces, naturalmente, se actualizará cuando cambie el estado.

A partir de esto, finalmente descubrimos el proceso de actualización del subcomponente de percepción y notificación del árbol de componentes de InheritedWidget.

Resumir

Desde la perspectiva del proceso de renderizado de componentes en InheritedWidget, todo el proceso se divide en los siguientes pasos:

  • En la fase de montaje, el tipo de tiempo de ejecución del árbol de componentes se enlaza con el InheritedElement correspondiente y se almacena en el HashMap de _inheritedWidgets;
  • Cuando un componente secundario agrega una dependencia en el estado, el elemento Element correspondiente al componente secundario en realidad está vinculado a InheritedElement (el objeto Element específico se obtiene de _inheritedWidgets) y se almacena en el HashMap de _dependientes;
  • Cuando se actualiza el estado, InheritedElement usa directamente la configuración del componente anterior para notificar a los elementos secundarios que sus dependencias han cambiado. Esto se hace llamando al método didChangeDependencies de Element.
  • Marque el elemento que necesita ser actualizado en didChangeDependencies of Element y espere a que se actualice el siguiente cuadro.
  • Para los subcomponentes que no dependen del estado, no se agregarán a _dependiente, por lo que no se les notificará que se actualicen, lo que mejorará el rendimiento.

Hay varios artículos sobre el principio de la gestión estatal, a través de estos artículos espero lograr el propósito de saber qué es y por qué . De hecho, el núcleo de la representación de componentes de Flutter es cómo elegir la gestión de estado para lograr la representación de componentes, lo que tiene un gran impacto en el rendimiento. A continuación, describiremos la aplicación en ejemplos prácticos en la aplicación de complementos de administración de estado.

Supongo que te gusta

Origin blog.csdn.net/jdsjlzx/article/details/123320566
Recomendado
Clasificación