Flutter相机功能插件penguin_camera的使用
Flutter相机功能插件penguin_camera的使用
penguin_camera
一个跨平台框架,用于访问设备上可用的各种摄像头及其特性,允许你在应用中捕获图片和视频。
除了提供跨平台的接口外,该框架还提供了对平台特定功能的简便访问。
支持平台
- Android 16+
- iOS 10.0+
权限
Android 和 iOS 需要权限才能访问摄像头和音频设备,但此包不提供请求这些权限的 API。建议使用 permission_handler
包来请求权限。请参见示例中的示例。
使用案例
获取摄像头设备信息
final List<CameraDevice> devices = await CameraController.getAllCameraDevices();
for (CameraDevice device in devices) {
print('CameraDevice: ${device.name}, ${device.position}');
}
显示预览
final List<CameraDevice> devices = await CameraController.getAllCameraDevices();
final CameraDevice device = devices.firstWhere(
(CameraDevice device) => device.position == CameraPosition.back,
);
final PreviewOutput previewOutput = PreviewOutput();
final CameraController controller = CameraController(
device: device,
outputs: <CameraOutput>[previewOutput],
);
// 这必须在调用其他方法之前完成。
await controller.initialize();
final Widget previewWidget = await previewOutput.previewWidget();
controller.start();
捕获图片
final List<CameraDevice> devices = await CameraController.getAllCameraDevices();
final CameraDevice device = devices.firstWhere(
(CameraDevice device) => device.position == CameraPosition.back,
);
final ImageCaptureOutput imageOutput = ImageCaptureOutput();
// Android 需要 PreviewOutput,所以尽管它未被使用也添加了。
final CameraController controller = CameraController(
device: device,
outputs: <CameraOutput>[PreviewOutput(), imageOutput],
);
// 这必须在调用其他方法之前完成。
await controller.initialize();
controller.start();
imageOutput.takePicture((Uint8List data) {
print(data.length);
});
controller.stop();
controller.dispose();
录制视频
final List<CameraDevice> devices = await CameraController.getAllCameraDevices();
final CameraDevice device = devices.firstWhere(
(CameraDevice device) => device.position == CameraPosition.back,
);
final VideoCaptureOutput videoOutput = VideoCaptureOutput();
// Android 需要 PreviewOutput,所以尽管它未被使用也添加了。
final CameraController controller = CameraController(
device: device,
outputs: <CameraOutput>[PreviewOutput(), videoOutput],
);
// 这必须在调用其他方法之前完成。
await controller.initialize();
controller.start();
videoOutput.startRecording(fileOutput: 'myFile');
videoOutput.stopRecording();
controller.stop();
controller.dispose();
设置曝光
final CameraController controller = ....
final List<ExposureMode> modes = await controller.supportedExposureModes();
if (modes.contains(ExposureMode.continuous)) {
controller.setExposureMode(ExposureMode.continuous);
}
设置焦点
final CameraController controller = ....
final List<FocusMode> modes = await controller.supportedFocusModes();
if (modes.contains(FocusMode.continuousImageAutoFocus)) {
controller.setFocusMode(FocusMode.continuousImageAutoFocus);
}
设置缩放
final CameraController controller = ....
if (await controller.zoomSupported()) {
final double minZoom = await controller.minZoom();
final double maxZoom = await controller.maxZoom();
controller.setZoom((maxZoom + minZoom) / 2);
}
获取输出大小
final List<CameraDevice> devices = await CameraController.getAllCameraDevices();
final CameraDevice device = devices.firstWhere(
(CameraDevice device) => device.position == CameraPosition.back,
);
final PreviewOutput previewOutput = PreviewOutput();
final CameraController controller = CameraController(
device: device,
outputs: <CameraOutput>[previewOutput],
);
// 这必须完成以保证 CameraOutput.outputSize() 返回非空。
await controller.setControllerPreset(CameraControllerPreset.high);
final Size? outputSize = await previewOutput.outputSize();
设置闪光灯
final List<CameraDevice> devices = await CameraController.getAllCameraDevices();
final CameraDevice device = devices.firstWhere(
(CameraDevice device) => device.position == CameraPosition.back,
);
final ImageCaptureOutput imageOutput = ImageCaptureOutput();
// Android 需要 PreviewOutput,所以尽管它未被使用也添加了。
final CameraController controller = CameraController(
device: device,
outputs: <CameraOutput>[PreviewOutput(), imageOutput],
);
// 这必须在调用其他方法之前完成。
await controller.initialize();
final List<FlashMode> modes = await imageOutput.supportedFlashModes();
if (modes.contains(FlashMode.auto)) {
imageOutput.setFlashMode(FlashMode.auto);
}
使用 Android 特定功能
import 'package:penguin_camera/penguin_camera.dart';
import 'package:penguin_camera/android.dart' as android;
final CameraController controller = ....
if (defaultTargetPlatform == TargetPlatform.android) {
final android.CameraController androidController =
controller as android.CameraController;
final android.CameraParameters parameters =
androidController.cameraParameters;
parameters.setColorEffect(android.CameraParameters.effectNegative);
androidController.camera.setParameters(parameters);
}
使用 iOS 特定功能
import 'package:penguin_camera/penguin_camera.dart';
import 'package:penguin_camera/ios.dart' as ios;
final CameraController controller = ....
if (defaultTargetPlatform == TargetPlatform.iOS) {
final ios.CameraController iosController =
controller as ios.CameraController;
iosController.session.setSessionPreset(ios.CaptureSessionPreset.photo);
}
完整示例代码
// ignore_for_file: public_member_api_docs
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:penguin_camera/penguin_camera.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(const MaterialApp(home: _MyApp()));
}
enum CameraMode {
prePicture,
preVideo,
picture,
video,
none,
videoRecording,
}
class _MyApp extends StatefulWidget {
const _MyApp({Key? key}) : super(key: key);
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<_MyApp> {
CameraMode currentMode = CameraMode.none;
late CameraController _cameraController;
PreviewOutput _previewOutput = PreviewOutput();
late ImageCaptureOutput _imageCaptureOutput;
late VideoCaptureOutput _videoCaptureOutput;
Size? _previewOutputSize;
CameraPosition _cameraPosition = CameraPosition.front;
final double _deviceRotation = 0;
[@override](/user/override)
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.bottom],
);
SystemChrome.setPreferredOrientations(<DeviceOrientation>[
DeviceOrientation.portraitUp,
]);
_setupForPicture();
}
Future<void> _getPicturePermission() async {
while (!(await Permission.camera.request().isGranted)) {}
while (!(await Permission.storage.request().isGranted)) {}
if (defaultTargetPlatform == TargetPlatform.iOS) {
while (!(await Permission.photosAddOnly.request().isGranted)) {}
}
}
Future<void> _getAudioPermission() async {
while (!(await Permission.microphone.request().isGranted)) {}
}
Future<void> _setupForPicture() async {
await _getPicturePermission();
_imageCaptureOutput = ImageCaptureOutput();
_cameraController = await _setupCameraController(_imageCaptureOutput);
final List<FocusMode> supportedFocusMode =
await _cameraController.supportedFocusModes();
if (supportedFocusMode.contains(FocusMode.continuousImageAutoFocus)) {
_cameraController.setFocusMode(FocusMode.continuousImageAutoFocus);
}
_cameraController.start();
setState(() {
currentMode = CameraMode.picture;
});
}
Future<void> _setupForVideo() async {
await _getPicturePermission();
await _getAudioPermission();
_videoCaptureOutput = VideoCaptureOutput(includeAudio: true);
_cameraController = await _setupCameraController(_videoCaptureOutput);
final List<FocusMode> supportedFocusMode =
await _cameraController.supportedFocusModes();
if (supportedFocusMode.contains(FocusMode.continuousVideoAutoFocus)) {
_cameraController.setFocusMode(FocusMode.continuousVideoAutoFocus);
}
_cameraController.start();
setState(() {
currentMode = CameraMode.video;
});
}
Future<CameraController> _setupCameraController(CameraOutput output) async {
final List<CameraDevice> allCameraDevices =
await CameraController.getAllCameraDevices();
final CameraDevice device = allCameraDevices.firstWhere(
(CameraDevice device) => device.position == _cameraPosition,
);
_previewOutput = PreviewOutput();
final CameraController cameraController = CameraController(
device: device,
outputs: <CameraOutput>[_previewOutput, output],
);
await cameraController.initialize();
await cameraController.setControllerPreset(CameraControllerPreset.high);
_previewOutput.setRotation(OutputRotation.rotation0);
output.setRotation(OutputRotation.rotation0);
_previewOutputSize = await _previewOutput.outputSize();
return cameraController;
}
[@override](/user/override)
void dispose() {
super.dispose();
if (currentMode != CameraMode.none) _cameraController.dispose();
}
Future<void> _toggleCameraMode() {
switch (currentMode) {
case CameraMode.prePicture:
case CameraMode.preVideo:
case CameraMode.none:
return Future<void>.value();
case CameraMode.picture:
setState(() => currentMode = CameraMode.preVideo);
_cameraController.dispose();
return _setupForVideo();
case CameraMode.video:
setState(() => currentMode = CameraMode.prePicture);
_cameraController.dispose();
return _setupForPicture();
case CameraMode.videoRecording:
const SnackBar snackBar = SnackBar(
content: Text('Please end video recording first.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return Future<void>.value();
}
}
Future<void> _toggleCameraPosition(BuildContext context) {
void switchCameraPosition() {
if (_cameraPosition == CameraPosition.front) {
_cameraPosition = CameraPosition.back;
} else {
_cameraPosition = CameraPosition.front;
}
}
switch (currentMode) {
case CameraMode.prePicture:
case CameraMode.preVideo:
case CameraMode.none:
return Future<void>.value();
case CameraMode.picture:
setState(() => currentMode = CameraMode.prePicture);
_cameraController.dispose();
switchCameraPosition();
return _setupForPicture();
case CameraMode.video:
setState(() => currentMode = CameraMode.preVideo);
_cameraController.dispose();
switchCameraPosition();
return _setupForVideo();
case CameraMode.videoRecording:
const SnackBar snackBar = SnackBar(
content: Text('Please end video recording first.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return Future<void>.value();
}
}
Future<Directory> _getStorageDir() async {
final Directory? directory;
if (defaultTargetPlatform == TargetPlatform.android) {
final List<Directory>? directories = await getExternalStorageDirectories(
type: StorageDirectory.dcim,
);
directory = directories?.first;
} else {
directory = await getApplicationDocumentsDirectory();
}
if (directory == null) {
throw StateError('Could not get storage directory.');
}
return directory;
}
void _takeImage(BuildContext context) {
_imageCaptureOutput.takePicture((Uint8List bytes) async {
debugPrint('Image taken with jpeg data length: ${bytes.length}');
final Directory dir = await _getStorageDir();
final File imageFile = File('${dir.path}/my_image${bytes.hashCode}.jpg');
imageFile.writeAsBytes(bytes);
final String message = 'Picture stored at: $imageFile';
debugPrint(message);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
});
}
Future<void> _startRecording() async {
final Directory dir = await _getStorageDir();
_videoCaptureOutput.startRecording(
fileOutput: '${dir.path}/my_video${Random().nextInt(10000)}.mp4',
);
setState(() {
currentMode = CameraMode.videoRecording;
});
}
void _stopRecording() {
_videoCaptureOutput.stopRecording();
setState(() {
currentMode = CameraMode.video;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Container(
decoration: const BoxDecoration(color: Colors.black),
child: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
CameraPreview(
previewOutput: _previewOutput,
cameraMode: currentMode,
size: _previewOutputSize,
),
if (currentMode == CameraMode.picture ||
currentMode == CameraMode.video ||
currentMode == CameraMode.videoRecording)
Positioned(
bottom: 0,
child: ZoomWidget(_cameraController),
)
],
),
),
),
),
Container(
decoration: const BoxDecoration(color: Colors.black),
padding: const EdgeInsets.only(
bottom: 30,
left: 10,
right: 10,
top: 15,
),
child: Stack(
children: <Widget>[
Container(
margin: const EdgeInsets.only(top: 10),
alignment: Alignment.centerLeft,
child: Transform.rotate(
angle: _deviceRotation,
child: IconButton(
icon: const Icon(
Icons.switch_camera,
color: Colors.white,
size: 32,
),
onPressed: () => _toggleCameraPosition(context),
),
),
),
Container(
alignment: Alignment.center,
child: CameraButton(
cameraMode: currentMode,
onTakePicture: () => _takeImage(context),
onStartRecording: _startRecording,
onStopRecording: _stopRecording,
),
)
],
),
),
Container(
alignment: Alignment.center,
decoration: const BoxDecoration(color: Colors.black),
child: ToggleButtons(
color: Colors.blue,
fillColor: Colors.grey,
selectedColor: Colors.blue,
onPressed: (int index) {
if (index == 0 && currentMode != CameraMode.picture) {
_toggleCameraMode();
} else if (index == 1 && currentMode != CameraMode.video) {
_toggleCameraMode();
}
},
isSelected: <bool>[
currentMode == CameraMode.picture ||
currentMode == CameraMode.prePicture,
currentMode == CameraMode.video ||
currentMode == CameraMode.preVideo ||
currentMode == CameraMode.videoRecording,
],
children: const <Widget>[
Icon(Icons.camera_alt),
Icon(Icons.videocam),
],
),
),
],
),
),
);
}
}
class ZoomWidget extends StatefulWidget {
const ZoomWidget(this.controller, {Key? key}) : super(key: key);
final CameraController controller;
[@override](/user/override)
State<StatefulWidget> createState() {
return ZoomWidgetState();
}
}
class ZoomWidgetState extends State<ZoomWidget> {
static const _maxAllowedZoom = 5;
late int _minZoom;
late int _maxZoom;
late int _currentZoom;
bool _supportsZoom = false;
bool _supportsSmoothZoom = false;
[@override](/user/override)
void initState() {
super.initState();
getZoomInfo();
}
Future<void> getZoomInfo() async {
final CameraController controller = widget.controller;
final bool supportsZoom = await controller.zoomSupported();
if (!supportsZoom) return;
final bool supportsSmoothZoom = await controller.smoothZoomSupported();
final int minZoom = (await controller.minZoom()).ceil();
final int maxZoom = min(
(await controller.maxZoom()).floor(),
_maxAllowedZoom,
);
setState(() {
_supportsZoom = supportsZoom;
_supportsSmoothZoom = supportsSmoothZoom;
_currentZoom = 0;
_minZoom = minZoom;
_maxZoom = maxZoom;
});
}
Future<void> zoom(int value) {
if (_supportsSmoothZoom) {
return widget.controller.smoothZoomTo((value + _minZoom).toDouble());
}
return widget.controller.setZoom((value + _minZoom).toDouble());
}
[@override](/user/override)
Widget build(BuildContext context) {
if (!_supportsZoom) return Container();
final int length = _maxZoom - _minZoom + 1;
return ToggleButtons(
color: Colors.blue,
fillColor: Colors.grey,
selectedColor: Colors.blue,
onPressed: (int index) {
setState(() => _currentZoom = index);
zoom(index);
},
isSelected: List<int>.generate(length, (index) => index)
.map<bool>((int value) => value == _currentZoom)
.toList(),
children: List<int>.generate(length, (index) => index)
.map<Widget>((int value) => Text('${value + 1}x'))
.toList(),
);
}
}
class CameraPreview extends StatelessWidget {
const CameraPreview({
Key? key,
required this.previewOutput,
required this.cameraMode,
this.size,
}) : super(key: key);
final PreviewOutput previewOutput;
final CameraMode cameraMode;
final Size? size;
[@override](/user/override)
Widget build(BuildContext context) {
switch (cameraMode) {
case CameraMode.picture:
case CameraMode.video:
case CameraMode.videoRecording:
return FutureBuilder<Widget>(
future: previewOutput.previewWidget(),
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
if (snapshot.hasData) {
if (size != null) {
return AspectRatio(
aspectRatio: size!.height / size!.width,
child: snapshot.data!,
);
}
return snapshot.data!;
}
return Container();
},
);
case CameraMode.prePicture:
case CameraMode.preVideo:
case CameraMode.none:
return Container();
}
}
}
class CameraButton extends StatelessWidget {
const CameraButton({
Key? key,
required this.cameraMode,
required this.onTakePicture,
required this.onStartRecording,
required this.onStopRecording,
}) : super(key: key);
final CameraMode cameraMode;
final VoidCallback onTakePicture;
final Future<void> Function() onStartRecording;
final VoidCallback onStopRecording;
[@override](/user/override)
Widget build(BuildContext context) {
switch (cameraMode) {
case CameraMode.picture:
case CameraMode.prePicture:
return CircledIconButton(icon: Icons.camera_alt, onTap: onTakePicture);
case CameraMode.video:
case CameraMode.preVideo:
return CircledIconButton(icon: Icons.videocam, onTap: onStartRecording);
case CameraMode.videoRecording:
return CircledIconButton(
icon: Icons.videocam,
color: Colors.red,
onTap: onStopRecording,
);
case CameraMode.none:
return Container();
}
}
}
class CircledIconButton extends StatelessWidget {
const CircledIconButton({
Key? key,
required this.icon,
this.onTap,
this.color = Colors.white,
}) : super(key: key);
final IconData icon;
final VoidCallback? onTap;
final Color color;
[@override](/user/override)
Widget build(BuildContext context) {
return InkResponse(
onTap: () {
if (onTap != null) onTap!();
},
child: Container(
width: 65,
height: 65,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(color: Colors.grey, width: 2),
),
child: Icon(icon, color: Colors.grey, size: 60),
),
);
}
}
更多关于Flutter相机功能插件penguin_camera的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter相机功能插件penguin_camera的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
penguin_camera
是一个 Flutter 插件,用于在 Flutter 应用中集成相机功能。它提供了一个简单易用的 API,允许开发者快速实现拍照、录像、切换摄像头等功能。以下是如何使用 penguin_camera
插件的基本步骤。
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 penguin_camera
插件的依赖:
dependencies:
flutter:
sdk: flutter
penguin_camera: ^0.0.1 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 基本使用
2.1 导入包
在你的 Dart 文件中导入 penguin_camera
包:
import 'package:penguin_camera/penguin_camera.dart';
2.2 初始化相机
你可以使用 PenguinCamera
小部件来初始化相机并显示相机预览:
class CameraScreen extends StatefulWidget {
[@override](/user/override)
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
PenguinCameraController? _controller;
[@override](/user/override)
void initState() {
super.initState();
_controller = PenguinCameraController();
}
[@override](/user/override)
void dispose() {
_controller?.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Camera Example'),
),
body: PenguinCamera(
controller: _controller,
),
);
}
}
2.3 拍照
你可以使用 PenguinCameraController
的 takePicture
方法来拍照:
void _takePicture() async {
if (_controller != null) {
final image = await _controller!.takePicture();
// 处理拍摄的照片,例如保存到本地或显示在界面上
print('Picture taken: ${image.path}');
}
}
2.4 切换摄像头
你可以使用 PenguinCameraController
的 switchCamera
方法来切换前后摄像头:
void _switchCamera() {
if (_controller != null) {
_controller!.switchCamera();
}
}
2.5 录制视频
penguin_camera
也支持录制视频。你可以使用 startRecording
和 stopRecording
方法来控制视频录制:
void _startRecording() async {
if (_controller != null) {
await _controller!.startRecording();
print('Recording started');
}
}
void _stopRecording() async {
if (_controller != null) {
final video = await _controller!.stopRecording();
// 处理录制的视频,例如保存到本地或显示在界面上
print('Recording stopped: ${video.path}');
}
}
3. 其他功能
penguin_camera
还提供了其他一些功能,例如:
- 闪光灯控制:你可以使用
setFlashMode
方法来控制闪光灯的模式(自动、开启、关闭)。 - 相机分辨率:你可以通过
setResolution
方法来设置相机的分辨率。
4. 注意事项
- 在使用相机功能时,确保在
AndroidManifest.xml
和Info.plist
中添加必要的权限。 - 由于相机功能依赖于设备硬件,建议在真实设备上进行测试。
5. 示例代码
以下是一个完整的示例代码,展示了如何使用 penguin_camera
插件实现拍照和切换摄像头功能:
import 'package:flutter/material.dart';
import 'package:penguin_camera/penguin_camera.dart';
class CameraScreen extends StatefulWidget {
[@override](/user/override)
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
PenguinCameraController? _controller;
[@override](/user/override)
void initState() {
super.initState();
_controller = PenguinCameraController();
}
[@override](/user/override)
void dispose() {
_controller?.dispose();
super.dispose();
}
void _takePicture() async {
if (_controller != null) {
final image = await _controller!.takePicture();
print('Picture taken: ${image.path}');
}
}
void _switchCamera() {
if (_controller != null) {
_controller!.switchCamera();
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Camera Example'),
),
body: Column(
children: [
Expanded(
child: PenguinCamera(
controller: _controller,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(Icons.camera),
onPressed: _takePicture,
),
IconButton(
icon: Icon(Icons.switch_camera),
onPressed: _switchCamera,
),
],
),
],
),
);
}
}
void main() => runApp(MaterialApp(
home: CameraScreen(),
));