我一直在使用
Navigator.pop
并轻松地将数据传回 1 个屏幕。但是,如果我使用 Navigator.popUntil
,将对象传递回目标屏幕的可接受方式是什么?
您可以在
arguments
中使用 RouteSettings
将数据传回特定的 Route
。
例如:
// in MaterialApp
MaterialApp(
onGenerateRoute: (settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(
settings: RouteSettings(name: '/', arguments: Map()), // (1)
builder: (_) => HomePage()
);
}
}
)
// in HomePage
Navigator.of(context).push(MaterialPageRoute(builder: (_) => StuffPage())
.then(_) {
final arguments = ModalRoute.of(context).settings.arguments as Map;
final result = arguments['result'];
};
// in StuffPage
Navigator.of(context).popUntil((route) {
if (route.settings.name == '/') {
(route.settings.arguments as Map)['result'] = 'something';
return true;
} else {
return false;
}
});
注意:你必须初始化
arguments
,否则它将为空,这就是(1)的目的
没有。这也没有意义,因为结果应该返回给推送该路由的路由。
如果您确实必须这样做,请使用多个
pop
调用:
// Widget of Route A:
String resultOfC = await Navigator.push(context, routeB);
// Widget of Route B:
String resultOfC = await Navigator.push<String>(context, routeC);
Navigator.pop(context, resultOfC); // pass result of C to A
// Widget of Route C:
Navigator.pop(context, 'my result'); // pass result to B
加上@hunghd answer's,确保您将弹出的
screen settings
不能有const
键。否则你将面临以下错误:
Unhandled Exception: Unsupported operation: Cannot modify unmodifiable map
我刚刚从以下代码的设置中删除了
const
键,用户将在其中弹出。
case HomeViewRoute:
return MaterialPageRoute(
builder: (context) => HomePage(),
settings: RouteSettings(name: HomeViewRoute, arguments: {}),
);
这是单次论证阅读的解决方案。由于@hunghd 提出的解决方案没有考虑到这一点。这里的技巧是消耗参数一次,而不是多次,因为这可能会导致错误的行为。
如果我们想“返回单一结果”,我们应该做以下事情。我们可以分三步起草以下引擎:
第一步 - 在 Main.app 中设置:
MaterialApp(
onGenerateRoute: (settings) {
switch (settings.name) {
case Routes.HOME: // Or your other path, like "/"
return generateRouteWithMapArguments(
Routes.HOME,
HomePage(), // A page Widget
);
// Other switch cases
}
}
)
// util:
static MaterialPageRoute generateRouteWithMapArguments(
String routeName,
Widget page,
) =>
MaterialPageRoute(
settings: RouteSettings(name: routeName, arguments: Map()),
builder: (context) => page,
);
第二步 - 执行导航:
const String ARGUMENTS_REMOVED = "ARGUMENTS_REMOVED";
void _navigateToDetails(BuildContext context) async {
// navigate here help with the help of Navigator.of(context).push() or GetX
await Navigator.of(context).push(...)
var result = ArgsUtils.consumeArgument(
context,
ARGUMENTS_REMOVED,
() {
// HANDLE SINGLE SHOT HERE
},
);
}
// utils:
class ArgsUtils {
static T readArgs<T>(BuildContext context) {
return ModalRoute.of(context)!.settings.arguments as T;
}
static bool consumeArgument(
BuildContext context,
String argument,
VoidCallback onArgument,
) {
var map = readArgs<Map>(context);
if (map.containsKey(argument)) {
onArgument.call();
map.clear();
return true;
}
return false;
}
}
*第三步 - 传回参数:
// passing back. Call this method two pages after navigating deeper from HOME
NavigatorUtils.popTwoPagesWithArgument(
context,
routeName: Routes.HOME,
argument: ARGUMENTS_REMOVED,
);
// util:
class NavigatorUtils {
// you can pop more pages here or add different predicate:
static void popTwoPagesWithArgument(
final BuildContext context, {
required final String routeName,
required final String argument,
}) {
int count = 0;
Navigator.of(context).popUntil((route) {
if (route.settings.name == routeName &&
route.settings.arguments != null) {
(route.settings.arguments! as Map)[argument] = true;
}
return count++ >= 2;
});
}
}
当然这个解决方案是多余的
bool
。这就提出了是否需要 Map
的问题。对于单次情况,我相信可以通过使用 Set<String>
而不是 Map
来改进。
如果您正在使用命名路由,那么这里是一个完全有效的答案(Flutter3.16)
在导航到主页或首页时传递空地图“{}”作为参数(在我的例子中它将是 UserList
Navigator.of(context).pushNamed(RouteConst.kUserList,arguments: {}),
现在,在最终用户创建屏幕之前,将再推送 2 个屏幕(示例)
最后,在“用户创建”屏幕中,我使用此代码使用 popUntil,在其中我将返回用户的全名。
Navigator.of(context).popUntil((route) {
if (route.settings.name == RouteConst.kUserList) {
(route.settings.arguments as Map)['name'] = "Full Name";
return true;
}
else {
return false;
}
});
正常工作的代码让我知道您是否仍然卡在某个地方。
对于 Flutter 团队提供的解决方案,您可以使用共享首选项来完成。
static const isBackFromPopUntil = "isBackFromPopUntil";
Future<void> saveIsBackFromPopUntil(bool isBack) async =>
await preferences.setBool(isBackFromPopUntil, isBack);
bool getIsBackFromPopUntil() =>
preferences.getBool(isBackFromPopUntil) ?? false;
popUntil
关闭时,您需要检查“someting”,否则不需要。 void _openBottomSheet(BuildContext context) async {
await showCupertinoModalBottomSheet(
context: context,
isDismissible: true,
...
);
final isBack = _prefHelper.getIsBackFromPopUntil();
if (isBack) {
// do stuff
await _prefHelper.saveIsBackFromPopUntil(false);
}
}
popUntil
。void _popUntilDetailSavingPlan(BuildContext context) async {
final routeName = 'your name route that have bottom sheet (in the number 2 above)';
await _prefHelper.saveIsBackFromPopUntil(true);
Navigator.popUntil(context, ModalRoute.withName(routeName));
}