目录
- 1.Navigator使用简介
- 2.fluro
- 1.配置
- 2.使用方法
- 3.路由拦截
- 3.封装
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的功能足够使用了。