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});@overrideWidget 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 URLreturn 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 URLreturn 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 routesGoRoute(path: 'sub-details',// NOTE: Don't need to specify "/" character for router’s parentsbuilder: (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 authenticatedif (!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,) {// ErrorPagereturn ErrorScreen();}
九、路由跳转监测
在flutter自带的会有NavigatorObserver,go_router页提供路由跳转监测功能
class MyNavigatorObserver extends NavigatorObserver {@overridevoid didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {print('did push route');}@overridevoid 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 {@overridevoid didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {print('did push route');}@overridevoid 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 authenticatedif (!isLogin) {return '/login';} else {return null; // return "null" to display the intended route without redirecting}},errorBuilder: (BuildContext context,GoRouterState state,) {// ErrorPagereturn 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 routesGoRoute(path: 'sub-details',// NOTE: Don't need to specify "/" character for router’s parentsbuilder: (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 URLreturn CommentScreen(id: id);},),GoRoute(path: 'search',builder: (BuildContext context, GoRouterState state) {String? keyword = state.queryParameters['keyword'];print("keyword:${keyword}"); // Get "id" param from URLreturn SearchScreen(keyword: keyword);},),],),],
);/// The main app.
class MyApp extends StatelessWidget {/// Constructs a [MyApp]const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp.router(routerConfig: _router,);}
}/// The home screen
class HomeScreen extends StatelessWidget {/// Constructs a [HomeScreen]const HomeScreen({super.key});@overrideWidget 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});@overrideWidget 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});@overrideWidget 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;@overrideWidget 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;@overrideWidget 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();@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('ErrorScreen Screen')),body: Center(child: Container(child: const Text('Error info'),),),);}
}
十、小结
flutter开发实战-go_router使用
学习记录,每天不停进步。