Flutter -使用.popUntil传回数据

7cjasjjr  于 2023-01-18  发布在  Flutter
关注(0)|答案(5)|浏览(112)

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

jogvjijk

jogvjijk1#

您可以在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)的目的

6vl6ewon

6vl6ewon2#

没有,这也没有意义,因为结果应该返回给推动路由的路由。
如果您真的必须这样做,请使用多个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
uyhoqukh

uyhoqukh3#

plus @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:  {}),
      );
vohkndzv

vohkndzv4#

以下是单次参数阅读的解决方案。因为@hunghd提出的解决方案没有考虑到这一点。这里的技巧是消耗参数一次,而不是多次,因为这可能会导致错误的行为。
如果我们想“返回单一结果”,我们应该做以下事情。我们可以分三步起草以下**引擎 *:

第一步-在www.example.com中进行设置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来改善。

sf6xfgos

sf6xfgos5#

对于Flutter团队提供之前的变通方案,您可以使用shared_preferences执行此操作。
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));
  }

相关问题