Forzar al navegador Flutter a recargar el estado al hacer estallar

Resuelto bartektartanus asked hace 6 años • 24 respuestas

Tengo uno StatefulWidgeten Flutter con un botón, que me lleva a otro StatefulWidgetusando Navigator.push(). En el segundo widget, estoy cambiando el estado global (algunas preferencias del usuario). Cuando vuelvo del segundo widget al primero, el uso Navigator.pop()del primer widget está en estado antiguo, pero quiero forzar su recarga. ¿Alguna idea de cómo hacer esto? Tengo una idea pero se ve fea:

  1. pop para eliminar el segundo widget (el actual)
  2. pop nuevamente para eliminar el primer widget (el anterior)
  3. Empuje el primer widget (debería forzar el redibujado)
bartektartanus avatar Apr 13 '18 02:04 bartektartanus
Aceptado

Hay un par de cosas que puedes hacer aquí. La respuesta de @Mahi, aunque correcta, podría ser un poco más concisa y, de hecho, usar push en lugar de showDialog como preguntaba el OP. Este es un ejemplo que utiliza Navigator.push:

import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () => Navigator.pop(context),
            child: Text('back'),
          ),
        ],
      ),
    );
  }
}

class FirstPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new FirstPageState();
}

class FirstPageState extends State<FirstPage> {

  Color color = Colors.white;

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: color,
      child: Column(
        children: <Widget>[
          RaisedButton(
            child: Text("next"),
            onPressed: () async {
              final value = await Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => SecondPage()),
                ),
              );
              setState(() {
                color = color == Colors.white ? Colors.grey : Colors.white;
              });
            },
          ),
        ],
      ),
    );
  }
}

void main() => runApp(
      MaterialApp(
        builder: (context, child) => SafeArea(child: child),
        home: FirstPage(),
      ),
    );

Sin embargo, hay otra forma de hacer esto que podría adaptarse bien a su caso de uso. Si está utilizando globalcomo algo que afecta la construcción de su primera página, puede usar un InheritedWidget para definir sus preferencias de usuario globales, y cada vez que se cambien, su FirstPage se reconstruirá. Esto incluso funciona dentro de un widget sin estado como se muestra a continuación (pero también debería funcionar en un widget con estado).

Un ejemplo de widget heredado en flutter es el tema de la aplicación, aunque lo definen dentro de un widget en lugar de compilarlo directamente como lo hago aquí.

import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () {
              ColorDefinition.of(context).toggleColor();
              Navigator.pop(context);
            },
            child: new Text("back"),
          ),
        ],
      ),
    );
  }
}

class ColorDefinition extends InheritedWidget {
  ColorDefinition({
    Key key,
    @required Widget child,
  }): super(key: key, child: child);

  Color color = Colors.white;

  static ColorDefinition of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ColorDefinition);
  }

  void toggleColor() {
    color = color == Colors.white ? Colors.grey : Colors.white;
    print("color set to $color");
  }

  @override
  bool updateShouldNotify(ColorDefinition oldWidget) =>
      color != oldWidget.color;
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var color = ColorDefinition.of(context).color;

    return new Container(
      color: color,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                Navigator.push(
                  context,
                  new MaterialPageRoute(builder: (context) => new SecondPage()),
                );
              }),
        ],
      ),
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(
              child: new ColorDefinition(child: child),
            ),
        home: new FirstPage(),
      ),
    );

Si usa un widget heredado, no tiene que preocuparse por observar la ventana emergente de la página que envió, que funcionará para casos de uso básicos pero puede terminar teniendo problemas en un escenario más complejo.

rmtmckenzie avatar Apr 12 '2018 21:04 rmtmckenzie

Respuesta corta:

Utilice esto en la primera página:

Navigator.pushNamed(context, '/page2').then((_) => setState(() {}));

y esto en la segunda página:

Navigator.pop(context);

Hay 2 cosas, pasar datos de

  • 1ra página a 2da

    Utilice esto en la primera página.

      // sending "Foo" from 1st
      Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
    

    Utilice esto en la segunda página.

      class Page2 extends StatelessWidget {
        final String string;
    
        Page2(this.string); // receiving "Foo" in 2nd
    
        ...
      }
    

  • 2da página a 1ra

    Utilice esto en la segunda página.

      // sending "Bar" from 2nd
      Navigator.pop(context, "Bar");
    

    Use esto en la primera página, es el mismo que se usó antes pero con pocas modificaciones.

      // receiving "Bar" in 1st
      String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
    
CopsOnRoad avatar Aug 01 '2019 12:08 CopsOnRoad