
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 SystemChrome 屏幕方向控制的使用方法,带你全面掌握在应用中控制屏幕旋转和方向切换的各种技巧。
一、屏幕方向控制概述
在 Flutter for OpenHarmony 应用开发中,屏幕方向控制是一个常见的需求。Flutter 提供了原生的 SystemChrome API,可以方便地控制应用的屏幕显示方向,实现横屏、竖屏、自动旋转等功能。对于视频播放、游戏、阅读等场景,屏幕方向控制是提升用户体验的重要功能。
📋 屏幕方向控制特点
| 特点 | 说明 |
|---|---|
| 原生支持 | Flutter 内置 API,无需第三方依赖 |
| 跨平台支持 | 支持 Android、iOS、OpenHarmony |
| 多方向支持 | 支持竖屏、横屏、自动旋转等多种模式 |
| 简单易用 | API 简洁直观,轻松实现方向控制 |
| 灵活切换 | 可在运行时动态切换屏幕方向 |
💡 使用场景:视频播放横屏、游戏方向锁定、阅读器竖屏、相机预览方向控制等。
二、OpenHarmony 平台适配说明
2.1 兼容性信息
本文基于 Flutter 3.27.5-ohos-1.0.4 开发,使用 Flutter 原生 SystemChrome API。
2.2 支持的功能
在 OpenHarmony 平台上,SystemChrome 支持以下功能:
| 功能 | 说明 | OpenHarmony 支持 |
|---|---|---|
| 竖屏锁定 | 强制竖屏显示 | ✅ yes |
| 横屏锁定 | 强制横屏显示 | ✅ yes |
| 自动旋转 | 跟随系统方向 | ✅ yes |
| 动态切换 | 运行时切换方向 | ✅ yes |
三、基础用法
3.1 导入库
在使用屏幕方向控制之前,需要先导入库:
dart
import 'package:flutter/services.dart';
3.2 设置屏幕方向
使用 SystemChrome.setPreferredOrientations 设置屏幕方向:
dart
// 设置竖屏
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
// 设置横屏
await SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
]);
// 设置自动旋转(允许所有方向)
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
3.3 在 main() 中初始化
dart
void main() {
WidgetsFlutterBinding.ensureInitialized();
// 设置默认方向
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
runApp(const MyApp());
}
四、常用 API 详解
4.1 DeviceOrientation - 方向枚举
屏幕方向的枚举值:
| 枚举值 | 说明 |
|---|---|
| DeviceOrientation.portraitUp | 竖屏(正常) |
| DeviceOrientation.portraitDown | 竖屏(倒置) |
| DeviceOrientation.landscapeLeft | 横屏(左旋转) |
| DeviceOrientation.landscapeRight | 横屏(右旋转) |
4.2 setPreferredOrientations - 设置方向
设置应用支持的屏幕方向:
dart
SystemChrome.setPreferredOrientations(
List<DeviceOrientation> orientations
);
使用示例:
dart
// 仅竖屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
// 仅横屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
// 所有方向
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
4.3 SystemChrome 其他常用方法
dart
// 隐藏状态栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
// 显示状态栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]);
// 设置状态栏颜色
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
));
五、实际应用场景
5.1 视频播放横屏模式
dart
class VideoPlayerPage extends StatefulWidget {
const VideoPlayerPage({super.key});
@override
State<VideoPlayerPage> createState() => _VideoPlayerPageState();
}
class _VideoPlayerPageState extends State<VideoPlayerPage> {
bool _isFullscreen = false;
@override
void initState() {
super.initState();
// 默认竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
@override
void dispose() {
// 退出时恢复竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}
void _toggleFullscreen() {
setState(() {
_isFullscreen = !_isFullscreen;
});
if (_isFullscreen) {
// 切换到横屏
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
} else {
// 切换到竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _isFullscreen ? null : AppBar(title: const Text('视频播放')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.black,
width: double.infinity,
height: _isFullscreen ? double.infinity : 200,
child: const Center(
child: Icon(Icons.play_circle, size: 64, color: Colors.white),
),
),
if (!_isFullscreen)
Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton.icon(
onPressed: _toggleFullscreen,
icon: Icon(_isFullscreen ? Icons.fullscreen_exit : Icons.fullscreen),
label: Text(_isFullscreen ? '退出全屏' : '全屏播放'),
),
),
],
),
),
);
}
}
5.2 游戏方向锁定
dart
class GamePage extends StatefulWidget {
const GamePage({super.key});
@override
State<GamePage> createState() => _GamePageState();
}
class _GamePageState extends State<GamePage> {
@override
void initState() {
super.initState();
// 游戏强制横屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
@override
void dispose() {
// 退出游戏恢复竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
),
),
child: const Center(
child: Text(
'游戏界面(横屏模式)',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
);
}
}
5.3 阅读器竖屏锁定
dart
class ReaderPage extends StatefulWidget {
final String content;
const ReaderPage({super.key, required this.content});
@override
State<ReaderPage> createState() => _ReaderPageState();
}
class _ReaderPageState extends State<ReaderPage> {
@override
void initState() {
super.initState();
// 阅读器强制竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
@override
void dispose() {
// 退出恢复自动旋转
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('阅读器'),
actions: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
// 显示设置面板
},
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Text(
widget.content,
style: const TextStyle(fontSize: 18, height: 1.8),
),
),
);
}
}
5.4 全屏沉浸模式
dart
class FullscreenPage extends StatefulWidget {
const FullscreenPage({super.key});
@override
State<FullscreenPage> createState() => _FullscreenPageState();
}
class _FullscreenPageState extends State<FullscreenPage> {
bool _isFullscreen = false;
void _toggleFullscreen() {
setState(() {
_isFullscreen = !_isFullscreen;
});
if (_isFullscreen) {
// 全屏模式:隐藏系统UI,横屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
} else {
// 正常模式:显示系统UI,竖屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
}
@override
void dispose() {
// 恢复正常模式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: _toggleFullscreen,
child: Container(
color: Colors.black,
child: Center(
child: Text(
_isFullscreen ? '点击退出全屏' : '点击进入全屏',
style: const TextStyle(color: Colors.white, fontSize: 24),
),
),
),
),
);
}
}
5.5 设置页面方向选择
dart
class OrientationSettingsPage extends StatefulWidget {
const OrientationSettingsPage({super.key});
@override
State<OrientationSettingsPage> createState() => _OrientationSettingsPageState();
}
class _OrientationSettingsPageState extends State<OrientationSettingsPage> {
DeviceOrientation _selectedOrientation = DeviceOrientation.portraitUp;
final List<Map<String, dynamic>> _orientationOptions = [
{'label': '竖屏', 'value': DeviceOrientation.portraitUp, 'icon': Icons.stay_current_portrait},
{'label': '横屏', 'value': DeviceOrientation.landscapeLeft, 'icon': Icons.stay_current_landscape},
{'label': '自动旋转', 'value': null, 'icon': Icons.screen_rotation},
];
Future<void> _setOrientation(DeviceOrientation? orientation) async {
if (orientation == null) {
// 自动旋转
await SystemChrome.setPreferredOrientations(DeviceOrientation.values);
} else {
await SystemChrome.setPreferredOrientations([orientation]);
}
setState(() {
_selectedOrientation = orientation ?? DeviceOrientation.portraitUp;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('屏幕方向设置')),
body: ListView.builder(
itemCount: _orientationOptions.length,
itemBuilder: (context, index) {
final option = _orientationOptions[index];
final isSelected = option['value'] == _selectedOrientation ||
(option['value'] == null && _selectedOrientation == DeviceOrientation.portraitUp);
return RadioListTile<DeviceOrientation?>(
title: Row(
children: [
Icon(option['icon']),
const SizedBox(width: 12),
Text(option['label']),
],
),
value: option['value'],
groupValue: _selectedOrientation,
onChanged: (value) => _setOrientation(value),
);
},
),
);
}
}
六、完整示例代码
下面是一个完整的示例应用,展示了屏幕方向控制的各种用法:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Orientation 示例',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6366F1)),
useMaterial3: true,
),
home: const OrientationDemoPage(),
);
}
}
class OrientationDemoPage extends StatefulWidget {
const OrientationDemoPage({super.key});
@override
State<OrientationDemoPage> createState() => _OrientationDemoPageState();
}
class _OrientationDemoPageState extends State<OrientationDemoPage> {
DeviceOrientation _currentOrientation = DeviceOrientation.portraitUp;
@override
void initState() {
super.initState();
}
Future<void> _setOrientation(DeviceOrientation orientation) async {
await SystemChrome.setPreferredOrientations([orientation]);
setState(() {
_currentOrientation = orientation;
});
}
Future<void> _enableAllOrientations() async {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
String _getOrientationName(DeviceOrientation orientation) {
switch (orientation) {
case DeviceOrientation.portraitUp:
return '竖屏(正常)';
case DeviceOrientation.portraitDown:
return '竖屏(倒置)';
case DeviceOrientation.landscapeLeft:
return '横屏(左旋转)';
case DeviceOrientation.landscapeRight:
return '横屏(右旋转)';
}
}
IconData _getOrientationIcon(DeviceOrientation orientation) {
switch (orientation) {
case DeviceOrientation.portraitUp:
return Icons.stay_current_portrait;
case DeviceOrientation.portraitDown:
return Icons.stay_current_portrait;
case DeviceOrientation.landscapeLeft:
return Icons.stay_current_landscape;
case DeviceOrientation.landscapeRight:
return Icons.stay_current_landscape;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Orientation 示例'),
centerTitle: true,
elevation: 0,
),
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFE8F4FF),
Color(0xFFF8F9FF),
],
),
),
child: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildCurrentOrientationCard(),
const SizedBox(height: 24),
_buildSectionTitle('方向控制'),
const SizedBox(height: 12),
_buildOrientationButtons(),
const SizedBox(height: 24),
_buildSectionTitle('应用场景'),
const SizedBox(height: 12),
_buildScenarioButtons(),
],
),
),
),
),
);
}
Widget _buildCurrentOrientationCard() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF6366F1), Color(0xFF8B5CF6)],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: const Color(0xFF6366F1).withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Column(
children: [
Icon(
_getOrientationIcon(_currentOrientation),
size: 64,
color: Colors.white,
),
const SizedBox(height: 16),
const Text(
'当前屏幕方向',
style: TextStyle(color: Colors.white70, fontSize: 14),
),
const SizedBox(height: 8),
Text(
_getOrientationName(_currentOrientation),
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF6366F1), Color(0xFF8B5CF6)],
),
borderRadius: BorderRadius.circular(8),
),
child: Text(
title,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
);
}
Widget _buildOrientationButtons() {
return Column(
children: [
Row(
children: [
Expanded(
child: _buildControlButton(
'竖屏',
Icons.stay_current_portrait,
() => _setOrientation(DeviceOrientation.portraitUp),
Colors.blue,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildControlButton(
'横屏',
Icons.stay_current_landscape,
() => _setOrientation(DeviceOrientation.landscapeLeft),
Colors.purple,
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildControlButton(
'横屏(右)',
Icons.screen_rotation,
() => _setOrientation(DeviceOrientation.landscapeRight),
Colors.teal,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildControlButton(
'自动旋转',
Icons.flip,
() => _enableAllOrientations(),
Colors.orange,
),
),
],
),
],
);
}
Widget _buildScenarioButtons() {
return Column(
children: [
_buildScenarioButton(
'视频播放模式',
'切换到横屏,适合视频播放',
Icons.video_library,
() => _setOrientation(DeviceOrientation.landscapeLeft),
Colors.red,
),
const SizedBox(height: 12),
_buildScenarioButton(
'阅读模式',
'切换到竖屏,适合阅读',
Icons.book,
() => _setOrientation(DeviceOrientation.portraitUp),
Colors.green,
),
const SizedBox(height: 12),
_buildScenarioButton(
'游戏模式',
'切换到横屏,适合游戏',
Icons.games,
() => _setOrientation(DeviceOrientation.landscapeLeft),
Colors.indigo,
),
],
);
}
Widget _buildControlButton(
String text,
IconData icon,
VoidCallback onPressed,
Color color,
) {
return ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon, size: 20),
label: Text(text),
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
);
}
Widget _buildScenarioButton(
String title,
String subtitle,
IconData icon,
VoidCallback onPressed,
Color color,
) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
),
],
),
child: ListTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color),
),
title: Text(title),
subtitle: Text(subtitle, style: const TextStyle(fontSize: 12)),
trailing: const Icon(Icons.chevron_right),
onTap: onPressed,
),
);
}
}
七、常见问题与解决方案
7.1 方向设置不生效
问题原因:
- 未调用
WidgetsFlutterBinding.ensureInitialized() - 在 Widget 构建前调用
解决方案:
dart
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
runApp(const MyApp());
}
7.2 退出页面后方向未恢复
问题原因:
- 页面退出时未恢复默认方向
解决方案:
dart
@override
void dispose() {
// 退出时恢复竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}
7.3 全屏模式无法退出
问题原因:
- 沉浸模式隐藏了系统UI
解决方案:
dart
// 恢复系统UI
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
八、总结
Flutter 原生的 SystemChrome API 提供了强大的屏幕方向控制功能。通过本文的学习,我们掌握了:
- 基础用法:设置竖屏、横屏、自动旋转
- API 详解:DeviceOrientation 枚举、setPreferredOrientations 方法
- 实际应用:视频播放、游戏、阅读器、全屏模式等场景
- 常见问题:方向设置不生效、退出未恢复等问题的解决方案
💡 开发建议:使用 SystemChrome 时应注意:
- 在 main() 中初始化默认方向
- 页面退出时恢复默认方向
- 根据应用场景选择合适的方向模式
- 考虑用户体验,避免频繁切换方向