Flutter路由传参详解

目录


一、路由传参的基本概念

路由传参是指在Flutter应用中,从一个页面(Widget)导航到另一个页面时,传递数据或参数的过程。

核心原理

  • Flutter的路由系统基于NavigatorRoute
  • 参数传递通过RouteSettings对象实现
  • 目标页面通过ModalRoute.of(context).settings.arguments获取参数

二、路由传参的几种方式

1. 构造函数传参

原理:直接通过目标页面的构造函数传递参数。

实现方式

1
2
3
4
5
6
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(data: 'Hello World'),
),
);

参数获取

1
2
3
4
5
6
7
8
9
10
11
12
class DetailScreen extends StatelessWidget {
final String data;

const DetailScreen({Key? key, required this.data}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text(data)),
);
}
}

2. RouteSettings传参

原理:通过RouteSettingsarguments属性传递参数。

实现方式

1
2
3
4
5
6
7
8
9
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: {'id': 1, 'name': 'Flutter'},
),
builder: (context) => DetailScreen(),
),
);

参数获取

1
2
3
4
5
6
@override
void didChangeDependencies() {
super.didChangeDependencies();
final args = ModalRoute.of(context)?.settings.arguments as Map;
// 处理参数
}

3. 命名路由传参

原理:通过命名路由的arguments参数传递数据。

实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注册路由
MaterialApp(
routes: {
'/detail': (context) => DetailScreen(),
},
);

// 导航并传参
Navigator.pushNamed(
context,
'/detail',
arguments: {'id': 1, 'name': 'Flutter'},
);

参数获取:与RouteSettings传参相同。

4. onGenerateRoute传参

原理:通过onGenerateRoute回调函数处理路由和参数。

实现方式

1
2
3
4
5
6
7
8
9
10
11
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/detail') {
final args = settings.arguments;
return MaterialPageRoute(
builder: (context) => DetailScreen(args: args),
);
}
return null;
},
);

参数获取:通过构造函数获取。

5. 全局状态管理传参

原理:使用Provider、Riverpod等状态管理库传递参数。

实现方式

1
2
3
4
5
6
7
8
9
10
11
// 定义状态
final userProvider = StateProvider<User>((ref) => User());

// 设置状态
context.read(userProvider).state = User(id: 1, name: 'Flutter');

// 导航
Navigator.pushNamed(context, '/detail');

// 获取状态
final user = context.watch(userProvider).state;

三、各种传参方式的比较

传参方式 优点 缺点 适用场景
构造函数传参 1. 类型安全 2. 代码清晰 3. 直接获取 1. 不支持命名路由 2. 参数变更不会触发重建 简单页面,参数较少
RouteSettings传参 1. 支持命名路由 2. 灵活传递各种类型 1. 类型不安全 2. 需要类型转换 复杂参数,命名路由
命名路由传参 1. 路由集中管理 2. 代码简洁 1. 类型不安全 2. 参数获取复杂 应用内页面导航
onGenerateRoute传参 1. 集中处理路由逻辑 2. 支持类型安全 1. 代码复杂度增加 2. 配置繁琐 大型应用,复杂路由
全局状态管理 1. 跨页面共享 2. 实时更新 3. 类型安全 1. 增加依赖 2. 过度设计风险 复杂状态,多页面共享

四、最佳实践

1. 根据场景选择传参方式

  • 简单参数:使用构造函数传参
  • 复杂参数:使用RouteSettings或onGenerateRoute
  • 跨页面共享:使用状态管理

2. 参数获取的最佳时机

不推荐:在initState()中直接获取参数

  • 原因:路由参数可能还未传递到Widget中

推荐

  1. 构造函数传参:直接使用构造函数参数
  2. RouteSettings传参:在didChangeDependencies()中获取
  3. 状态管理:在build()方法中通过watch获取

3. 类型安全

推荐使用类型化的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ScreenArguments {
final int id;
final String name;

ScreenArguments(this.id, this.name);
}

// 传递
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter'),
);

// 获取
final args = ModalRoute.of(context)?.settings.arguments as ScreenArguments;

4. 性能优化

  • 避免在build方法中获取参数:可能导致重复获取
  • 使用didChangeDependencies:只在依赖变化时调用
  • 合理使用状态管理:避免过度使用

五、代码示例

示例1:构造函数传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 导航页面
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
id: 1,
title: 'Flutter Demo',
),
),
);
},
child: Text('Go to Detail'),
),
),
);
}
}

// 目标页面
class DetailScreen extends StatelessWidget {
final int id;
final String title;

const DetailScreen({Key? key, required this.id, required this.title})
: super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Text('ID: $id'),
),
);
}
}

示例2:RouteSettings传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 导航页面
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: {'id': 1, 'title': 'Flutter Demo'},
),
builder: (context) => DetailScreen(),
),
);

// 目标页面
class DetailScreen extends StatefulWidget {
@override
_DetailScreenState createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
late Map<String, dynamic> _args;

@override
void didChangeDependencies() {
super.didChangeDependencies();
_args = ModalRoute.of(context)?.settings.arguments as Map;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_args['title'])),
body: Center(
child: Text('ID: ${_args['id']}'),
),
);
}
}

示例3:命名路由传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 注册路由
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/detail': (context) => DetailScreen(),
},
);

// 导航
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter Demo'),
);

// 目标页面
class DetailScreen extends StatefulWidget {
@override
_DetailScreenState createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
late ScreenArguments _args;

@override
void didChangeDependencies() {
super.didChangeDependencies();
_args = ModalRoute.of(context)?.settings.arguments as ScreenArguments;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_args.title)),
body: Center(
child: Text('ID: ${_args.id}'),
),
);
}
}

class ScreenArguments {
final int id;
final String title;

ScreenArguments(this.id, this.title);
}

示例4:onGenerateRoute传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 配置路由
MaterialApp(
onGenerateRoute: (settings) {
switch (settings.name) {
case '/detail':
final args = settings.arguments as ScreenArguments;
return MaterialPageRoute(
builder: (context) => DetailScreen(args: args),
);
default:
return MaterialPageRoute(builder: (context) => HomeScreen());
}
},
);

// 导航
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter Demo'),
);

// 目标页面
class DetailScreen extends StatelessWidget {
final ScreenArguments args;

const DetailScreen({Key? key, required this.args}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(args.title)),
body: Center(
child: Text('ID: ${args.id}'),
),
);
}
}

示例5:全局状态管理传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 定义状态
final userProvider = StateProvider<User>((ref) => User());

// 设置状态
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
context.read(userProvider).state = User(id: 1, name: 'Flutter');
Navigator.pushNamed(context, '/detail');
},
child: Text('Go to Detail'),
),
),
);
}
}

// 获取状态
class DetailScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider).state;
return Scaffold(
appBar: AppBar(title: Text(user.name)),
body: Center(
child: Text('ID: ${user.id}'),
),
);
}
}

class User {
final int id;
final String name;

User({this.id = 0, this.name = ''});
}

总结

Flutter提供了多种路由传参方式,每种方式都有其适用场景:

  1. 构造函数传参:简单直接,类型安全,适用于简单页面
  2. RouteSettings传参:灵活多样,支持命名路由,适用于复杂参数
  3. 命名路由传参:集中管理,代码简洁,适用于应用内导航
  4. onGenerateRoute传参:集中处理,支持类型安全,适用于大型应用
  5. 全局状态管理:跨页面共享,实时更新,适用于复杂状态

选择合适的传参方式需要考虑:

  • 参数的复杂度和类型
  • 页面间的关系
  • 代码的可维护性
  • 性能要求

通过合理选择和组合使用这些方式,可以构建出清晰、高效的Flutter应用导航系统。

Flutter 路由详解

目录


一、路由基础概念

1. 什么是路由?

路由(Route)是指应用中页面之间的导航机制,在Flutter中,路由是由Navigator组件管理的Route对象的堆栈。

2. 核心组件

  • Navigator:管理路由堆栈的核心类,负责页面的推入、弹出和替换
  • Route:表示一个页面的抽象类,常用实现有MaterialPageRouteCupertinoPageRoute
  • RouteSettings:包含路由的配置信息,如路由名称和参数

3. 路由堆栈

  • 推入(push):将新页面添加到堆栈顶部
  • 弹出(pop):移除堆栈顶部的页面
  • 替换(replace):用新页面替换堆栈顶部的页面
  • 清空(popUntil):弹出页面直到指定条件

二、基本导航方式

1. 基本导航

推入新页面

1
2
3
4
5
6
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);

返回上一页

1
Navigator.pop(context);

带返回值的导航

1
2
3
4
5
6
7
8
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SelectionScreen(),
),
);
// 处理返回结果
print('Result: $result');

2. 替换路由

1
2
3
4
5
6
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => NewScreen(),
),
);

3. 移除指定路由

1
2
3
4
Navigator.popUntil(
context,
ModalRoute.withName('/home'),
);

4. 清空堆栈并导航

1
2
3
4
5
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
(Route<dynamic> route) => false, // 移除所有路由
);

三、路由传参详解

1. 构造函数传参

传递参数

1
2
3
4
5
6
7
8
9
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
id: 1,
title: 'Flutter Demo',
),
),
);

接收参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DetailScreen extends StatelessWidget {
final int id;
final String title;

const DetailScreen({Key? key, required this.id, required this.title})
: super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('ID: $id')),
);
}
}

2. RouteSettings传参

传递参数

1
2
3
4
5
6
7
8
9
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: {'id': 1, 'title': 'Flutter Demo'},
),
builder: (context) => DetailScreen(),
),
);

接收参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class DetailScreen extends StatefulWidget {
@override
_DetailScreenState createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
late Map<String, dynamic> _args;

@override
void didChangeDependencies() {
super.didChangeDependencies();
_args = ModalRoute.of(context)?.settings.arguments as Map;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_args['title'])),
body: Center(child: Text('ID: ${_args['id']}')),
);
}
}

3. 类型安全的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ScreenArguments {
final int id;
final String title;

ScreenArguments(this.id, this.title);
}

// 传递
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: ScreenArguments(1, 'Flutter Demo'),
),
builder: (context) => DetailScreen(),
),
);

// 接收
final args = ModalRoute.of(context)?.settings.arguments as ScreenArguments;

四、命名路由管理

1. 基本配置

1
2
3
4
5
6
7
8
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/second': (context) => SecondScreen(),
'/detail': (context) => DetailScreen(),
},
);

2. 导航到命名路由

1
Navigator.pushNamed(context, '/second');

3. 带参数的命名路由

1
2
3
4
5
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter Demo'),
);

4. onGenerateRoute

用于处理未在routes中注册的路由,或需要动态生成的路由:

1
2
3
4
5
6
7
8
9
10
11
12
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/detail') {
final args = settings.arguments as ScreenArguments;
return MaterialPageRoute(
builder: (context) => DetailScreen(args: args),
);
}
// 处理其他路由...
return MaterialPageRoute(builder: (context) => NotFoundScreen());
},
);

5. onUnknownRoute

onGenerateRoute无法处理路由时调用:

1
2
3
4
5
MaterialApp(
onUnknownRoute: (settings) {
return MaterialPageRoute(builder: (context) => NotFoundScreen());
},
);

五、路由守卫与拦截

1. 自定义路由拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class AuthGuard extends StatelessWidget {
final Widget child;

const AuthGuard({Key? key, required this.child}) : super(key: key);

@override
Widget build(BuildContext context) {
final isLoggedIn = AuthService.isLoggedIn();

if (!isLoggedIn) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
});
}

return child;
}
}

2. 路由监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class RouteObserverExample extends StatefulWidget {
@override
_RouteObserverExampleState createState() => _RouteObserverExampleState();
}

class _RouteObserverExampleState extends State<RouteObserverExample> with RouteAware {
static final routeObserver = RouteObserver<PageRoute>();

@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
}

@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}

@override
void didPush() {
// 路由被推入时调用
print('Route pushed');
}

@override
void didPop() {
// 路由被弹出时调用
print('Route popped');
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Route Observer')),
body: Center(child: Text('Route Observer Example')),
);
}
}

// 在MaterialApp中注册
MaterialApp(
navigatorObservers: [RouteObserverExample.routeObserver],
);

六、深度链接与通用链接

1. 配置深度链接

Android配置 (AndroidManifest.xml):

1
2
3
4
5
6
7
8
9
10
11
12
13
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="example.com"
android:pathPrefix="/detail" />
</intent-filter>
</activity>

iOS配置 (Info.plist):

1
2
3
4
5
6
7
8
9
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>

2. 处理深度链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MaterialApp(
initialRoute: '/',
onGenerateRoute: (settings) {
if (settings.name?.startsWith('/detail/') ?? false) {
// 解析路径参数
final pathSegments = Uri.parse(settings.name!).pathSegments;
final id = pathSegments[1];
return MaterialPageRoute(
builder: (context) => DetailScreen(id: id),
);
}
// 其他路由处理...
},
);

3. 使用uni_links库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import 'package:uni_links/uni_links.dart';

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
StreamSubscription? _linkSubscription;

@override
void initState() {
super.initState();
_initDeepLinks();
}

