弹出时强制颤振导航器重新加载状态

我在Flutter with按钮中有一个StatefulWidget,它使用Navigator.push()导航到另一个StatefulWidget。在第二个小部件上,我正在更改全局状态(一些用户首选项)。当我从第二个小部件返回到第一个小部件时,使用Navigator.pop(),第一个小部件处于旧状态,但我想强制它重新加载。知道怎么做吗?我有一个想法,但看起来很难看:

  1. 弹出以删除第二个小部件(当前小部件)
  2. 再次弹出以删除第一个小部件(前一个)
  3. 推送第一个小部件(它应该强制重绘)
214206 次浏览

你可以在弹出上下文时返回dynamic result,然后在值为true时调用setState((){}),否则就保持状态不变。

我粘贴了一些代码片段供您参考。

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;
}
< p >问候, Mahi < / p >

这里有几件事你可以做。@Mahi的回答虽然正确,但可以更简洁一点,实际上使用push而不是showDialog,因为OP正在询问。下面是一个使用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(),
),
);

但是,还有另一种方法可以很好地满足您的用例。如果你正在使用global来影响你的第一个页面的构建,你可以使用InheritedWidget来定义你的全局用户首选项,每次他们被改变你的FirstPage都会重新构建。这甚至可以在无状态小部件中工作,如下所示(但也应该可以在有状态小部件中工作)。

在flutter中inheritedWidget的一个例子是应用程序的Theme,尽管他们在小部件中定义了它,而不是像我这里这样直接构建它。

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(),
),
);

如果您使用继承的小部件,则不必担心查看所推送的页面是否弹出,这适用于基本用例,但在更复杂的场景中可能最终会出现问题。

您可以使用pushReplacement并指定新的Route

简短的回答:

在第一页使用这个:

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

在第二页

Navigator.pop(context);

有两个东西,传递数据

  • 第一页到第二页

    在第一页使用这个

      // 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");
    

    在第一页使用这个,它是相同的,但有一点修改。

      // receiving "Bar" in 1st
    String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
    

把这个放在你要推到第二个屏幕的地方(在一个异步函数中)

Function f;
f= await Navigator.pushNamed(context, 'ScreenName');
f();

把这个放在你要弹的地方

Navigator.pop(context, () {
setState(() {});
});

pop闭包中调用setState来更新数据。

需要强制重建我的一个无状态小部件。不想使用有状态的。我想出了这个解决方案:

await Navigator.of(context).pushNamed(...);
ModalRoute.of(enclosingWidgetContext);

注意,context和enclosingWidgetContext可以是相同的上下文,也可以是不同的上下文。例如,如果你从StreamBuilder内部推送,它们将是不同的。

我们在这里没有用ModalRoute做任何事情。仅订阅行为就足以强制重新构建。

我的解决方案是在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);

所有需要从前一页重新加载的内容都应该在弹出窗口后更新。

如果您正在使用警报对话框,那么您可以使用一个Future,该Future在对话框结束时完成。完成以后可以强制小部件重新加载状态。

第一页

onPressed: () async {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
....
);
}
);
setState(() {});
}

在警报对话框中

Navigator.of(context).pop();

这个工作真的很好,我从这个文档从颤振页:颤振医生

我定义了从第一个页面控制导航的方法。

_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(() {});
}
}

所以,我调用它在一个动作方法从一个墨水瓶,但也可以调用从一个按钮:

onTap: () {
_navigateAndDisplaySelection(context);
},

最后在第二页,返回一些东西(我返回了一个bool,你可以返回任何你想要的):

onTap: () {
Navigator.pop(context, true);
}

简单的技巧是使用Navigator.pushReplacement方法

第1页

Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Page2(),
),
);

第二页

Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Page1(),
),
);

这段简单的代码让我找到根目录并重新加载状态:

    ...
onPressed: () {
Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/'));
},
...

简而言之,您应该让小部件监视状态。为此,您需要进行状态管理。

我的方法基于颤振架构样本颤振文档中解释的Provider。请参考他们更简洁的解释,但或多或少的步骤是:

  • 用小部件需要观察的状态定义状态模型。

你可以有多个状态,比如dataisLoading,来等待某个API进程。模型本身扩展了ChangeNotifier

  • 用监控器类包装依赖于这些状态的小部件。

它可以是ConsumerSelector

  • 当你需要“重新加载”时,你基本上更新了这些状态并发布了这些变化。

对于状态模型,类大致如下所示。注意广播更改的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提供。Selector和Consumer分别监视isLoadingdata的状态。有它们之间差别不大,但就个人而言,如何使用它们取决于它们的构建器提供的内容。Consumer提供了对状态模型的访问,因此对于它下面的任何小部件来说,调用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);
}

在这种情况下,不管你如何弹出(与按钮在UI或"后退"在android)更新将完成。

对我来说,这似乎奏效:

Navigator.of(context).pushNamed("/myRoute").then((value) => setState(() {}));

然后在子对象中调用Navigator.pop()

onTapFunction(BuildContext context) async {
final reLoadPage = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => IdDetailsScreen()),
);


if (reLoadPage) {
setState(() {});
}
}

现在,当从第二页执行Navigator.pop返回到第一页时,只返回一些值,在我的情况下是bool类型

onTap: () {
Navigator.pop(context, true);
}

只需在Navigator之后添加.then((value) { setState(() {});即可。按page1(),如下所示:

Navigator.push(context,MaterialPageRoute(builder: (context) => Page2())).then((value) { setState(() {});

现在,当你从page2使用Navigator.pop(context)时,你的page1会自行重建

非常简单地使用“then”;在你推送之后,当导航器弹出回来时,它会触发setState并且视图会刷新。

Navigator.push(blabla...).then((value) => setState(() {}))

我也遇到过类似的问题。

请试试这个:

第一页:

Navigator.push( context, MaterialPageRoute( builder: (context) => SecondPage()), ).then((value) => setState(() {}));

当你从SecondPage()跳转回FirstPage()后,“then”;语句将运行并刷新页面。

在flutter 2.5.2中,这对我来说是有效的,它也适用于更新列表

Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage()))
.then((value) => setState(() {}));

然后在第二页写代码

Navigator.pop(context);

我在第一页中有一个ListView,它显示了一个list[]数据,第二页正在更新我的list[]数据,所以上面的代码适用于我。

// Push to second screen
await Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => SecondScreen(),
),
);


// Call build method to update any changes
setState(() {});

在导航推送代码中使用setstate。

Navigator.push(context, MaterialPageRoute(builder: (context) => YourPage())).then((value) {
setState(() {
// refresh state
});
});