flutter 设置全屏 和隐藏状态栏和导航栏
'# Flutter 设置全屏 和隐藏状态栏和导航栏
一、背景与问题
在 Flutter 开发中,很多场景需要实现全屏显示效果,例如:
- 视频播放器
- 图片查看器
- 游戏界面
- 状态栏自定义样式页面
但实现全屏时会遇到以下问题:
- 状态栏残留(Android/iOS)
- 导航栏遮挡内容
- 无法返回上一页
- 横屏时的适配问题
- 不同设备的兼容性差异
传统解决方案常使用 SystemNavigator.pop 或 WidgetsApp.fullscreenDialog,但这些方法存在局限性。本文将深入分析全屏机制,探讨多种实现方案,并提供完整的工程实践。
二、基本原理
Flutter 的全屏控制涉及三个核心机制:
1. 系统导航栏控制
通过 WidgetsApp 的 fullscreenDialog 属性控制是否启用系统导航栏:
WidgetsApp(
fullscreenDialog: true,
...
)该属性会触发以下行为:
- 隐藏系统状态栏和导航栏
- 禁用返回键
- 禁用系统手势(如返回手势)
2. 状态栏控制
通过 SystemNavigator 控制状态栏显示:
SystemNavigator.pop(); // 退出当前页面3. 窗口 insets 控制
通过 LayoutBuilder 获取窗口 insets:
LayoutBuilder(
builder: (context, constraints) {
return Container(
padding: EdgeInsets.only(top: constraints.maxHeight - 300),
);
}
)三、环境准备
确保项目中包含以下依赖:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2四、核心实现
1. 基础全屏设置
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WidgetsApp(
title: 'Full Screen Demo',
fullscreenDialog: true,
builder: (context) => Scaffold(
appBar: AppBar(
title: Text('Full Screen'),
),
body: Center(
child: Text('This is a full screen page'),
),
),
);
}
}关键点解释:
fullscreenDialog: true会隐藏系统导航栏- 但 Android 12 以上系统会显示状态栏
- 需要手动处理状态栏
2. 状态栏隐藏方案
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.dark,
),
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WidgetsApp(
title: 'Status Bar Demo',
builder: (context) => Scaffold(
appBar: AppBar(
title: Text('Status Bar Control'),
),
body: Center(
child: Text('Status bar is hidden'),
),
),
);
}
}关键点解释:
- 使用
SystemChrome.setSystemUIOverlayStyle控制状态栏样式 statusBarColor: Colors.transparent隐藏状态栏statusBarBrightness控制状态栏文字颜色
3. 自定义导航栏
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Navigation',
home: Scaffold(
appBar: AppBar(
title: Text('Custom Navigation'),
),
body: Center(
child: Text('Custom navigation bar'),
),
),
);
}
}关键点解释:
- 自定义导航栏完全替代系统导航栏
- 需要手动处理返回逻辑
- 建议使用
WillPopScope控制返回行为
五、完整案例
1. 视频播放器页面
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class VideoPlayerPage extends StatefulWidget {
@override
_VideoPlayerPageState createState() => _VideoPlayerPageState();
}
class _VideoPlayerPageState extends State<VideoPlayerPage> {
@override
void initState() {
super.initState();
// 隐藏状态栏
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.black,
statusBarBrightness: Brightness.dark,
),
);
}
@override
void dispose() {
// 恢复状态栏
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.white,
statusBarBrightness: Brightness.light,
),
);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// 视频播放区域
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height * 0.8,
child: Center(
child: Text('Video Player'),
),
),
// 自定义控制栏
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 60,
child: Container(
color: Colors.black85,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(icon: Icon(Icons.replay), onPressed: () {}),
IconButton(icon: Icon(Icons.play_arrow), onPressed: () {}),
IconButton(icon: Icon(Icons.forward), onPressed: () {}),
],
),
),
),
],
),
);
}
}关键点分析:
- 使用
SystemChrome.setSystemUIOverlayStyle隐藏状态栏 - 自定义底部控制栏替代系统导航栏
- 在
dispose方法中恢复状态栏 - 使用
Positioned实现自定义布局
六、源码解析
1. 系统导航栏控制源码
在 WidgetsApp 中,fullscreenDialog 属性会触发:
final Widget? fullscreenDialog = widget.fullscreenDialog;通过 MediaQuery 获取窗口信息:
final EdgeInsets windowInsets = MediaQuery.of(context).padding;2. 状态栏控制源码
SystemChrome.setSystemUIOverlayStyle 实际调用的是:
SystemNavigator.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: color,
statusBarBrightness: brightness,
),
);3. 自定义导航栏实现
通过 PreferredSize 控制导航栏高度:
PreferredSize(
preferredSize: Size.fromHeight(60),
child: SafeArea(
child: Container(
height: 60,
color: Colors.black,
),
),
)七、进阶使用
1. 横屏适配方案
import 'package:flutter/widgets.dart';
class LandscapeLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > constraints.maxHeight) {
return OrientationLayout(
child: Container(
color: Colors.blue,
child: Center(child: Text('Landscape')),
),
);
} else {
return OrientationLayout(
child: Container(
color: Colors.green,
child: Center(child: Text('Portrait')),
),
);
}
},
);
}
}2. 响应式布局方案
LayoutBuilder(
builder: (context, constraints) {
return Container(
padding: EdgeInsets.only(
top: constraints.maxHeight - 300,
),
);
},
)八、性能与工程实践
1. 性能优化
- 避免频繁调用
SystemChrome.setSystemUIOverlayStyle - 在页面离开时恢复系统设置
- 使用
LayoutBuilder处理 insets - 对于复杂布局,使用
CustomPaint优化绘制性能
2. 异常处理
try {
SystemNavigator.pop();
} catch (e) {
// 处理异常
}3. 安全考虑
- 隐藏系统导航栏可能导致用户误操作
- 需要提供明确的返回方式
- 避免完全隐藏系统功能
九、常见问题与踩坑
1. 状态栏残留问题
错误示例:
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle.dark,
);问题分析:
- Android 12 以上系统会显示状态栏
- 需要手动处理状态栏高度
解决办法:
SystemChrome.setEnabledSystemUIFeatures([
SystemUiFeature.darkMode,
SystemUiFeature.statusBar,
]);2. 横屏时布局错乱
错误示例:
Container(
height: 300,
...
)问题分析:
- 横屏时高度计算错误
- 没有处理宽高比
解决办法:
LayoutBuilder(
builder: (context, constraints) {
return Container(
height: constraints.maxHeight * 0.6,
);
},
)3. 导航栏无法返回
错误示例:
Navigator.pop(context);问题分析:
- 全屏页面可能无法正常返回
- 需要处理
WillPopScope
解决办法:
WillPopScope(
onWillPop: () async {
return false;
},
child: ...,
)十、最佳实践
- 使用
WidgetsApp.fullscreenDialog实现基本全屏 - 需要精细控制时使用
SystemChrome.setSystemUIOverlayStyle - 自定义导航栏时使用
PreferredSize和SafeArea - 在页面离开时恢复系统设置
- 横屏页面使用
LayoutBuilder处理布局 - 重要页面添加返回按钮或自定义返回逻辑
- 避免完全隐藏系统功能,保持用户操作的可预见性
十一、总结
Flutter 的全屏控制需要综合考虑系统行为、平台差异和用户体验。通过深入理解 SystemChrome、WidgetsApp 和 LayoutBuilder 的工作机制,我们可以实现灵活的全屏布局。在实际开发中,需要根据具体场景选择合适的方案:
- 简单场景:使用
fullscreenDialog - 精细控制:使用
SystemChrome.setSystemUIOverlayStyle - 自定义导航:结合
PreferredSize和WillPopScope
需要注意的是,全屏控制会带来一定的用户体验风险,需要在功能性和可用性之间找到平衡点。通过合理的异常处理、性能优化和安全考虑,我们可以实现既美观又可靠的全屏界面。
评论已关闭