目录
本教程教大家如何在flutter中使用摄像头。
可随意转载!
创建项目
命令行执行以下命令:
flutter create -a java -i objc ability_camera
创建flutter项目,并且指定Android平台的编程语言为Java,iOS平台的编程语言为Objective-C。
打开Android Studio,选择Open an exist Android Studio Project
配置项目
编辑flutter配置文件pubspec.yaml ,如下图:
注意:本教程需要path_provider, video_player, camera三个库。
调整系统设置
1. Android调整minSdkVersion
2. iOS设置开启摄像头&麦克风提示
<key>NSCameraUsageDescription</key>
<string>请授权打开摄像头</string>
<key>NSMicrophoneUsageDescription</key>
<string>请授权打开麦克风</string>
编写main.dart
1. import 依赖包
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';
2. 定义类变量
CameraController controller;
String imagePath;
String videoPath;
VideoPlayerController videoController;
VoidCallback videoPlayerListener;
bool enableAudio = true;
3. 定义stateful widget初始化函数
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
4. 定义析构函数
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
5. 定义WidgetsBindingObserver生命周期
在初始化和析构函数中我们看到了新的知识:WidgetBinding。它是来自WidgetsBindingObserver。如下:
class _CameraExampleHomeState extends State<CameraExampleHome>
with WidgetsBindingObserver {
...
}
除了初始化和析构之外,我们通过重载它的方法: didChangeAppLifecycleState,来获取Widget的生命周期回调。
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// App state changed before we got the chance to initialize.
if (controller == null || !controller.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
controller?.dispose();
} else if (state == AppLifecycleState.resumed) {
if (controller != null) {
onNewCameraSelected(controller.description);
}
}
}
6. 定义Camera的视窗Widget
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Tap a camera',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
fontWeight: FontWeight.w900,
),
);
} else {
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
);
}
}
7. 定义控制按钮Widget
/// Display the control bar with buttons to take pictures and record videos.
Widget _captureControlRowWidget() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: const Icon(Icons.camera_alt),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.videocam),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onVideoRecordButtonPressed
: null,
),
IconButton(
icon: controller != null && controller.value.isRecordingPaused
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? (controller != null && controller.value.isRecordingPaused
? onResumeButtonPressed
: onPauseButtonPressed)
: null,
),
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? onStopButtonPressed
: null,
)
],
);
}
8. 定义麦克风控制Widget
/// Toggle recording audio
Widget _toggleAudioWidget() {
return Padding(
padding: const EdgeInsets.only(left: 25),
child: Row(
children: <Widget>[
const Text('Enable Audio:'),
Switch(
value: enableAudio,
onChanged: (bool value) {
enableAudio = value;
if (controller != null) {
onNewCameraSelected(controller.description);
}
},
),
],
),
);
}
9. 定义选择前置/后置摄像头Widget
/// Display a row of toggle to select the camera (or a message if no camera is available).
Widget _cameraTogglesRowWidget() {
final List<Widget> toggles = <Widget>[];
if (cameras.isEmpty) {
return const Text('No camera found');
} else {
for (CameraDescription cameraDescription in cameras) {
toggles.add(
SizedBox(
width: 90.0,
child: RadioListTile<CameraDescription>(
title: Icon(getCameraLensIcon(cameraDescription.lensDirection)),
groupValue: controller?.description,
value: cameraDescription,
onChanged: controller != null && controller.value.isRecordingVideo
? null
: onNewCameraSelected,
),
),
);
}
}
return Row(children: toggles);
}
10. 定义显示缩略图Widget
/// Display the thumbnail of the captured image or video.
Widget _thumbnailWidget() {
return Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
videoController == null && imagePath == null
? Container()
: SizedBox(
child: (videoController == null)
? Image.file(File(imagePath))
: Container(
child: Center(
child: AspectRatio(
aspectRatio:
videoController.value.size != null
? videoController.value.aspectRatio
: 1.0,
child: VideoPlayer(videoController)),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.pink)),
),
width: 64.0,
height: 64.0,
),
],
),
),
);
}
真机调试
黑色部分为Camera视窗
四个灰色按钮为控制按钮
Enable Audio为麦克控制
单选按钮为选择后置/前置摄像头
右下角为预览窗口
机型适配问题
1. Android会报如下错误(不影响使用)
Access denied finding property “vendor.camera.aux.packagelist”