Flutter活体检测插件oiti_liveness2d的使用

Flutter活体检测插件oiti_liveness2d的使用

OitiHeader.png

Oiti - Liveness 2D插件简介

在这个仓库中,我们找到了Oiti的Liveness 2D插件。该插件允许Flutter应用程序通过访问设备的原生功能来实现活体检测。

插件简介

Pub.dev是Dart语言的包管理器,包含了许多可重用的库和包,适用于Flutter和Dart程序。在Flutter中,插件是一种软件包,它提供对设备原生资源的访问,使Flutter应用能够与iOS和Android操作系统通信,并增加额外的功能,从而改善用户体验。这些插件通常使用Dart编写,并可能包含Kotlin或Swift等本地语言的代码。

最低要求

Android iOS
Gradle: 6.8 iOS: 11

概要

为了简化集成和定制流程,我们为Flutter开发者提供了以下指南:

更新日志

可以通过此链接查看各个版本的新功能。

更多信息

完整示例代码

以下是完整的示例代码,展示了如何在Flutter中使用oiti_liveness2d插件进行活体检测。

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:oiti_liveness2d/common/doccore_success_result.dart';
import 'package:oiti_liveness2d/oiti_liveness2d.dart';
import 'package:oiti_liveness2d_example/widgets/camera_permission.dart';
import 'package:oiti_liveness2d_example/widgets/documentscopy.dart';

