我一直很容易地使用Navigator.pop
返回数据到上一个屏幕。然而,如果我使用Navigator.popUntil
,有什么可接受的方法将一个对象传回目标屏幕?
我一直很容易地使用Navigator.pop
返回数据到上一个屏幕。然而,如果我使用Navigator.popUntil
,有什么可接受的方法将一个对象传回目标屏幕?
RouteSettings
中使用arguments
将数据传递回特定的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
,否则它将为null,这就是(1)的目的。
popUntil
时,它将一直执行到定义屏幕的下一个步骤。我正在收到 null 值。.then((value) { print(value); });
- ashutoshresult
参数后,应该通过 (ModalRoute.of(context)!.settings.arguments as Map).clear()
清除地图。 - Michał Dobi Dobrzański (route.settings.arguments as Map)['result'] = 'something';
中显示错误:List<Object?>
不是类型为 Map<dynamic, dynamic>
的子类型。 - Moony-Stary这是不可能的。而且也没有意义,因为结果应该返回到推送路由的路由。
如果你真的必须这样做,使用多个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
popUntil
不会。因此,如果我们能够弹出并传递数据,那就更好了。 - Dinesh BalasubramanianNavigator
的方式可能存在概念问题。 - boformer加上 @hunghd 答案 确保你将要弹回的 屏幕设置
不能有一个 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
来改进。在Flutter团队提供解决方案之前,您可以使用shared_preferences来实现。
static const isBackFromPopUntil = "isBackFromPopUntil";
Future<void> saveIsBackFromPopUntil(bool isBack) async =>
await preferences.setBool(isBackFromPopUntil, isBack);
bool getIsBackFromPopUntil() =>
preferences.getBool(isBackFromPopUntil) ?? false;
popUntil
关闭时,您需要检查“某些内容”,否则不需要。 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));
}