Flutter SystemChrome 完整指南
📋 目录
- 🏗️ SystemChrome 基础概念
- ⚙️ 主要配置方法
- 📱 iOS 和 Android 平台差异
- 🔄 与 AppBar systemOverlayStyle 的差异
- 🌟 沉浸式模式详解
- 💡 最佳实践和注意事项
- 🔧 常见问题和解决方案
- 💻 实际代码示例
- 📊 快速参考表
🏗️ SystemChrome 基础概念
SystemChrome是 Flutter 中用于控制系统界面元素(如状态栏、导航栏)的核心类。它提供了全局级别的系统 UI 配置能力。
🎯 核心作用
- 系统 UI 控制:管理状态栏、导航栏的显示和样式
- 全局配置:一次设置,全应用生效
- 跨平台适配:自动处理 iOS 和 Android 的差异
- 实时调整:支持动态切换不同的 UI 状态
📋 完整功能列表
| 功能分类 | 方法 | 描述 | 支持平台 |
|---|---|---|---|
| 🎨 样式控制 | setSystemUIOverlayStyle() | 控制状态栏、导航栏颜色和亮度 | iOS/Android |
| 🖥️ 显示模式 | setEnabledSystemUIMode() | 控制系统UI的显示/隐藏模式 | iOS/Android |
| 🔄 屏幕方向 | setPreferredOrientations() | 锁定或允许特定的屏幕方向 | iOS/Android |
| 📱 任务切换器 | setApplicationSwitcherDescription() | 设置应用在多任务界面的显示 | Android |
🎨 系统 UI 样式控制
- ✅ 状态栏背景颜色(仅Android)
- ✅ 状态栏图标亮度
- ✅ 导航栏背景颜色(仅Android)
- ✅ 导航栏图标亮度(仅Android)
- ✅ 导航栏分割线颜色(仅Android)
🖥️ 系统 UI 显示模式
- 🔹 全屏模式:完全隐藏系统UI
- 🔹 沉浸式模式:边缘滑动显示系统UI
- 🔹 粘性沉浸式:系统UI自动隐藏
- 🔹 边缘到边缘:透明系统UI,内容延伸到边缘
- 🔹 手动模式:精确控制显示的UI元素
🔄 屏幕方向控制
- 📱 强制竖屏(Portrait)
- 📱 强制横屏(Landscape)
- 📱 允许特定方向组合
- 📱 恢复所有方向自由切换
⚙️ 主要配置方法
1. 设置系统 UI 覆盖样式
import 'package:flutter/services.dart';
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
// 状态栏配置
statusBarColor: Colors.transparent, // Android: 状态栏背景色
statusBarIconBrightness: Brightness.light, // Android: 状态栏图标亮度
statusBarBrightness: Brightness.dark, // iOS: 状态栏内容亮度
// 导航栏配置 (仅 Android)
systemNavigationBarColor: Colors.white, // 导航栏背景色
systemNavigationBarIconBrightness: Brightness.dark, // 导航栏图标亮度
systemNavigationBarDividerColor: Colors.grey, // 导航栏分割线颜色
));
2. 设置系统 UI 模式
// 隐藏所有系统 UI(全屏模式)
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: []
);
// 显示所有系统 UI
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]
);
// 沉浸式模式(边缘滑动显示)
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
// 沉浸式粘性模式(自动隐藏)
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
// 边缘到边缘模式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
3. 锁定屏幕方向
// 锁定竖屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
// 锁定横屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
// 恢复所有方向
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
4. 设置应用切换器描述(仅 Android)
import 'package:flutter/services.dart';
// 设置应用在任务切换器中的显示信息
SystemChrome.setApplicationSwitcherDescription(
ApplicationSwitcherDescription(
label: '我的应用', // 应用在任务切换器中显示的标题
primaryColor: 0xFF2196F3, // 应用的主题色(可选)
),
);
5. 系统 UI 状态恢复
// 恢复到系统默认状态(通常在应用退出或进入后台时调用)
void restoreSystemUI() {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
// 恢复默认的系统 UI 样式
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: null, // 使用系统默认
systemNavigationBarColor: null, // 使用系统默认
));
}
// 在应用生命周期中使用
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
// 应用退出时恢复系统 UI
restoreSystemUI();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
// 应用进入后台时恢复系统 UI
restoreSystemUI();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(home: MyHomePage());
}
}
📱 iOS 和 Android 平台差异
状态栏和系统导航栏的位置分布
状态栏(Status Bar)位置
Android 平台:
- 位置:屏幕顶部
- 显示内容:时间、电量、信号强度、通知图标等
- 特点:
- 可以设置背景颜色 (
statusBarColor) - 可以控制图标亮度 (
statusBarIconBrightness) - 可以完全隐藏
- 支持透明效果
- 可以设置背景颜色 (
iOS 平台:
- 位置:屏幕顶部(包括刘海屏/动态岛区域)
- 显示内容:时间、电量、信号强度、运营商信息等
- 特点:
- 背景颜色由系统控制,无法自定义
- 只能控制内容亮度 (
statusBarBrightness) - 始终存在,无法完全隐藏(全屏模式下变为透明覆盖)
- 刘海屏/动态岛设备需要考虑 Safe Area
系统导航栏(System Navigation Bar)位置
Android 平台:
- 位置:屏幕底部(部分设备可能在侧面)
- 显示内容:返回键、主页键、多任务键(虚拟按键)或手势导航区域
- 特点:
- 可以设置背景颜色 (
systemNavigationBarColor) - 可以控制图标亮度 (
systemNavigationBarIconBrightness) - 可以设置分割线颜色 (
systemNavigationBarDividerColor) - 可以完全隐藏
- 不同厂商可能有定制化行为
- 可以设置背景颜色 (
iOS 平台:
- 位置:iOS 没有系统导航栏概念
- 说明:
- iOS 使用 Home Indicator(主页指示器)在屏幕底部
- iPhone X 及以后机型使用手势导航
- 所有导航栏相关的 Flutter 配置在 iOS 上都无效
状态栏配置差异
| 属性 | iOS | Android | 说明 |
|---|---|---|---|
statusBarColor | ❌ 无效 | ✅ 有效 | iOS 状态栏颜色由系统控制 |
statusBarBrightness | ✅ 有效 | ❌ 无效 | iOS 专用,控制状态栏内容亮度 |
statusBarIconBrightness | ❌ 无效 | ✅ 有效 | Android 专用,控制状态栏图标亮度 |
导航栏配置差异
| 属性 | iOS | Android | 说明 |
|---|---|---|---|
systemNavigationBarColor | ❌ 无效 | ✅ 有效 | iOS 无系统导航栏概念 |
systemNavigationBarIconBrightness | ❌ 无效 | ✅ 有效 | iOS 无系统导航栏概念 |
systemNavigationBarDividerColor | ❌ 无效 | ✅ 有效 | iOS 无系统导航栏概念 |
系统 UI 模式差异
iOS 特点:
- 状态栏始终存在,无法完全隐藏
- 全屏模式下状态栏变为透明覆盖
- 刘海屏设备需要考虑 Safe Area
Android 特点:
- 可以完全隐藏状态栏和导航栏
- 支持更多的沉浸式模式
- 不同厂商可能有定制化行为
平台特定代码示例
import 'dart:io';
void setPlatformSpecificSystemUI() {
if (Platform.isIOS) {
// iOS 专用配置
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark, // iOS 用这个
));
} else if (Platform.isAndroid) {
// Android 专用配置
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light, // Android 用这个
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
));
}
}
🔄 与 AppBar systemOverlayStyle 的差异
SystemChrome.setSystemUIOverlayStyle()
- 作用域: 全局级别
- 生效时机: 立即生效,影响整个应用
- 优先级: 较低,会被 AppBar 的设置覆盖
- 使用场景: 应用启动时的全局配置
AppBar.systemOverlayStyle
- 作用域: 页面级别
- 生效时机: 当该 AppBar 显示时生效
- 优先级: 较高,会覆盖全局设置
- 使用场景: 特定页面的定制化配置
代码对比
// 全局配置(在 main() 或 initState() 中)
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.blue,
statusBarIconBrightness: Brightness.light,
));
// 页面级配置(在 AppBar 中)
AppBar(
title: Text('页面标题'),
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // 会覆盖全局设置
statusBarIconBrightness: Brightness.dark,
),
)
MaterialApp 中的系统UI设置
除了上述三种方式,还有一个重要的设置方式是通过 MaterialApp 的主题配置:
MaterialApp.theme.appBarTheme.systemOverlayStyle
- 作用域: 全应用级别的默认设置
- 生效时机: 当页面的 AppBar 没有设置 systemOverlayStyle 时生效
- 优先级: 中等,高于 SystemChrome 但低于 AppBar 直接设置
- 使用场景: 统一管理整个应用的状态栏样式,支持主题切换
MaterialApp 设置的优势
- 全局生效:一次设置,整个应用都会应用这个样式
- 主题一致性:与应用的整体主题保持一致
- 自动适配:支持明暗主题自动切换
- 维护简单:避免在每个页面重复设置
实际应用示例
基础设置:
MaterialApp(
theme: ThemeData(
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
),
),
home: MyHomePage(),
)
支持明暗主题切换:
MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.dark, // 浅色主题用深色状态栏
),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.light, // 深色主题用浅色状态栏
),
),
home: MyHomePage(),
)
完整的主题配置示例:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
// 浅色主题
theme: ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.blue,
appBarTheme: AppBarTheme(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light, // 白色图标
statusBarBrightness: Brightness.dark, // iOS
),
),
),
// 深色主题
darkTheme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
appBarTheme: AppBarTheme(
backgroundColor: Colors.grey[900],
foregroundColor: Colors.white,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light, // 白色图标
statusBarBrightness: Brightness.light, // iOS
),
),
),
home: MyHomePage(),
);
}
}
使用场景对比
适合使用 MaterialApp 设置的情况:
- 整个应用需要统一的状态栏样式
- 需要支持明暗主题自动切换
- 希望减少重复代码
- 应用结构相对简单
需要特殊页面定制的情况:
// 大部分页面使用 MaterialApp 中的默认设置
class NormalPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('普通页面'),
// 自动使用主题中的 systemOverlayStyle
),
);
}
}
// 特殊页面覆盖默认设置
class SpecialPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('特殊页面'),
systemOverlayStyle: SystemUiOverlayStyle.light, // 覆盖主题设置
),
);
}
}
优先级层次
- 最高优先级:
AppBar.systemOverlayStyle - 中等优先级:
MaterialApp.theme.appBarTheme.systemOverlayStyle和AnnotatedRegion<SystemUiOverlayStyle> - 最低优先级:
SystemChrome.setSystemUIOverlayStyle()
💡 最佳实践和注意事项
1. 初始化时机
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 在 runApp 之前设置全局样式
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
));
runApp(MyApp());
}
2. 响应主题变化
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.dark, // 浅色主题
),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.light, // 深色主题
),
),
);
}
}
3. 处理页面切换
class MyPageRoute extends PageRoute {
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
child: YourPageWidget(),
);
}
}
4. 安全区域处理
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: YourContent(),
),
);
}
🔧 常见问题和解决方案
1. SystemChrome.setSystemUIOverlayStyle 无效问题 ⭐
问题: 设置了 SystemChrome.setSystemUIOverlayStyle() 但是不生效,而在 AppBar 中设置 systemOverlayStyle 才生效。
根本原因:
AppBar内部使用AnnotatedRegion<SystemUiOverlayStyle>包装AnnotatedRegion的优先级高于SystemChrome.setSystemUIOverlayStyle()- Flutter 框架按优先级应用最新的系统 UI 样式
优先级顺序 (从高到低):
AppBar.systemOverlayStyle(最高优先级)AnnotatedRegion<SystemUiOverlayStyle>SystemChrome.setSystemUIOverlayStyle()(最低优先级)
解决方案:
方案1: 移除 AppBar 中的 systemOverlayStyle
// 在应用启动时设置全局样式
void main() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
));
runApp(MyApp());
}
// AppBar 中不设置 systemOverlayStyle
AppBar(
title: Text('标题'),
// 移除 systemOverlayStyle 属性
)
方案2: 使用 AnnotatedRegion 包装页面
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
child: Scaffold(
appBar: AppBar(
title: Text('标题'),
// 不设置 systemOverlayStyle
),
body: YourContent(),
),
);
}
}
方案3: 在 MaterialApp 的主题中统一设置
MaterialApp(
theme: ThemeData(
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
),
),
home: MyHomePage(),
)
方案4: 延迟设置 SystemChrome (不推荐)
@override
void initState() {
super.initState();
// 延迟到下一帧执行
WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
));
});
}
方案5: 条件性设置 AppBar.systemOverlayStyle
AppBar(
title: Text('标题'),
systemOverlayStyle: _shouldOverrideSystemUI()
? SystemUiOverlayStyle(
statusBarColor: Colors.blue,
statusBarIconBrightness: Brightness.light,
)
: null, // null 时不覆盖全局设置
)
2. 状态栏颜色在某些设备上不生效
问题: 在某些 Android 设备上状态栏颜色设置不生效
解决方案:
// 确保在 MaterialApp 之前设置
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
));
// 延迟一帧确保生效
await Future.delayed(Duration.zero);
runApp(MyApp());
}
3. iOS 键盘遮挡问题
问题: iOS 数字键盘没有完成按钮
解决方案:
TextField(
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
],
// 添加完成按钮
onEditingComplete: () {
FocusScope.of(context).unfocus();
},
)
4. 全屏模式下的交互问题
问题: 全屏模式下用户无法访问系统功能
解决方案:
// 使用沉浸式模式而不是完全隐藏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
// 或者提供退出全屏的按钮
IconButton(
icon: Icon(Icons.fullscreen_exit),
onPressed: () {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
},
)
5. 状态栏文字可读性问题
问题: 状态栏文字与背景颜色对比度不足
解决方案:
// 动态调整状态栏亮度
SystemUiOverlayStyle getSystemUiOverlayStyle(Color backgroundColor) {
// 计算背景颜色亮度
final brightness = ThemeData.estimateBrightnessForColor(backgroundColor);
return SystemUiOverlayStyle(
statusBarColor: backgroundColor,
statusBarIconBrightness: brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
statusBarBrightness: brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
);
}
💻 实际代码示例
当前项目中的使用
基于您当前项目 lib/main.dart 中的代码,您已经在 AppBar 中正确使用了 systemOverlayStyle:
AppBar(
title: Text("WhatsApp"),
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
),
// ... 其他配置
)
改进建议
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late TabController tabController;
@override
void initState() {
super.initState();
tabController = TabController(vsync: this, length: 3);
// 设置全局系统 UI 样式
_setSystemUIOverlayStyle();
}
void _setSystemUIOverlayStyle() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.dark, // iOS
systemNavigationBarColor: Colors.white, // Android
systemNavigationBarIconBrightness: Brightness.dark,
));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: AppBarTheme(
// 全局 AppBar 样式
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.dark,
),
),
),
home: Scaffold(
appBar: AppBar(
title: Text("WhatsApp"),
// 可以移除这里的 systemOverlayStyle,使用主题中的全局设置
bottom: TabBar(
controller: tabController,
isScrollable: true,
tabs: [
Tab(icon: Icon(Icons.camera_alt)),
Tab(child: Text("CHATS")),
Tab(child: Text("STATUS")),
],
),
),
// ... 其他代码
),
);
}
@override
void dispose() {
tabController.dispose();
super.dispose();
}
}
完整的多平台适配示例
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SystemUIHelper {
static void setSystemUIOverlayStyle({
Color? statusBarColor,
Brightness? statusBarIconBrightness,
Color? systemNavigationBarColor,
Brightness? systemNavigationBarIconBrightness,
}) {
SystemUiOverlayStyle overlayStyle;
if (Platform.isIOS) {
overlayStyle = SystemUiOverlayStyle(
statusBarBrightness: statusBarIconBrightness == Brightness.light
? Brightness.dark
: Brightness.light,
);
} else {
overlayStyle = SystemUiOverlayStyle(
statusBarColor: statusBarColor ?? Colors.transparent,
statusBarIconBrightness: statusBarIconBrightness ?? Brightness.dark,
systemNavigationBarColor: systemNavigationBarColor ?? Colors.white,
systemNavigationBarIconBrightness:
systemNavigationBarIconBrightness ?? Brightness.dark,
);
}
SystemChrome.setSystemUIOverlayStyle(overlayStyle);
}
static void setFullScreen(bool fullScreen) {
if (fullScreen) {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [],
);
} else {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
}
}
}
🌟 沉浸式模式详解
什么是沉浸式模式
沉浸式(Immersive)是指隐藏或最小化系统UI元素(如状态栏、导航栏),让应用内容能够占据整个屏幕空间,为用户提供更加专注和沉浸的体验。
沉浸式的核心概念
- 隐藏系统UI:状态栏、导航栏等系统元素被隐藏或变为透明
- 全屏体验:应用内容延伸到屏幕边缘,充分利用屏幕空间
- 减少干扰:去除不必要的视觉元素,让用户专注于内容
视觉对比
普通模式:
┌─────────────────┐
│ 状态栏区域 │ ← 系统状态栏
├─────────────────┤
│ │
│ 应用内容区域 │
│ │
├─────────────────┤
│ 导航栏区域 │ ← 系统导航栏(Android)
└─────────────────┘
沉浸式模式:
┌─────────────────┐
│ │
│ │
│ 应用内容区域 │ ← 内容占据整个屏幕
│ (全屏显示) │
│ │
│ │
└─────────────────┘
Flutter中的沉浸式模式类型
1. SystemUiMode.immersive(标准沉浸式模式)
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
特点:
- 系统UI完全隐藏
- 用户从屏幕边缘滑动时会显示系统UI
- 系统UI显示后不会自动隐藏,需要用户点击内容区域才会重新隐藏
适用场景:
- 阅读应用
- 图片查看器
- 文档浏览器
2. SystemUiMode.immersiveSticky(粘性沉浸式模式)
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
特点:
- 系统UI完全隐藏
- 用户交互后系统UI会短暂显示
- 几秒后系统UI自动隐藏
适用场景:
- 游戏应用
- 全屏媒体播放
- 演示应用
3. SystemUiMode.edgeToEdge(边缘到边缘模式)
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
特点:
- 系统UI变为透明或半透明
- 内容延伸到屏幕边缘
- 系统UI始终可见但不占用额外空间
适用场景:
- 现代应用设计
- 需要透明状态栏效果
- 保持系统功能可访问性
实际应用示例
图片查看器的沉浸式体验
class ImageViewerPage extends StatefulWidget {
final String imageUrl;
const ImageViewerPage({Key? key, required this.imageUrl}) : super(key: key);
@override
_ImageViewerPageState createState() => _ImageViewerPageState();
}
class _ImageViewerPageState extends State<ImageViewerPage> {
bool _isImmersive = false;
@override
void initState() {
super.initState();
// 进入页面时设置边缘到边缘模式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
}
void _toggleImmersive() {
setState(() {
_isImmersive = !_isImmersive;
});
if (_isImmersive) {
// 进入完全沉浸式模式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
} else {
// 回到边缘到边缘模式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: GestureDetector(
onTap: _toggleImmersive, // 点击切换沉浸式模式
child: Stack(
children: [
// 图片显示区域
Center(
child: InteractiveViewer(
child: Image.network(
widget.imageUrl,
fit: BoxFit.contain,
width: double.infinity,
height: double.infinity,
),
),
),
// 工具栏(非沉浸式模式下显示)
if (!_isImmersive)
Positioned(
top: MediaQuery.of(context).padding.top,
left: 0,
right: 0,
child: Container(
height: kToolbarHeight,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.black54, Colors.transparent],
),
),
child: Row(
children: [
IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
Spacer(),
IconButton(
icon: Icon(Icons.share, color: Colors.white),
onPressed: () {/* 分享功能 */},
),
IconButton(
icon: Icon(Icons.more_vert, color: Colors.white),
onPressed: () {/* 更多选项 */},
),
],
),
),
),
],
),
),
);
}
@override
void dispose() {
// 退出页面时恢复正常模式
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
super.dispose();
}
}
视频播放器的沉浸式体验
class VideoPlayerPage extends StatefulWidget {
final String videoUrl;
const VideoPlayerPage({Key? key, required this.videoUrl}) : super(key: key);
@override
_VideoPlayerPageState createState() => _VideoPlayerPageState();
}
class _VideoPlayerPageState extends State<VideoPlayerPage> {
bool _showControls = true;
bool _isPlaying = false;
double _progress = 0.3;
Timer? _controlsTimer;
@override
void initState() {
super.initState();
// 进入页面时启用粘性沉浸式模式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
_startControlsTimer();
}
void _startControlsTimer() {
_controlsTimer?.cancel();
if (_showControls) {
_controlsTimer = Timer(Duration(seconds: 3), () {
if (mounted) {
setState(() {
_showControls = false;
});
}
});
}
}
void _onScreenTap() {
setState(() {
_showControls = !_showControls;
});
_startControlsTimer();
}
void _togglePlayPause() {
setState(() {
_isPlaying = !_isPlaying;
});
_startControlsTimer();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: GestureDetector(
onTap: _onScreenTap,
child: Stack(
children: [
// 视频播放区域
Center(
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
color: Colors.grey[800],
child: Stack(
children: [
// 模拟视频内容
Center(
child: Icon(
_isPlaying ? Icons.pause_circle : Icons.play_circle,
color: Colors.white.withOpacity(0.8),
size: 80,
),
),
// 播放/暂停按钮(中央)
if (_showControls)
Center(
child: GestureDetector(
onTap: _togglePlayPause,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.black54,
shape: BoxShape.circle,
),
child: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 32,
),
),
),
),
],
),
),
),
),
// 顶部控制栏
if (_showControls)
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
height: kToolbarHeight + MediaQuery.of(context).padding.top,
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.black54, Colors.transparent],
),
),
child: Row(
children: [
IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
Expanded(
child: Text(
'视频标题',
style: TextStyle(color: Colors.white, fontSize: 16),
overflow: TextOverflow.ellipsis,
),
),
IconButton(
icon: Icon(Icons.more_vert, color: Colors.white),
onPressed: () {/* 更多选项 */},
),
],
),
),
),
// 底部控制栏
if (_showControls)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 100,
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black54],
),
),
child: Column(
children: [
// 进度条
Row(
children: [
Text('01:23', style: TextStyle(color: Colors.white, fontSize: 12)),
Expanded(
child: Slider(
value: _progress,
onChanged: (value) {
setState(() {
_progress = value;
});
_startControlsTimer();
},
activeColor: Colors.red,
inactiveColor: Colors.white30,
),
),
Text('05:47', style: TextStyle(color: Colors.white, fontSize: 12)),
],
),
// 控制按钮
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(Icons.skip_previous, color: Colors.white),
onPressed: () => _startControlsTimer(),
),
IconButton(
icon: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 32,
),
onPressed: _togglePlayPause,
),
IconButton(
icon: Icon(Icons.skip_next, color: Colors.white),
onPressed: () => _startControlsTimer(),
),
IconButton(
icon: Icon(Icons.fullscreen, color: Colors.white),
onPressed: () => _startControlsTimer(),
),
],
),
],
),
),
),
],
),
),
);
}
@override
void dispose() {
_controlsTimer?.cancel();
// 退出页面时恢复正常模式
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
super.dispose();
}
}
阅读应用的沉浸式体验
class ReadingPage extends StatefulWidget {
final String content;
const ReadingPage({Key? key, required this.content}) : super(key: key);
@override
_ReadingPageState createState() => _ReadingPageState();
}
class _ReadingPageState extends State<ReadingPage> {
bool _isImmersive = false;
ScrollController _scrollController = ScrollController();
double _fontSize = 16.0;
@override
void initState() {
super.initState();
// 监听滚动,自动进入沉浸式模式
_scrollController.addListener(_onScroll);
}
void _onScroll() {
// 滚动时自动进入沉浸式模式
if (!_isImmersive && _scrollController.offset > 100) {
_enterImmersiveMode();
}
}
void _enterImmersiveMode() {
setState(() {
_isImmersive = true;
});
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
}
void _exitImmersiveMode() {
setState(() {
_isImmersive = false;
});
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
}
void _toggleImmersive() {
if (_isImmersive) {
_exitImmersiveMode();
} else {
_enterImmersiveMode();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: _isImmersive ? null : AppBar(
title: Text('阅读模式'),
actions: [
IconButton(
icon: Icon(Icons.text_fields),
onPressed: () => _showFontSizeDialog(),
),
IconButton(
icon: Icon(Icons.fullscreen),
onPressed: _toggleImmersive,
),
],
),
body: GestureDetector(
onTap: () {
if (_isImmersive) {
_exitImmersiveMode();
}
},
child: SingleChildScrollView(
controller: _scrollController,
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_isImmersive)
SizedBox(height: MediaQuery.of(context).padding.top + 16),
Text(
widget.content,
style: TextStyle(
fontSize: _fontSize,
height: 1.6,
color: Colors.black87,
),
),
SizedBox(height: 100), // 底部留白
],
),
),
),
// 沉浸式模式下的浮动操作按钮
floatingActionButton: _isImmersive ? FloatingActionButton(
mini: true,
backgroundColor: Colors.black54,
child: Icon(Icons.fullscreen_exit, color: Colors.white),
onPressed: _exitImmersiveMode,
) : null,
);
}
void _showFontSizeDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('调整字体大小'),
content: StatefulBuilder(
builder: (context, setState) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('字体大小: ${_fontSize.toInt()}'),
Slider(
value: _fontSize,
min: 12.0,
max: 24.0,
divisions: 12,
onChanged: (value) {
setState(() {
_fontSize = value;
});
this.setState(() {}); // 更新外部状态
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('确定'),
),
],
),
);
}
@override
void dispose() {
_scrollController.dispose();
// 退出页面时恢复正常模式
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
super.dispose();
}
}
沉浸式模式的优势与注意事项
优势
- 更大的内容显示区域:充分利用屏幕空间
- 减少视觉干扰:去除不必要的系统UI元素
- 增强用户专注度:让用户更专注于内容本身
- 提升视觉体验:创造更加现代和专业的界面
- 适合媒体消费:特别适合视频、图片、阅读等场景
注意事项
- 提供明确的退出方式:确保用户能够轻松退出沉浸式模式
- 适当的使用场景:不是所有界面都适合沉浸式
- 考虑用户习惯:某些用户可能不习惯全屏体验
- 安全区域处理:注意刘海屏、动态岛等特殊屏幕的适配
- 系统手势冲突:避免与系统手势产生冲突
- 电池和性能:全屏渲染可能增加电池消耗
最佳实践
- 渐进式进入:不要突然进入沉浸式,给用户适应时间
- 保持一致性:同类型页面使用相同的沉浸式策略
- 提供视觉反馈:让用户知道如何操作和退出
- 测试各种设备:在不同屏幕尺寸和形状上测试效果
📊 快速参考表
SystemUiOverlayStyle 属性速查
| 属性 | iOS | Android | 作用 | 示例值 |
|---|---|---|---|---|
statusBarColor | ❌ | ✅ | 状态栏背景色 | Colors.transparent |
statusBarBrightness | ✅ | ❌ | 状态栏内容亮度 | Brightness.dark |
statusBarIconBrightness | ❌ | ✅ | 状态栏图标亮度 | Brightness.light |
systemNavigationBarColor | ❌ | ✅ | 导航栏背景色 | Colors.white |
systemNavigationBarIconBrightness | ❌ | ✅ | 导航栏图标亮度 | Brightness.dark |
systemNavigationBarDividerColor | ❌ | ✅ | 导航栏分割线色 | Colors.grey |
SystemUiMode 模式对比
| 模式 | 特点 | 适用场景 | 用户交互 |
|---|---|---|---|
manual | 手动控制显示元素 | 普通应用界面 | 正常显示 |
immersive | 隐藏UI,边缘滑动显示 | 阅读、图片查看 | 滑动显示,点击隐藏 |
immersiveSticky | 隐藏UI,交互后自动隐藏 | 游戏、视频播放 | 短暂显示后自动隐藏 |
edgeToEdge | 透明UI,内容延伸到边缘 | 现代应用设计 | UI始终可见 |
设置优先级(从高到低)
- 🥇 AppBar.systemOverlayStyle - 页面级,最高优先级
- 🥈 MaterialApp.theme.appBarTheme.systemOverlayStyle - 主题级
- 🥈 AnnotatedRegion - 区域级
- 🥉 SystemChrome.setSystemUIOverlayStyle() - 全局级,最低优先级
常用代码片段
快速设置透明状态栏
// Android + iOS 兼容
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // Android
statusBarBrightness: Brightness.dark, // iOS
statusBarIconBrightness: Brightness.light, // Android
));
快速设置沉浸式模式
// 进入沉浸式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
// 退出沉浸式
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
快速锁定屏幕方向
// 锁定竖屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
📝 总结
🎯 核心要点
- 🏗️ SystemChrome - 提供全局级别的系统 UI 控制
- 📱 AppBar.systemOverlayStyle - 提供页面级别的系统 UI 控制
- 🌟 沉浸式模式 - 提供专注的全屏体验,适合媒体和阅读场景
- 🔄 平台差异 - 需要针对 iOS 和 Android 分别处理
- 📊 优先级 - AppBar 设置会覆盖全局设置
- 💡 最佳实践 - 在应用启动时设置全局样式,在特定页面使用 AppBar 样式覆盖
🚀 推荐实践流程
- 项目初期:在
main()中设置全局默认样式 - 主题配置:在
MaterialApp主题中统一管理样式 - 页面定制:在特定页面的
AppBar中覆盖样式 - 沉浸式场景:在媒体播放、阅读等场景中使用沉浸式模式
- 生命周期管理:在页面退出时恢复默认系统UI状态
通过合理使用这些 API,可以实现一致且美观的系统 UI 体验,并在适当的场景下提供沉浸式的用户体验。
💡 提示: 本指南涵盖了 SystemChrome 的所有核心功能和最佳实践。建议收藏此文档,在开发过程中随时参考快速参考表和代码示例。