void main() {
  runApp(const MaterialApp(
    title: 'Navigation Basics',
    home: MyApp(),
  ));
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late TextEditingController _controller;
  late TextEditingController _controllerT;

  String _platformVersion = 'Unknown';
  var appKey = '';
  var ticket = '';
  var showFeedback = false;
  var resultTitle = '';
  var resultContent = '';
  final environment = Environment.hml;

  // 初始化平台状态
  Future<void> initPlatformState() async {
    String? platformVersion = "10";
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion!;
    });
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    initPlatformState();
    _controller = TextEditingController();
    _controllerT = TextEditingController();
  }

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    _controllerT.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Liveness + Doc - Flutter'),
        ),
        body: ListView(
          children: [
            _liveness2DWidgetOption(
              context,
              'Livneness2D',
              true,
            ),
            _liveness2DWidgetOption(
              context,
              'Livneness2D - S/ Feedback',
              false,
            ),
            const Padding(
              padding: EdgeInsets.all(0),
              child: Text(""),
            ),
            _documentscopyWidgetOption(
              context,
              'Doc Core',
            ),
            _documentscopyWidgetOption(
              context,
              'Doc Core - S/ Feedback',
              showFeedback: false,
            ),
            _documentscopyWidgetOption(
              context,
              'Doc Core Custom',
              themeBuilder: _themeCustomization(),
              instructionWidget: instructionScreen(),
              permissionWidget: CameraPermissionWidget(),
            ),
            _documentscopyWidgetOption(
              context,
              'Doc Intrução & Permissão Custom',
              instructionWidget: instructionScreen(),
              permissionWidget: CameraPermissionWidget(),
            ),
            _documentscopyWidgetOption(
              context,
              'Doc Core Intrução Custom',
              instructionWidget: instructionScreen(),
            ),
            _documentscopyWidgetOption(
              context,
              'Doc Core Permissão Custom',
              permissionWidget: CameraPermissionWidget(),
            ),
            _hideInstructionWidgetOption(
              context,
              'Doc Core - Pular Telas',
              themeBuilder: _themeCustomization(),
            ),
            Padding(
              padding: const EdgeInsets.all(20),
              child: Text(resultContent),
            ),
            Padding(
              padding: const EdgeInsets.all(10),
              child:
                  Text(ticket.isEmpty ? 'Ticket vazio' : 'Ticket disponivel'),
            ),
            Padding(
              padding: const EdgeInsets.all(10),
              child:
                  Text(appKey.isEmpty ? 'AppKey vazia' : 'AppKey disponivel'),
            ),
            ticketSection(),
            appKeySection()
          ],
        ),
      ),
    );
  }

  hideInstructions(
    BuildContext context,
    String appKey,
    String ticket,
    Environment environment,
    ThemeBuilder? themeBuilder,
  ) {
    OitiLiveness2d().checkPermission().then((authorized) => {
          if (authorized)
            {
              OitiLiveness2d()
                  .openDocumentscopy(
                    appKey: appKey,
                    ticket: ticket,
                    themeBuilder: themeBuilder,
                    environment: environment,
                    showFeedback: true,
                  )
                  .then((result) async => {_onDocSuccess(result)})
                  .onError((error, stackTrace) async =>
                      {_onDocError(error as PlatformException)})
                  .catchError((error) async =>
                      {_onDocError(error as PlatformException)})
                  .whenComplete(() => _showAlertDialog(
                        context,
                        resultTitle,
                        resultContent,
                      ))
            }
        });
  }

  Widget _liveness2DWidgetOption(
    BuildContext context,
    String title,
    bool showFeedback,
  ) {
    return Padding(
      padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 5),
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          minimumSize: const Size.fromHeight(50),
        ),
        onPressed: () => Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => OitiLiveness2d.createLiveness2DWidget(
              appKey: appKey,
              environment: environment,
              showFeedback: showFeedback,
              onSuccess: (result) => _onLiveness2DSuccess(result),
              onError: (error) =>
                  _onLiveness2DError(error as PlatformException),
            ),
          ),
        ).whenComplete(
          () => _showAlertDialog(
            context,
            resultTitle,
            resultContent,
          ),
        ),
        child: Text(title),
      ),
    );
  }

  Widget _hideInstructionWidgetOption(
    BuildContext context,
    String title, {
    ThemeBuilder? themeBuilder,
  }) {
    return Padding(
      padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 5),
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          minimumSize: const Size.fromHeight(50),
        ),
        onPressed: () => hideInstructions(
          context,
          appKey,
          ticket,
          environment,
          themeBuilder,
        ),
        child: Text(title),
      ),
    );
  }

  Widget _documentscopyWidgetOption(
    BuildContext context,
    String title, {
    ThemeBuilder? themeBuilder,
    Widget? instructionWidget,
    Widget? permissionWidget,
    bool showFeedback = true,
  }) {
    return Padding(
      padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 5),
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          minimumSize: const Size.fromHeight(50),
        ),
        onPressed: () => Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => OitiLiveness2d.createDocumentscopyWidget(
              appKey: appKey,
              ticket: ticket,
              environment: environment,
              showFeedback: showFeedback,
              themeBuilder: themeBuilder,
              onSuccess: (result) => _onDocSuccess(result),
              onError: (error) => _onDocError(error as PlatformException),
              instructionWidget: instructionWidget,
              permissionWidget: permissionWidget,
            ),
          ),
        ).whenComplete(
          () => _showAlertDialog(
            context,
            resultTitle,
            resultContent,
          ),
        ),
        child: Text(title),
      ),
    );
  }

  Widget appKeySection() {
    return Padding(
      padding: const EdgeInsets.only(left: 20, right: 20, bottom: 45),
      child: TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          labelText: 'App Key',
        ),
        obscureText: false,
        controller: _controller,
        onChanged: (value) => chageAppKey(value),
        onSubmitted: (value) => _pasteAppKey(),
      ),
    );
  }

  _pasteAppKey() {
    setState(() => appKey = _controller.text);
    _controller.text = appKey;
  }

  chageAppKey(String appkey) {
    setState(() => appKey = appkey);
    _controller.text = appkey;
  }

  Widget instructionScreen() {
    return DocumentscopyWidget(
      onError: (error) => _onDocError(error as PlatformException),
      onSuccess: (result) => _onDocSuccess(result),
    );
  }

  Widget ticketSection() {
    return Padding(
      padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10),
      child: TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          labelText: 'Ticket',
        ),
        obscureText: false,
        controller: _controllerT,
        onChanged: (value) => chageTicket(value),
        onSubmitted: (value) => _pasteTicket(),
      ),
    );
  }

  _pasteTicket() {
    setState(() => ticket = _controllerT.text);
    _controllerT.text = ticket;
  }

  chageTicket(String ticketS) {
    setState(() => ticket = ticketS);
    _controllerT.text = ticketS;
  }

  Future<void> _showAlertDialog(
      BuildContext context, String resultType, String content) async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Resultado: $resultType'),
          content: Text(content),
          actions: <Widget>[
            TextButton(
              child: const Text('OK'),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ],
        );
      },
    );
  }

  /// Liveness 3D Callbacks
  _onLiveness2DSuccess(LivenessSuccessResult result) {
    resultTitle = 'Sucesso';
    resultContent =
        'Valid: ${result.valid}\nCodID: ${result.codId}\nCause: ${result.cause}\nProtocol: ${result.protocol}\n';
  }

  _onLiveness2DError(PlatformException? error) {
    resultTitle = 'Error';
    resultContent = 'Cause: ${error?.message}';
  }

  _onDocSuccess(DocCoreSuccessResult result) {
    resultTitle = 'Sucesso';
    resultContent = result.message;
  }

  _onDocError(PlatformException? error) {
    resultTitle = 'Error';
    resultContent = 'Cause: ${error?.message}';
  }

  ThemeBuilder _themeCustomization() {
    return ThemeBuilder()
      ..setCaptureBackgroundColor = "#ff1d0d"
      ..setInstructionBackgroundColor = "#123456"
      ..setInstructionBackButtonColorsIcon = "#789abc"
      ..setInstructionBackButtonColorsBackground = "#def123"
      ..setInstructionBackButtonColorsBorder = "#456789"
      ..setInstructionLoadingColor = "#abc789"
      ..setInstructionBottomSheetColor = "#345678"
      ..setInstructionBottomSheetRadius = 8
      ..setInstructionTitleText = "Your Title"
      ..setInstructionTitleColor = "#234567"
      ..setInstructionTitleFont = "Roboto"
      ..setInstructionCaptionText = "Your Caption"
      ..setInstructionCaptionColor = "#345678"
      ..setInstructionCaptionFont = "Arial"
      ..setInstructionDocOptionBackgroundColor = "#456789"
      ..setInstructionDocOptionTitleText = "Document Option"
      ..setInstructionDocOptionTitleColor = "#567890"
      ..setInstructionDocOptionTitleFont = "Courier New"
      ..setInstructionDocOptionBorderColor = "#678901"
      ..setInstructionDocOptionBorderWidth = 2
      ..setInstructionDocOptionBorderRadius = 4
      ..setInstructionDocOptionBackgroundColor = "#456789"
      ..setInstructionDocOptionTitleText = "Document Option"
      ..setInstructionDocOptionTitleColor = "#567890"
      ..setInstructionDocOptionTitleFont = "Courier New"
      ..setInstructionDocOptionBorderColor = "#678901"
      ..setInstructionDocOptionBorderWidth = 2
      ..setInstructionDocOptionBorderRadius = 4
      ..setInstructionEnvOptionBackgroundColor = "#789012"
      ..setInstructionEnvOptionTitleText = "Environment Option"
      ..setInstructionEnvOptionTitleColor = "#890123"
      ..setInstructionEnvOptionTitleFont = "Helvetica"
      ..setInstructionEnvOptionBorderColor = "#901234"
      ..setInstructionEnvOptionBorderWidth = 3
      ..setInstructionEnvOptionBorderRadius = 6
      ..setInstructionContinueButtonBackgroundColor = "#234567"
      ..setInstructionContinueButtonHighlightedBackgroundColor = "#345678"
      ..setInstructionContinueButtonBorderColor = "#456789"
      ..setInstructionContinueButtonHighlightedBorderColor = "#567890"
      ..setInstructionContinueButtonContentColor = "#678901"
      ..setInstructionContinueButtonHighlightedContentColor = "#789012"
      ..setInstructionContinueButtonTextColor = "#890123"
      ..setInstructionContinueButtonFont = "Arial"
      ..setCaptureInstructionGuideReviewText = "Review"
      ..setLoadingBackgroundColor = "#901234"
      ..setLoadingSpinnerColor = "#012345"
      ..setLoadingSpinnerWidth = 2
      ..setLoadingSpinnerScale = 1
      ..setCaptureBackgroundColor = "#123456"
      ..setTextFront = "Front Text"
      ..setTextBack = "Back Text"
      ..setCaptureInstructionGuideTextFront = "Front Guide Text"
      ..setCaptureInstructionGuideTextBack = "Back Guide Text"
      ..setTextOk = "OK Text"
      ..setCaptureTakeNewPictureButtonText = "Take New Picture"
      ..setCaptureInstructionGuideTextColor = "#345678"
      ..setTextConfirmation = "Confirmation Text"
      ..setBackgroundOkColor = "#456789"
      ..setCaptureBackButtonIcon = "back_icon.png"
      ..setCaptureBackButtonColorsIcon = "#567890"
      ..setCaptureBackButtonColorsBackground = "#678901"
      ..setCaptureBackButtonColorsBorder = "#789012"
      ..setCaptureCloseButtonColorsIcon = "#890123"
      ..setCaptureCloseButtonColorsBackground = "#901234"
      ..setCaptureCloseButtonColorsBorder = "#012345"
      ..setCaptureFrontIndicatorColor = "#123456"
      ..setCaptureFrontIndicatorFocusedStateColor = "#234567"
      ..setCaptureFrontIndicatorUnfocusedStateColor = "#345678"
      ..setCaptureBackIndicatorColor = "#456789"
      ..setCaptureBackIndicatorFocusedStateTextColor = "#567890"
      ..setCaptureBackIndicatorUnfocusedStateTextColor = "#678901"
      ..setCaptureInstructionTextColor = "#789012"
      ..setCapturePreviewBorderColorForCapture = "#234567"
      ..setCapturePreviewBorderColorForUncapturedState = "#345678"
      ..setCaptureCaptureButtonHighlightedStateColorsIcon = "#456789"
      ..setCaptureCaptureButtonHighlightedStateColorsBackground = "#567890"
      ..setCaptureCaptureButtonHighlightedStateColorsBorder = "#678901"
      ..setCaptureCaptureButtonNormalStateColorsIcon = "#789012"
      ..setCaptureCaptureButtonNormalStateColorsBackground = "#890123"
      ..setCaptureCaptureButtonNormalStateColorsBorder = "#901234"
      ..setCaptureCaptureButtonDisabledStateColorsIcon = "#012345"
      ..setCaptureCaptureButtonDisabledStateColorsBackground = "#123456"
      ..setCaptureCaptureButtonDisabledStateColorsBorder = "#234567"
      ..setCaptureBottomSheetShapeColor = "#345678"
      ..setCaptureBottomSheetShapeCornerRadius = 10
      ..setCaptureTakeNewPictureButtonHighlightedStateColorsText = "#456789"
      ..setCaptureTakeNewPictureButtonHighlightedStateColorsBackground =
          "#567890"
      ..setCaptureTakeNewPictureButtonHighlightedStateColorsBorder = "#678901"
      ..setCaptureTakeNewPictureButtonNormalStateColorsText = "#789012"
      ..setCaptureTakeNewPictureButtonNormalStateColorsBackground = "#890123"
      ..setCaptureTakeNewPictureButtonNormalStateColorsBorder = "#901234"
      ..setCaptureTakeNewPictureButtonDisabledStateColorsText = "#012345"
      ..setCaptureTakeNewPictureButtonDisabledStateColorsBackground = "#123456"
      ..setCaptureTakeNewPictureButtonDisabledStateColorsBorder = "#234567"
      ..setCaptureUsePictureButtonText = "Use Picture"
      ..setCaptureUsePictureButtonConfirmationText = "Confirm"
      ..setCaptureUsePictureButtonHighlightedStateColorsText = "#345678"
      ..setCaptureUsePictureButtonHighlightedStateColorsBackground = "#456789"
      ..setCaptureUsePictureButtonHighlightedStateColorsBorder = "#567890"
      ..setCaptureUsePictureButtonNormalStateColorsText = "#678901"
      ..setCaptureUsePictureButtonNormalStateColorsBackground = "#789012"
      ..setCaptureUsePictureButtonNormalStateColorsBorder = "#890123"
      ..setCaptureUsePictureButtonDisabledStateColorsText = "#901234"
      ..setCaptureUsePictureButtonDisabledStateColorsBackground = "#012345"
      ..setCaptureUsePictureButtonDisabledStateColorsBorder = "#123456"
      ..setResultBackgroundColorSuccess = "#234567"
      ..setResultBackgroundColorError = "#345678"
      ..setResultBackgroundColorTryAgain = "#456789"
      ..setResultMessageSuccess = "Success Message"
      ..setResultMessageError = "Error Message"
      ..setResultMessageTryAgain = "Try Again Message"
      ..setResultMessageColorSuccess = "#567890"
      ..setResultMessageColorError = "#678901"
      ..setResultMessageColorTryAgain = "#789012"
      ..setResultTryAgainButtonText = "Try Again"
      ..setResultTryAgainButtonHighlightedStateColorsText = "#890123"
      ..setResultTryAgainButtonHighlightedStateColorsBackground = "#901234"
      ..setResultTryAgainButtonHighlightedStateColorsBorder = "#012345"
      ..setResultTryAgainButtonNormalStateColorsText = "#123456"
      ..setResultTryAgainButtonNormalStateColorsBackground = "#234567"
      ..setResultTryAgainButtonNormalStateColorsBorder = "#345678";
  }
}

更多关于Flutter活体检测插件oiti_liveness2d的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter活体检测插件oiti_liveness2d的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于Flutter中的活体检测插件oiti_liveness2d的使用,下面是一个简单的代码示例,展示如何在Flutter应用中集成和使用该插件。请注意,为了运行此代码,你需要在pubspec.yaml文件中添加oiti_liveness2d依赖,并确保已经正确配置好Android和iOS平台的权限和依赖。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加oiti_liveness2d依赖:

dependencies:
  flutter:
    sdk: flutter
  oiti_liveness2d: ^最新版本号  # 请替换为最新的版本号

然后运行flutter pub get来安装依赖。

2. 配置权限

确保在AndroidManifest.xmlInfo.plist中配置了必要的相机和面部识别权限。

Android (AndroidManifest.xml)

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

iOS (Info.plist)

<key>NSCameraUsageDescription</key>
<string>App需要访问相机进行活体检测</string>
<key>NSFaceIDUsageDescription</key>
<string>App需要访问面部识别进行活体检测</string>

3. 使用插件

下面是一个简单的Flutter应用示例,展示如何使用oiti_liveness2d插件进行活体检测:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 活体检测示例',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LivenessDetectionScreen(),
    );
  }
}

class LivenessDetectionScreen extends StatefulWidget {
  @override
  _LivenessDetectionScreenState createState() => _LivenessDetectionScreenState();
}

class _LivenessDetectionScreenState extends State<LivenessDetectionScreen> {
  OitiLiveness2dController? _controller;
  bool _isDetecting = false;
  String _result = '';

  @override
  void initState() {
    super.initState();
    _initLivenessDetection();
  }

  Future<void> _initLivenessDetection() async {
    _controller = OitiLiveness2dController();
    _controller!.initCamera().then((_) {
      setState(() {
        _isDetecting = true;
      });
    }).catchError((error) {
      print('初始化相机失败: $error');
    });

    _controller!.setListener((result) {
      setState(() {
        _result = result ? '活体检测通过' : '活体检测失败';
        _isDetecting = false;  // 假设单次检测后停止
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('活体检测示例'),
      ),
      body: Center(
        child: _isDetecting
            ? Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  CircularProgressIndicator(),
                  SizedBox(height: 20),
                  Text('正在检测中...'),
                ],
              )
            : Stack(
                alignment: Alignment.center,
                children: <Widget>[
                  Container(
                    height: 300,
                    width: 300,
                    decoration: BoxDecoration(
                      color: Colors.black,
                    ),
                    child: _controller!.previewWidget,
                  ),
                  ElevatedButton(
                    onPressed: _startDetection,
                    child: Text('开始检测'),
                  ),
                  SizedBox(height: 20),
                  Text(_result),
                ],
              ),
      ),
    );
  }

  void _startDetection() {
    if (_controller!.isCameraReady) {
      _controller!.startDetection();
    } else {
      print('相机未准备好');
    }
  }

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

注意事项

  1. 权限请求:在实际应用中,你需要在开始检测前请求用户授权相机权限。这可以通过permission_handler等插件来实现。
  2. UI优化:示例代码中的UI较为简单,你可以根据实际需求进行优化。
  3. 错误处理:示例代码中对错误处理较为简单,实际应用中应添加更详细的错误处理逻辑。

希望这个示例能帮你快速上手oiti_liveness2d插件的使用。如果有更多问题,欢迎继续提问!

回到顶部