Ich habe eine StatefulWidget
in Flutter mit Knopf, die mich zu einem anderen navigiert StatefulWidget
mit Navigator.push()
. Beim zweiten Widget ändere ich den globalen Status (einige Benutzereinstellungen). Wenn ich vom zweiten zum ersten Widget zurückkehre, befindet sich die Verwendung Navigator.pop()
des ersten Widgets im alten Zustand, aber ich möchte das Neuladen erzwingen. Irgendeine Idee, wie das geht? Ich habe eine Idee, aber sie sieht hässlich aus:
- Pop, um das zweite Widget zu entfernen (aktuelles)
- Pop erneut, um das erste Widget zu entfernen (vorheriges)
- Erstes Widget drücken (es sollte das Neuzeichnen erzwingen)
Antworten:
Es gibt ein paar Dinge, die Sie hier tun könnten. @ Mahis Antwort, obwohl sie korrekt ist, könnte etwas prägnanter sein und tatsächlich Push anstelle von showDialog verwenden, nach dem das OP gefragt hat. Dies ist ein Beispiel, das Folgendes verwendet
Navigator.push
:import 'package:flutter/material.dart'; class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return new Container( color: Colors.green, child: new Column( children: <Widget>[ new RaisedButton( onPressed: () => Navigator.pop(context), child: new 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: new Column( children: <Widget>[ new RaisedButton( child: new Text("next"), onPressed: () { Navigator .push( context, new MaterialPageRoute(builder: (context) => new SecondPage()), ) .then((value) { setState(() { color = color == Colors.white ? Colors.grey : Colors.white; }); }); }), ], ), ); } } void main() => runApp( new MaterialApp( builder: (context, child) => new SafeArea(child: child), home: new FirstPage(), ), );
Es gibt jedoch eine andere Möglichkeit, die zu Ihrem Anwendungsfall passt. Wenn Sie das
global
als etwas verwenden, das sich auf die Erstellung Ihrer ersten Seite auswirkt, können Sie ein InheritedWidget verwenden , um Ihre globalen Benutzereinstellungen zu definieren. Bei jeder Änderung wird Ihre FirstPage neu erstellt. Dies funktioniert sogar in einem zustandslosen Widget, wie unten gezeigt (sollte aber auch in einem zustandsbehafteten Widget funktionieren).Ein Beispiel für vererbtes Widget im Flattern ist das Design der App, obwohl es innerhalb eines Widgets definiert wird, anstatt es wie hier direkt erstellen zu lassen.
import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return new Container( color: Colors.green, child: new Column( children: <Widget>[ new 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(), ), );
Wenn Sie ein geerbtes Widget verwenden, müssen Sie sich keine Gedanken darüber machen, ob die von Ihnen gepusste Seite angezeigt wird. Dies funktioniert für grundlegende Anwendungsfälle, kann jedoch in einem komplexeren Szenario zu Problemen führen.
quelle
Es gibt zwei Dinge, von denen Daten übergeben werden
1. Seite bis 2 ..
Verwenden Sie dies auf der 1. Seite
// sending "Foo" from 1st Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
Verwenden Sie dies auf der 2. Seite.
class Page2 extends StatelessWidget { final String string; Page2(this.string); // receiving "Foo" in 2nd ... }
2. Seite bis 1 ..
Verwenden Sie dies auf der 2. Seite
// sending "Bar" from 2nd Navigator.pop(context, "Bar");
Verwenden Sie dies auf der ersten Seite, es ist das gleiche, das früher verwendet wurde, aber mit wenig Modifikation.
// receiving "Bar" in 1st String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
quelle
Sie können pushReplacement verwenden und die neue Route angeben
quelle
Der einfache Trick besteht darin, die Navigator.pushReplacement- Methode zu verwenden
Seite 1
Seite 2
quelle
Diese Arbeit ist wirklich gut, ich habe von diesem Dokument von der Flatter-Seite bekommen: Flatter-Dokument
Ich habe die Methode zur Steuerung der Navigation von der ersten Seite an definiert.
_navigateAndDisplaySelection(BuildContext context) async { final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => AddDirectionPage()), ); //below you can get your result and update the view with setState //changing the value if you want, i just wanted know if i have to //update, and if is true, reload state if (result) { setState(() {}); } }
Also nenne ich es in einer Aktionsmethode aus einem Tintenfass, kann aber auch über eine Schaltfläche aufgerufen werden:
Und schließlich auf der zweiten Seite, um etwas zurückzugeben (ich habe einen Bool zurückgegeben, Sie können zurückgeben, was Sie wollen):
onTap: () { Navigator.pop(context, true); }
quelle
await Navigator....
einen Ergebniswert in Pop einfach und weglassen.Stellen Sie dies dort ein, wo Sie zum zweiten Bildschirm wechseln (innerhalb einer asynchronen Funktion).
Function f; f= await Navigator.pushNamed(context, 'ScreenName'); f();
Platzieren Sie dies dort, wo Sie auftauchen
Das
setState
wird innerhalb despop
Verschlusses aufgerufen , um die Daten zu aktualisieren.quelle
setState
als Argument vor? Sie rufensetState
im Grunde genommen in einem Verschluss an. Nicht als Argument übergeben.Navigator.pop
.Meine Lösung bestand darin, einen Funktionsparameter auf SecondPage hinzuzufügen, dann die Neuladefunktion zu erhalten, die von FirstPage ausgeführt wird, und dann die Funktion vor der Zeile Navigator.pop (Kontext) auszuführen.
Erste Seite
refresh() { setState(() { //all the reload processes }); }
dann auf die nächste Seite schieben ...
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage(refresh)),);
Zweite Seite
final Function refresh; SecondPage(this.refresh); //constructor
dann weiter vor der Navigator-Popline,
widget.refresh(); // just refresh() if its statelesswidget Navigator.pop(context);
Alles, was von der vorherigen Seite neu geladen werden muss, sollte nach dem Pop aktualisiert werden.
quelle
Sie können a zurückgeben,
dynamic result
wenn Sie den Kontext öffnen, und dann aufrufen,setState((){})
wenn der Werttrue
andernfalls den Status unverändert lässt.Ich habe einige Codefragmente als Referenz eingefügt.
handleClear() async { try { var delete = await deleteLoanWarning( context, 'Clear Notifications?', 'Are you sure you want to clear notifications. This action cannot be undone', ); if (delete.toString() == 'true') { //call setState here to rebuild your state. } } catch (error) { print('error clearing notifications' + error.toString()); } } Future<bool> deleteLoanWarning(BuildContext context, String title, String msg) async { return await showDialog<bool>( context: context, child: new AlertDialog( title: new Text( title, style: new TextStyle(fontWeight: fontWeight, color: CustomColors.continueButton), textAlign: TextAlign.center, ), content: new Text( msg, textAlign: TextAlign.justify, ), actions: <Widget>[ new Container( decoration: boxDecoration(), child: new MaterialButton( child: new Text('NO',), onPressed: () { Navigator.of(context).pop(false); }, ), ), new Container( decoration: boxDecoration(), child: new MaterialButton( child: new Text('YES', ), onPressed: () { Navigator.of(context).pop(true); }, ), ), ], ), ) ?? false; }
Grüße, Mahi
quelle
Navigator.pop(context, true)
, oder? Aber wie kann ich diesentrue
Wert erhalten? VerwendenBuildContext
?setState() called after dispose(): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. ....
if(!mounted) return;
vor jedem Aufruf aufsetState((){});
diese Weise verwendet, um zu vermeiden, dass die bereitgestellten Widgets oder Widgets, die nicht mehr aktiv sind, aktualisiert werden.Musste den Wiederaufbau eines meiner zustandslosen Widgets erzwingen. Wollte nicht stateful verwenden. Kam mit dieser Lösung:
await Navigator.of(context).pushNamed(...); ModalRoute.of(enclosingWidgetContext);
Beachten Sie, dass Kontext und einschließender WidgetContext dieselben oder unterschiedliche Kontexte sein können. Wenn Sie beispielsweise aus StreamBuilder heraus pushen, sind diese unterschiedlich.
Wir machen hier nichts mit ModalRoute. Das Abonnieren allein reicht aus, um den Wiederaufbau zu erzwingen.
quelle
Wenn Sie ein Warnungsdialogfeld verwenden, können Sie eine Zukunft verwenden, die abgeschlossen wird, wenn das Dialogfeld geschlossen wird. Nach Abschluss der Zukunft können Sie das Widget zwingen, den Status neu zu laden.
Erste Seite
onPressed: () async { await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( .... ); } ); setState(() {}); }
Im Alarmdialog
quelle
Heute war ich mit der gleichen Situation konfrontiert, aber es gelang mir, sie viel einfacher zu lösen. Ich habe nur eine globale Variable definiert, die in der ersten Stateful-Klasse verwendet wurde, und wenn ich zu einem zweiten Stateful-Widget navigiere, lasse ich sie den Wert der Global-Version aktualisieren Variable, die automatisch die Aktualisierung des ersten Widgets erzwingt. Hier ist ein Beispiel (ich habe es in Eile geschrieben, also habe ich kein Gerüst oder keine Material-App aufgestellt, ich wollte nur meinen Standpunkt veranschaulichen):
import 'package:flutter/material.dart'; int count = 0 ; class FirstPage extends StatefulWidget { FirstPage({Key key}) : super(key: key); @override _FirstPageState createState() => _FirstPageState(); } class _FirstPageState extends State<FirstPage> { @override Widget build(BuildContext context) { return InkWell( onTap(){ Navigator.of(context).push(MaterialPageRoute(builder: (context) => new SecondPage()); }, child: Text('First', style : TextStyle(fontSize: count == 0 ? 20.0 : 12.0)), ), } class SecondPage extends StatefulWidget { SecondPage({Key key}) : super(key: key); @override _SecondPageState createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> { @override Widget build(BuildContext context) { return IconButton( icon: new Icon(Icons.add), color: Colors.amber, iconSize: 15.0, onPressed: (){ count++ ; }, ), }
quelle
Navigator.pop()
es den Status vorher beibehalten hat,Navigator.push()
bissetState
es erneut aufgerufen wird, was in diesem Code nicht passiert. Vermisse ich etwasDieser einfache Code funktionierte für mich, um zum Stammverzeichnis zu gelangen und den Status neu zu laden:
... onPressed: () { Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/')); }, ...
quelle