flutter开发实战-go_router使用
go_router是一个Flutter的第三方声明式路由插件,使用路由器API提供一个方便的、基于url的API,用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场景。使用模板语法解析路由路径和路由查询(query)参数;支持单个目标路由展示多个页面(子路由);重定向:可以基于应用状态跳转到不同的URL,比如用户没有登录时跳转到登录页;使用 Stat
flutter开发实战-go_router使用
一、go_router介绍与特性
go_router是一个Flutter的第三方声明式路由插件,使用路由器API提供一个方便的、基于url的API,用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场景。
GoRouter具有许多功能,使导航变得简单明了:
- 使用模板语法解析路由路径和路由查询(query)参数;
- 支持单个目标路由展示多个页面(子路由);
- 重定向:可以基于应用状态跳转到不同的URL,比如用户没有登录时跳转到登录页;
- 使用 StatefulShellRoute 可以支持嵌套的 Tab 导航;
- 同时支持 Material 风格和 Cupertino 风格应用;
- 兼容 Navigator API 。
二、引入go_router
根据自己需要的版本引入对应的版本,在pubspec.yaml加入
go_router: ^8.2.0
稍后执行flutter pub get命令
三、go_router路由配置
引入插件后,我们需要配置MaterialApp.router的routerConfig
/// The route configuration.
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
),
],
),
],
);
配置MaterialApp.router
/// The main app.
class MyApp extends StatelessWidget {
/// Constructs a [MyApp]
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}
四、go_router路由跳转
如果跳转页面,可以使用content.go
context.go('/details')
4.1、路径参数
GoRouter 的每一个路由都通过 GoRoute对象来配置,可以通过路径参数进行传递相关参数到目标页面。例如路径comment/参数id
GoRoute(
path: 'comment/:id',
builder: (BuildContext context, GoRouterState state) {
String? id = state.pathParameters['id'];
print("id:${id}");// Get "id" param from URL
return CommentScreen(id: id);
},
),
那么可以通过context.go(’/comment/55555’)传参数
ElevatedButton(
onPressed: () => context.go('/comment/55555'),
child: const Text('Go to the comment screen'),
)
4.2、路径查询参数
可以获取路径的查询参数 URL 路径中的查询(query)参数,例如从/search?keyword=myname中获取search参数。
GoRoute(
path: 'search',
builder: (BuildContext context, GoRouterState state) {
String? keyword = state.queryParameters['keyword'];
print("keyword:${keyword}"); // Get "id" param from URL
return SearchScreen(keyword: keyword);
},
),
传递参数context.go(’/search?keyword=myName’)目标页面可以得到参数myName
ElevatedButton(
onPressed: () => context.go('/search?keyword=myName'),
child: const Text('Go to the Search screen'),
),
五、添加子路由
子路由,路由匹配支持多个页面,当一个新的页面在旧的页面之上展示时
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
routes: <RouteBase>[
// Add child routes
GoRoute(
path: 'sub-details',
// NOTE: Don't need to specify "/" character for router’s parents
builder: (context, state) {
return SubDetailsScreen();
},
),
],
),
可以通过context.go(’/details/sub-details’)来进行调用
ElevatedButton(
onPressed: () => context.go('/details/sub-details'),
child: const Text('Go to the SubDetails screen'),
),
六、页面导航
go_router提供多种方式进行跳转
context.goNamed方式
ElevatedButton(
onPressed: () => context.goNamed('/details'),
child: const Text('Go to the Details screen'),
),
七、路由重定向
go_router提供全局重定向,比如在没有登录的用户,需要跳转到登录页面.在 GoRouter 中,可以通过redirect 参数配置重定向.
redirect: (BuildContext context, GoRouterState state) {
final isLogin = false;// your logic to check if user is authenticated
if (!isLogin) {
return '/login';
} else {
return null; // return "null" to display the intended route without redirecting
}
}
八、错误处理(404页面)
go_router为MaterialApp 和CupertinoApp定义了默认的错误页面,也可以通过 errorBuilder 参数自定义错误页面。
errorBuilder: (
BuildContext context,
GoRouterState state,
) {
// ErrorPage
return ErrorScreen();
}
九、路由跳转监测
在flutter自带的会有NavigatorObserver,go_router页提供路由跳转监测功能
class MyNavigatorObserver extends NavigatorObserver {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
print('did push route');
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
print('did pop route');
}
}
在observers中配置
GoRouter(
observers: [MyNavigatorObserver()],
...
)
这里只是代码尝试了一下常用功能。还有一些转场动画、嵌套导航等等特性需要去尝试。
可以查看https://juejin.cn/post/7270343009790853172
注意:不同版本的代码有所不同。
测试的完整代码如下
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MyApp());
}
class MyNavigatorObserver extends NavigatorObserver {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
print('did push route');
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
print('did pop route');
}
}
/// The route configuration.
final GoRouter _router = GoRouter(
observers: [MyNavigatorObserver()],
redirect: (BuildContext context, GoRouterState state) {
final isLogin = false; // your logic to check if user is authenticated
if (!isLogin) {
return '/login';
} else {
return null; // return "null" to display the intended route without redirecting
}
},
errorBuilder: (
BuildContext context,
GoRouterState state,
) {
// ErrorPage
return ErrorScreen();
},
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
routes: <RouteBase>[
// Add child routes
GoRoute(
path: 'sub-details',
// NOTE: Don't need to specify "/" character for router’s parents
builder: (context, state) {
return SubDetailsScreen();
},
),
],
),
GoRoute(
path: 'comment/:id',
builder: (BuildContext context, GoRouterState state) {
String? id = state.pathParameters['id'];
print("id:${id}"); // Get "id" param from URL
return CommentScreen(id: id);
},
),
GoRoute(
path: 'search',
builder: (BuildContext context, GoRouterState state) {
String? keyword = state.queryParameters['keyword'];
print("keyword:${keyword}"); // Get "id" param from URL
return SearchScreen(keyword: keyword);
},
),
],
),
],
);
/// The main app.
class MyApp extends StatelessWidget {
/// Constructs a [MyApp]
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}
/// The home screen
class HomeScreen extends StatelessWidget {
/// Constructs a [HomeScreen]
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => context.goNamed('/details'),
child: const Text('Go to the Details screen'),
),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.go('/details/sub-details'),
child: const Text('Go to the SubDetails screen'),
),
],
),
),
);
}
}
/// The details screen
class DetailsScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => context.go('/comment/55555'),
child: const Text('Go to the comment screen'),
),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.go('/search?keyword=myName'),
child: const Text('Go to the Search screen'),
),
],
),
),
);
}
}
/// The details screen
class SubDetailsScreen extends StatelessWidget {
/// Constructs a [SubDetailsScreen]
const SubDetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('SubDetailsScreen Screen')),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/details'),
child: const Text('Go back to the Details screen'),
),
),
);
}
}
/// The details screen
class CommentScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const CommentScreen({super.key, this.id});
final String? id;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('CommentScreen Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("param id:${id}"),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go back to the Details screen'),
),
],
)),
);
}
}
/// The Search screen
class SearchScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const SearchScreen({super.key, this.keyword});
final String? keyword;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('SearchScreen Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("param keyword:${keyword}"),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go back to the details screen'),
),
],
)),
);
}
}
/// The Search screen
class ErrorScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const ErrorScreen();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ErrorScreen Screen')),
body: Center(
child: Container(
child: const Text('Error info'),
),
),
);
}
}
十、小结
flutter开发实战-go_router使用
学习记录,每天不停进步。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)