flutter 设置全屏 和隐藏状态栏和导航栏

'# Flutter 设置全屏 和隐藏状态栏和导航栏

一、背景与问题

在 Flutter 开发中,很多场景需要实现全屏显示效果,例如:

  • 视频播放器
  • 图片查看器
  • 游戏界面
  • 状态栏自定义样式页面

但实现全屏时会遇到以下问题:

  1. 状态栏残留(Android/iOS)
  2. 导航栏遮挡内容
  3. 无法返回上一页
  4. 横屏时的适配问题
  5. 不同设备的兼容性差异

传统解决方案常使用 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. 性能优化

  1. 避免频繁调用 SystemChrome.setSystemUIOverlayStyle
  2. 在页面离开时恢复系统设置
  3. 使用 LayoutBuilder 处理 insets
  4. 对于复杂布局,使用 CustomPaint 优化绘制性能

2. 异常处理

try {
  SystemNavigator.pop();
} catch (e) {
  // 处理异常
}

3. 安全考虑

  1. 隐藏系统导航栏可能导致用户误操作
  2. 需要提供明确的返回方式
  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: ...,
)

十、最佳实践

  1. 使用 WidgetsApp.fullscreenDialog 实现基本全屏
  2. 需要精细控制时使用 SystemChrome.setSystemUIOverlayStyle
  3. 自定义导航栏时使用 PreferredSize 和 SafeArea
  4. 在页面离开时恢复系统设置
  5. 横屏页面使用 LayoutBuilder 处理布局
  6. 重要页面添加返回按钮或自定义返回逻辑
  7. 避免完全隐藏系统功能,保持用户操作的可预见性

十一、总结

Flutter 的全屏控制需要综合考虑系统行为、平台差异和用户体验。通过深入理解 SystemChrome、WidgetsApp 和 LayoutBuilder 的工作机制,我们可以实现灵活的全屏布局。在实际开发中,需要根据具体场景选择合适的方案:

  • 简单场景:使用 fullscreenDialog
  • 精细控制:使用 SystemChrome.setSystemUIOverlayStyle
  • 自定义导航:结合 PreferredSize 和 WillPopScope

需要注意的是,全屏控制会带来一定的用户体验风险,需要在功能性和可用性之间找到平衡点。通过合理的异常处理、性能优化和安全考虑,我们可以实现既美观又可靠的全屏界面。

none
最后修改于:2026年09月24日 21:51

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日