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

iku*_*rek 7 android deep-linking ios flutter flutter-navigation

我正在使用带有嵌套路由器的 Flutter Navigator 2.0 - 有一个通过调用创建的主路由器,MaterialApp.router()以及使用适当的 RouterDelegates 创建为小部件的子路由器Router()(子路由器用作底部导航的页面)。

在当前的用例中,我想使用深层链接打开嵌套路由器内的页面之一,因此我按照说明进行配置:

  1. 用于接收自定义架构和主机 URL 的 Android 清单
  2. RouteInformationParser将其解析为我自己的模型
  3. PlatformRouteInformationProvider通知所有嵌套路由器有关路由更改的信息

当应用程序位于前台时(在启动屏幕上),一切正常 - 我收到一个 PushRoute 事件,并且根和嵌套Router小部件正确处理深层链接。但是,当应用程序未启动时,我没有收到设置为我的深层链接的初始路由,并且我得到默认值,空RouteInformation即空。为什么会发生这种情况?这是我的代码示例:

Android Manifest 中的FlutterMainActivity配置:

        <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>
Run Code Online (Sandbox Code Playgroud)

主应用程序类(在 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(),
          ),
        ),
      );
    ...
}
Run Code Online (Sandbox Code Playgroud)

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,
      );
}
Run Code Online (Sandbox Code Playgroud)

带有嵌套(子)路由器的屏幕:

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,
        ),
      );
}
Run Code Online (Sandbox Code Playgroud)

我用这个命令测试深度链接:

adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "$1"
Run Code Online (Sandbox Code Playgroud)

对于给定的类,启动应用程序时的深层链接始终为空。我最好的猜测是RouteInformationProvider

  static PlatformRouteInformationProvider routeInformationProvider =
      PlatformRouteInformationProvider(initialRouteInformation: const RouteInformation());
Run Code Online (Sandbox Code Playgroud)

或者某种配置错误,但我自己无法识别。

更新:

我在 iOS 上对此进行了测试,令人惊讶的是,它的工作原理与 Android 完全相反 - 当应用程序被终止时,它可以通过深层链接正常打开,但当它在前台时永远Router不会更新,RouteInformationProvider并且RouteInformationParser两者都不会被调用。我在 Flutter GitHub 存储库上发现了一些相关问题,虽然它们已关闭,但我认为它们不能解决我的问题。这个问题似乎与我的问题几乎相同,但我查看了应该解决它的 PR,我可以看到其他用户也报告了 iOS 上的深层链接问题。

Cav*_*dev 3

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

将空值更改routeInformationProvider为导航器默认实际使用的值。

  static PlatformRouteInformationProvider routeInformationProvider =
      PlatformRouteInformationProvider(
    initialRouteInformation: RouteInformation(
        location: PlatformDispatcher.instance.defaultRouteName),
  );
Run Code Online (Sandbox Code Playgroud)

请记住,您需要导入dart:ui.

这将使用 Android 和 iOS 的特定行为来在启动应用程序时读取深层链接路由,因为它从操作系统接收路由。

但这并不能解决 iOS 问题。