TL; DR
No use mucho cálculo dentro del método updateShouldNotify y use const en lugar de new al crear un widget
En primer lugar, debemos entender qué son los objetos Widget, Element y Render.
- Los objetos renderizados son lo que realmente se renderiza en la pantalla. Son mutables , contienen la lógica de la pintura y el diseño. El árbol de renderizado es muy similar al modelo de objetos de documento (DOM) en la web y puede ver un objeto de renderizado como un nodo DOM en este árbol
- Widget : es una descripción de lo que se debe representar. Son inmutables y baratos. Entonces, si un widget responde a la pregunta "¿Qué?" (Enfoque declarativo), entonces un objeto Render responde a la pregunta "¿Cómo?" (Enfoque imperativo). Una analogía de la web es un "DOM virtual".
- Element / BuildContext : es un proxy entre los objetos Widget y Render . Contiene información sobre la posición de un widget en el árbol * y cómo actualizar el objeto Render cuando se cambia un widget correspondiente.
Ahora estamos listos para sumergirnos en InheritedWidget y el método de BuildContext hereitFromWidgetOfExactType .
Como ejemplo, recomiendo que consideremos este ejemplo de la documentación de Flutter sobre InheritedWidget:
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) {
return color != old.color;
}
}
InheritedWidget: solo un widget que implementa en nuestro caso un método importante: updateShouldNotify .
updateShouldNotify : una función que acepta un parámetro oldWidget y devuelve un valor booleano: verdadero o falso.
Como cualquier widget, InheritedWidget tiene un objeto Element correspondiente. Es InheritedElement . InheritedElement llama updateShouldNotify en el widget cada vez que construimos un nuevo widget (llama setState en un ancestro). Cuando updateShouldNotify devuelve true, InheritedElement itera a través de dependencias (?) Y llama al método didChangeDependencies en él.
¿Dónde InheritedElement obtiene las dependencias ? Aquí deberíamos mirar el método heritageFromWidgetOfExactType .
HereitFromWidgetOfExactType : este método definido en BuildContext y
cada elemento implementa la interfaz BuildContext (Element == BuildContext). Entonces, cada elemento tiene este método.
Veamos el código de heritageFromWidgetOfExactType:
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
Aquí intentamos encontrar un antepasado en _inheritedWidgets mapeado por tipo. Si se encuentra el antepasado, llamamos hereitFromElement .
El código de hereitFromElement :
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
- Agregamos ancestor como una dependencia del elemento actual (_dependencies.add (ancestor))
- Agregamos el elemento actual a las dependencias del ancestro (ancestor.updateDependencies (this, aspect))
- Devolvemos el widget del ancestro como resultado de heritageFromWidgetOfExactType (return ancestor.widget)
Entonces ahora sabemos de dónde obtiene InheritedElement sus dependencias.
Ahora veamos el método didChangeDependencies . Cada elemento tiene este método:
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
Como podemos ver, este método solo marca un elemento como sucio y este elemento debe reconstruirse en el siguiente cuadro. Reconstruir significa que el método de llamada se basa en el elemento del widget correspondiente.
Pero, ¿qué pasa con las "reconstrucciones completas del subárbol cuando reconstruyo InheritedWidget?". Aquí debemos recordar que los widgets son inmutables y si crea un nuevo widget, Flutter reconstruirá el subárbol. ¿Cómo podemos solucionarlo?
- Almacenar widgets con las manos (manualmente)
- Use const porque const crea la única instancia de value / class