案例代码
完整代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// MaterialApp 换成 GetMaterialApp
return GetMaterialApp(
title: 'Navigator 1.0 + GetX 嵌套导航 Demo',
home: const MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _index = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("GetX+Navigator 1.0 嵌套导航 Demo")),
body: IndexedStack(
index: _index,
children: const [Tab1Page(), Tab2Page()],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _index,
onTap: (i) => setState(() => _index = i),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Tab1"),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Tab2"),
],
),
);
}
}
class Tab1Page extends StatelessWidget {
const Tab1Page({super.key});
@override
Widget build(BuildContext context) {
// 从 GetX 4.6.x 之后,GetNavigator 就逐渐和 Flutter 原生的 Navigator 2.0 完全对齐,
// 因此现在不再支持onGenerateRoute这个参数,如果想用只能通过Navigator
return Navigator(
key: Get.nestedKey(1),
onGenerateRoute: (settings) {
if (settings.name == "/tab1/detail") {
return MaterialPageRoute(builder: (_) => const DetailPage());
}
return MaterialPageRoute(builder: (_) => const Tab1Home());
},
);
}
}
class Tab1Home extends StatelessWidget {
const Tab1Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Tab1 首页")),
body: Center(
child: ElevatedButton(
onPressed: () {
// ✅ GetX 的跳转方式
Get.toNamed("/tab1/detail", id: 1);
// Get.to(
// () => const DetailPage(),
// id: 1, // 注意指定子导航器 ID
// );
// 这里的 id:1 是对应 Tab1Page 里的 Navigator
},
child: const Text("进入详情页"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("详情页")),
body: Center(
child: ElevatedButton(
onPressed: () => Get.back(id: 1), // ✅ 返回 Tab1 的路由栈
child: const Text("返回 Tab1"),
),
),
);
}
}
class Tab2Page extends StatelessWidget {
const Tab2Page({super.key});
@override
Widget build(BuildContext context) {
return Navigator(
key: Get.nestedKey(2), // ✅ 指定子导航器 ID 为 2
onGenerateRoute: (settings) {
// 这里始终返回 Tab2 首页
return MaterialPageRoute(builder: (_) => const Tab2Home());
},
);
}
}
class Tab2Home extends StatelessWidget {
const Tab2Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Tab2 页面")),
body: const Center(child: Text("这里是 Tab2")),
);
}
}
关键代码截图
class Tab1Page extends StatelessWidget {
const Tab1Page({super.key});
@override
Widget build(BuildContext context) {
// 从 GetX 4.6.x 之后,GetNavigator 就逐渐和 Flutter 原生的 Navigator 2.0 完全对齐,
// 因此现在不再支持onGenerateRoute这个参数,如果想用只能通过Navigator
return Navigator(
key: Get.nestedKey(1),
onGenerateRoute: (settings) {
if (settings.name == "/tab1/detail") {
return MaterialPageRoute(builder: (_) => const DetailPage());
}
return MaterialPageRoute(builder: (_) => const Tab1Home());
},
);
}
}
// ✅ GetX 的跳转方式
Get.toNamed("/tab1/detail", id: 1);
// Get.to(
// () => const DetailPage(),
// id: 1, // 注意指定子导航器 ID
// );
// 这里的 id:1 是对应 Tab1Page 里的 Navigator
() => Get.back(id: 1), // ✅ 返回 Tab1 的路由栈
onGenerateRoute
是什么
在 Flutter 中,Navigator
是一个路由栈管理器,管理着一组页面的进出。
当你创建一个 Navigator
并提供了 onGenerateRoute
参数时,它会在「需要创建路由时」调用 onGenerateRoute
来生成一个页面(Route 对象)。
工作流程
应用创建 Navigator
Navigator(
key: Get.nestedKey(1),
onGenerateRoute: (settings) {
return MaterialPageRoute(builder: (_) => const Tab1Home());
},
);
简图:
[App 启动]
↓
[创建 Navigator (id=1)]
↓
[配置 onGenerateRoute 回调函数]
第一次显示页面时调用 onGenerateRoute
当这个 Navigator
创建时,它会调用一次 onGenerateRoute
,并传入一个默认的 RouteSettings
(通常 name=null
或 name="/"
)。
此时 onGenerateRoute
返回一个 MaterialPageRoute
,例如:
MaterialPageRoute(builder: (_) => const Tab1Home());
简图:
[Navigator 需要显示第一个页面]
↓
[调用 onGenerateRoute(settings)]
↓
[返回一个 MaterialPageRoute(Tab1Home)]
↓
[Tab1Home 被压入路由栈并显示]
当调用 Navigator.pushNamed(...)
时
如果你调用:
Navigator.of(context).pushNamed("/tab1/detail");
或者 Get.toNamed("/tab1/detail", id: 1);
则会再次调用 onGenerateRoute
,这次传入的 settings.name == "/tab1/detail"
。
你的代码中会判断:
if (settings.name == "/tab1/detail") {
return MaterialPageRoute(builder: (_) => const DetailPage());
}
于是 DetailPage
被创建并压入当前 Navigator
的路由栈中。
简图:
[调用 pushNamed("/tab1/detail")]
↓
[调用 onGenerateRoute(settings.name = "/tab1/detail")]
↓
[返回一个 MaterialPageRoute(DetailPage)]
↓
[DetailPage 被压入当前 Navigator 的路由栈]
Get.to(...)
不会触发onGenerateRoute
return Navigator(
key: Get.nestedKey(1),
onGenerateRoute: (settings) {
if (settings.name == "/tab1/detail") {
return MaterialPageRoute(builder: (_) => const DetailPage());
}
return MaterialPageRoute(builder: (_) => const Tab1Home());
},
);
当如果不使用Get.toNamed
,而使用Get.to
:
Get.to(() => const DetailPage(), id: 1);
GetX 会直接找到 id=1
的 Navigator
,然后直接调用它的 push()
方法,压入一个新的路由(MaterialPageRoute),而不是通过 onGenerateRoute
。
也就是说:
Get.to(() => const DetailPage(), id: 1)
并不会走onGenerateRoute
。- 它直接用你传入的
Widget
(DetailPage
)创建一个新的MaterialPageRoute
,然后直接push
进路由栈。
简图:
[Get.to(() => DetailPage(), id: 1)]
↓
[找到 Navigator(key: Get.nestedKey(1))]
↓
[直接 push 一个 MaterialPageRoute(DetailPage)]
↓
[DetailPage 出现在 Tab1 的 Navigator 中]
所以上面的代码可以简化为:
Navigator(
key: Get.nestedKey(1),
onGenerateRoute: (settings) {
return MaterialPageRoute(builder: (_) => const Tab1Home());
},
);
只保留初始页面的生成逻辑就够了。