You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter固定竖屏运行时如何实现横竖屏方向正确的拍照功能

解决方法

核心思路

因为你全局固定了App竖屏显示,系统UI方向不会随手机倾斜变化,所以不能依赖系统的界面方向判断拍摄方向,需要:

  • 监听设备物理传感器的方向,获取用户实际持握手机的角度
  • 拍照时根据物理方向动态调整拍摄输出的方向
  • 同步旋转相机预览,保证预览和实际持握方向一致

步骤1:引入物理方向监听库

使用native_device_orientation库获取设备真实物理方向,不受系统UI方向锁定影响,在pubspec.yaml中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  camera: ^latest_version
  native_device_orientation: ^latest_version

步骤2:完整修改后的代码

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:flutter/services.dart';
import 'package:native_device_orientation/native_device_orientation.dart';

List<CameraDescription> cameras = [];

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // 保留原有的全局竖屏配置
  await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
  cameras = await availableCameras();
  runApp(CameraApp());
}

class CameraApp extends StatefulWidget {
  @override
  _CameraAppState createState() => _CameraAppState();
}

class _CameraAppState extends State<CameraApp> {
  CameraController? controller;
  bool takingPicture = false;
  XFile? selectedImage;
  NativeDeviceOrientation currentOrientation = NativeDeviceOrientation.portraitUp;
  StreamSubscription<NativeDeviceOrientation>? orientationSub;

  @override
  void initState() {
    super.initState();
    controller = CameraController(cameras[0], ResolutionPreset.max);
    controller!.initialize().then((_) {
      if (!mounted) return;
      setState(() {});
    });
    // 监听设备物理方向变化,不受UI方向锁定影响
    orientationSub = NativeDeviceOrientationCommunicator().onOrientationChanged(useSensor: true).listen((event) {
      setState(() {
        currentOrientation = event;
      });
    });
  }

  @override
  void dispose() {
    orientationSub?.cancel();
    controller?.dispose();
    super.dispose();
  }

  // 计算预览需要旋转的角度
  int _getQuarterTurns() {
    int sensorOrient = controller!.description.sensorOrientation ~/ 90;
    switch(currentOrientation) {
      case NativeDeviceOrientation.portraitUp:
        return 1 - sensorOrient;
      case NativeDeviceOrientation.portraitDown:
        return 3 - sensorOrient;
      case NativeDeviceOrientation.landscapeLeft:
        return 0 - sensorOrient;
      case NativeDeviceOrientation.landscapeRight:
        return 2 - sensorOrient;
      default:
        return 1 - sensorOrient;
    }
  }

  // 获取当前持握方向对应的拍摄方向
  DeviceOrientation _getCaptureOrientation() {
    switch(currentOrientation) {
      case NativeDeviceOrientation.portraitUp:
        return DeviceOrientation.portraitUp;
      case NativeDeviceOrientation.portraitDown:
        return DeviceOrientation.portraitDown;
      case NativeDeviceOrientation.landscapeLeft:
        return DeviceOrientation.landscapeLeft;
      case NativeDeviceOrientation.landscapeRight:
        return DeviceOrientation.landscapeRight;
      default:
        return DeviceOrientation.portraitUp;
    }
  }

  @override
  Widget build(BuildContext context) {
    if (!controller!.value.isInitialized) {
      return Container();
    }
    return MaterialApp(
      home: Stack(
        fit: StackFit.expand,
        children: [
          // 修正预览旋转逻辑,移除原代码重复的CameraPreview
          RotatedBox(
            quarterTurns: _getQuarterTurns(),
            child: CameraPreview(controller!),
          ),
          Align(
            alignment: Alignment.bottomRight,
            child: FloatingActionButton(
              onPressed: () async {
                if (takingPicture) return;
                takingPicture = true;
                // 拍照前临时设置捕获方向
                await controller!.lockCaptureOrientation(_getCaptureOrientation());
                final value = await controller!.takePicture();
                // 重置捕获方向,不影响全局竖屏配置
                await controller!.unlockCaptureOrientation();
                selectedImage = value;
                takingPicture = false;
                setState(() {});
              },
              child: const Icon(Icons.camera),
            ),
          ),
          Align(
            alignment: Alignment.bottomLeft,
            child: selectedImage != null
                ? RotatedBox(
                    quarterTurns: _getQuarterTurns(),
                    child: Container(
                      height: 100,
                      width: 200,
                      child: Image.file(
                        File(selectedImage!.path),
                        fit: BoxFit.cover,
                      ),
                    ),
                  )
                : Container(),
          ),
        ],
      ),
    );
  }
}

说明

  • 完全保留了原有的全局竖屏固定配置,App其他页面不受任何影响
  • 拍摄的照片会自动写入正确的EXIF方向信息,所有图片查看器都能正确识别横竖屏
  • 前置摄像头的方向适配已经通过camera库的sensorOrientation自动处理,无需额外调整

内容的提问来源于stack exchange,提问作者Ozan Taskiran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 16:24:05