环境
Flutter 3.29
macOS Sequoia 15.4.1
Xcode 16.3
集成
- 创建一个带有 State 类的 StatefulWidget 组件
- 添加一个变量到 State 类来存放 CameraController
- 添加另外一个变量到 State 类中来存放 CameraController.initialize() 返回的 Future
- 在 initState() 方法中创建并初始化控制器
- 在 dispose() 方法中销毁控制器
创建并初始化 CameraController
新建take_photo.dart
文件,输入stf快捷创建一个有状态的Widget,命名为TakePictureScreen
dart
class TakePictureScreen extends StatefulWidget {
const TakePictureScreen({super.key, required this.cameras});
/// 1.接收调用时传入的摄像头列表
final List<CameraDescription> cameras;
@override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
/// 2. 相机控制器
late CameraController _controller;
/// 3. Future类型存放CameraController初始化返回的Future
late Future<void> _initializeControllerFuture;
}
在 initState 方法中创建并初始化控制器
dart
class TakePictureScreenState extends State<TakePictureScreen> {
/// 根据camera的原生代码,从availableCameras获取可用摄像头列表时,后置索引为0
/**
switch device.position {
case .back:
lensFacing = .back
case .front:
lensFacing = .front
case .unspecified:
lensFacing = .external
...
reply.append(cameraDescription)
*/
int frontCamera = 0;
...
@override
void initState() {
super.initState();
/// 设置初始化后置摄像头
_controller = CameraController(
widget.cameras[frontCamera],
/// 清晰度
ResolutionPreset.medium,
);
_initializeControllerFuture = _controller.initialize();
}
...
预览
使用CameraPreview类实现相机的预览
dart
FutureBuilder<void>(
future: _initializeControllerFuture, // initState方法中Controller返回的Future对象
builder: (context, snapshot) {
// Controller初始化完成
if (snapshot.connectionState == ConnectionState.done) {
/// 显示预览
return CameraPreview(_controller);
} else {
// 不然显示一个Loading动画
return const Center(child: CircularProgressIndicator());
}
},
),
使用 CameraController 拍照
使用CameraController的takePicture()方法实现拍照功能
dart
class TakePictureScreenState extends State<TakePictureScreen> {
String photoPath = "";
...
Widget build(BuildContext context) {
...
onPressed: () async {
try {
/// 确认摄像头已初始化
await _initializeControllerFuture;
/// 拍照并返回图片
final image = await _controller.takePicture();
/// 检查是否已挂载
if (!context.mounted) return;
/// 可以持久化存储
/// 设置更新状态,将这次的照片缩略图显示在左下角的预览图中
setState(() {
photoPath = image.path;
});
} catch (e) {
// If an error occurs, log the error to the console.
// print(e);
}
},
}
}
发送消息到原生方法
objc
/// FLTCam.m
- (void)captureToFileWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion
{
AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings];
// 对照片进行配置,比如是否高清,格式jpg还是heif,闪光灯,存储路径
...
/// 照片存储后的回调操作,移除该照片的uniqueID
__weak typeof(self) weakSelf = self;
FLTSavePhotoDelegate *savePhotoDelegate = [[FLTSavePhotoDelegate alloc]
initWithPath:path
ioQueue:self.photoIOQueue
completionHandler:^(NSString *_Nullable path, NSError *_Nullable error) {
typeof(self) strongSelf = weakSelf;
if (!strongSelf) return;
dispatch_async(strongSelf.captureSessionQueue, ^{
// cannot use the outter `strongSelf`
typeof(self) strongSelf = weakSelf;
if (!strongSelf) return;
[strongSelf.inProgressSavePhotoDelegates removeObjectForKey:@(settings.uniqueID)];
});
if (error) {
completion(nil, FlutterErrorFromNSError(error));
} else {
NSAssert(path, @"Path must not be nil if no error.");
completion(path, nil);
}
}];
...
/// 传入配置 + 回调代理对象 + 通知进行拍照
[self.capturePhotoOutput capturePhotoWithSettings:settings delegate:savePhotoDelegate];
}
在 dispose 方法中销毁控制器
dart
@override
void dispose() {
_controller.dispose();
super.dispose();
}
开启闪光灯
使用CameraController对象的setFlashMode方法
dart
class TakePictureScreenState extends State<TakePictureScreen> {
FlashMode _flashMode = FlashMode.always;
...
onPressed: () {
/// 闪光灯不同配置下图标不一样,因此修改后触发build
setState(() {
if (_flashMode == FlashMode.always) {
_flashMode = FlashMode.auto;
} else if (_flashMode == FlashMode.auto) {
_flashMode = FlashMode.off;
} else if (_flashMode == FlashMode.off) {
_flashMode = FlashMode.always;
}
_controller.setFlashMode(_flashMode);
});
};
}

切换摄像头
通过更新CameraController对象的description参数来切换摄像头
dart
onPressed: () async {
if (frontCamera == 1) {
/// 重新创建CameraController并设置后置摄像头
_controller = CameraController(
widget.cameras[0],
ResolutionPreset.medium,
);
/// 初始化完成后更新标记,刷新界面
_controller.initialize().then((_) {
if (mounted) {
frontCamera = 0;
setState(() {});
}
});
} else {
_controller = CameraController(
widget.cameras[1],
ResolutionPreset.medium,
);
_controller.initialize().then((_) {
if (mounted) {
frontCamera = 1;
setState(() {});
}
});
}
},
设置焦距
使用CameraController对象的setZoomLevel方法设置焦距
dart
...
onPressed: () {
setState(() {
_controller.setZoomLevel(1);
_isMinZoomLevel = true;
minZoomLevelDesc = "0.5x";
});
},
拍照后显示缩略图
使用Align设置缩略图靠左
dart
SizedBox(
width: MediaQuery.of(context).size.width,
child: Stack(
fit: StackFit.loose,
children: [
Align(
alignment: Alignment(-0.8, 0),
child: SizedBox(
width: 60,
height: 60,
child:
photoPath.isEmpty // 相片为空,隐藏
? Container()
: Image.file(File(photoPath)),
),
),

其它
装修结束,闲居在家
