Saltar al contenido principal

Optimización de reconstrucción para OverlayEntries y Routes

Los OverlayEntries solo se reconstruyen ante cambios explícitos de State.

Resumen

#

Esta optimización mejora el rendimiento de las transiciones de rutas, pero podría revelar llamadas faltantes a setState en tu aplicación.

Contexto

#

Antes de este cambio, un OverlayEntry se reconstruía cuando se añadía una nueva entrada opaca encima de él o se eliminaba por encima. Estas reconstrucciones eran innecesarias porque no eran provocadas por un cambio de State en el OverlayEntry afectado. Este cambio importante optimizó cómo manejamos la adición y eliminación de OverlayEntrys y elimina reconstrucciones innecesarias para mejorar el rendimiento.

Dado que Navigator coloca internamente cada Route dentro de un OverlayEntry, este cambio también se aplica a las transiciones de Route: si se inserta una Route opaca encima o se elimina por encima de otra Route, las Routes por debajo de la Route opaca ya no se reconstruyen innecesariamente.

Descripción del cambio

#

En la mayoría de los casos, este cambio no requiere modificaciones en tu código. Sin embargo, si tu aplicación dependía erróneamente de las reconstrucciones implícitas, es posible que veas problemas, que se pueden resolver envolviendo cualquier cambio de State en una llamada a setState.

Además, este cambio modificó ligeramente la estructura del árbol de widgets: antes de este cambio, los OverlayEntrys estaban envueltos en un Widget Stack. El Widget Stack explícito fue eliminado de la jerarquía de widgets.

Guía de migración

#

Si ves problemas después de actualizar a una versión de Flutter que incluye este cambio, audita tu código en busca de llamadas faltantes a setState. En el siguiente ejemplo, asignar el valor de retorno de Navigator.pushNamed a buttonLabel modifica implícitamente el State y debería envolverse en una llamada a setState explícita.

Código antes de la migración:

dart
class FooState extends State<Foo> {
  String buttonLabel = 'Click Me';
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        // Illegal state modification that should be wrapped in setState.
        buttonLabel = await Navigator.pushNamed(context, '/bar');
      },
      child: Text(buttonLabel),
    );
  }
}

Código después de la migración:

dart
class FooState extends State<Foo> {
  String buttonLabel = 'Click Me';
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        final newLabel = await Navigator.pushNamed(context, '/bar');
        setState(() {
          buttonLabel = newLabel;
        });
      },
      child: Text(buttonLabel),
    );
  }
}

Timeline

#

Lanzado en la versión: 1.16.3
En versión estable: 1.17

Referencias

#

Documentación de la API:

Issues relevantes:

PRs relevantes: