目录
1.Navigator使用简介
使用Flutter 的Navigator
导航器可以实现页面的跳转,Navigator
的使用方法简单介绍一下:
页面跳转:
Navigator.push<void>( context, MaterialPageRoute( builder: (BuildContext context) => const MyHomePage(), ), );
页面跳转的同时关闭当前页面(页面替换):
Navigator.pushReplacement<void, void>( context, MaterialPageRoute( builder: (BuildContext context) => const MyHomePage(), ), );
页面跳转的同时关闭到之前的某一个页面:
Navigator.pushAndRemoveUntil<void>( context, MaterialPageRoute( builder: (BuildContext context) => const MyHomePage(), ), (route) => false // ModalRoute.withName('/') );
也可以直接使用路由名称进行上面操作,例如跳转:Navigator.pushNamed(context, '/home');
路由名称需要提前在MaterialApp
中定义好。
MaterialApp( title: 'Flutter Demo', home: MyHomePage(), routes: { "/page1": (context) => PageA(), "/page2": (context) => PageB(), }, );
接收参数:var args = ModalRoute.of(context).settings.arguments;
页面返回
Navigator.pop(context);
接收页面的返回值:
Navigator.push<void>( context, MaterialPageRoute( builder: (BuildContext context) => const MyHomePage(), ), ).then((dynamic result) { // 页面返回result });
必须同时配合Navigator.pop<dynamic>(context, result);
还有路由删除removeRoute
,路由替换replace
等。
2.fluro
直接使用Navigator
的主要问题是不易维护。如果某个页面的传参发生了变化,那么所有跳转处都需要做修改。
所以我们可以使用现有封装好的路由框架来替我们解决这些问题。比如fluro。
1.配置
添加依赖至pubspec.yaml
:
dependencies: fluro: ^2.0.3
定义唯一一个FluroRouter
对象:
static final FluroRouter router = FluroRouter();
剩下的就是添加路由处理器Handler
,下面代码举例添加了两个页面:
class Routes { static String home = '/home'; static String webViewPage = '/webView'; static final List<IRouterProvider> _listRouter = []; static final FluroRouter router = FluroRouter(); static void initRoutes() { /// 指定路由跳转错误返回页 router.notFoundHandler = Handler( handlerFunc: (BuildContext? context, Map<String, List<String>> params) { debugPrint('未找到目标页'); return const NotFoundPage(); }); router.define(home, handler: Handler( handlerFunc: (BuildContext? context, Map<String, List<String>> params) => const Home())); // Routes.router.navigateTo(context, '${Routes.webViewPage}?title=标题&url=地址'); router.define(webViewPage, handler: Handler(handlerFunc: (_, params) { /// 接收参数 final String title = params['title']?.first ?? ''; final String url = params['url']?.first ?? ''; return WebViewPage(title: title, url: url); })); } }
配置fluro:
MaterialApp( onGenerateRoute: Routes.router.generator, );
初始化:
class MyApp extends StatelessWidget { MyApp() { Routes.initRoutes(); } ... }
2.使用方法
核心就一个方法navigateTo
,源码如下:
Future navigateTo(BuildContext context, String path, {bool replace = false, bool clearStack = false, bool maintainState = true, bool rootNavigator = false, TransitionType? transition, Duration? transitionDuration, RouteTransitionsBuilder? transitionBuilder, RouteSettings? routeSettings}) { RouteMatch routeMatch = matchRoute(context, path, transitionType: transition, transitionsBuilder: transitionBuilder, transitionDuration: transitionDuration, maintainState: maintainState, routeSettings: routeSettings); Route<dynamic>? route = routeMatch.route; Completer completer = Completer(); Future future = completer.future; if (routeMatch.matchType == RouteMatchType.nonVisual) { completer.complete("Non visual route type."); } else { ///找不到时走`notFoundHandler` if (route == null && notFoundHandler != null) { route = _notFoundRoute(context, path, maintainState: maintainState); } if (route != null) { final navigator = Navigator.of(context, rootNavigator: rootNavigator); if (clearStack) { future = navigator.pushAndRemoveUntil(route, (check) => false); } else { future = replace ? navigator.pushReplacement(route) : navigator.push(route); } completer.complete(); } else { final error = "No registered route was found to handle '$path'."; print(error); completer.completeError(RouteNotFoundException(error, path)); } } return future; }
path
:路由名称。replace
:等同于pushReplacement
。clearStack
:等同于pushAndRemoveUntil
。transition
:页面跳转动画,默认native,平台默认动画。transitionDuration
:动画时长。transitionBuilder
:自定义动画。routeSettings
:用于传递数据。可使用context.settings.arguments
获取。
具体的使用见项目routers目录。
3.路由拦截
路由拦截可以实现权限控制。比如用户没有登录,当进入某些需要登录后才能显示的页面时,可以拦截跳转进行判断,引导用户进入登录页。
MaterialApp有 onGenerateRoute
方法可以在跳转时进行路由拦截。但是使用的fluro将这一属性占用了,所以我们可以继承 FluroRouter
类,重写navigateTo
方法实现。
class MyFluroRouter extends FluroRouter { List<String> _loginList; set loginList(value) => _loginList = value; @override Future navigateTo( BuildContext context, String path, { bool replace = false, bool clearStack = false, bool maintainState = true, bool rootNavigator = false, TransitionType transition, Duration transitionDuration, transitionBuilder, RouteSettings routeSettings, }) { String pathToNavigate = path; AppRouteMatch routeMatched = this.match(path); String routePathMatched = routeMatched?.route?.route; if (routePathMatched != null) { //如果页面需要登录,修改路由路径到登录页面 if (_loginList != null && !_loginList.contains(routePathMatched)) { pathToNavigate = '/login‘; } } return super.navigateTo(context, pathToNavigate, replace: replace, clearStack: clearStack, maintainState: maintainState, rootNavigator: rootNavigator, transition: transition, transitionDuration: transitionDuration, transitionBuilder: transitionBuilder, routeSettings: routeSettings); } }
3.封装
fluro工具类:
class NavigatorUtils { static void push(BuildContext context, String path, {bool replace = false, bool clearStack = false, Object? arguments}) { unfocus(); Routes.router.navigateTo(context, path, replace: replace, clearStack: clearStack, transition: TransitionType.native, routeSettings: RouteSettings( arguments: arguments, ), ); } static void pushResult(BuildContext context, String path, Function(Object) function, {bool replace = false, bool clearStack = false, Object? arguments}) { unfocus(); Routes.router.navigateTo(context, path, replace: replace, clearStack: clearStack, transition: TransitionType.native, routeSettings: RouteSettings( arguments: arguments, ), ).then((Object? result) { // 页面返回result为null if (result == null) { return; } function(result); }).catchError((dynamic error) { debugPrint('$error'); }); } /// 返回 static void goBack(BuildContext context) { unfocus(); Navigator.pop(context); } /// 带参数返回 static void goBackWithParams(BuildContext context, Object result) { unfocus(); Navigator.pop<Object>(context, result); } static void unfocus() { FocusManager.instance.primaryFocus?.unfocus(); } }
模块管理:
import 'package:fluro/fluro.dart'; abstract class IRouterProvider { void initRouter(FluroRouter router); }
实现接口:
class LoginRouter implements IRouterProvider{ static String loginPage = '/login'; static String registerPage = '/login/register'; @override void initRouter(FluroRouter router) { router.define(loginPage, handler: Handler(handlerFunc: (_, __) => const LoginPage())); router.define(registerPage, handler: Handler(handlerFunc: (_, __) => const RegisterPage())); } }
各模块初始化,放在Routes的initRoutes中:
/// 各自路由由各自模块管理,统一在此添加初始化 _listRouter.add(LoginRouter()); ... /// 初始化路由 void initRouter(IRouterProvider routerProvider) { routerProvider.initRouter(router); } _listRouter.forEach(initRouter);
目前Flutter团队有维护一款路由框架go_router
(支持Navigator 2.0),但目前有部分功能缺失,比如不支持接收页面的返回值,没有pushAndRemoveUntil
方法。
期待后面功能的完善。但就目前来说,对于Android 和iOS平台开发来说fluro的功能足够使用了。