基础入门 Flutter for OpenHarmony:SystemChrome 屏幕方向控制详解

欢迎加入开源鸿蒙跨平台社区: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 提供了强大的屏幕方向控制功能。通过本文的学习,我们掌握了:

  1. 基础用法:设置竖屏、横屏、自动旋转
  2. API 详解:DeviceOrientation 枚举、setPreferredOrientations 方法
  3. 实际应用:视频播放、游戏、阅读器、全屏模式等场景
  4. 常见问题:方向设置不生效、退出未恢复等问题的解决方案

💡 开发建议:使用 SystemChrome 时应注意:

  • 在 main() 中初始化默认方向
  • 页面退出时恢复默认方向
  • 根据应用场景选择合适的方向模式
  • 考虑用户体验,避免频繁切换方向
相关推荐
键盘鼓手苏苏4 小时前
Flutter for OpenHarmony 实战:Flutter Rust Bridge — 极致计算性能方案
开发语言·后端·flutter·华为·rust·json·harmonyos
夏小鱼的blog4 小时前
【AtomGit 携手开源鸿蒙】Flutter-OH三方库鸿蒙化 - 1
flutter·开源·harmonyos
哈__4 小时前
基础入门 Flutter for OpenHarmony:webview_flutter 内嵌浏览器详解
flutter·华为·harmonyos
松叶似针4 小时前
Flutter三方库适配OpenHarmony【secure_application】— 插件功能全景与适配价值
flutter·harmonyos·鸿蒙
钛态5 小时前
Flutter for OpenHarmony:leak_tracker 自动监测内存泄漏,精准定位未释放对象(内存性能优化) 深度解析与鸿蒙适配指南
flutter·华为·性能优化·harmonyos
松叶似针5 小时前
Flutter三方库适配OpenHarmony【secure_application】— 开发环境与工具链准备
flutter·harmonyos
钛态5 小时前
Flutter for OpenHarmony:formz 简化表单验证逻辑,分离 UI 与业务状态(声明式表单验证) 深度解析与鸿蒙适配指南
flutter·ui·华为·harmonyos
不爱吃糖的程序媛5 小时前
Puro 全面解析:比 FVM 更快更省的 Flutter 版本管理器(鸿蒙定制版首选)
flutter
阿林来了5 小时前
Flutter三方库适配OpenHarmony【flutter_speech】— Flutter Plugin 机制解析
flutter·harmonyos·鸿蒙