Flutter Navigator 2.0 - 当应用程序从URL启动时,路由器不接收深层链接?

7
我正在使用Flutter Navigator 2.0与嵌套路由 - 使用MaterialApp.router()创建一个主路由,使用Router()小部件和适当的RouterDelegates创建子路由(在底部导航栏中用作页面)。
在当前使用情况下,我想使用深链接打开嵌套路由中的页面,因此我按照说明进行了配置:
1. Android清单文件中接收自定义模式和主机URL 2. RouteInformationParser将其解析为我的模型 3. PlatformRouteInformationProvider通知所有嵌套的路由器有关路由更改的信息
当应用程序在前台时(例如在启动屏幕上),一切正常 - 我接收到pushRoute事件,并且根和嵌套Router小部件正确处理深链接。但是,当应用程序未启动时,我没有接收到设置为我的深链接的initialRoute,而是收到了默认的空RouteInformation。为什么会这样?以下是我的代码示例:
在Android清单文件中配置Flutter MainActivity
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:windowSoftInputMode="adjustResize">

            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />

            <meta-data
                android:name="io.flutter.embedding.android.SplashScreenDrawable"
                android:resource="@drawable/launch_background" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <!-- For deeplinking -->
            <meta-data
                android:name="flutter_deeplinking_enabled"
                android:value="true" />

            <!-- Accepts links in format myapp://*.myapp.com/ -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:host="*.myapp.com"
                    android:scheme="myapp" />
            </intent-filter>
        </activity>

主要应用程序类(在RunApp中使用):
class MyApp extends StatelessWidget {
  static PlatformRouteInformationProvider routeInformationProvider =
      PlatformRouteInformationProvider(
    initialRouteInformation: const RouteInformation(),
  );

  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => FutureBuilder(
        future: AppInit.initApp(),
        builder: (context, snapshot) {
          return _app();
        },
      );

  Widget _app() => MultiRepositoryProvider(
        providers: AppRepositoryProviders().list,
        child: MultiBlocProvider(
          providers: AppBlocProviders().list,
          child: MaterialApp.router(
            supportedLocales: LocalizationConfig.supportedLocales,
            localizationsDelegates: LocalizationConfig.localizationDelegates,
            theme: LightTheme().themeData,
            routerDelegate: UserSessionRouter(),
            routeInformationParser: AppRouteInformationParser(),
            routeInformationProvider: routeInformationProvider,
            backButtonDispatcher: RootBackButtonDispatcher(),
          ),
        ),
      );
    ...
}

RouteInformationParser 实现:

class AppRouteInformationParser extends RouteInformationParser<DeepLinkRoute> {
  @override
  Future<DeepLinkRoute> parseRouteInformation(
      RouteInformation routeInformation) async {
    if (routeInformation.location.isNullOrEmpty) {
      return DeepLinkRoute.none();
    } else {
      return DeepLinkParser.parse(routeInformation.location!).fold(
        (data) => DeepLinkRoute(
          link: data,
          route: _getRouteFromDeeplink(data),
        ),
        (error) => DeepLinkRoute.none(),
      );
    }
  }

  RouteDefinition _getRouteFromDeeplink(DeepLink deepLink) {
    switch (deepLink.path) {
      case '/auth/signup':
        return AppRoutes.authSignup;
      default:
        return AppRoutes.none;
    }
  }

  @override
  RouteInformation restoreRouteInformation(DeepLinkRoute configuration) =>
      RouteInformation(
        location: configuration.link.path,
        state: configuration.link.queryParams,
      );
}

嵌套(子)路由的屏幕:

class AuthScreen extends StatefulWidget {
  const AuthScreen({Key? key}) : super(key: key);

  @override
  _AuthScreenState createState() => _AuthScreenState();
}

class _AuthScreenState extends State<AuthScreen> {
  final AuthRouter _routerDelegate = AuthRouter();
  ChildBackButtonDispatcher? _backButtonDispatcher;

  @override
  void didChangeDependencies() {
    _initBackButtonDispatcher();
    super.didChangeDependencies();
  }

  void _initBackButtonDispatcher() {
    _backButtonDispatcher ??=
        ChildBackButtonDispatcher(context.router.backButtonDispatcher!);
    _backButtonDispatcher?.takePriority();
  }

  @override
  Widget build(BuildContext context) => MultiBlocProvider(
        providers: [
          BlocProvider(
              create: (context) => SigninScreenCubit(
                    usersRepository: context.read<UsersRepository>(),
                  ))
        ],
        child: Router(
          routerDelegate: _routerDelegate,
          backButtonDispatcher: _backButtonDispatcher,
          routeInformationParser: AppRouteInformationParser(),
          routeInformationProvider:
              MyApp.routeInformationProvider,
        ),
      );
}

我使用以下命令测试深层链接:

adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "$1"

使用给定的类,启动应用程序时的深链接始终为空。我的最佳猜测是 RouteInformationProvider 实例:
  static PlatformRouteInformationProvider routeInformationProvider =
      PlatformRouteInformationProvider(initialRouteInformation: const RouteInformation());

可能是某种配置错误,但我自己无法确定。

更新:

我已在iOS上进行了测试,令人惊讶的是,它与Android完全相反——当应用程序被杀死时,它可以正常使用深层链接打开,但当它在前台运行时,路由器(Router)永远不会更新,且路线信息提供程序(RouteInformationProvider)路线信息解析器(RouteInformationParser)都从未被调用。我在Flutter GitHub存储库中找到了一些相关问题,虽然它们已经关闭,但我认为它们并没有解决我的问题。这个问题似乎与我的问题几乎相同,但我查看了应该解决它的PR,我发现其他用户也报告了iOS上深层链接的问题。

1个回答

3

这是因为当应用程序刚刚打开时,您的routeInformationProvider使用空路由来覆盖该值,而不是从操作系统接收正确的路由。

将空的routeInformationProvider更改为Navigator默认使用的那个。

  static PlatformRouteInformationProvider routeInformationProvider =
      PlatformRouteInformationProvider(
    initialRouteInformation: RouteInformation(
        location: PlatformDispatcher.instance.defaultRouteName),
  );

记住你需要导入dart:ui

这将使用特定的行为,以便在启动应用程序时从操作系统接收路由并读取深度链接路由,以在android和iOS上运行。

这并不解决问题中提到的iOS问题。


1
谢谢您的回答!我已经尝试将初始路由值设置为不同的内容,但我不知道可以使用平台来以这种方式检索初始路由。我注意到使用您的解决方案与Flutter的开发通道似乎可以修复应用在后台时的深度链接处理问题。然而,它并没有解决iOS前台应用程序的问题。 - ikurek
我明白了。我没有硬件来测试iOS设备,所以在那方面无法帮助你。不过我很高兴这解决了Android的问题。 - Cavitedev
@Cavitedev -- 我已经成功让我的监听器识别通过深度链接传递的参数。然而,该监听器位于main()中,我无法使用Navigator 2.0 API路由到由从深度链接捕获的值确定的页面。 - satchel
我从未尝试在主方法上执行此操作,但我建议您在通过命令打开深度链接时调试解析器方法。通常会出现清单或接收到的输入问题。 - Cavitedev
@Cavitedev,感谢您的回复。我不太确定您的意思。我目前可以从main()中的监听器传递值。但是,我需要一种方法,在这些参数可用时转到特定路由。https://dev59.com/48Tsa4cB1Zd3GeqPI9lB - satchel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接