У меня есть один StatefulWidget
в флаттера с помощью кнопки, которая управляет , меня к другому , StatefulWidget
используя Navigator.push()
. Во втором виджете я меняю глобальное состояние (некоторые пользовательские настройки). Когда я возвращаюсь от второго виджета к первому, Navigator.pop()
первый виджет находится в старом состоянии, но я хочу принудительно перезагрузить его. есть идеи как это сделать? У меня есть одна идея, но выглядит она некрасиво:
- pop, чтобы удалить второй виджет (текущий)
- нажмите еще раз, чтобы удалить первый виджет (предыдущий)
- нажмите первый виджет (он должен принудительно перерисовать)
Ответы:
Здесь вы можете сделать пару вещей. Ответ @Mahi, хотя и правильный, может быть немного более лаконичным и фактически использовать push, а не showDialog, как спрашивал OP. Это пример, в котором используются
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(), ), );
Однако есть другой способ сделать это, который может хорошо соответствовать вашему варианту использования. Если вы используете
global
как что-то, что влияет на сборку вашей первой страницы, вы можете использовать InheritedWidget для определения ваших глобальных пользовательских предпочтений, и каждый раз, когда они меняются, ваша FirstPage будет перестраиваться. Это работает даже в виджете без состояния, как показано ниже (но должно работать и с виджетом с сохранением состояния).Примером унаследованного виджета во флаттере является тема приложения, хотя они определяют ее в виджете, а не напрямую, как я здесь.
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(), ), );
Если вы используете унаследованный виджет, вам не нужно беспокоиться о том, чтобы следить за появлением всплывающей страницы, которую вы нажали, что будет работать для базовых сценариев использования, но может привести к проблемам в более сложном сценарии.
источник
Есть 2 вещи, передающие данные из
1-я страница - 2-я
Используйте это на 1-й странице
// sending "Foo" from 1st Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
Используйте это на 2-й странице.
class Page2 extends StatelessWidget { final String string; Page2(this.string); // receiving "Foo" in 2nd ... }
2-я страница на 1-ю
Используйте это на 2-й странице
// sending "Bar" from 2nd Navigator.pop(context, "Bar");
Используйте это на 1-й странице, это то же самое, что использовалось ранее, но с небольшими изменениями.
// receiving "Bar" in 1st String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
источник
Вы можете использовать pushReplacement и указать новый маршрут
источник
Easy Trick является использование Navigator.pushReplacement метод
Страница 1
Страница 2
источник
Эта работа действительно хороша, я почерпнул из этого документа со страницы flutter : flutter doc
Я определил метод управления навигацией с первой страницы.
_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(() {}); } }
Итак, я вызываю его в методе действия из чернильницы, но его также можно вызвать с помощью кнопки:
И, наконец, на второй странице, чтобы что-то вернуть (я вернул bool, вы можете вернуть все, что хотите):
onTap: () { Navigator.pop(context, true); }
источник
await Navigator....
и опустить значение результата в pop.Поместите это туда, где вы нажимаете на второй экран (внутри асинхронной функции)
Function f; f= await Navigator.pushNamed(context, 'ScreenName'); f();
Поместите это туда, где вы появляетесь
setState
Называется внутриpop
закрытия , чтобы обновить данные.источник
setState
приводите в качестве аргумента? Вы в основном звонитеsetState
внутри закрытия. Не передавая это как аргумент.Navigator.pop
.Мое решение пошло путем добавления параметра функции на SecondPage, затем была получена функция перезагрузки, которая выполняется с FirstPage, а затем функция была выполнена перед строкой Navigator.pop (context).
Первая страница
refresh() { setState(() { //all the reload processes }); }
затем при переходе на следующую страницу ...
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage(refresh)),);
SecondPage
final Function refresh; SecondPage(this.refresh); //constructor
затем перед всплывающей строкой навигатора,
widget.refresh(); // just refresh() if its statelesswidget Navigator.pop(context);
Все, что нужно перезагрузить с предыдущей страницы, должно быть обновлено после всплывающего окна.
источник
Вы можете передать обратно a,
dynamic result
когда вы открываете контекст, а затем вызвать,setState((){})
когда значение, вtrue
противном случае, просто оставьте состояние как есть.Я вставил несколько фрагментов кода для вашей справки.
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; }
С уважением, Махи
источник
Navigator.pop(context, true)
, не так ли? Но как я могу получить этуtrue
ценность? ИспользуяBuildContext
?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;
перед каждым вызовомsetState((){});
этот способ, чтобы избежать обновления удаленных виджетов или виджетов, которые больше не активны.Требуется принудительное перестроение одного из моих виджетов без сохранения состояния. Не хотел использовать stateful. Придумал это решение:
await Navigator.of(context).pushNamed(...); ModalRoute.of(enclosingWidgetContext);
Обратите внимание, что context и enclosingWidgetContext могут быть одинаковыми или разными контекстами. Если, например, вы нажимаете изнутри StreamBuilder, они будут другими.
Здесь мы ничего не делаем с ModalRoute. Одного действия по подписке достаточно для принудительного восстановления.
источник
Если вы используете диалоговое окно с предупреждением, вы можете использовать Future, который завершается, когда диалоговое окно закрывается. После завершения будущего вы можете заставить виджет перезагрузить состояние.
Первая страница
onPressed: () async { await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( .... ); } ); setState(() {}); }
В диалоговом окне предупреждения
источник
Сегодня я столкнулся с той же ситуацией, но мне удалось решить ее гораздо проще: я просто определил глобальную переменную, которая использовалась в первом классе с сохранением состояния, и когда я перехожу ко второму виджету с отслеживанием состояния, я позволяю ему обновлять значение глобального переменная, которая автоматически вызывает обновление первого виджета. Вот пример (я написал его в спешке, поэтому я не поставил каркас или приложение для материалов, я просто хотел проиллюстрировать свою точку зрения):
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++ ; }, ), }
источник
Navigator.pop()
он будет сохранять состояниеNavigator.push()
доsetState
повторного вызова, чего не происходит в этом коде. Я что-то упускаю?Этот простой код помог мне перейти в корень и перезагрузить состояние:
... onPressed: () { Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/')); }, ...
источник