void _initDeepLinks() async {
// 处理初始链接
final initialLink = await getInitialLink();
_handleDeepLink(initialLink);

// 监听后续链接
_linkSubscription = linkStream.listen((String? link) {
_handleDeepLink(link);
});
}

void _handleDeepLink(String? link) {
if (link != null) {
final uri = Uri.parse(link);
if (uri.path.startsWith('/detail')) {
final id = uri.queryParameters['id'];
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(id: id)),
);
}
}
}

@override
void dispose() {
_linkSubscription?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return MaterialApp(
// ...
);
}
}

七、路由动画与过渡效果

1. 自定义页面过渡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.ease;

var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
),
);

2. 预定义过渡效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 淡入淡出
Navigator.push(
context,
PageRouteBuilder(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
),
);

// 缩放
Navigator.push(
context,
PageRouteBuilder(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return ScaleTransition(
scale: animation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
),
);

3. 平台特定过渡

1
2
3
4
5
6
7
8
9
10
11
// Android风格
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen()),
);

// iOS风格
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => DetailScreen()),
);

八、嵌套路由与Tab导航

1. 嵌套Navigator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class HomeScreen extends StatelessWidget {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Navigator(
key: _navigatorKey,
initialRoute: '/home',
onGenerateRoute: (settings) {
switch (settings.name) {
case '/home':
return MaterialPageRoute(builder: (context) => HomeContent());
case '/home/detail':
return MaterialPageRoute(builder: (context) => HomeDetail());
default:
return null;
}
},
),
);
}
}

2. Tab导航与路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
class TabNavigator extends StatelessWidget {
final String tabItem;
final GlobalKey<NavigatorState> navigatorKey;

const TabNavigator({
Key? key,
required this.tabItem,
required this.navigatorKey,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (settings) {
Widget child;
switch (settings.name) {
case '/':
child = getTabScreen(tabItem);
break;
case '/detail':
child = DetailScreen();
break;
default:
child = getTabScreen(tabItem);
}
return MaterialPageRoute(builder: (context) => child);
},
);
}

Widget getTabScreen(String tabItem) {
switch (tabItem) {
case 'home':
return HomeScreen();
case 'profile':
return ProfileScreen();
default:
return HomeScreen();
}
}
}

class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
String _currentTab = 'home';
final Map<String, GlobalKey<NavigatorState>> _navigatorKeys = {
'home': GlobalKey<NavigatorState>(),
'profile': GlobalKey<NavigatorState>(),
};

void _selectTab(String tabItem) {
if (tabItem == _currentTab) {
_navigatorKeys[tabItem]?.currentState?.popUntil((route) => route.isFirst);
} else {
setState(() => _currentTab = tabItem);
}
}

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab = !await _navigatorKeys[_currentTab]!.currentState!.maybePop();
if (isFirstRouteInCurrentTab) {
if (_currentTab != 'home') {
_selectTab('home');
return false;
}
}
return isFirstRouteInCurrentTab;
},
child: Scaffold(
body: Stack(children: [
_buildOffstageNavigator('home'),
_buildOffstageNavigator('profile'),
]),
bottomNavigationBar: BottomNavigationBar(
currentIndex: ['home', 'profile'].indexOf(_currentTab),
onTap: (index) => _selectTab(['home', 'profile'][index]),
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
),
);
}

Widget _buildOffstageNavigator(String tabItem) {
return Offstage(
offstage: _currentTab != tabItem,
child: TabNavigator(
tabItem: tabItem,
navigatorKey: _navigatorKeys[tabItem]!,
),
);
}
}

九、路由性能优化

1. 延迟加载路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/heavy') {
return MaterialPageRoute(builder: (context) {
// 延迟加载重量级页面
return FutureBuilder(
future: Future.delayed(Duration(milliseconds: 100)),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return HeavyScreen();
}
return Center(child: CircularProgressIndicator());
},
);
});
}
// 其他路由...
},
);

2. 路由预加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class PreloadManager {
static final Map<String, Widget> _preloadedWidgets = {};

static Future<void> preload(String routeName) async {
switch (routeName) {
case '/detail':
_preloadedWidgets['/detail'] = DetailScreen();
break;
case '/settings':
_preloadedWidgets['/settings'] = SettingsScreen();
break;
}
}

static Widget? getPreloaded(String routeName) {
return _preloadedWidgets[routeName];
}
}

// 使用
await PreloadManager.preload('/detail');

// 导航时使用预加载的Widget
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PreloadManager.getPreloaded('/detail')!),
);

