当前位置: 首页 > news >正文

Flutter 导航深度解析:从入门到精通

Flutter 导航深度解析:从入门到精通

掌握 Flutter 导航的每一种模式,构建流畅的用户体验。

一、导航的核心概念

作为一名追求像素级还原的 UI 匠人,我深知导航是应用的骨架。Flutter 提供了多种导航方式,从简单的Navigator.push到复杂的声明式路由,每一种都有其适用场景。

二、命令式导航

1. 基础导航

// 跳转到新页面 Navigator.push( context, MaterialPageRoute(builder: (context) => DetailPage()), ); // 返回上一页 Navigator.pop(context); // 替换当前页面 Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => NewPage()), ); // 清除栈并跳转到新页面 Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => HomePage()), (route) => false, // 清除所有历史 );

2. 传递参数

// 跳转时传递参数 Navigator.push( context, MaterialPageRoute( builder: (context) => DetailPage(itemId: '123'), ), ); // 接收参数 class DetailPage extends StatelessWidget { final String itemId; DetailPage({required this.itemId}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('详情')), body: Center(child: Text('Item ID: $itemId')), ); } } // 返回时传递数据 Navigator.pop(context, 'result_data'); // 接收返回数据 final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => DetailPage()), );

3. 自定义路由动画

class FadeRoute extends PageRouteBuilder { final Widget page; FadeRoute({required this.page}) : super( pageBuilder: (context, animation, secondaryAnimation) => page, transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: child, ); }, ); } // 使用 Navigator.push(context, FadeRoute(page: DetailPage()));

三、声明式导航(Navigator 2.0)

1. 基础结构

class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String _selectedItem = ''; bool _showDetail = false; @override Widget build(BuildContext context) { return MaterialApp( home: Navigator( pages: [ MaterialPage( key: ValueKey('HomePage'), child: HomePage( onItemSelected: (item) { setState(() { _selectedItem = item; _showDetail = true; }); }, ), ), if (_showDetail) MaterialPage( key: ValueKey('DetailPage_$_selectedItem'), child: DetailPage( itemId: _selectedItem, onBack: () { setState(() => _showDetail = false); }, ), ), ], onPopPage: (route, result) { if (!route.didPop(result)) return false; setState(() => _showDetail = false); return true; }, ), ); } }

2. 使用 Router

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp.router( routerDelegate: MyRouterDelegate(), routeInformationParser: MyRouteInformationParser(), ); } } // 路由信息 class RouteInformation { final String? itemId; RouteInformation({this.itemId}); } // 路由解析器 class MyRouteInformationParser extends RouteInformationParser<RouteInformation> { @override Future<RouteInformation> parseRouteInformation( RouteInformation routeInformation, ) async { final uri = Uri.parse(routeInformation.location ?? '/'); if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'item') { return RouteInformation(itemId: uri.pathSegments[1]); } return RouteInformation(); } @override RouteInformation restoreRouteInformation(RouteInformation configuration) { if (configuration.itemId != null) { return RouteInformation(location: '/item/${configuration.itemId}'); } return RouteInformation(location: '/'); } } // 路由委托 class MyRouterDelegate extends RouterDelegate<RouteInformation> with ChangeNotifier, PopNavigatorRouterDelegateMixin<RouteInformation> { final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); String? _selectedItem; set selectedItem(String? value) { _selectedItem = value; notifyListeners(); } @override RouteInformation get currentConfiguration { return RouteInformation(itemId: _selectedItem); } @override Widget build(BuildContext context) { return Navigator( key: navigatorKey, pages: [ MaterialPage( key: ValueKey('HomePage'), child: HomePage( onItemSelected: (item) { selectedItem = item; }, ), ), if (_selectedItem != null) MaterialPage( key: ValueKey('DetailPage_$_selectedItem'), child: DetailPage( itemId: _selectedItem!, onBack: () { selectedItem = null; }, ), ), ], onPopPage: (route, result) { if (!route.didPop(result)) return false; selectedItem = null; return true; }, ); } @override Future<void> setNewRoutePath(RouteInformation configuration) async { _selectedItem = configuration.itemId; } }

四、GoRouter:简化导航

