Flutter 全屏页面路由实现完全指南
在实际开发中,我们经常需要某些页面(如视频播放器、游戏、图片查看器等)以全屏模式显示。本文将详细介绍在 Flutter 中实现全屏页面的各种方法、最佳实践和常见问题解决方案。
目录
· 实现原理
· 核心方法
· 完整实现方案
· 路由集成
· 平台差异处理
· 常见问题解决
· 最佳实践
实现原理
Flutter 通过 SystemChrome 类来控制系统 UI 的显示和隐藏。全屏的本质是隐藏状态栏、导航栏等系统 UI 元素,让应用内容占据整个屏幕。
系统 UI 模式说明
模式 描述 适用场景
SystemUiMode.edgeToEdge 正常模式,显示状态栏和导航栏 普通页面
SystemUiMode.immersive 全屏模式,需要手动退出 游戏、视频播放
SystemUiMode.immersiveSticky 全屏模式,可临时显示系统 UI 图片查看、阅读器
核心方法
- 基本全屏控制
dart
import 'package:flutter/services.dart';
// 进入全屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
// 退出全屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
- 更彻底的全屏控制
dart
// 完全隐藏所有系统 UI
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [], // 空数组表示隐藏所有覆盖层
);
// 恢复正常
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values, // 显示所有覆盖层
);
完整实现方案
方案一:基于页面生命周期的实现
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:io';
class FullScreenPage extends StatefulWidget {
final Widget child;
final bool enableFullScreen;
const FullScreenPage({
Key? key,
required this.child,
this.enableFullScreen = true,
}) : super(key: key);
@override
_FullScreenPageState createState() => _FullScreenPageState();
}
class _FullScreenPageState extends State<FullScreenPage>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
if (widget.enableFullScreen) {
_enterFullScreen();
}
}
Future<void> _enterFullScreen() async {
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersiveSticky,
);
// 可选:锁定横屏
// await SystemChrome.setPreferredOrientations([
// DeviceOrientation.landscapeLeft,
// DeviceOrientation.landscapeRight,
// ]);
}
Future<void> _exitFullScreen() async {
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.edgeToEdge,
);
// 恢复屏幕方向
// await SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// DeviceOrientation.landscapeLeft,
// DeviceOrientation.landscapeRight,
// ]);
}
@override
void didChangeMetrics() {
// 当系统 UI 状态变化时,可以重新进入全屏
if (widget.enableFullScreen) {
_enterFullScreen();
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_exitFullScreen();
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
方案二:全屏管理器类
dart
// full_screen_manager.dart
import 'package:flutter/services.dart';
import 'dart:io';
class FullScreenManager {
static bool _isFullScreen = false;
/// 进入全屏模式
static Future<void> enter() async {
if (_isFullScreen) return;
try {
if (Platform.isAndroid) {
// Android: 使用 immersiveSticky 效果最好
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersiveSticky,
);
} else if (Platform.isIOS) {
// iOS: 使用 manual 模式控制更精细
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [],
);
}
_isFullScreen = true;
print('进入全屏模式成功');
} catch (e) {
print('进入全屏失败: $e');
_isFullScreen = false;
}
}
/// 退出全屏模式
static Future<void> exit() async {
if (!_isFullScreen) return;
try {
// 恢复到正常模式
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.edgeToEdge,
);
// 确保所有系统 UI 都显示
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
_isFullScreen = false;
print('退出全屏模式成功');
} catch (e) {
print('退出全屏失败: $e');
}
}
/// 切换全屏状态
static Future<void> toggle() async {
if (_isFullScreen) {
await exit();
} else {
await enter();
}
}
/// 获取当前状态
static bool get isFullScreen => _isFullScreen;
/// 临时显示系统 UI(仅限 immersiveSticky 模式)
static void showSystemUI() {
if (_isFullScreen) {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
// 3秒后自动隐藏
Future.delayed(Duration(seconds: 3), () {
if (_isFullScreen) {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [],
);
}
});
}
}
}
方案三:全屏路由包装器
dart
// full_screen_route.dart
import 'package:flutter/material.dart';
import 'full_screen_manager.dart';
/// 全屏路由包装器
class FullScreenRoute<T> extends MaterialPageRoute<T> {
FullScreenRoute({
required WidgetBuilder builder,
RouteSettings? settings,
bool maintainState = true,
bool fullscreenDialog = false,
}) : super(
builder: builder,
settings: settings,
maintainState: maintainState,
fullscreenDialog: fullscreenDialog,
);
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
// 在页面构建时进入全屏
Future.microtask(() => FullScreenManager.enter());
return FullScreenPage(
enableFullScreen: true,
child: builder(context),
);
}
@override
void didComplete(T? result) {
// 路由完成时退出全屏
FullScreenManager.exit();
super.didComplete(result);
}
}
路由集成
- 基本路由跳转
dart
// 使用全屏路由跳转
Navigator.push(
context,
FullScreenRoute(
builder: (context) => VideoPlayerScreen(videoUrl: videoUrl),
),
);
// 或者使用命名路由
MaterialApp(
title: '全屏示例',
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/video': (context) => FullScreenPage(
enableFullScreen: true,
child: VideoPlayerScreen(),
),
},
);
- 带参数的全屏路由
dart
class FullScreenVideoPage extends StatelessWidget {
final String videoUrl;
const FullScreenVideoPage({Key? key, required this.videoUrl})
: super(key: key);
@override
Widget build(BuildContext context) {
return FullScreenPage(
enableFullScreen: true,
child: VideoPlayerScreen(videoUrl: videoUrl),
);
}
}
// 跳转
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullScreenVideoPage(videoUrl: 'https://example.com/video.mp4'),
),
);
平台差异处理
Android 特殊处理
dart
// android_full_screen.dart
import 'package:flutter/services.dart';
class AndroidFullScreen {
/// Android 专属的全屏处理
static Future<void> enterFullScreen() async {
// 方法1:推荐方式
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersiveSticky,
);
// 方法2:更彻底的全屏
// await SystemChrome.setEnabledSystemUIMode(
// SystemUiMode.immersive,
// );
// 处理返回按钮
// SystemChrome.setPreferredOrientations([
// DeviceOrientation.landscapeLeft,
// DeviceOrientation.landscapeRight,
// ]);
}
/// 处理 Android 物理返回键
static Widget wrapWithBackHandler(Widget child, VoidCallback onBack) {
return WillPopScope(
onWillPop: () async {
onBack();
return false; // 阻止默认返回行为
},
child: child,
);
}
}
iOS 特殊处理
dart
// ios_full_screen.dart
import 'package:flutter/services.dart';
class IOSFullScreen {
/// iOS 专属的全屏处理
static Future<void> enterFullScreen() async {
// iOS 对全屏的支持略有不同
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [], // 隐藏所有覆盖层
);
// iOS 通常不需要处理横屏,除非特别需求
}
/// 处理 iOS 状态栏样式
static void setStatusBarStyle(Brightness brightness) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarBrightness: brightness,
statusBarIconBrightness: brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
),
);
}
}
常见问题解决
问题1:Android 手势导航冲突
现象:在 Android 10+ 使用手势导航时,底部导航条可能偶尔出现。
解决方案:
dart
class FullScreenWithGestureHandler extends StatefulWidget {
final Widget child;
const FullScreenWithGestureHandler({Key? key, required this.child})
: super(key: key);
@override
_FullScreenWithGestureHandlerState createState() =>
_FullScreenWithGestureHandlerState();
}
class _FullScreenWithGestureHandlerState
extends State<FullScreenWithGestureHandler> {
Timer? _hideTimer;
@override
void initState() {
super.initState();
_enterFullScreen();
}
void _enterFullScreen() async {
// 使用更彻底的全屏模式
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersive,
);
}
void _onUserInteraction() {
// 用户交互时重新隐藏系统 UI
_hideTimer?.cancel();
_enterFullScreen();
// 设置定时器,定期检查并重新进入全屏
_hideTimer = Timer.periodic(Duration(seconds: 1), (timer) {
_enterFullScreen();
});
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _onUserInteraction,
onPanDown: (_) => _onUserInteraction(),
behavior: HitTestBehavior.opaque,
child: widget.child,
);
}
@override
void dispose() {
_hideTimer?.cancel();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
super.dispose();
}
}
问题2:页面切换动画冲突
解决方案:自定义页面过渡
dart
class FadeFullScreenRoute<T> extends PageRouteBuilder<T> {
final Widget page;
final bool enableFullScreen;
FadeFullScreenRoute({
required this.page,
this.enableFullScreen = true,
}) : super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
if (enableFullScreen) {
return FullScreenPage(
enableFullScreen: true,
child: page,
);
}
return page;
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: Duration(milliseconds: 300),
);
}
问题3:多个全屏页面连续跳转
dart
class FullScreenStackManager {
static final List<String> _fullScreenPages = [];
static void push(String pageName) {
_fullScreenPages.add(pageName);
if (_fullScreenPages.length == 1) {
// 第一个全屏页面,进入全屏
FullScreenManager.enter();
}
}
static void pop(String pageName) {
_fullScreenPages.remove(pageName);
if (_fullScreenPages.isEmpty) {
// 最后一个全屏页面退出,恢复普通模式
FullScreenManager.exit();
}
}
static bool isFullScreenActive() {
return _fullScreenPages.isNotEmpty;
}
}
// 在页面中使用
class FullScreenPageWrapper extends StatefulWidget {
final String pageName;
final Widget child;
@override
void initState() {
super.initState();
FullScreenStackManager.push(pageName);
}
@override
void dispose() {
FullScreenStackManager.pop(pageName);
super.dispose();
}
}
最佳实践
- 封装统一的全屏组件
dart
// responsive_full_screen.dart
import 'package:flutter/material.dart';
import 'full_screen_manager.dart';
/// 响应式全屏组件,根据平台自动适配
class ResponsiveFullScreen extends StatefulWidget {
final Widget child;
final bool enableFullScreen;
final bool enableGestureControl;
final bool autoExitOnDispose;
const ResponsiveFullScreen({
Key? key,
required this.child,
this.enableFullScreen = true,
this.enableGestureControl = true,
this.autoExitOnDispose = true,
}) : super(key: key);
@override
_ResponsiveFullScreenState createState() => _ResponsiveFullScreenState();
}
class _ResponsiveFullScreenState extends State<ResponsiveFullScreen> {
@override
void initState() {
super.initState();
if (widget.enableFullScreen) {
_enterFullScreen();
}
}
Future<void> _enterFullScreen() async {
await FullScreenManager.enter();
}
Future<void> _exitFullScreen() async {
if (widget.autoExitOnDispose) {
await FullScreenManager.exit();
}
}
void _handleUserInteraction() {
if (widget.enableGestureControl && widget.enableFullScreen) {
FullScreenManager.showSystemUI();
}
}
@override
void dispose() {
_exitFullScreen();
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.enableGestureControl
? GestureDetector(
onTap: _handleUserInteraction,
behavior: HitTestBehavior.opaque,
child: widget.child,
)
: widget.child;
}
}
- 完整的视频播放器示例
dart
// video_player_screen.dart
import 'package:flutter/material.dart';
import 'responsive_full_screen.dart';
class VideoPlayerScreen extends StatefulWidget {
final String videoUrl;
const VideoPlayerScreen({Key? key, required this.videoUrl})
: super(key: key);
@override
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
bool _showControls = false;
@override
Widget build(BuildContext context) {
return ResponsiveFullScreen(
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: () {
// 退出全屏逻辑
},
),
],
),
),
),
],
),
),
);
}
}
- 配置示例
dart
// main.dart
import 'package:flutter/material.dart';
import 'video_player_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 全屏示例',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('首页')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VideoPlayerScreen(
videoUrl: 'https://example.com/video.mp4',
),
),
);
},
child: const Text('进入全屏视频播放'),
),
const SizedBox(height: 20),
const Text('提示:点击屏幕可以临时显示系统控制栏'),
],
),
),
);
}
}
总结
- 核心方法:使用 SystemChrome.setEnabledSystemUIMode() 控制全屏
- 推荐模式:SystemUiMode.immersiveSticky 用户体验最佳
- 生命周期管理:确保在 dispose() 中恢复系统 UI
- 平台适配:Android 和 iOS 需要不同的处理策略
- 用户体验:提供手势控制,让用户可以临时查看系统 UI
- 错误处理:添加适当的异常处理和状态恢复
通过以上方案,你可以灵活地在 Flutter 应用中实现全屏页面,并提供良好的用户体验。根据具体需求选择合适的实现方式,建议使用封装好的 ResponsiveFullScreen 组件来简化开发。