manual_camera_view
一个在 Flutter 页面中嵌入原生相机预览的轻量插件。
当前能力:
- 原生相机实时预览
- 点击预览区域进行对焦与测光,并显示对焦框
- 统一的亮度调节接口
setNormalizedExposure(-1.0 ~ 1.0)- Android:直接走原生归一化曝光逻辑
- iOS:内部换算为曝光补偿档位
- 设置 JPEG 图片质量
setImageQuality(1~100) - 拍照并直接返回 JPEG 二进制
Uint8List
适合场景:
- Flutter 页面里直接嵌入原生相机画面
- 使用 Flutter 自定义拍照按钮、曝光滑杆和业务 UI
- 拍照后自行上传、压缩、保存或展示图片
适用场景
如果你想要的是:
- Flutter 页面里直接嵌入原生相机画面
- 用 Flutter 按钮控制拍照
- 手动调暗或调亮画面
- 拿到拍照结果后自己上传、展示或处理
这个插件就可以直接用。
安装
在你的 Flutter 项目中引入插件:
dependencies:
manual_camera_view: ^0.1.0
然后执行:
flutter pub get
如果你是在插件仓库本地联调,可以把 example/pubspec.yaml 里的路径依赖保留为 path: ../。
权限配置
Android
在 AndroidManifest.xml 中添加相机权限:
<uses-permission android:name="android.permission.CAMERA" />
iOS
在 Info.plist 中添加相机权限说明:
<key>NSCameraUsageDescription</key>
<string>需要使用相机进行拍照</string>
基本用法
先导入:
import 'package:manual_camera_view/manual_camera.dart';
先创建一个 ManualCameraController,再传给 ManualCameraView:
final ManualCameraController _controller = ManualCameraController();
ManualCameraView(
controller: _controller,
onViewCreated: () {
debugPrint('camera view ready');
},
)
说明:
controller需要由 Flutter 层自己持有onViewCreated触发后,控制器才算完成与原生视图绑定- 在视图 ready 之前调用拍照、曝光等方法,会抛出
StateError
调节亮度
使用一个滑杆即可:
Slider(
min: -1.0,
max: 1.0,
value: _exposure,
onChanged: (value) {
setState(() => _exposure = value);
_controller.setNormalizedExposure(value);
},
)
说明:
-1.0:更暗0.0:默认曝光1.0:更亮- Flutter 层无需区分 Android 和 iOS,统一调用这一套 API
点击对焦
ManualCameraView 已内置点击对焦能力:
- 点击预览画面后,会自动把触点换算成 0~1 的归一化坐标
- 原生侧会按该坐标执行 AF/AE 对焦与测光
- Flutter 层会短暂显示一个对焦框
如果你有自定义交互需求,也可以直接调用:
await _controller.setFocusPoint(0.5, 0.5);
拍照
拍照前可以按需设置图片质量:
await _controller.setImageQuality(90);
然后调用:
final bytes = await _controller.takePicture();
返回值就是 JPEG 二进制数据 Uint8List,你可以直接用于:
- 上传服务器
- 写入本地文件
- 再次转存到相册
- 页面预览显示
示例:
final bytes = await _controller.takePicture();
debugPrint('image bytes: ${bytes.length}');
完整示例
下面是一份和当前插件 API 对应的最小示例:
import 'package:manual_camera_view/manual_camera.dart';
import 'package:flutter/material.dart';
class CameraPage extends StatefulWidget {
const CameraPage({super.key});
@override
State<CameraPage> createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
final ManualCameraController _controller = ManualCameraController();
double _exposure = 0.0;
bool _isCapturing = false;
Future<void> _takePicture() async {
if (!_controller.isReady || _isCapturing) return;
setState(() => _isCapturing = true);
try {
await _controller.setImageQuality(90);
final bytes = await _controller.takePicture();
debugPrint('captured: ${bytes.length}');
} finally {
if (mounted) setState(() => _isCapturing = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
ManualCameraView(controller: _controller),
Positioned(
left: 16,
right: 16,
bottom: 120,
child: Slider(
min: -1.0,
max: 1.0,
value: _exposure,
onChanged: (value) {
setState(() => _exposure = value);
_controller.setNormalizedExposure(value);
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
onPressed: _isCapturing ? null : _takePicture,
child: const Text('拍照'),
),
),
),
],
),
);
}
}
当前对外可用能力
主要对外 API:
ManualCameraController.isReadyManualCameraController.setNormalizedExposure()ManualCameraController.setImageQuality()ManualCameraController.setFocusPoint()ManualCameraController.takePicture()ManualCameraView
说明
- Android 和 iOS 的底层实现不同,但 Flutter 使用方式保持统一
- 当前
takePicture()返回的是内存中的 JPEG 字节,不直接返回文件路径 - 是否保存到系统相册,由你的业务层自行决定
- 如果想看更完整的页面接入方式,可以参考
example/lib/main.dart