我有一个StatefulWidget
在颤振按钮,导航我到另一个StatefulWidget
使用Navigator.push()
。在第二个小部件上,我正在更改全局状态(某些用户首选项)。当我从第二个窗口小部件返回到第Navigator.pop()
一个窗口小部件时,使用第一个窗口小部件处于旧状态,但是我想强制重新加载它。任何想法如何做到这一点?我有一个主意,但看起来很丑:
- 弹出以删除第二个小部件(当前一个)
- 再次弹出以删除第一个小部件(上一个)
- 推送第一个小部件(应强制重绘)
我有一个StatefulWidget
在颤振按钮,导航我到另一个StatefulWidget
使用Navigator.push()
。在第二个小部件上,我正在更改全局状态(某些用户首选项)。当我从第二个窗口小部件返回到第Navigator.pop()
一个窗口小部件时,使用第一个窗口小部件处于旧状态,但是我想强制重新加载它。任何想法如何做到这一点?我有一个主意,但看起来很丑:
Answers:
您可以在这里做几件事。@Mahi的正确答案可能会更简洁一些,实际上是在OP询问时使用push而不是showDialog。这是一个使用示例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都会重新生成。甚至可以在如下所示的无状态小部件中使用(但也应在有状态小部件中使用)。
应用程序的主题是flutter中的InheritedWidget的一个示例,尽管它们是在窗口小部件中定义的,而不是像我在此处那样直接构建它。
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(),
),
);
如果您使用继承的小部件,则不必担心要查看所推送页面的弹出状态,该弹出窗口适用于基本用例,但在更复杂的情况下最终可能会遇到问题。
有两件事,从
在第一页中使用
// sending "Foo" from 1st
Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
在第二页中使用它。
class Page2 extends StatelessWidget {
final String string;
Page2(this.string); // receiving "Foo" in 2nd
...
}
在第二页中使用此
// sending "Bar" from 2nd
Navigator.pop(context, "Bar");
在第1页中使用它,它与以前使用的相同,但几乎没有修改。
// receiving "Bar" in 1st
String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
这项工作真的很好,我从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(() {});
}
}
因此,我在墨水池的action方法中调用它,但也可以从按钮中调用它:
onTap: () {
_navigateAndDisplaySelection(context);
},
最后在第二页中,返回一些内容(我返回了布尔值,您可以返回任何想要的值):
onTap: () {
Navigator.pop(context, true);
}
await Navigator....
忽略pop中的结果值。
将其放在您要推送到第二个屏幕的位置(在异步功能内部)
Function f;
f= await Navigator.pushNamed(context, 'ScreenName');
f();
把它放在你弹出的地方
Navigator.pop(context, () {
setState(() {});
});
在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)),);
第二页
final Function refresh;
SecondPage(this.refresh); //constructor
然后在导航器弹出行之前
widget.refresh(); // just refresh() if its statelesswidget
Navigator.pop(context);
弹出后,应更新需要从上一页重新加载的所有内容。
您可以dynamic result
在弹出上下文时传递回a ,然后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;
}
问候,Mahi
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((){});
此方法之前都使用过,可以避免更新已处置的小部件或不再处于活动状态的小部件。
如果您使用的是警报对话框,则可以使用在关闭对话框时完成的Future。将来完成后,您可以强制窗口小部件重新加载状态。
第一页
onPressed: () async {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
....
);
}
);
setState(() {});
}
在警报对话框中
Navigator.of(context).pop();
今天,我遇到了同样的情况,但是我设法以更简单的方式解决了这个问题,我只定义了一个在第一个有状态类中使用的全局变量,当我导航到第二个有状态小部件时,我让它更新了全局值变量,它会自动强制第一个小部件进行更新。这是一个示例(我匆忙编写了它,所以我没有放置脚手架或材料应用程序,我只是想说明我的观点):
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('/'));
},
...
简而言之,您应该使小部件监视状态。您需要为此进行状态管理。
我的方法基于Flutter体系结构示例以及Flutter Docs中所述的Provider 。请参考它们以获得更简洁的说明,但步骤大致如下:
您可能有多个状态,例如data
和isLoading
,以等待某些API进程。模型本身会扩展ChangeNotifier
。
这可能是Consumer
或Selector
。
对于状态模型,该类大致如下所示。注意notifyListeners
哪个广播更改。
class DataState extends ChangeNotifier{
bool isLoading;
Data data;
Future loadData(){
isLoading = true;
notifyListeners();
service.get().then((newData){
isLoading = false;
data = newData;
notifyListeners();
});
}
}
现在为小部件。这将是非常简单的代码。
return ChangeNotifierProvider(
create: (_) => DataState()..loadData(),
child: ...{
Selector<DataState, bool>(
selector: (context, model) => model.isLoading,
builder: (context, isLoading, _) {
if (isLoading) {
return ProgressBar;
}
return Container(
child: Consumer<DataState>(builder: (context, dataState, child) {
return WidgetData(...);
}
));
},
),
}
);
状态模型的实例由ChangeNotifierProvider提供。选择器和消费者监视状态,分别为isLoading
和data
。有他们之间没有太大的区别,但个人如何使用它们将依靠什么他们的建设者提供。消费者提供对状态模型的访问,因此loadData
对于直接在其下的任何小部件调用都更加简单。
如果没有,则可以使用Provider.of
。如果我们想在从第二个屏幕返回时刷新页面,则可以执行以下操作:
await Navigator.push(context,
MaterialPageRoute(
builder: (_) {
return Screen2();
));
Provider.of<DataState>(context, listen: false).loadData();
对我来说工作:
...
onPressed: (){pushUpdate('/somePageName');}
...
pushUpdate (string pageName) async { //in the same class
await pushPage(context, pageName);
setState(() {});
}
//---------------------------------------------
//general sub
pushPage (context, namePage) async {
await Navigator.pushNamed(context, namePage);
}
在这种情况下,您的弹出方式(用户界面中的按钮或android中的“后退”)都无关紧要,更新将完成。