Flutter 屏幕旋转适配

mindmap root((Flutter 屏幕旋转适配)) 原理 旋转手机 = 窗口尺寸变了 Flutter 检测到 → 自动重新布局 怎么监听 OrientationBuilder 根据横竖屏切布局 MediaQuery.sizeOf 根据宽度切布局 更推荐 怎么锁定屏幕 SystemChrome.setPreferredOrientations 锁定竖屏 portraitUp 锁定横屏 landscapeLeft Right 恢复自动 DeviceOrientation.values 安卓 vs iOS Android 一行代码搞定 iOS 要多配置 3 层 Info.plist 声明支持方向 AppDelegate 写回调动态控制 ViewController 也会参与判断 视频全屏实战 进全屏 → 切横屏 退出全屏 → 切回竖屏 iOS 需要额外在 AppDelegate 里配合

在移动开发中,屏幕旋转是非常常见的场景。

例如:

  • 视频播放器全屏播放
  • 平板横屏适配
  • 表单页面横竖屏切换
  • 折叠屏展开与收起

Flutter 已提供了完整的横竖屏支持。

本文将从实际开发角度介绍:

  • 如何监听屏幕旋转
  • 如何适配横竖屏布局
  • 如何锁定横屏或竖屏
  • Android 与 iOS 的区别,尤其是 iOS 特别要注意的地方

什么是屏幕旋转

当用户旋转手机时:

text 复制代码
设备方向变化
      ↓
系统更新窗口尺寸
      ↓
Flutter 重新布局
      ↓
Widget 重新构建

因此:

Flutter 屏幕旋转本质上是窗口尺寸变化后的重新布局过程。


使用 OrientationBuilder 监听方向变化

Flutter 官方推荐使用:

dart 复制代码
OrientationBuilder(builder: (context, orientation) {
    return orientation == Orientation.portrait ? const PortraitPage()
        : const LandscapePage();
  },
);

效果:

  • 竖屏显示 PortraitPage
  • 横屏显示 LandscapePage

实现横竖屏不同布局

例如商品列表页面:

竖屏:

text 复制代码
2列 Grid

横屏:

text 复制代码
3列 Grid

代码:

dart 复制代码
OrientationBuilder(builder: (context, orientation) {
  return GridView.count(rossAxisCount: orientation == Orientation.portrait ? 2 : 3);
  },
);

使用 MediaQuery 获取方向

除了 OrientationBuilder。

还可以直接获取方向:

dart 复制代码
final orientation = MediaQuery.orientationOf(context);

判断:

dart 复制代码
if (orientation == Orientation.landscape) {
  print('横屏');
}

使用 MediaQuery 获取屏幕尺寸

实际开发中更推荐:

dart 复制代码
final size = MediaQuery.sizeOf(context);

例如:

dart 复制代码
final width = MediaQuery.sizeOf(context).width;

根据宽度决定布局:

dart 复制代码
if (width > 600) {
  return TabletPage();
}

return MobilePage();

相比 Orientation。

这种方式更适合:

  • 平板
  • 折叠屏
  • 多窗口

场景。


OrientationBuilder 和 MediaQuery 的区别

方式 作用
OrientationBuilder 根据方向切换布局
MediaQuery.orientationOf 获取当前方向
MediaQuery.sizeOf 获取屏幕尺寸(推荐)

Flutter 官方建议:

text 复制代码
优先根据 Size 设计布局
而不是只依赖 Orientation

因为大屏设备和折叠屏场景下:

text 复制代码
方向不变

尺寸也可能变化

锁定竖屏

很多 App 会禁止横屏。

实现方式:

dart 复制代码
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
  ]);

  runApp(const MyApp());
}

锁定横屏

例如:

  • 视频播放器
  • 游戏
  • 车机应用

代码:

dart 复制代码
await SystemChrome.setPreferredOrientations([
  DeviceOrientation.landscapeLeft,
  DeviceOrientation.landscapeRight,
]);

恢复自动旋转

dart 复制代码
await SystemChrome.setPreferredOrientations(
  DeviceOrientation.values,
);

恢复系统默认行为。


Android 与 iOS 的区别

为什么

dart 复制代码
SystemChrome.setPreferredOrientations(...)

Android 可以正常工作。

但 iOS 经常不生效

原因是Android 和 iOS 的方向管理机制完全不同


Android

