Forzar al navegador Flutter a recargar el estado al hacer estallar
Tengo uno StatefulWidget
en Flutter con un botón, que me lleva a otro StatefulWidget
usando 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:
- pop para eliminar el segundo widget (el actual)
- pop nuevamente para eliminar el primer widget (el anterior)
- Empuje el primer widget (debería forzar el redibujado)
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 global
como 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.
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")));