【发布时间】:2021-11-10 17:05:47
【问题描述】:
我正在使用带有嵌套路由器的 Flutter Navigator 2.0 - 有一个通过调用 MaterialApp.router() 创建的主路由器和作为 Router() 具有适当 RouterDelegates 的小部件创建的子路由器(子路由器用作底部导航的页面)。
在当前用例中,我想使用深层链接打开嵌套路由器中的一个页面,因此我按照说明进行了配置:
- 用于接收自定义架构和主机 URL 的 Android 清单
-
RouteInformationParser将其解析为我自己的模型 -
PlatformRouteInformationProvider通知所有嵌套路由器路由变化
当应用程序处于前台时(例如在启动屏幕上)一切正常 - 我收到 pushRoute 事件,并且根和嵌套的 Router 小部件正确处理了深层链接。但是,当应用程序未启动时,我没有收到设置为我的深层链接的 initialRoute,而是得到默认的空RouteInformation。为什么会这样?这是我的代码示例:
Android Manifest 中的 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 存储库上发现了一些相关问题,虽然它们已关闭,但我认为它们不能解决我的问题。 This issue 似乎与我的问题几乎相同,但我查看了the PR that's supposed to solve it,我可以看到其他用户也报告了 iOS 上的深层链接问题。
【问题讨论】:
标签: android ios flutter deep-linking flutter-navigation