¿Cómo lidiar con la creación de widgets no deseados?

Resuelto Rémi Rousselet asked hace 6 años • 7 respuestas

Por diversas razones, a veces buildse vuelve a llamar al método de mis widgets.

Sé que sucede porque un padre actualizó. Pero esto provoca efectos no deseados. Una situación típica donde causa problemas es cuando se usa FutureBuilderde esta manera:

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: httpCall(),
    builder: (context, snapshot) {
      // create some layout here
    },
  );
}

En este ejemplo, si se volviera a llamar al método de compilación , se activaría otra solicitud HTTP. Lo cual no es deseado.

Teniendo esto en cuenta, ¿cómo lidiar con la compilación no deseada? ¿Hay alguna forma de evitar una llamada de compilación?

Rémi Rousselet avatar Sep 10 '18 07:09 Rémi Rousselet
Aceptado

El método de construcción está diseñado de tal manera que debe ser puro/sin efectos secundarios . Esto se debe a que muchos factores externos pueden desencadenar la creación de un nuevo widget, como por ejemplo:

  • Ruta pop/push
  • Cambio de tamaño de pantalla, generalmente debido a la apariencia del teclado o al cambio de orientación
  • El widget principal recreó a su hijo.
  • Un Widget heredado del que depende el widget ( Class.of(context)cambio de patrón)

Esto significa que el buildmétodo no debe activar una llamada http ni modificar ningún estado .


¿Cómo se relaciona esto con la pregunta?

El problema al que se enfrenta es que su método de compilación tiene efectos secundarios/no es puro, lo que hace que las llamadas de compilación extrañas sean problemáticas.

En lugar de impedir las llamadas de compilación, debe hacer que su método de compilación sea puro, de modo que pueda llamarse en cualquier momento sin impacto.

En el caso de su ejemplo, transformaría su widget en un StatefulWidgetluego extraería esa llamada HTTP a initStatesu State:

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  Future<int> future;

  @override
  void initState() {
    super.initState();
    future = Future.value(42);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: future,
      builder: (context, snapshot) {
        // create some layout here
      },
    );
  }
}

Esto ya lo sé. Vine aquí porque realmente quiero optimizar las reconstrucciones.

También es posible crear un widget capaz de reconstruirse sin obligar a sus hijos a construir también.

Cuando la instancia de un widget permanece igual; Flutter deliberadamente no reconstruirá a los niños. Implica que puede almacenar en caché partes de su árbol de widgets para evitar reconstrucciones innecesarias.

La forma más sencilla es utilizar constconstructores de dardos:

@override
Widget build(BuildContext context) {
  return const DecoratedBox(
    decoration: BoxDecoration(),
    child: Text("Hello World"),
  );
}

Gracias a esa constpalabra clave, la instancia de DecoratedBoxpermanecerá igual incluso si la compilación se llamó cientos de veces.

Pero puedes lograr el mismo resultado manualmente:

@override
Widget build(BuildContext context) {
  final subtree = MyWidget(
    child: Text("Hello World")
  );

  return StreamBuilder<String>(
    stream: stream,
    initialData: "Foo",
    builder: (context, snapshot) {
      return Column(
        children: <Widget>[
          Text(snapshot.data),
          subtree,
        ],
      );
    },
  );
}

En este ejemplo, cuando se notifica a StreamBuilder sobre nuevos valores, subtreeno se reconstruirá incluso si StreamBuilder/Column lo hace. Sucede porque, gracias al cierre, la instancia de MyWidgetno cambió.

Este patrón se usa mucho en animaciones. Los usos típicos son AnimatedBuildery todas las transiciones como AlignTransition.

También puede almacenar subtreeen un campo de su clase, aunque es menos recomendado ya que interrumpe la función de recarga en caliente.

Rémi Rousselet avatar Sep 10 '2018 00:09 Rémi Rousselet

Puede evitar llamadas de compilación no deseadas de esta manera

  1. Crear clase secundaria con estado completo para una pequeña parte individual de la interfaz de usuario

  2. Utilice la biblioteca del proveedor , de modo que pueda detener las llamadas a métodos de compilación no deseados.

En las siguientes situaciones, llame al método de compilación

  • Después de llamar a initState
  • Después de llamar a didUpdateWidget
  • cuando se llama a setState() .
  • cuando el teclado está abierto
  • cuando la orientación de la pantalla cambió
  • Si se construye el widget principal, el widget secundario también se reconstruye
Sanjayrajsinh avatar Jan 28 '2020 10:01 Sanjayrajsinh