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
相关产品推荐
相关产品推荐

