如何使用路由处理 Flutter 中的深度链接
How to handle deeplinking in Flutter using routes
我正在尝试构建深层链接功能,到目前为止,应用程序的初始启动和从深层链接检索参数都进行得很顺利。
但是,在深入链接到应用程序后,我在导航到屏幕时遇到了问题。我应该怎么做?
我的代码如下所示:
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Uri _latestUri;
Object _err;
StreamSubscription _sub;
@override void initState() {
super.initState();
_handleIncomingLinks();
}
@override void dispose() {
_sub?.cancel();
super.dispose();
}
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
Navigator.pushNamed(context, 'login'); // This doesn't work because the context does not include navigator
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
@override Widget build(BuildContext context) {
return MaterialApp(
initialRoute: 'splash-screen',
onGenerateRoute: (settings) {
switch (settings.name) {
case 'splash-screen':
return
PageTransition(
child: BlocProvider(
create: (context) => SplashScreenCubit(APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: SplashScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
case 'create-account':
return PageTransition(
child: BlocProvider(
create: (context) => CreateAccountScreenCubit(
APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: CreateAccountScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
case 'login':
return PageTransition(
child: BlocProvider(
create: (context) => LoginScreenCubit(APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: LoginScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
default:
return null;
},
);
}
}
如果您需要的是能够在不从 Navigtor.of
获取上下文的情况下进行导航,因为您想要处理深层链接,则需要使用 navigatorKey
属性,您可以阅读详情 here.
那么您的代码将如下所示。 [已编辑,我添加了在 material 应用程序上添加导航键的位置]
void main() { ... }
class MyApp extends StatefulWidget { ... }
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Uri _latestUri;
Object _err;
GlobalKey<NavigatorState> navigatorKey = GlobalKey();
StreamSubscription _sub;
@override void initState() { ... }
@override void dispose() { ... }
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
});
// use the navigatorkey currentstate to navigate to the page you are intended to visit
navigatorKey.currentState.pushNamedAndRemoveUntil('login', (route) => false);
}, onError: (Object err) { ... });
@override Widget build(BuildContext context) {
return MaterialApp(
...
navigatorKey: navigatorKey,
...
);
}
}
您的深度 link 流可以在构建方法之前触发,但不允许您当时调用 Navigator
。因此,您可以使用 SchedulerBinding
:
提供的 addPostFrameCallback
修复它
addPostFrameCallback
Schedule a callback for the end of this frame.
Does not request a new frame.
This callback is run during a frame, just after the persistent frame
callbacks (which is when the main rendering pipeline has been
flushed). If a frame is in progress and post-frame callbacks haven't
been executed yet, then the registered callback is still executed
during the frame. Otherwise, the registered callback is executed
during the next frame.
The callbacks are executed in the order in which they have been added.
Post-frame callbacks cannot be unregistered. They are called exactly
once.
...
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
// Call your navigator inside addPostFrameCallback
WidgetsBinding.instance?.addPostFrameCallback((_) {
Navigator.pushNamed(context, 'login');
});
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
...
我正在尝试构建深层链接功能,到目前为止,应用程序的初始启动和从深层链接检索参数都进行得很顺利。
但是,在深入链接到应用程序后,我在导航到屏幕时遇到了问题。我应该怎么做?
我的代码如下所示:
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Uri _latestUri;
Object _err;
StreamSubscription _sub;
@override void initState() {
super.initState();
_handleIncomingLinks();
}
@override void dispose() {
_sub?.cancel();
super.dispose();
}
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
Navigator.pushNamed(context, 'login'); // This doesn't work because the context does not include navigator
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
@override Widget build(BuildContext context) {
return MaterialApp(
initialRoute: 'splash-screen',
onGenerateRoute: (settings) {
switch (settings.name) {
case 'splash-screen':
return
PageTransition(
child: BlocProvider(
create: (context) => SplashScreenCubit(APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: SplashScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
case 'create-account':
return PageTransition(
child: BlocProvider(
create: (context) => CreateAccountScreenCubit(
APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: CreateAccountScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
case 'login':
return PageTransition(
child: BlocProvider(
create: (context) => LoginScreenCubit(APIRepository(
apiClient: APIClient(httpClient: http.Client()))),
child: LoginScreen(),
),
type: PageTransitionType.rightToLeft,
settings: settings);
break;
default:
return null;
},
);
}
}
如果您需要的是能够在不从 Navigtor.of
获取上下文的情况下进行导航,因为您想要处理深层链接,则需要使用 navigatorKey
属性,您可以阅读详情 here.
那么您的代码将如下所示。 [已编辑,我添加了在 material 应用程序上添加导航键的位置]
void main() { ... }
class MyApp extends StatefulWidget { ... }
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Uri _latestUri;
Object _err;
GlobalKey<NavigatorState> navigatorKey = GlobalKey();
StreamSubscription _sub;
@override void initState() { ... }
@override void dispose() { ... }
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
});
// use the navigatorkey currentstate to navigate to the page you are intended to visit
navigatorKey.currentState.pushNamedAndRemoveUntil('login', (route) => false);
}, onError: (Object err) { ... });
@override Widget build(BuildContext context) {
return MaterialApp(
...
navigatorKey: navigatorKey,
...
);
}
}
您的深度 link 流可以在构建方法之前触发,但不允许您当时调用 Navigator
。因此,您可以使用 SchedulerBinding
:
addPostFrameCallback
修复它
addPostFrameCallback
Schedule a callback for the end of this frame.
Does not request a new frame.
This callback is run during a frame, just after the persistent frame callbacks (which is when the main rendering pipeline has been flushed). If a frame is in progress and post-frame callbacks haven't been executed yet, then the registered callback is still executed during the frame. Otherwise, the registered callback is executed during the next frame.
The callbacks are executed in the order in which they have been added.
Post-frame callbacks cannot be unregistered. They are called exactly once.
...
void _handleIncomingLinks() {
_sub = uriLinkStream.listen((Uri uri) {
if (!mounted) return;
print('got uri: $uri'); // printed: got uri: myapp://?key1=test
setState(() {
_latestUri = uri;
_err = null;
// Call your navigator inside addPostFrameCallback
WidgetsBinding.instance?.addPostFrameCallback((_) {
Navigator.pushNamed(context, 'login');
});
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
...