我有一个页面可以推送另一个页面,并等待返回/弹出的值进行处理。我希望第二页能够进行委派(让另一页返回值)。像这样:
// page 1
Navigator.of(context).push(Page2()).then((value) => print(value)); // process value; perhaps setState or whatever
// page 2
Navigator.of(context).pushReplacement(Page3()); // let another page handle the rest
// page 3
Navigator.of(context).pop(99);
但是,第一页上的未来永远不会返回,并且永远不会打印(或以我认为合适的方式处理)值。之所以使用pushReplacement,是因为我不希望用户回到第2页。
这是一个flutter示例应用程序,您可以尝试https://dartpad.dartlang.org/flutter来了解我的意思。
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Page1(),
);
}
}
class Page1 extends StatefulWidget {
@override
Page1State createState() => Page1State();
}
class Page1State extends State<Page1> {
List<int> numbers = [];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text('Page 1'),
...numbers.map((number) => Text(number.toString())),
RaisedButton(
child: Text('go to page 2'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Page2()))
.then((number) {
if (number != null) {
setState(() {
numbers.add(number);
});
}
});
},
),
],
),
),
);
}
}
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text('Ready?'),
RaisedButton(
child: Text('Yes'),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => Page3()));
},
),
RaisedButton(
child: Text('No'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
);
}
}
class Page3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text('Pick a number'),
...[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(
(number) => RaisedButton(
child: Text(number.toString()),
onPressed: () => Navigator.of(context).pop(number),
),
),
RaisedButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
);
}
}
我必须弄清楚用例。第一页将从服务器或其他任何设备上获取数据,因此我将使用FutureBuilder或类似的设备。第二页是用于创建/添加其他项目的表单。提交表单,如果服务器对数据满意,则响应将包含新创建的数据。然后,我想用显示数据的成功页面替换当前表单页面。当用户弹出此页面时,我希望使用数据更新第一页的列表。
可以使用Bloc,ValueNotifier等类似的状态管理功能,但是此页面寿命很短,我希望使用尽可能少的样板代码来实现此功能。另外,我发现,如果需要访问其他页面,则需要在MaterialApp上方/之前添加使用这些状态管理类。对于可能深藏在我的应用程序中的页面,我想避免在main中添加Bloc / ValueNotifier /任何内容。只是觉得它太重要了。
答案 0 :(得分:0)
您可以给pushReplacement
一个结果来返回,就像弹出一样。
因此,在这种情况下,您可以做的是再次给它一个将来(例如,使用Completer),以便原始推送小部件可以再次等待其真实结果
void method() async {
final completer = Completer();
final result = await navigator.pushReplacement(route, result: completer.future);
completer.complete(result);
}
答案 1 :(得分:0)
在这种情况下,您不能使用navigator.pushReplacement,因为它会触发Page1中的回调运行,并且该回调将不再可用。回调需要从/通过Page2获取值;只有从Page2中调用Navigator.pop(context)时,才会调用Page1中的'then'。
// replace this
Navigator.of(context).pushReplacement(Page3());
// to this
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page3()));
现在您需要弹出2次才能从Page3转到Page1:
不幸的是,这不可能-只需-使用Navigator.of(context).popUntil。
A)丑陋且冒险的版本-不需要命名路由
Navigator.of(context).pop();
Navigator.of(context).pop(number);
也许您也应该在第一个弹出窗口中包含返回值(数字),以确保正确到达。 不确定第二行中的弹出窗口是否会在此解决方案之后结束!
B)也要在第2页中等待导航响应(如第1页),然后在第3页中弹出一次,然后在第2页中-为此不需要命名路由
也许您可以实现以下内容(即使使用命名的路由):Flutter popuntil with return data
C)将Page1设置为命名路线,并使用路线设置传递数据
(由于渲染效果不佳,我在代码中将mainAxisAlignment参数设置了几个位置)
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Page1(),
initialRoute: '/page1',
onGenerateRoute: (settings) {
switch (settings.name) {
case '/page1':
return MaterialPageRoute(
settings: RouteSettings(name: '/page1', arguments: Map()),
builder: (_) => Page1(),
);
default:
return null;
}
},
);
}
}
class Page1 extends StatefulWidget {
@override
Page1State createState() => Page1State();
}
class Page1State extends State<Page1> {
List<int> numbers = [];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Page 1'),
...numbers.map((number) => Text(number.toString())),
RaisedButton(
child: Text('go to page 2'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => Page2())).then((_) {
final Map arguments = ModalRoute.of(context).settings.arguments as Map;
final int number = arguments['number'] ?? null;
if (number != null) {
setState(() {
numbers.add(number);
});
}
});
},
),
],
),
),
);
}
}
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Ready?'),
RaisedButton(
child: Text('Yes'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page3()));
},
),
RaisedButton(
child: Text('No'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
);
}
}
class Page3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Pick a number'),
...[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(
(number) => RaisedButton(
child: Text(number.toString()),
onPressed: () {
Navigator.of(context).popUntil((route) {
if (route.settings.name == '/page1') {
(route.settings.arguments as Map)['number'] = number;
return true;
} else {
return false;
}
});
},
),
),
RaisedButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
);
}
}
尽可能使用命名的路由,并使用整体模式或提供者模式实施一些更高级别,更严格的路由,状态,事件处理功能。
现在要花很长时间来解释这些,只是寻找这些。
无论如何,在我看来,这似乎是一个xy问题,一个设计错误。整个问题似乎源于设计缺陷。如果给定的示例代码不是仅为测试而创建的,那么我认为您应该用flutter dialog代替Page2,否则您有很多其他机会可以避免陷入这种极端情况。