Android 中:

dart 复制代码
SystemChrome.setPreferredOrientations(...)

Flutter 最终会调用 Android 原生:

java 复制代码
Activity.setRequestedOrientation()

请求系统切换方向。App一般能够正确旋转方向

因此大多数 Flutter 项目不需要编写 Android 原生代码 只使用 Flutter API 即可。


iOS

iOS 不一样。 很多人以为:

dart 复制代码
SystemChrome.setPreferredOrientations(...)

就能直接控制方向。

实际上 Flutter 只是向系统提出请求

最终是否旋转由 UIKit 决定

Apple 官方文档也说明:

系统会综合判断:

  • App 支持哪些方向
  • 当前 ViewController 支持哪些方向
  • 当前 Window 支持哪些方向

来判断当前是否允许旋转。


iOS 配置 Info.plist

如果需要用户旋转手机屏幕 首页保持竖屏, 需要在 Info.plist 配置 Portrait

xml 复制代码
<key>UISupportedInterfaceOrientations</key>
<array>
    <string>UIInterfaceOrientationPortrait</string>
</array>

表示整个 App 默认只支持竖屏

那视频播放器为什么还能横屏

在 AppDelegate 实现如下回调: 例如:

swift 复制代码
override func application(
    _ application: UIApplication,
    supportedInterfaceOrientationsFor window: UIWindow?
) -> UIInterfaceOrientationMask {
    return OrientationUtils.shared.orientationLock
}

作用: 动态控制当前页面允许的方向

例如:

text 复制代码
首页
↓
Portrait

视频页
↓
Landscape

退出视频页, 返回其他页面
↓
Portrait

Apple 官方说明:

如果实现了这个方法。 系统会优先询问使用这里返回的方向,而不仅仅使用 Info.plist。


Flutter + iOS 常见方案

Info.plist

默认:

xml 复制代码
Portrait

AppDelegate

动态控制:

swift 复制代码
var orientationLock:
UIInterfaceOrientationMask = .portrait

进入视频页:

swift 复制代码
orientationLock = .landscape

退出视频页:

swift 复制代码
orientationLock = .portrait

然后 Flutter 调用:

dart 复制代码
SystemChrome.setPreferredOrientations(...)

完成横竖屏切换。

Android iOS 横竖屏切换总结

Android:

text 复制代码
Flutter 请求方向

系统通常直接执行

iOS:

text 复制代码
Flutter 请求方向
      ↓
Info.plist 判断
      ↓
AppDelegate 判断(如果实现,覆盖Info.plist中条件)
      ↓
ViewController 判断(和AppDelegate/Info.plist取交集)
      ↓
UIKit 最终决定(交集结果)

因此:

  • Android 通常只需要 Flutter 代码;
  • iOS 除了 Flutter 代码外,还需要正确配置 Info.plist,还要通过 AppDelegate 动态控制横竖屏。

视频播放器横屏实战

进入全屏:

dart 复制代码
await SystemChrome
    .setPreferredOrientations([
  DeviceOrientation.landscapeLeft,
  DeviceOrientation.landscapeRight,
]);

退出全屏:

dart 复制代码
await SystemChrome
    .setPreferredOrientations([
  DeviceOrientation.portraitUp,
]);

这是最常见的使用场景。

相关推荐
程序员陆业聪9 小时前
绕过Frida/Xposed的最后防线:SVC直接系统调用与Native反Hook实战
android
程序员陆业聪9 小时前
WebView与原生JS交互:JSBridge生产级实现与安全防护
android
我命由我1234513 小时前
Android 开发问题:MlKitException: An internal error occurred during initialization.
android·java·java-ee·android jetpack·android-studio·androidx·android runtime
Meteors.13 小时前
Android自定义 View 三核心方法详解
android
2501_9160074713 小时前
前端开发常用软件与工具全面指南
android·ios·小程序·https·uni-app·iphone·webview
赏金术士14 小时前
Android Tinker 热修复集成与使用指南 1.9.15.2
android·热修复·tinker
2603_9541383915 小时前
安卓误删文件先别慌!5个实用小技巧指南教你补救
android·智能手机
波诺波16 小时前
5-SOFA可变形的3D物体 5-elasticity.scn
android
sweet丶17 小时前
iOS AI 编程环境配置:Agent、Skill、Rules、Hook、Command
ios·ai编程