Flutter SystemChrome 完整指南

331 阅读17分钟

Flutter SystemChrome 完整指南

📋 目录

  1. 🏗️ SystemChrome 基础概念
  2. ⚙️ 主要配置方法
  3. 📱 iOS 和 Android 平台差异
  4. 🔄 与 AppBar systemOverlayStyle 的差异
  5. 🌟 沉浸式模式详解
  6. 💡 最佳实践和注意事项
  7. 🔧 常见问题和解决方案
  8. 💻 实际代码示例
  9. 📊 快速参考表

🏗️ 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 上都无效

状态栏配置差异

属性iOSAndroid说明
statusBarColor❌ 无效✅ 有效iOS 状态栏颜色由系统控制
statusBarBrightness✅ 有效❌ 无效iOS 专用,控制状态栏内容亮度
statusBarIconBrightness❌ 无效✅ 有效Android 专用,控制状态栏图标亮度

导航栏配置差异

属性iOSAndroid说明
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 设置的优势
  1. 全局生效:一次设置,整个应用都会应用这个样式
  2. 主题一致性:与应用的整体主题保持一致
  3. 自动适配:支持明暗主题自动切换
  4. 维护简单:避免在每个页面重复设置
实际应用示例

基础设置:

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, // 覆盖主题设置
      ),
    );
  }
}

优先级层次

  1. 最高优先级: AppBar.systemOverlayStyle
  2. 中等优先级: MaterialApp.theme.appBarTheme.systemOverlayStyleAnnotatedRegion<SystemUiOverlayStyle>
  3. 最低优先级: 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 样式

优先级顺序 (从高到低):

  1. AppBar.systemOverlayStyle (最高优先级)
  2. AnnotatedRegion<SystemUiOverlayStyle>
  3. 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元素(如状态栏、导航栏),让应用内容能够占据整个屏幕空间,为用户提供更加专注和沉浸的体验。

沉浸式的核心概念
  1. 隐藏系统UI:状态栏、导航栏等系统元素被隐藏或变为透明
  2. 全屏体验:应用内容延伸到屏幕边缘,充分利用屏幕空间
  3. 减少干扰:去除不必要的视觉元素,让用户专注于内容
视觉对比

普通模式:

┌─────────────────┐
│   状态栏区域     │ ← 系统状态栏
├─────────────────┤
│                 │
│   应用内容区域   │
│                 │
├─────────────────┤
│   导航栏区域     │ ← 系统导航栏(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();
  }
}

沉浸式模式的优势与注意事项

优势
  1. 更大的内容显示区域:充分利用屏幕空间
  2. 减少视觉干扰:去除不必要的系统UI元素
  3. 增强用户专注度:让用户更专注于内容本身
  4. 提升视觉体验:创造更加现代和专业的界面
  5. 适合媒体消费:特别适合视频、图片、阅读等场景
注意事项
  1. 提供明确的退出方式:确保用户能够轻松退出沉浸式模式
  2. 适当的使用场景:不是所有界面都适合沉浸式
  3. 考虑用户习惯:某些用户可能不习惯全屏体验
  4. 安全区域处理:注意刘海屏、动态岛等特殊屏幕的适配
  5. 系统手势冲突:避免与系统手势产生冲突
  6. 电池和性能:全屏渲染可能增加电池消耗
最佳实践
  1. 渐进式进入:不要突然进入沉浸式,给用户适应时间
  2. 保持一致性:同类型页面使用相同的沉浸式策略
  3. 提供视觉反馈:让用户知道如何操作和退出
  4. 测试各种设备:在不同屏幕尺寸和形状上测试效果

📊 快速参考表

SystemUiOverlayStyle 属性速查

属性iOSAndroid作用示例值
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始终可见

设置优先级(从高到低)

  1. 🥇 AppBar.systemOverlayStyle - 页面级,最高优先级
  2. 🥈 MaterialApp.theme.appBarTheme.systemOverlayStyle - 主题级
  3. 🥈 AnnotatedRegion - 区域级
  4. 🥉 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,
]);

📝 总结

🎯 核心要点

  1. 🏗️ SystemChrome - 提供全局级别的系统 UI 控制
  2. 📱 AppBar.systemOverlayStyle - 提供页面级别的系统 UI 控制
  3. 🌟 沉浸式模式 - 提供专注的全屏体验,适合媒体和阅读场景
  4. 🔄 平台差异 - 需要针对 iOS 和 Android 分别处理
  5. 📊 优先级 - AppBar 设置会覆盖全局设置
  6. 💡 最佳实践 - 在应用启动时设置全局样式,在特定页面使用 AppBar 样式覆盖

🚀 推荐实践流程

  1. 项目初期:在 main() 中设置全局默认样式
  2. 主题配置:在 MaterialApp 主题中统一管理样式
  3. 页面定制:在特定页面的 AppBar 中覆盖样式
  4. 沉浸式场景:在媒体播放、阅读等场景中使用沉浸式模式
  5. 生命周期管理:在页面退出时恢复默认系统UI状态

通过合理使用这些 API,可以实现一致且美观的系统 UI 体验,并在适当的场景下提供沉浸式的用户体验。


💡 提示: 本指南涵盖了 SystemChrome 的所有核心功能和最佳实践。建议收藏此文档,在开发过程中随时参考快速参考表和代码示例。