需求
用前置摄像头采集用户头像
1. 添加所需依赖
运行 flutter pub add 将Camera模块其添加为依赖:
flutter pub add camera
2. 获取可用相机列表
使用相机首先要获取可用相机列表。
// Ensure that plugin services are initialized so that `availableCameras()`
// can be called before `runApp()`
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
3. 创建并初始化 CameraController
在选择了一个相机后,你需要创建并初始化 CameraController。在这个过程中,与设备相机建立了连接并允许你控制相机并展示相机的预览帧流。
在使用相机前,请确保控制器已经完成初始化。因此,要等待前一个步骤创建 initialize() 执行完毕才去展示 CameraPreview。
Future<void> _initCamera() async {
List<CameraDescription> cameras = await availableCameras();
CameraDescription frontCamera = cameras[0];
for (var camera in cameras) {
if (camera.lensDirection == CameraLensDirection.front) {
frontCamera = camera;
break;
}
}
controller = CameraController(frontCamera, ResolutionPreset.max, enableAudio: false);
try {
// 初始化
await controller.initialize();
isCameraInitialized.value = true;
applyEnabled.value = true;
} on CameraException catch (e) {
print(e);
}
}
@override
void onInit() {
_initCamera();
super.onInit();
}
// 需要销毁Controller
@override
void onClose() {
controller.dispose();
super.onClose();
}
// 组件
Widget _buildCamera() {
return Obx(() {
if (!controller.isCameraInitialized.value) {
return Container(
color: Colors.black54,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return CameraPreview(controller.controller);
}
});
}
注意
如果你没有初始化
CameraController,你就 不能 使用相机预览和拍照。
4. 使用 CameraController 拍照
接着就可以调用controller.takePicture()来拍照,它获得一个XFile对象,直接就可以通过path展示出来。
Future<void> takePicture() async {
if (!isCameraInitialized.value) {
return;
}
applyEnabled.value = false;
try {
final XFile file = await controller.takePicture();
imagePath.value = file.path;
});
} on CameraException catch (e) {
applyEnabled.value = true;
}
}
// 展示图片
Widget _buildShowPicture() {
return Obx(
() => controller.imagePath.value.isEmpty
? SizedBox()
: Image.file(
File(controller.imagePath.value),
width: 100,
fit: BoxFit.fitWidth,
),
);
}
// 拍照
FloatingActionButton(
// Provide an onPressed callback.
onPressed: () async {
controller.apply();
},
child: const Icon(Icons.camera_alt),
)
问题
以上已经可以拍照了,但是会发现预览镜头是横向的,拍摄出来的图片是正常竖屏的,所以需要将预览镜头进行旋转。
Widget _buildCamera() {
return Obx(() {
if (!controller.isCameraInitialized.value) {
return Container(
color: Colors.black54,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return Transform.rotate(
angle: -pi / 2, // 将预览画面旋转 90 度
child: Center(
child: AspectRatio(
aspectRatio: controller.controller.value.aspectRatio,
child: CameraPreview(controller.controller),
),
),
);
}
});
}
同样,在预览中有可能会出现拉伸现象,所以需要进行缩放。
Widget _buildCamera() {
return Obx(() {
if (!controller.isCameraInitialized.value) {
return Container(
color: Colors.black54,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return Transform.rotate(
angle: -pi / 2, // 将预览画面旋转 90 度
child: Transform.scale(
scale: Get.pixelRatio,
child: Center(
child: AspectRatio(
aspectRatio: controller.controller.value.aspectRatio,
child: CameraPreview(controller.controller),
),
),
),
);
}
});
}
这里有个疑问,采用final scale = Get.window.physicalSize / controller.value.previewSize.width 计算出来的 scale = 1, 但展示其实只有设备的一半左右,实际像素和展示像素不一致,故而采用Get.pixelRatio计算缩放。