3. 路由缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class RouteCache {
static final Map<String, Widget> _cache = {};

static Widget getOrCreate(String routeName, Widget Function() creator) {
if (!_cache.containsKey(routeName)) {
_cache[routeName] = creator();
}
return _cache[routeName]!;
}

static void clear() {
_cache.clear();
}

static void remove(String routeName) {
_cache.remove(routeName);
}
}

// 使用
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RouteCache.getOrCreate(
'/detail',
() => DetailScreen(),
),
),
);

十、最佳实践

1. 路由管理架构

推荐的路由管理方式

  • 使用命名路由进行集中管理
  • 结合onGenerateRoute处理动态路由
  • 使用类型安全的参数传递

目录结构

1
2
3
4
5
6
7
8
9
10
lib/
├── routes/
│ ├── route_names.dart # 路由名称常量
│ ├── route_arguments.dart # 路由参数类型
│ └── route_generator.dart # 路由生成器
├── screens/
│ ├── home_screen.dart
│ ├── detail_screen.dart
│ └── ...
└── main.dart

2. 路由名称管理

1
2
3
4
5
6
7
// route_names.dart
class RouteNames {
static const String home = '/';
static const String detail = '/detail';
static const String settings = '/settings';
static const String profile = '/profile';
}

3. 路由参数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
// route_arguments.dart
class DetailArguments {
final int id;
final String title;

DetailArguments(this.id, this.title);
}

class ProfileArguments {
final String userId;

ProfileArguments(this.userId);
}

4. 路由生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// route_generator.dart
import 'package:flutter/material.dart';
import 'package:your_app/screens/home_screen.dart';
import 'package:your_app/screens/detail_screen.dart';
import 'package:your_app/screens/settings_screen.dart';
import 'package:your_app/screens/profile_screen.dart';
import 'package:your_app/routes/route_names.dart';
import 'package:your_app/routes/route_arguments.dart';

class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case RouteNames.home:
return MaterialPageRoute(builder: (_) => HomeScreen());

case RouteNames.detail:
final args = settings.arguments as DetailArguments;
return MaterialPageRoute(
builder: (_) => DetailScreen(args: args),
);

case RouteNames.settings:
return MaterialPageRoute(builder: (_) => SettingsScreen());

case RouteNames.profile:
final args = settings.arguments as ProfileArguments;
return MaterialPageRoute(
builder: (_) => ProfileScreen(args: args),
);

default:
return _errorRoute();
}
}

static Route<dynamic> _errorRoute() {
return MaterialPageRoute(builder: (_) {
return Scaffold(
appBar: AppBar(title: Text('Error')),
body: Center(child: Text('Page not found')),
);
});
}
}

5. 应用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// main.dart
import 'package:flutter/material.dart';
import 'package:your_app/routes/route_generator.dart';
import 'package:your_app/routes/route_names.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Route Demo',
initialRoute: RouteNames.home,
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}

6. 导航工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

static Future<dynamic> navigateTo(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushNamed(routeName, arguments: arguments);
}

static void goBack() {
navigatorKey.currentState!.pop();
}

static Future<dynamic> replaceWith(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushReplacementNamed(routeName, arguments: arguments);
}

static void popUntil(String routeName) {
navigatorKey.currentState!.popUntil(ModalRoute.withName(routeName));
}

static Future<dynamic> pushAndRemoveUntil(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushNamedAndRemoveUntil(
routeName,
(Route<dynamic> route) => false,
arguments: arguments,
);
}
}

// 配置
MaterialApp(
navigatorKey: NavigationService.navigatorKey,
// ...
);

// 使用
NavigationService.navigateTo(RouteNames.detail, arguments: DetailArguments(1, 'Flutter'));

总结

Flutter的路由系统是一个强大而灵活的导航框架,通过掌握以下核心概念和技术,可以构建出流畅、高效的应用导航体验:

  1. 基础导航:掌握Navigator的基本操作
  2. 参数传递:选择合适的传参方式
  3. 命名路由:实现集中化的路由管理
  4. 路由守卫:处理权限和导航逻辑
  5. 深度链接:支持外部链接打开应用
  6. 路由动画:提升用户体验
  7. 嵌套路由:实现复杂的导航结构
  8. 性能优化:确保导航流畅性

通过合理的路由设计和管理,可以显著提升应用的用户体验和代码可维护性。希望这份指南能帮助你更好地理解和使用Flutter的路由系统。