Flutter相机功能插件camera_normal的使用
Flutter相机功能插件camera_normal的使用
支持情况
Android | iOS | |
---|---|---|
支持 | SDK 21+ | iOS 12.0+ |
iOS配置
在ios/Runner/Info.plist
文件中添加以下行:
<key>NSCameraUsageDescription</key>
<string>你的相机使用描述</string>
<key>NSMicrophoneUsageDescription</key>
<string>你的麦克风使用描述</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>为了访问您的照片库</string>
- 最低iOS部署目标:12.0
- Xcode 13.2.1或更高版本
- Swift 5
如果要排除armv7架构,可以在Xcode中设置:
Project > Runner > Build Settings > Excluded Architectures > Any SDK > armv7
确保Podfile配置如下:
platform :ios, '12.0' # 或更新的版本
...
# 添加这一行
$iOSVersion = '12.0' # 或更新的版本
post_install do |installer|
# 添加这些行
installer.pods_project.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=*]"] = "armv7"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
end
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# 添加这些行
target.build_configurations.each do |config|
if Gem::Version.new($iOSVersion) > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'])
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
end
end
end
end
Android配置
在android/app/build.gradle
文件中将最小Android SDK版本改为21(或更高):
minSdkVersion 21
如果compileSdkVersion
或targetSdkVersion
不是29,则可以跳过此部分。
对于Android 10及更高版本,引入了Scoped Storage。如果compileSdkVersion
或targetSdkVersion
为29,则可以在AndroidManifest.xml
中添加以下内容以获取资源:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fluttercandies.photo_manager_example">
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-feature
android:name="android.hardware.camera"
android:required="true"/>
<application
android:label="example"
android:name="${applicationName}"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher">
</manifest>
示例代码
以下是完整的示例代码,展示了如何在Flutter应用中使用camera_normal插件:
import 'dart:io';
import 'package:camera_normal/camera_custom.dart';
import 'package:camera_normal/components/language.dart';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as image;
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo Camera',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: false,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final notiBtnTake = ValueNotifier<bool>(false);
final content = ValueNotifier<String>("");
CameraController? cameraController;
final sizeBtn = 55.0;
[@override](/user/override)
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final heightFrameQR = 400.0 * size.aspectRatio;
return Scaffold(
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(content.value),
),
FilledButton(
onPressed: () async {
final result = await CameraNormal().show(context, const CameraLanguage());
print(result);
},
child: const Text('Camera normal'),
),
FilledButton(
onPressed: () async {
final result = await CameraQr().show(context);
setState(() {
content.value = result ?? '';
});
},
child: const Text('Camera QR'),
),
//todo: take cic
FilledButton(
onPressed: () async {
final path = await CameraTakeCIC(
onSetController: (p0) => cameraController = p0,
build: (context) => Stack(
children: [
Positioned(
top: size.height / 2 - heightFrameQR / 2 - 70,
left: size.width * .2,
right: size.width * .2,
child: const Text(
"Include citizen identification card in the frame",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
Align(
alignment: const Alignment(.95, -.92),
child: IconButton(
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.close,
color: Colors.white,
size: 25,
),
),
),
Align(
alignment: const Alignment(0, .8),
child: GestureDetector(
onTap: () => onTakePicture(context, size),
child: Container(
decoration: BoxDecoration(
color: Colors.white38,
borderRadius: BorderRadius.circular(50),
),
width: sizeBtn + 5,
height: sizeBtn + 5,
child: ValueListenableBuilder(
valueListenable: notiBtnTake,
builder: (BuildContext context, value, Widget? child) {
if (value) {
return Center(
child: SizedBox(
width: sizeBtn - 10,
height: sizeBtn - 10,
child: const CircularProgressIndicator(
color: Colors.white,
),
),
);
}
return Icon(
Icons.camera_alt_outlined,
color: Colors.white,
size: sizeBtn / 2,
);
},
),
),
),
),
],
),
).show(context);
if (path is String && context.mounted) {
showDialog(
context: context,
builder: (context) => Dialog(
child: Image.file(File(path)),
),
);
}
},
child: const Text(
'Camera take CIC',
textAlign: TextAlign.center,
),
),
FilledButton(
onPressed: () async {
final path = await SelectImage().show(context, CameraLanguage());
if (path is String && context.mounted) {
showDialog(
context: context,
builder: (context) => Dialog(
child: Image.file(File(path)),
),
);
}
},
child: const Text(
'Show image',
textAlign: TextAlign.center,
),
),
],
),
),
);
}
void onTakePicture(BuildContext context, Size size) async {
if (notiBtnTake.value) return;
notiBtnTake.value = true;
try {
final xFile = await cameraController?.takePicture();
await cameraController?.pausePreview();
if (xFile != null) {
final img = await image.decodeImageFile(xFile.path);
if (img != null) {
final heightImg = img.height;
final widthImg = img.width;
final heightFrame = widthImg * .55;
final widthImage = (widthImg * .9).toInt();
final heightImage = (widthImg * .55).toInt();
final imageCrop = image.copyCrop(
img,
x: 0,
y: (heightImg / 2 - heightFrame / 2).toInt(),
width: widthImage,
height: heightImage,
);
await File(xFile.path).writeAsBytes(image.encodePng(imageCrop));
notiBtnTake.value = false;
if (context.mounted) {
final result = await DialogConfirmImage(context).show(xFile.path, size);
if (result is String && context.mounted) {
Navigator.pop(context, result);
}
}
}
}
} catch (e, s) {
print(e);
print(s);
}
await cameraController?.resumePreview();
notiBtnTake.value = false;
}
}
更多关于Flutter相机功能插件camera_normal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter相机功能插件camera_normal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用camera_normal
插件(注意:通常我们使用的是camera
插件,因为camera_normal
并不是官方的Flutter插件名。不过,我会基于camera
插件给出示例,因为camera_normal
可能是一个非官方或特定项目的插件名,但功能和用法通常类似)来实现相机功能的代码案例。
首先,你需要在你的pubspec.yaml
文件中添加camera
插件的依赖:
dependencies:
flutter:
sdk: flutter
camera: ^0.x.x # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
接下来,你需要请求相机权限,并在你的应用中实现相机功能。以下是一个简单的示例:
1. 请求权限
在AndroidManifest.xml
(对于Android)和Info.plist
(对于iOS)中添加必要的权限请求。
AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
Info.plist
对于iOS,你需要在Info.plist
中添加NSCameraUsageDescription
来请求相机权限。
<key>NSCameraUsageDescription</key>
<string>需要访问相机以拍照</string>
2. Flutter代码实现
创建一个新的Flutter项目或在现有项目中添加以下代码。
main.dart
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
List<CameraDescription> cameras;
CameraController? controller;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Camera App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CameraApp(),
);
}
}
class CameraApp extends StatefulWidget {
@override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
CameraController? _controller;
@override
void initState() {
super.initState();
_controller = cameras.first.createCameraController();
_controller!.initialize().then((_) {
if (!mounted) return;
setState(() {});
});
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_controller == null || !_controller!.value.isInitialized) {
return Container();
}
return Scaffold(
appBar: AppBar(
title: Text('Camera'),
),
body: CameraPreview(_controller!),
floatingActionButton: FloatingActionButton(
onPressed: _takePicture,
tooltip: 'Capture',
child: Icon(Icons.camera_alt),
),
);
}
Future<void> _takePicture() async {
final image = await _controller!.takePicture();
final path = image.path;
// 显示图片或处理图片路径
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Image.file(File(path)),
),
);
}
}
3. 运行应用
确保你的设备或模拟器支持相机功能,并运行你的Flutter应用。你应该能够看到一个相机预览,并可以通过点击浮动操作按钮来拍照。
这个示例展示了如何使用camera
插件来请求相机权限、初始化相机控制器、显示相机预览以及拍照。如果你使用的确实是camera_normal
插件,并且它与camera
插件的API有所不同,请参考该插件的官方文档进行调整。