Flutter机器学习扫描插件fl_mlkit_scanning的使用

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

Flutter机器学习扫描插件fl_mlkit_scanning的使用

fl_mlkit_scanning 是一个基于 Google ML Kit 实现的快速稳定扫码功能的 Flutter 插件,支持 Android 和 iOS 平台。该插件依赖于 fl_camera 插件来处理相机相关的功能。

使用

1. 添加相机权限

对于 iOS,需要在 Info.plist 文件中添加相机权限:

<key>NSCameraUsageDescription</key>
<string>是否允许FlMlKitScanning使用你的相机?</string>

2. 预览

以下是一个简单的示例,展示如何使用 FlMlKitScanning 进行扫码预览:

import 'package:flutter/material.dart';
import 'package:fl_mlkit_scanning/fl_mlkit_scanning.dart';
import 'package:toast/toast.dart';

class FlMlKitScanningPage extends StatefulWidget {
  const FlMlKitScanningPage({Key? key}) : super(key: key);

  @override
  _FlMlKitScanningPageState createState() => _FlMlKitScanningPageState();
}

class _FlMlKitScanningPageState extends State<FlMlKitScanningPage> {
  late FlMlKitScanningController controller;

  @override
  void initState() {
    super.initState();
    controller = FlMlKitScanningController();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Fl MlKit Scanning')),
      body: FlMlKitScanning(
        frequency: 500, // 解析频率,单位为毫秒
        camera: controller.camera, // 默认为后摄像头
        resolution: CameraResolution.high, // 预览分辨率
        autoScanning: false, // 是否自动扫描
        overlay: const ScannerLine(), // 显示在预览上层
        updateReset: false, // 更新组件时是否重置相机
        fit: BoxFit.fitWidth, // 相机预览位置
        onFlashChanged: (FlashState state) {
          showToast('$state', duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
        },
        onZoomChanged: (double zoom) {
          showToast('Zoom: $zoom', duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
        },
        uninitialized: Container(
          color: Colors.black,
          alignment: Alignment.center,
          child: const Text('Camera not initialized', style: TextStyle(color: Colors.white)),
        ),
        barcodeFormats: <BarcodeFormat>[BarcodeFormat.qr_code], // 识别类型
        onDataChanged: (AnalysisImageModel data) {
          final List<Barcode>? barcodes = data.barcodes;
          if (barcodes != null && barcodes.isNotEmpty) {
            Navigator.of(context).pop(barcodes);
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.toggleFlash(); // 切换闪光灯
        },
        child: Icon(Icons.flash_on),
      ),
    );
  }
}

3. 方法

以下是 FlMlKitScanningController 提供的一些常用方法:

void func() {
  // 设置识别码类型
  controller.setBarcodeFormat();

  // 识别图片字节
  controller.scanningImageByte();

  // 打开/关闭闪光灯
  controller.setFlashMode();

  // 相机缩放
  controller.setZoomRatio();

  // 暂停扫描
  controller.pauseScanning();

  // 开始扫描
  controller.startScanning();
}

示例代码

以下是一个完整的示例项目,展示了如何使用 fl_mlkit_scanning 插件进行扫码和图片识别:

import 'package:example/image_scanning.dart';
import 'package:example/mlkit_scanning.dart';
import 'package:fl_extended/fl_extended.dart';
import 'package:fl_mlkit_scanning/fl_mlkit_scanning.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MaterialApp(
      navigatorKey: FlExtended().navigatorKey,
      scaffoldMessengerKey: FlExtended().scaffoldMessengerKey,
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(useMaterial3: true),
      darkTheme: ThemeData.dark(useMaterial3: true),
      home: _App(),
      title: 'FlMlKitScanning'));
}

class _App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<_App> {
  List<Barcode> list = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBarText('Fl MlKit Scanning'),
        body: Universal(width: double.infinity, children: [
          ElevatedText(onPressed: openCamera, text: 'Turn on camera scanning'),
          ElevatedText(onPressed: scanImage, text: 'Image scanning'),
          const SizedBox(height: 30),
          CodeBox(list)
        ]));
  }

  void scanImage() {
    push(const ImageScanningPage());
  }

  Future<void> openCamera() async {
    bool permission = await getPermission(Permission.camera);
    if (permission) {
      final List<Barcode>? data = await push(const FlMlKitScanningPage());
      if (data != null) {
        list = data;
        setState(() {});
      }
    }
  }
}

class CodeBox extends StatelessWidget {
  const CodeBox(this.list, {super.key, this.expanded = true});

  final List<Barcode> list;
  final bool expanded;

  @override
  Widget build(BuildContext context) {
    return Universal(
        expanded: expanded,
        isScroll: expanded,
        children: list.builderEntry((MapEntry<int, Barcode> entry) {
          return Column(children: [
            Text('NO.${entry.key + 1}'),
            const SizedBox(height: 6),
            Text('value:${entry.value.value}').sizedBox(width: double.infinity),
            const SizedBox(height: 6),
            Text('type:${entry.value.type}').sizedBox(width: double.infinity),
            Text('boundingBox:${entry.value.boundingBox?.size}')
                .sizedBox(width: double.infinity),
            Text('boundingBox:${entry.value.boundingBox}')
                .sizedBox(width: double.infinity),
            Text('corners:${entry.value.corners}')
                .sizedBox(width: double.infinity),
          ]);
        }));
  }
}

class AppBarText extends AppBar {
  AppBarText(String text, {super.key})
      : super(
            elevation: 0,
            title: BText(text, fontSize: 18, fontWeight: FontWeight.bold));
}

class TextBox extends StatelessWidget {
  final dynamic keyName;
  final dynamic value;

  const TextBox(this.keyName, this.value, {super.key});

  @override
  Widget build(BuildContext context) {
    return Visibility(
        visible: value != null &&
            value.toString().isNotEmpty &&
            value.toString() != 'null',
        child: Container(
            margin: const EdgeInsets.all(10),
            child: Text('$keyName = $value')));
  }
}

Future<bool> getPermission(Permission permission) async {
  PermissionStatus status = await permission.status;
  if (status.isGranted) {
    return true;
  } else {
    status = await permission.request();
    if (!status.isGranted) openAppSettings();
    return status.isGranted;
  }
}

class ElevatedText extends StatelessWidget {
  const ElevatedText({super.key, this.onPressed, required this.text});

  final VoidCallback? onPressed;
  final String text;

  @override
  Widget build(BuildContext context) =>
      ElevatedButton(onPressed: onPressed, child: Text(text));
}

class ElevatedIcon extends StatelessWidget {
  const ElevatedIcon({super.key, this.onPressed, required this.icon});

  final VoidCallback? onPressed;
  final IconData icon;

  @override
  Widget build(BuildContext context) =>
      ElevatedButton(onPressed: onPressed, child: Icon(icon));
}

以上代码展示了如何使用 fl_mlkit_scanning 插件进行扫码和图片识别,并处理相机权限和结果显示。希望对你有所帮助!


更多关于Flutter机器学习扫描插件fl_mlkit_scanning的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter机器学习扫描插件fl_mlkit_scanning的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用fl_mlkit_scanning插件进行机器学习扫描的示例代码。这个插件通常用于二维码、条形码扫描等功能,基于Google的ML Kit。

首先,确保你已经在pubspec.yaml文件中添加了fl_mlkit_scanning依赖:

dependencies:
  flutter:
    sdk: flutter
  fl_mlkit_scanning: ^0.x.x  # 请替换为最新版本号

然后,运行flutter pub get来获取依赖。

接下来,是一个完整的示例代码,展示了如何使用fl_mlkit_scanning进行二维码扫描:

import 'package:flutter/material.dart';
import 'package:fl_mlkit_scanning/fl_mlkit_scanning.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ScannerScreen(),
    );
  }
}

class ScannerScreen extends StatefulWidget {
  @override
  _ScannerScreenState createState() => _ScannerScreenState();
}

class _ScannerScreenState extends State<ScannerScreen> {
  late final BarcodeScannerController _controller = BarcodeScannerController();
  final GlobalKey _qrKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      if (mounted) {
        setState(() {});
      }
      if (_controller.value.data != null) {
        _controller.pause();
        showDialog(
          context: context,
          builder: (context) => AlertDialog(
            title: Text('Scan Result'),
            content: Text(_controller.value.data!),
            actions: <Widget>[
              TextButton(
                child: Text('Close'),
                onPressed: () {
                  Navigator.of(context).pop();
                  _controller.resume();
                },
              ),
            ],
          ),
        );
      }
    });
    _controller.initialize().then((_) {
      if (!mounted) return;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!_controller.value.isInitialized) {
      return Center(child: CircularProgressIndicator());
    }

    return Scaffold(
      appBar: AppBar(
        title: Text('Barcode Scanner'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              flex: 5,
              child: CustomPaint(
                size: Size.infinite,
                painter: CameraPreviewPainter(_controller),
                key: _qrKey,
              ),
            ),
            Expanded(
              flex: 1,
              child: Center(
                child: _controller.value.data == null
                    ? Text('No data captured')
                    : Text(
                        '${_controller.value.data!}',
                        style: TextStyle(color: Colors.red, fontSize: 24),
                      ),
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggleFlash,
        tooltip: 'Toggle Flash',
        child: Icon(_controller.value.isFlashOn ? Icons.flash_on : Icons.flash_off),
      ),
    );
  }

  Future<void> _toggleFlash() async {
    if (!_controller.value.isFlashAvailable) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Flash is not available on this device')),
      );
      return;
    }
    await _controller.toggleFlash();
  }
}

class CameraPreviewPainter extends CustomPainter {
  final BarcodeScannerController controller;

  CameraPreviewPainter(this.controller);

  @override
  void paint(Canvas canvas, Size size) {
    final renderBox = _qrKey.currentContext?.findRenderObject() as RenderBox?;
    if (renderBox == null || !controller.value.isPreviewRunning) {
      return;
    }

    final offset = renderBox.localToGlobal(Offset.zero);
    final size = renderBox.size;
    controller.processImage(
      Image.memory(controller.value.previewBytes!)
          .toByteData(format: ui.ImageByteFormat.rawRgba)!
          .buffer
          .asUint8List(),
      size.width,
      size.height,
    );

    final paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0;

    controller.value.overlay?.forEach((Rect rect) {
      canvas.drawRect(
        Rect.fromLTWH(
          rect.left + offset.dx,
          rect.top + offset.dy,
          rect.width,
          rect.height,
        ),
        paint,
      );
    });
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个用于扫描二维码的界面。注意以下几点:

  1. 初始化BarcodeScannerController:在initState方法中初始化BarcodeScannerController,并添加一个监听器来处理扫描结果。
  2. 显示扫描结果:如果扫描到数据,显示一个对话框来显示扫描结果,并暂停扫描,直到对话框关闭。
  3. 自定义Painter:使用CustomPainter来在相机预览上绘制扫描框(如果有的话)。
  4. 控制闪光灯:提供了一个浮动操作按钮来切换闪光灯。

请确保在实际使用中处理错误和权限请求(如相机权限),并根据需要调整UI和逻辑。

回到顶部