import 'package:go_router/go_router.dart'; final router = GoRouter( initialLocation: '/', routes: [ GoRoute( path: '/', builder: (context, state) => HomePage(), routes: [ GoRoute( path: 'item/:id', builder: (context, state) { final id = state.pathParameters['id']!; return DetailPage(itemId: id); }, ), ], ), GoRoute( path: '/settings', builder: (context, state) => SettingsPage(), ), ], ); // 使用 class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: router, ); } } // 导航 context.go('/'); context.go('/item/123'); context.push('/settings');

五、底部导航与嵌套导航

class MainPage extends StatefulWidget { @override _MainPageState createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { int _currentIndex = 0; final _pages = [ HomeTab(), SearchTab(), ProfileTab(), ]; @override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _currentIndex, children: _pages, ), bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) => setState(() => _currentIndex = index), items: [ BottomNavigationBarItem( icon: Icon(Icons.home), label: '首页', ), BottomNavigationBarItem( icon: Icon(Icons.search), label: '搜索', ), BottomNavigationBarItem( icon: Icon(Icons.person), label: '我的', ), ], ), ); } }

六、最佳实践

  1. 选择合适的方式:简单应用用命令式,复杂应用用声明式
  2. 状态管理:导航状态应该与应用状态同步
  3. URL 支持:Web 应用要确保每个页面都有对应的 URL
  4. 过渡动画:保持一致的页面过渡效果

导航是用户的空间地图,清晰的导航让探索成为享受。

#flutter #navigation #routing #mobile #architecture

http://www.jsqmd.com/news/594646/

相关文章:

  • OpenClaw多实例管理:Phi-3-vision-128k-instruct专用网关的隔离部署
  • SAP增强中多线程STARTING NEW TASK实现BAPI事务提交的实践指南
  • 深入解析Google AutoService:组件化通信的轻量级解决方案
  • 二维码识别性能优化:UniApp中canvas截取与qrcode.js的黄金参数配置
  • Linux文件系统探秘:当你删除一个文件时,inode位图究竟发生了什么变化?
  • 别再为OSGB发愁了!手把手教你用Cesium + 3DTiles在Vue3/Vite项目中搭建三维场景
  • 从一次系统宕机说起:深入FPGA异步复位释放的“亚稳态”陷阱与救赎之路
  • SEO_为什么你的SEO效果不好?关键原因分析
  • 手把手拆解:从浮栅晶体管到你的SD卡,Flash闪存‘写1擦0’全流程保姆级图解
  • Python实战:5分钟搞定MODIS数据NDVI提取(附完整代码)
  • seo咨询服务需要多长时间_seo咨询服务如何进行技术优化
  • 西门子1200系列Modbus RTU通讯及485通讯轮询的实践案例
  • 5分钟搞定Python+Matplotlib绘制专业气象色斑图(附完整代码)
  • 控制器故障诊断程序功能说明(基于原始代码解读)
  • 网站SEO优化与页面加载速度的关系是什么
  • pip安装报错?手把手教你解决ProxyError和HTTPSConnection问题(附最新解决方案)
  • 从恐龙书CH09的页表问题,聊聊现代Linux/Windows内存管理是怎么做的
  • 海康相机预置点优化:从255到无限可能的巡检机器人应用
  • 组件-Gradle
  • OpenClaw数据可视化:gemma-3-12b-it分析CSV并生成动态图表
  • MATLAB for AE b value calc with events v5 功能说明
  • ZYNQ AXI_DMA配置避坑指南:如何避免DDR3数据传输中的栈区溢出
  • 告别临时运行!手把手教你将Snipaste AppImage永久‘安装’到Ubuntu系统菜单
  • llama-factory || AutoDL || 自定义数据集微调实战指南
  • 基于ADRC的电机控制仿真源文件 模型主要包含: 1.直流电机ADRC仿真 2.永磁同步电机A...
  • Amadeus的知识库 | RAG高效向量检索的秘密?—— 关于向量数据库你必须知道的!
  • 别再只会用na.omit删数据了!R语言缺失值处理保姆级教程:从均值填补到随机森林实战
  • STM32H723ZGT6上FreeRTOS移植实战:从源码获取到任务调通的保姆级避坑指南
  • UE5地牢生成实战:从零搭建程序化地下城(附完整蓝图逻辑)
  • 深入解析Kubernetes中的Custom Resource Definitions(CRD):构建云原生“自定义积木”的终极武器