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
InheritedWidget
Heredado de ProxyWidget
, después Widget
y StatefulWidget
directamente heredado Widget
. La segunda es que la clase de elemento de representación creada es diferente, InheritedWidget
el createElement
retorno es InheritedElement
y StatefulWidget
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);
}
updateShouldNotify
Mé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á updateChild
para 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 InheritedElement
capa 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é InheritedWidget
el árbol de subcomponentes no se reconstruye cuando se actualiza el estado. Esto se debe ProxyElement
a 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 dependOnInheritedWidgetOfExactType
métodos aquí están BuildContext
definidos, pero realmente Element
implementados. Esto accederá a un HashMap
objeto _inheritedWidgets
y 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 Element
se cree , se asociará con el tipo de tiempo de ejecución del componente correspondiente.InheritedWidget
mount
InheritedElement
@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, dependOnInheritedWidgetOfExactType
el 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.InheritedWidget
aspect
_ModeBindScope<T>
InheritedElement
InheritedWidget
_dependents
InheritedElement
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:performRebuild
performRebuild
Element
updateChild
InheritedElement
updateChild
updateChild
child.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á markNeedsBuild
para 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. Y 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.