Flutter - 使用 .popUntil 传回数据

问题描述 投票:0回答:6

我一直在使用

Navigator.pop
并轻松地将数据传回 1 个屏幕。但是,如果我使用
Navigator.popUntil
,将对象传递回目标屏幕的可接受方式是什么?

flutter dart navigator
6个回答
26
投票

您可以在

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)的目的


2
投票

没有。这也没有意义,因为结果应该返回给推送该路由的路由。

如果您确实必须这样做,请使用多个

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

0
投票

加上@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:  {}),
      );

0
投票

这是单次论证阅读的解决方案。由于@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
来改进。


0
投票

如果您正在使用命名路由,那么这里是一个完全有效的答案(Flutter3.16)

  1. 在导航到主页或首页时传递空地图“{}”作为参数(在我的例子中它将是 UserList

    Navigator.of(context).pushNamed(RouteConst.kUserList,arguments: {}),
    
  2. 现在,在最终用户创建屏幕之前,将再推送 2 个屏幕(示例)

  3. 最后,在“用户创建”屏幕中,我使用此代码使用 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;
       }
    });
    

正常工作的代码让我知道您是否仍然卡在某个地方。


-1
投票

对于 Flutter 团队提供的解决方案,您可以使用共享首选项来完成。

  1. 添加带有函数的类来获取值并将其保存到shared_preferences。
  static const isBackFromPopUntil = "isBackFromPopUntil";

  Future<void> saveIsBackFromPopUntil(bool isBack) async =>
      await preferences.setBool(isBackFromPopUntil, isBack);

  bool getIsBackFromPopUntil() =>
      preferences.getBool(isBackFromPopUntil) ?? false;
  1. 例如,当模态底部表单从
    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);
    }
  }
  1. 您从屏幕返回,但使用
    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));
  }
© www.soinside.com 2019 - 2024. All rights reserved.