Flutter相机功能插件camera_normal的使用

发布于 1周前 作者 caililin 来自 Flutter

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

如果compileSdkVersiontargetSdkVersion不是29,则可以跳过此部分。

对于Android 10及更高版本,引入了Scoped Storage。如果compileSdkVersiontargetSdkVersion为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

1 回复

更多关于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有所不同,请参考该插件的官方文档进行调整。

回到顶部