Flutter 全屏页面路由实现完全指南
在实际开发中,我们经常需要某些页面(如视频播放器、游戏、图片查看器等)以全屏模式显示。本文将详细介绍在 Flutter 中实现全屏页面的各种方法、最佳实践和常见问题解决方案。
目录
· 实现原理
· 核心方法
· 完整实现方案
· 路由集成
· 平台差异处理
· 常见问题解决
· 最佳实践
实现原理
Flutter 通过 SystemChrome 类来控制系统 UI 的显示和隐藏。全屏的本质是隐藏状态栏、导航栏等系统 UI 元素,让应用内容占据整个屏幕。
系统 UI 模式说明
模式 描述 适用场景
SystemUiMode.edgeToEdge 正常模式,显示状态栏和导航栏 普通页面
SystemUiMode.immersive 全屏模式,需要手动退出 游戏、视频播放
SystemUiMode.immersiveSticky 全屏模式,可临时显示系统 UI 图片查看、阅读器
核心方法
- 基本全屏控制
import'package:flutter/services.dart';// 进入全屏SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);// 退出全屏SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);- 更彻底的全屏控制
// 完全隐藏所有系统 UISystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:[],// 空数组表示隐藏所有覆盖层);// 恢复正常SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:SystemUiOverlay.values,// 显示所有覆盖层);完整实现方案
方案一:基于页面生命周期的实现
import'package:flutter/material.dart';import'package:flutter/services.dart';import'dart:io';classFullScreenPageextendsStatefulWidget{finalWidgetchild;finalbool enableFullScreen;constFullScreenPage({Key?key,requiredthis.child,this.enableFullScreen=true,}):super(key:key);@override_FullScreenPageStatecreateState()=>_FullScreenPageState();}class_FullScreenPageStateextendsState<FullScreenPage>withWidgetsBindingObserver{@overridevoidinitState(){super.initState();WidgetsBinding.instance.addObserver(this);if(widget.enableFullScreen){_enterFullScreen();}}Future<void>_enterFullScreen()async{awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky,);// 可选:锁定横屏// await SystemChrome.setPreferredOrientations([// DeviceOrientation.landscapeLeft,// DeviceOrientation.landscapeRight,// ]);}Future<void>_exitFullScreen()async{awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge,);// 恢复屏幕方向// await SystemChrome.setPreferredOrientations([// DeviceOrientation.portraitUp,// DeviceOrientation.portraitDown,// DeviceOrientation.landscapeLeft,// DeviceOrientation.landscapeRight,// ]);}@overridevoiddidChangeMetrics(){// 当系统 UI 状态变化时,可以重新进入全屏if(widget.enableFullScreen){_enterFullScreen();}}@overridevoiddispose(){WidgetsBinding.instance.removeObserver(this);_exitFullScreen();super.dispose();}@overrideWidgetbuild(BuildContextcontext){returnwidget.child;}}方案二:全屏管理器类
// full_screen_manager.dartimport'package:flutter/services.dart';import'dart:io';classFullScreenManager{staticbool _isFullScreen=false;/// 进入全屏模式staticFuture<void>enter()async{if(_isFullScreen)return;try{if(Platform.isAndroid){// Android: 使用 immersiveSticky 效果最好awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky,);}elseif(Platform.isIOS){// iOS: 使用 manual 模式控制更精细awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:[],);}_isFullScreen=true;print('进入全屏模式成功');}catch(e){print('进入全屏失败:$e');_isFullScreen=false;}}/// 退出全屏模式staticFuture<void>exit()async{if(!_isFullScreen)return;try{// 恢复到正常模式awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge,);// 确保所有系统 UI 都显示awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:SystemUiOverlay.values,);_isFullScreen=false;print('退出全屏模式成功');}catch(e){print('退出全屏失败:$e');}}/// 切换全屏状态staticFuture<void>toggle()async{if(_isFullScreen){awaitexit();}else{awaitenter();}}/// 获取当前状态staticboolgetisFullScreen=>_isFullScreen;/// 临时显示系统 UI(仅限 immersiveSticky 模式)staticvoidshowSystemUI(){if(_isFullScreen){SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:SystemUiOverlay.values,);// 3秒后自动隐藏Future.delayed(Duration(seconds:3),(){if(_isFullScreen){SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:[],);}});}}}方案三:全屏路由包装器
// full_screen_route.dartimport'package:flutter/material.dart';import'full_screen_manager.dart';/// 全屏路由包装器classFullScreenRoute<T>extendsMaterialPageRoute<T>{FullScreenRoute({requiredWidgetBuilderbuilder,RouteSettings?settings,bool maintainState=true,bool fullscreenDialog=false,}):super(builder:builder,settings:settings,maintainState:maintainState,fullscreenDialog:fullscreenDialog,);@overrideWidgetbuildPage(BuildContextcontext,Animation<double>animation,Animation<double>secondaryAnimation){// 在页面构建时进入全屏Future.microtask(()=>FullScreenManager.enter());returnFullScreenPage(enableFullScreen:true,child:builder(context),);}@overridevoiddidComplete(T?result){// 路由完成时退出全屏FullScreenManager.exit();super.didComplete(result);}}路由集成
- 基本路由跳转
// 使用全屏路由跳转Navigator.push(context,FullScreenRoute(builder:(context)=>VideoPlayerScreen(videoUrl:videoUrl),),);// 或者使用命名路由MaterialApp(title:'全屏示例',initialRoute:'/',routes:{'/':(context)=>HomeScreen(),'/video':(context)=>FullScreenPage(enableFullScreen:true,child:VideoPlayerScreen(),),},);- 带参数的全屏路由
classFullScreenVideoPageextendsStatelessWidget{finalStringvideoUrl;constFullScreenVideoPage({Key?key,requiredthis.videoUrl}):super(key:key);@overrideWidgetbuild(BuildContextcontext){returnFullScreenPage(enableFullScreen:true,child:VideoPlayerScreen(videoUrl:videoUrl),);}}// 跳转Navigator.push(context,MaterialPageRoute(builder:(context)=>FullScreenVideoPage(videoUrl:'https://example.com/video.mp4'),),);平台差异处理
Android 特殊处理
// android_full_screen.dartimport'package:flutter/services.dart';classAndroidFullScreen{/// Android 专属的全屏处理staticFuture<void>enterFullScreen()async{// 方法1:推荐方式awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky,);// 方法2:更彻底的全屏// await SystemChrome.setEnabledSystemUIMode(// SystemUiMode.immersive,// );// 处理返回按钮// SystemChrome.setPreferredOrientations([// DeviceOrientation.landscapeLeft,// DeviceOrientation.landscapeRight,// ]);}/// 处理 Android 物理返回键staticWidgetwrapWithBackHandler(Widgetchild,VoidCallbackonBack){returnWillPopScope(onWillPop:()async{onBack();returnfalse;// 阻止默认返回行为},child:child,);}}iOS 特殊处理
// ios_full_screen.dartimport'package:flutter/services.dart';classIOSFullScreen{/// iOS 专属的全屏处理staticFuture<void>enterFullScreen()async{// iOS 对全屏的支持略有不同awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,overlays:[],// 隐藏所有覆盖层);// iOS 通常不需要处理横屏,除非特别需求}/// 处理 iOS 状态栏样式staticvoidsetStatusBarStyle(Brightnessbrightness){SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(statusBarBrightness:brightness,statusBarIconBrightness:brightness==Brightness.dark?Brightness.light:Brightness.dark,),);}}常见问题解决
问题1:Android 手势导航冲突
现象:在 Android 10+ 使用手势导航时,底部导航条可能偶尔出现。
解决方案:
classFullScreenWithGestureHandlerextendsStatefulWidget{finalWidgetchild;constFullScreenWithGestureHandler({Key?key,requiredthis.child}):super(key:key);@override_FullScreenWithGestureHandlerStatecreateState()=>_FullScreenWithGestureHandlerState();}class_FullScreenWithGestureHandlerStateextendsState<FullScreenWithGestureHandler>{Timer?_hideTimer;@overridevoidinitState(){super.initState();_enterFullScreen();}void_enterFullScreen()async{// 使用更彻底的全屏模式awaitSystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive,);}void_onUserInteraction(){// 用户交互时重新隐藏系统 UI_hideTimer?.cancel();_enterFullScreen();// 设置定时器,定期检查并重新进入全屏_hideTimer=Timer.periodic(Duration(seconds:1),(timer){_enterFullScreen();});}@overrideWidgetbuild(BuildContextcontext){returnGestureDetector(onTap:_onUserInteraction,onPanDown:(_)=>_onUserInteraction(),behavior:HitTestBehavior.opaque,child:widget.child,);}@overridevoiddispose(){_hideTimer?.cancel();SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);super.dispose();}}问题2:页面切换动画冲突
解决方案:自定义页面过渡
classFadeFullScreenRoute<T>extendsPageRouteBuilder<T>{finalWidgetpage;finalbool enableFullScreen;FadeFullScreenRoute({requiredthis.page,this.enableFullScreen=true,}):super(pageBuilder:(BuildContextcontext,Animation<double>animation,Animation<double>secondaryAnimation,){if(enableFullScreen){returnFullScreenPage(enableFullScreen:true,child:page,);}returnpage;},transitionsBuilder:(BuildContextcontext,Animation<double>animation,Animation<double>secondaryAnimation,Widgetchild,){returnFadeTransition(opacity:animation,child:child,);},transitionDuration:Duration(milliseconds:300),);}问题3:多个全屏页面连续跳转
classFullScreenStackManager{staticfinalList<String>_fullScreenPages=[];staticvoidpush(StringpageName){_fullScreenPages.add(pageName);if(_fullScreenPages.length==1){// 第一个全屏页面,进入全屏FullScreenManager.enter();}}staticvoidpop(StringpageName){_fullScreenPages.remove(pageName);if(_fullScreenPages.isEmpty){// 最后一个全屏页面退出,恢复普通模式FullScreenManager.exit();}}staticboolisFullScreenActive(){return_fullScreenPages.isNotEmpty;}}// 在页面中使用classFullScreenPageWrapperextendsStatefulWidget{finalStringpageName;finalWidgetchild;@overridevoidinitState(){super.initState();FullScreenStackManager.push(pageName);}@overridevoiddispose(){FullScreenStackManager.pop(pageName);super.dispose();}}最佳实践
- 封装统一的全屏组件
// responsive_full_screen.dartimport'package:flutter/material.dart';import'full_screen_manager.dart';/// 响应式全屏组件,根据平台自动适配classResponsiveFullScreenextendsStatefulWidget{finalWidgetchild;finalbool enableFullScreen;finalbool enableGestureControl;finalbool autoExitOnDispose;constResponsiveFullScreen({Key?key,requiredthis.child,this.enableFullScreen=true,this.enableGestureControl=true,this.autoExitOnDispose=true,}):super(key:key);@override_ResponsiveFullScreenStatecreateState()=>_ResponsiveFullScreenState();}class_ResponsiveFullScreenStateextendsState<ResponsiveFullScreen>{@overridevoidinitState(){super.initState();if(widget.enableFullScreen){_enterFullScreen();}}Future<void>_enterFullScreen()async{awaitFullScreenManager.enter();}Future<void>_exitFullScreen()async{if(widget.autoExitOnDispose){awaitFullScreenManager.exit();}}void_handleUserInteraction(){if(widget.enableGestureControl&&widget.enableFullScreen){FullScreenManager.showSystemUI();}}@overridevoiddispose(){_exitFullScreen();super.dispose();}@overrideWidgetbuild(BuildContextcontext){returnwidget.enableGestureControl?GestureDetector(onTap:_handleUserInteraction,behavior:HitTestBehavior.opaque,child:widget.child,):widget.child;}}- 完整的视频播放器示例
// video_player_screen.dartimport'package:flutter/material.dart';import'responsive_full_screen.dart';classVideoPlayerScreenextendsStatefulWidget{finalStringvideoUrl;constVideoPlayerScreen({Key?key,requiredthis.videoUrl}):super(key:key);@override_VideoPlayerScreenStatecreateState()=>_VideoPlayerScreenState();}class_VideoPlayerScreenStateextendsState<VideoPlayerScreen>{bool _showControls=false;@overrideWidgetbuild(BuildContextcontext){returnResponsiveFullScreen(enableGestureControl:true,child:Scaffold(backgroundColor:Colors.black,body:Stack(children:[// 视频播放器Center(child:Text('视频播放器',style:TextStyle(color:Colors.white,fontSize:24),),),// 控制栏if(_showControls)Positioned(bottom:0,left:0,right:0,child:Container(color:Colors.black54,padding:EdgeInsets.all(16),child:Row(mainAxisAlignment:MainAxisAlignment.spaceBetween,children:[IconButton(icon:Icon(Icons.arrow_back,color:Colors.white),onPressed:()=>Navigator.pop(context),),IconButton(icon:Icon(Icons.fullscreen_exit,color:Colors.white),onPressed:(){// 退出全屏逻辑},),],),),),],),),);}}- 配置示例
// main.dartimport'package:flutter/material.dart';import'video_player_screen.dart';voidmain(){runApp(constMyApp());}classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(title:'Flutter 全屏示例',theme:ThemeData(primarySwatch:Colors.blue,useMaterial3:true,),home:constHomeScreen(),);}}classHomeScreenextendsStatelessWidget{constHomeScreen({super.key});@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('首页')),body:Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[ElevatedButton(onPressed:(){Navigator.push(context,MaterialPageRoute(builder:(context)=>VideoPlayerScreen(videoUrl:'https://example.com/video.mp4',),),);},child:constText('进入全屏视频播放'),),constSizedBox(height:20),constText('提示:点击屏幕可以临时显示系统控制栏'),],),),);}}总结
- 核心方法:使用 SystemChrome.setEnabledSystemUIMode() 控制全屏
- 推荐模式:SystemUiMode.immersiveSticky 用户体验最佳
- 生命周期管理:确保在 dispose() 中恢复系统 UI
- 平台适配:Android 和 iOS 需要不同的处理策略
- 用户体验:提供手势控制,让用户可以临时查看系统 UI
- 错误处理:添加适当的异常处理和状态恢复
通过以上方案,你可以灵活地在 Flutter 应用中实现全屏页面,并提供良好的用户体验。根据具体需求选择合适的实现方式,建议使用封装好的 ResponsiveFullScreen 组件来简化开发。