曾经写了关于Fluro的路由配置,但Fluro目前已经很久不维护了,因此考虑使用flutter原生的路由。原文章:juejin.cn/post/729456…
前置知识
回调onGenerateRoute
MaterialApp
或者CupertinoApp
,接收onGenerateRoute
参数,是用于处理路由生成的回调函数,它可以根据路由名称动态生成页面- 用户使用
Navigator
路由跳转方法时,便会触发onGenerateRoute
onGenerateRoute
回调函数接收RouteSettings
参数,其中可以通过settings.name
获取路由的名称- 根据路由名称返回
Route
,MaterialPageRoute
最终继承自Route
MaterialPageRoute
中的builder
,即可返回你的页面
MaterialApp(
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (context) => HomePage());
case '/details':
return MaterialPageRoute(builder: (context) => DetailsPage());
default:
return MaterialPageRoute(builder: (context) => NotFoundPage());
}
},
);
使用
Navigator.pushNamed(context, '/details');
路由传参数
RouteSettings settings
中可以获取arguments
参数,这就是路由参数
MaterialApp(
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
//...
case '/details':
String? id = settings.arguments as String?;
return MaterialPageRoute(builder: (context) => DetailsPage(id:id));
//...
}
},
);
使用
Navigator.pushNamed(context, '/details',arguments: 'id123');
封装
封装onGenerateRoute
- 封装
onGenerateRoute
回调方法,通过map获取对应的页面 - lib/router/create_routes.dart
//...
typedef WidgetBuilder = Widget Function(Object? arguments);
class CreateRoutes {
static final routes = <String, WidgetBuilder>{
'/': (arguments) => const IndexPage(),
'/tokenDetail': (arguments) => TokenDetailPage(token: arguments as String),
'/auditDetail': (arguments) => const AuditDetailPage(),
};
static Route<dynamic>? generateRoute(RouteSettings settings) {
// 根据路由名称返回对应的页面Widget,并传递参数
// widget不存在则返回404页面
final Widget widget =
routes[settings.name]?.call(settings.arguments) ?? const Page404();
return MaterialPageRoute(builder: (context) => widget);
}
}
lib/main.dart
MaterialApp(
onGenerateRoute: CreateRoutes.generateRoute,
),
封装路由无context跳转
navigatorKey
是访问和控制Navigator
的键(GlobalKey<NavigatorState>
)。它允许你在应用的任意地方访问Navigator
,从而实现导航操作。
main.dart
MaterialApp(
onGenerateRoute: CreateRoutes.generateRoute,
navigatorKey: navigatorKey // navigatorKey
),
lib/router/nav_ctrl.dart
import 'package:flutter/material.dart';
// 全局key,用于无context跳转的情况
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class NavCtrl {
// 无context跳转
static Future<dynamic> push(
String path, {
Object? arguments,
}) {
return Navigator.of(navigatorKey.currentContext!)
.pushNamed(path, arguments: arguments);
}
// 无context replase
static Future<dynamic> replease(
String path, {
Object? arguments,
}) {
return Navigator.of(navigatorKey.currentContext!)
.pushReplacementNamed(path, arguments: arguments);
}
// 无context,清空路由栈跳转,一般用于跳转首页这种情况
static Future<dynamic> switchTab(
String path, {
Object? arguments,
}) {
return Navigator.of(navigatorKey.currentContext!)
.pushNamedAndRemoveUntil(path, (_) => false, arguments: arguments);
}
// 无context返回,并指定路由返回多少层,默认返回上一页面, 返回带参数arguments
static void back({int count = 1, Object? arguments}) {
NavigatorState state = Navigator.of(navigatorKey.currentContext!);
while (count-- > 0) {
if (state.canPop()) state = state..pop(arguments);
}
}
}
使用
NavCtrl.push('/tokenDetail', arguments: "USDT");
自动生成路由名的变量
以上步骤已经完成了对路由的封装,下面只是优化,可不做
- 我们在使用时是写的字符串,这样没有代码提示,且容易写错,因此我们可以单独用变量来存储字符串
例如:
class Routes {
static const String home = '/';
static const String tokenDetail = '/tokenDetail';
static const String auditDetail = '/auditDetail';
}
使用:
NavCtrl.push(Routes.tokenDetail, arguments: "USDT");
但每次都去写这个class比较麻烦,因此编写脚本自动生成Routes class
脚本
步骤:
- 通过路由文件,提取路由名称
- 拼接格式,生成代码,输出到文件
lib/router/script.dart
import 'dart:io';
void main() {
// 获取路由文件内容
var path = './lib/router/create_routes.dart';
var res = File(path).readAsStringSync();
// 提取路由名称
RegExp regExp = RegExp("['"]/(.*?)['"]: "); // 匹配引号中的内容
Iterable<Match> matches = regExp.allMatches(res);
var list = matches.map((match) => match.group(1)).toList();
// 拼接格式:static const String step1 = '/step1';
final String s = list.map((e) {
if (e?.isEmpty ?? true) {
return " static const String home = '/$e';";
}
return " static const String $e = '/$e';";
}).join('\n');
// 替换模版
String template = '''
class Routes {
%s
}
''';
template = template.replaceAll("%s", s);
// 写入文件
File('./lib/router/routes.dart').writeAsStringSync(template);
}
运行
dart ./lib/router/script.dart
运行时自动启动
在.vscode文件夹中新增文件tasks.json
- 任务配置文件必须命名为
tasks.json
.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "generate routers",
"type": "shell",
"command": "dart ./lib/router/script.dart",
"presentation": {
"echo": true,
"reveal": "always",
},
}
]
}
- 在launch.json添加运行前任务
preLaunchTask
.vscode/launch.json
{
"configurations": [
{
"name": "Flutter (dev)",
"request": "launch",
"preLaunchTask": "generate routers",
"type": "dart",
"args": [
"--dart-define",
"APP_ENV=dev"
]
},
{
"name": "Flutter (pro)",
"request": "launch",
"preLaunchTask": "generate routers",
"type": "dart",
"args": [
"--dart-define",
"APP_ENV=pro"
]
},
{
"name": "Flutter (test)",
"request": "launch",
"preLaunchTask": "generate routers",
"type": "dart",
"args": [
"--dart-define",
"APP_ENV=test"
]
}
]
}
这样运行就会自动生成文件了