Saltar al contenido

Flutter: ¿Cómo usar correctamente un widget heredado?

No dejes de compartir nuestro sitio y códigos con tus amigos, ayúdanos a hacer crecer esta comunidad.

Solución:

El problema proviene de su cotización, que es incorrecta.

Como dijiste, InheritedWidgets son, como otros widgets, inmutables. Por lo tanto ellos no actualizar. Se crean de nuevo.

La cosa es: InheritedWidget es solo un widget simple que no hace nada más que almacenar datos. No tiene ninguna lógica de actualización ni nada. Pero, como cualquier otro widget, está asociado con un Element. ¿Y adivina qué? ¡Esta cosa es mutable y Flutter la reutilizará siempre que sea posible!

La cotización corregida sería:

InheritedWidget, cuando se hace referencia a él de esta manera, hará que el consumidor reconstruya cuando InheritedWidget se asocie a un InheritedElement cambios.

Hay una gran charla sobre cómo se conectan los widgets / elementos / renderbox. Pero en resumen, son así (la izquierda es su widget típico, el medio es ‘elementos’ y la derecha son ‘cuadros de representación’):

ingrese la descripción de la imagen aquí

La cuestión es: cuando crea una instancia de un nuevo widget; Flutter lo comparará con el anterior. Reutilice su “Elemento”, que apunta a un RenderBox. Y mudar las propiedades de RenderBox.


Okey, pero ¿cómo responde esto a mi pregunta?

Al crear una instancia de un InheritedWidget y luego llamar context.inheritedWidgetOfExactType (o MyClass.of que es básicamente lo mismo); lo que está implícito es que escuchará el Element asociado con tu InheritedWidget. Y siempre que eso Element obtiene un nuevo widget, forzará la actualización de cualquier widget que haya llamado al método anterior.

En resumen, cuando reemplaza un InheritedWidget con uno nuevo; Flutter verá que cambió. Y notificará a los widgets enlazados de una posible modificación.

Si entendiste todo, ya deberías haber adivinado la solución:

Envuelve tu InheritedWidget dentro de una StatefulWidget que creará un nuevo InheritedWidget cada vez que algo cambiaba!

El resultado final en el código real sería:

class MyInherited extends StatefulWidget 
  static MyInheritedData of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(MyInheritedData) as MyInheritedData;

  const MyInherited(Key key, this.child) : super(key: key);

  final Widget child;

  @override
  _MyInheritedState createState() => _MyInheritedState();


class _MyInheritedState extends State 
  String myField;

  void onMyFieldChange(String newValue) 
    setState(() 
      myField = newValue;
    );
  

  @override
  Widget build(BuildContext context) 
    return MyInheritedData(
      myField: myField,
      onMyFieldChange: onMyFieldChange,
      child: widget.child,
    );
  


class MyInheritedData extends InheritedWidget 
  final String myField;
  final ValueChanged onMyFieldChange;

  MyInheritedData(
    Key key,
    this.myField,
    this.onMyFieldChange,
    Widget child,
  ) : super(key: key, child: child);

  static MyInheritedData of(BuildContext context) 
    return context.dependOnInheritedWidgetOfExactType();
  

  @override
  bool updateShouldNotify(MyInheritedData oldWidget) 
        oldWidget.onMyFieldChange != onMyFieldChange;
  


¿Pero crear un nuevo InheritedWidget no reconstruiría todo el árbol?

No, no necesariamente. Como su nuevo InheritedWidget puede potencialmente tener exactamente el mismo hijo que antes. Y por exacto, me refiero a la misma instancia. Los widgets que tienen la misma instancia que tenían antes no se reconstruyen.

Y en la mayoría de las situaciones (tener un widget heredado en la raíz de su aplicación), el widget heredado es constante. Así que no hay reconstrucción innecesaria.

TL; DR

No uses mucho cálculo en el interior updateShouldNotify método y uso constante en lugar de nuevo al crear un widget


En primer lugar, debemos entender qué son los objetos Widget, Element y Render.

  1. Hacer los objetos son lo que realmente se representa en la pantalla. Son mudable, 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.
  2. Widget – es una descripción de lo que se debe representar. Son inmutable y barato. 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”.
  3. Elemento / BuildContext – es un proxy entre Widget y Hacer objetos. 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 heritageFromWidgetOfExactType.

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: true o false.

Como cualquier widget, InheritedWidget tiene un objeto Element correspondiente. Está InheritedElement. InheritedElement llamada updateShouldNotify en el widget cada vez que creamos un nuevo widget (llame setState en un antepasado). Cuando updateShouldNotify devoluciones true InheritedElement itera a través de dependencias(?) y método de llamada didChangeDependencies en eso.

Donde se obtiene InheritedElement dependencias? Aquí deberíamos mirar heritageFromWidgetOfExactType método.

heritageFromWidgetOfExactType – Este método definido en BuildContext y
cada Element 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 heritageFromElement.

El código para heritageFromElement:

  InheritedWidget inheritFromElement(InheritedElement ancestor,  Object aspect ) 
    assert(ancestor != null);
    _dependencies ??= HashSet();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  
  1. Agregamos ancestor como una dependencia del elemento actual (_dependencies.add (ancestor))
  2. Agregamos el elemento actual a las dependencias del ancestro (ancestor.updateDependencies (this, aspect))
  3. Devolvemos el widget del antepasado como resultado de heritageFromWidgetOfExactType (devuelve ancestor.widget)

Entonces ahora sabemos de dónde obtiene InheritedElement sus dependencias.

Ahora veamos didChangeDependencies método. 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 simplemente marca un elemento como sucio y este elemento debe reconstruirse en el siguiente marco. Reconstruir significa método de llamada construir en el elemento de 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?

  1. Almacenar widgets con las manos (manualmente)
  2. Usar constante porque const crea la única instancia de valor / clase

De los documentos:

[BuildContext.inheritFromWidgetOfExactType] obtiene el widget más cercano del tipo dado, que debe ser del tipo de una subclase de InheritedWidget concreta, y registra este contexto de compilación con ese widget de manera que cuando ese widget cambia (o se introduce un nuevo widget de ese tipo, o el widget desaparece ), este contexto de compilación se reconstruye para que pueda obtener nuevos valores de ese widget.

Esto se suele llamar implícitamente desde de () static métodos, por ejemplo Theme.of.

Como señaló el OP, un InheritedWidget instancia no cambia … pero se puede reemplazar con una nueva instancia en la misma ubicación en el árbol de widgets. Cuando eso sucede, es posible que los widgets registrados deban reconstruirse. los InheritedWidget.updateShouldNotify El método hace esta determinación. (Ver: documentos)

Entonces, ¿cómo podría reemplazarse una instancia? Un InheritedWidget instancia puede estar contenida por un StatefulWidget, que puede reemplazar una instancia antigua por una nueva.

Aquí puedes ver las reseñas y valoraciones de los usuarios

Si te sientes motivado, puedes dejar una división acerca de qué le añadirías a esta crónica.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *