Flutter身份验证插件inapp_flutter_kyc的使用

Flutter身份验证插件inapp_flutter_kyc的使用

简介

inapp_flutter_kyc 是一个强大且易于使用的插件,为您的Flutter应用程序带来了必要的“了解您的客户”(KYC)功能。

功能

  • 活体检测:通过设备摄像头进行实时活体检测,确保用户在KYC过程中实际存在,防止欺诈企图。
  • 身份证扫描:利用先进的计算机视觉技术从护照、驾照、国家身份证等官方识别文件中提取信息,加速KYC过程。
  • 面部匹配:通过比较扫描身份证图像和用户提供的自拍图像之间的面部特征来增强身份验证。
  • 可定制UI:根据您应用的品牌和设计无缝定制用户界面。可以自定义颜色、字体和布局,以创建一致的用户体验。
  • 隐私和安全:优先考虑用户的隐私和安全。该插件实施了行业标准的安全措施,以保护KYC过程中的敏感用户数据。

开始使用

要开始使用 inapp_flutter_kyc 插件,请按照以下简单步骤操作:

  1. 添加插件到项目中:

    flutter pub add inapp_flutter_kyc
    
  2. 对于Android平台:

    • android/app/src/main/AndroidManifest.xml 文件中的 <application></application> 标签之前添加以下内容:
      <uses-feature
          android:name="android.hardware.camera"
          android:required="false" />
      <uses-permission android:name="android.permission.CAMERA" />
      
    • <activity> 标签中添加:
      android:requestLegacyExternalStorage="true"
      
    • android/app/build.gradle 中设置 minSdkVersion 为 23:
      defaultConfig {
          minSdkVersion 23
      }
      
    • android/build.gradle 中将 kotlin 版本设置为 1.8.0:
      buildscript {
          ext.kotlin_version = '1.8.0'
      }
      

身份证扫描

对于身份证扫描,有两种情况需要考虑:关键字和值在同一行(如图1所示)和关键字与值在下一行(如图2所示)。

EkycServices().openImageScanner() 函数中,传递关键字名称和布尔值:

  • 如果关键字和值在同一行,则传递 true
  • 如果关键字和值不在同一行,则传递 false

例如:

Map<String, bool> keyWordData = {
    'Name': false,
    'Date of Birth': true,
    'NID No': false
};

现在将 keyWordData 传递给 EkycServices().openImageScanner()

ExtractedDataFromId? extractedDataFromId;
extractedDataFromId = await EkycServices().openImageScanner(keyWordData);

ExtractedDataFromId 还包含 extractedText。如果OCR文本没有从这些情况解析出来,您也可以从 extractedText 操作文本。

面部匹配

为了运行面部匹配,请执行以下步骤:

  • 下载并解压相关文件夹。
  • 在文件夹目录中运行以下命令(针对Ubuntu):
    python3 face_match.py
    
  • 对于Windows:
    python face_match.py
    
  • 您将在控制台中看到类似以下输出:
    * Running on all addresses (0.0.0.0)
    * Running on http://127.0.0.1:5000
    * Running on http://10.0.3.50:5000
    
  • 复制最后一个地址(例如 http://10.0.3.50:5000),并在 EkycServices().runFaceMatch 函数中传递它以及两个图片路径(自拍照和身份证照片):
    await EkycServices().runFaceMatch("http://10.0.3.50:5000", selfieImage?.path, imageAndText?.imagePath);
    

完整示例Demo

以下是完整的示例代码,展示了如何使用 inapp_flutter_kyc 插件进行活体检测、身份证扫描和面部匹配:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:inapp_flutter_kyc/inapp_flutter_kyc.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'EKYC Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  File? selfieImage;
  ExtractedDataFromId? extractedDataFromId;
  bool? isMatchFace;
  bool isloading = false;
  bool faceMatchButtonPressed = false;
  Map<String, bool> keyWordData = {
    'Name': false,
    'Date of Birth': true,
    'NID No': false
  };

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // 活体检测按钮
            TextButton(
              onPressed: () {
                EkycServices().livenessDetct().then((result) {
                  if (result != null) {
                    print("File path: $result");
                    setState(() {
                      selfieImage = result;
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (context) =>
                                ShowImage(selfieImage: selfieImage)),
                      );
                    });
                  } else {
                    print("Liveness detection failed.");
                  }
                }).catchError((error) {
                  print("Error occurred during liveness detection: $error");
                });
              },
              style: TextButton.styleFrom(
                foregroundColor: Colors.white,
                padding: const EdgeInsets.all(16.0),
                backgroundColor: Colors.blue,
                elevation: 9.0,
                textStyle: const TextStyle(fontSize: 20),
              ),
              child: Text("活体检测"),
            ),
            SizedBox(height: 10),
            // 扫描身份证按钮
            TextButton(
              onPressed: () async {
                extractedDataFromId =
                    await EkycServices().openImageScanner(keyWordData);
                if (extractedDataFromId?.extractedText != null) {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => ShowScannedText(
                              scannedText: extractedDataFromId!.extractedText!,
                              keyNvalue: extractedDataFromId?.keywordNvalue,
                            )),
                  );
                }
              },
              style: TextButton.styleFrom(
                foregroundColor: Colors.white,
                padding: const EdgeInsets.all(16.0),
                backgroundColor: Colors.blue,
                elevation: 9.0,
                textStyle: const TextStyle(fontSize: 20),
              ),
              child: Text("扫描你的身份证"),
            ),
            SizedBox(height: 10),
            // 面部匹配按钮
            TextButton(
                onPressed: () async {
                  if (selfieImage == null) {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(
                        content: Text('请先使用活体检测拍摄一张自拍照'),
                        duration: Duration(seconds: 3),
                      ),
                    );
                  } else if (extractedDataFromId?.imagePath == null) {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(
                        content: Text('身份证上没有检测到人脸'),
                        duration: Duration(seconds: 3),
                      ),
                    );
                  } else {
                    isloading = true;
                    setState(() {
                      faceMatchButtonPressed = true;
                    });

                    isMatchFace = await EkycServices().runFaceMatch(
                        "http://10.255.187.82:5000",
                        selfieImage?.path,
                        extractedDataFromId?.imagePath);
                    setState(() {
                      isloading = false;
                    });
                  }
                },
                style: TextButton.styleFrom(
                  foregroundColor: Colors.white,
                  padding: const EdgeInsets.all(16.0),
                  backgroundColor: Colors.blue,
                  elevation: 9.0,
                  textStyle: const TextStyle(fontSize: 20),
                ),
                child: Text("将身份证与自拍照进行面部匹配")),
            Visibility(
              visible: faceMatchButtonPressed,
              child: Container(
                width: double.infinity,
                child: Card(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(Radius.circular(15))),
                  color: Color(0xFFe3e6f5),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      SizedBox(height: 15),
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          SizedBox(width: 10),
                          (isloading == true)
                              ? SizedBox(
                                  child: CircularProgressIndicator(
                                    strokeWidth: 2,
                                    valueColor:
                                        AlwaysStoppedAnimation(Colors.white),
                                  ),
                                  height: 50.0,
                                  width: 50.0,
                                )
                              : (isMatchFace == true)
                                  ? Icon(
                                      Icons.check_circle_sharp,
                                      size: 40,
                                      color: Color(0xFF9677eca),
                                    )
                                  : Transform.rotate(
                                      angle: 45 * pi / 180,
                                      child: Icon(
                                        Icons.add_circle,
                                        size: 40,
                                        color: Colors.red,
                                      ),
                                    ),
                          SizedBox(width: 5),
                          Expanded(
                            child: Text(
                                (isloading == true)
                                    ? '  正在运行面部匹配...'
                                    : (isMatchFace == true)
                                        ? "成功!身份证与自拍照匹配"
                                        : (isMatchFace == false)
                                            ? "出错了!请重试!"
                                            : '身份证与自拍照不匹配',
                                maxLines: 3,
                                textAlign: TextAlign.left,
                                style: TextStyle(
                                    fontSize: 17,
                                    height: 1.5,
                                    fontWeight: FontWeight.w400)),
                          ),
                          const SizedBox(width: 10),
                        ],
                      ),
                      SizedBox(height: 20),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class ShowImage extends StatelessWidget {
  File? selfieImage;
  ShowImage({this.selfieImage});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("活体检测成功!"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Image.file(selfieImage!),
            SizedBox(height: 20),
            TextButton(
              onPressed: () {
                Navigator.pop(context);
              },
              style: TextButton.styleFrom(
                foregroundColor: Colors.white,
                padding: const EdgeInsets.all(16.0),
                backgroundColor: Colors.blue,
                elevation: 9.0,
                textStyle: const TextStyle(fontSize: 20),
              ),
              child: Text("重新拍摄"),
            ),
          ],
        ),
      ),
    );
  }
}

class FormFieldData {
  final String label;
  String value;

  FormFieldData({required this.label, required this.value});
}

class ShowScannedText extends StatefulWidget {
  String scannedText;
  Map<String, dynamic>? keyNvalue;

  ShowScannedText({required this.scannedText, this.keyNvalue});

  [@override](/user/override)
  State<ShowScannedText> createState() => _ShowScannedTextState();
}

class _ShowScannedTextState extends State<ShowScannedText> {
  List<FormFieldData> formFields = [];
  [@override](/user/override)
  void initState() {
    super.initState();
    if (widget.keyNvalue != null) {
      widget.keyNvalue?.forEach((key, value) {
        formFields.add(FormFieldData(label: key, value: value.toString()));
      });
    }
    print(widget.keyNvalue);
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("已扫描文本"),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Visibility(
            visible: widget.keyNvalue != null,
            child: Expanded(
              child: ListView.builder(
                shrinkWrap: true,
                itemCount: formFields.length,
                itemBuilder: (context, index) {
                  FormFieldData fieldData = formFields[index];
                  return Padding(
                    padding: EdgeInsets.all(16),
                    child: TextFormField(
                      initialValue: fieldData.value,
                      decoration: InputDecoration(labelText: fieldData.label),
                      onChanged: (value) {
                        formFields[index].value = value;
                      },
                    ),
                  );
                },
              ),
            ),
          ),
          Text("完整提取文本", style: TextStyle(fontSize: 20)),
          SizedBox(height: 20),
          Padding(
            padding: const EdgeInsets.only(bottom: 20.0),
            child: Text(widget.scannedText),
          ),
        ],
      ),
    );
  }
}

更多关于Flutter身份验证插件inapp_flutter_kyc的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter身份验证插件inapp_flutter_kyc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 inapp_flutter_kyc 插件进行身份验证的示例代码。这个插件通常用于在Flutter应用中实现用户身份验证流程,特别是“了解你的客户”(KYC)流程。以下示例假设你已经在 pubspec.yaml 文件中添加了 inapp_flutter_kyc 依赖,并且已经运行了 flutter pub get

1. 安装依赖

首先,确保你的 pubspec.yaml 文件包含以下依赖:

dependencies:
  flutter:
    sdk: flutter
  inapp_flutter_kyc: ^最新版本号  # 请替换为实际最新版本号

然后运行 flutter pub get

2. 导入插件并初始化

在你的 Dart 文件中导入插件:

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

3. 配置插件

在你的应用初始化时,配置 InAppFlutterKyc 插件。这通常在你的 main.dart 文件中完成。

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化插件,这里需要替换为你的实际配置信息
  InAppFlutterKyc.initialize(
    apiKey: '你的API_KEY',
    environment: 'sandbox', // 或 'production'
    clientUserId: '用户唯一标识符', // 例如用户ID
    onKYCCompleted: (result) {
      // KYC流程完成后的回调
      print('KYC completed with result: $result');
    },
    onError: (error) {
      // 错误回调
      print('KYC error: $error');
    },
  );

  runApp(MyApp());
}

4. 启动KYC流程

在你的应用中的某个按钮点击事件中启动KYC流程:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('KYC Demo'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              try {
                // 启动KYC流程
                await InAppFlutterKyc.startKYC();
              } catch (e) {
                // 处理异常
                print('Error starting KYC: $e');
              }
            },
            child: Text('Start KYC'),
          ),
        ),
      ),
    );
  }
}

5. 处理回调

main 函数中,我们已经设置了 onKYCCompletedonError 回调。这些回调将在KYC流程完成或出现错误时被调用。

注意事项

  • 确保你已经从 inapp_flutter_kyc 的服务提供者那里获得了正确的 apiKey 和其他必要的配置信息。
  • 在生产环境中,请确保将 environment 设置为 'production'
  • 根据你的业务需求,你可能需要在用户完成KYC后更新你的用户数据或执行其他操作。

这个示例提供了一个基本框架,你可以根据实际需求进一步定制和扩展。

回到顶部