readid_flutter 0.1.2-beta copy "readid_flutter: ^0.1.2-beta" to clipboard
readid_flutter: ^0.1.2-beta copied to clipboard

Flutter plugin for ReadID.

example/lib/main.dart

/*
 * ReadID Flutter SDK Sample Code
 *
 * Copyright © 2024 Inverid B.V. All rights reserved.
 */

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:readid_example/document_selection.dart';
import 'package:readid_example/strings.dart';
import 'package:readid_example/ui/dropdown_widget.dart';
import 'package:readid_example/ui/generic_dialog.dart';
import 'package:readid_flutter/readid.dart';

import 'handle_response.dart';

void main() async {
  runApp(
    MaterialApp(
      theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, brightness: Brightness.light),
          fontFamily: Strings.roboto,
          useMaterial3: true),
      darkTheme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, brightness: Brightness.dark),
          fontFamily: Strings.roboto,
          useMaterial3: true),
      themeMode: ThemeMode.system,
      home: const ReadIDApp(),
    ),
  );
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}

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

  @override
  State<ReadIDApp> createState() => _ReadIDAppState();
}

class _ReadIDAppState extends State<ReadIDApp> {
  /// The possible flows the user can choose in this example app.
  final List<ReadIDFlow> _flows = [NFCWithAccessControlFlow(), VIZOnlyOnePageFlow()];

  /// The text that is displayed when the flow is finished.
  String? _statusText;

  /// The document selection the user selected from the dropdown menu depending on the flow
  DocumentSelection _documentSelection = DocumentTypeSelection(DocumentType.passport);

  /// The ReadID plugin, you use this to start a ReadID flow and verify identity documents.
  final _readidPlugin = ReadID();

  /// The Visual Inspection Zone result. This will be available after a ReadID Session is complete.
  VIZResult? _vizResult;

  /// When an only VIZ Flow is chosen, it's possible to chain the result to scan the NFC chip.
  bool _shouldShowFlowChainingButton = false;

  /// Will be populated with the face image on the identity document, if available.
  Uint8List? _image;

  // Put your API credentials in a json file and pass them using --dart-define-from-file
  final String _baseUrl = const String.fromEnvironment('BASE_URL');
  // Note: You should have different access keys for each server environment
  final String _accessKey = const String.fromEnvironment('ACCESS_KEY');

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      body: ListView(
        children: [
          DropdownWidget(
            flows: _flows,
            documentSelection: _documentSelection,
            onSubmit: (ReadIDFlow selectedFlow, DocumentSelection documentSelection) {
              _documentSelection = documentSelection;
              _startReadID(selectedFlow);
            },
            onFlowChain: () {
              _continueFlowChain();
            },
            statusText: _statusText,
            shouldShowFlowChain: _shouldShowFlowChainingButton,
            image: _image,
          )
        ],
      ),
    );
  }

  // Method to start the ReadID process
  // It takes two parameters: flow and session
  // flow is the ReadIDFlow object that will be used in the ReadID process
  // session is the ReadIDSession object that will be used in the ReadID process in case of flow chaining
  void _startReadID(ReadIDFlow flow, [ReadIDSession? session]) async {
    ReadIDResult? readIDResult;

    try {
      // If flow is an instance of NFCWithAccessControlFlow,
      // configure it with _configureNFCWithAccessControlFlow method
      if (flow is NFCWithAccessControlFlow) {
        _configureNFCWithAccessControlFlow(flow);
      }

      // If flow is an instance of VIZOnlyOnePageFlow,
      // configure it with _configureVIZOnlyOnePageFlow method
      if (flow is VIZOnlyOnePageFlow) {
        _configureVIZOnlyOnePageFlow(flow);
      }

      // Start the ReadID process with the specified flow
      // The result of the ReadID process will be stored in readIDResult
      readIDResult = await _readidPlugin.startReadID(flow: flow);
    } on Failure catch (e) {
      handleFailure(e);
    } on PlatformException catch (e) {
      handleErrorResponse(e);
    } finally {
      updateUI(readIDResult: readIDResult);
    }
  }

  // Method to configure NFCWithAccessControlFlow
  // It takes one parameter: flow, which is an instance of NFCWithAccessControlFlow
  void _configureNFCWithAccessControlFlow(NFCWithAccessControlFlow flow) {
    flow
      ..baseUrl = _baseUrl
      ..accessKey = _accessKey

      // Indicates whether an NFC result screen will be shown.
      ..shouldShowNFCResult = true

      // If enabled, tries to read images(face or signature)
      // If no images are needed, set to false to speed up reading process
      ..shouldReadImages = true

      // Shows a skip reading button after the specified number of attempts.
      // To hide or never show the skip button, set it to -1.
      // To show skip button immediately, set it to 0.
      // Set to a value > 0, to show skip button after specified number of attempts.
      ..allowSkipReadingAfterAttempts = 2

      // Add the type of documents to be processed here.
      ..allowedDocumentTypes = List.from({_documentSelection.value});
  }

  /// Method to configure VIZOnlyOnePageFlow.
  /// We assume that after this one page flow, we either go to the NFC flow if possible, or fall back to the optical flow.
  void _configureVIZOnlyOnePageFlow(VIZOnlyOnePageFlow flow) {
    flow
      ..baseUrl = _baseUrl
      ..accessKey = _accessKey

      // Add the type of documents to be processed here.
      // For a faster VIZ capture, it is recommended to keep a single document type, as it avoids the VIZ capture algorithm having to check for multiple document types.
      ..allowedPageTypes = List.from({_documentSelection.value})

      // We intend to followup with an NFCOnlyFlow, so do not commit the session yet. Note that ReadIDSessions can only be committed once.
      ..preventSessionCommit = true

      // Configures the preferred MRZ validation, the default is 'accessControl'.
      ..mrzValidation = MRZValidation.accessControl

      // Configure this property to specify if the qr code detection is required. This can be configured to .required if the QR code detection is mandatory
      ..qrCodeFeatureRequirement = FeatureRequirement.notRequired

      // If this is true, a document selection screen will be displayed.
      // This significantly improve processing time as only the selected document type has to be processed.
      ..shouldShowDocumentSelection = false

      // Set time interval here if it is required to show manual capture button after the specified time interval. Set it to 0 to show the button instantly.
      // Setting this to -1 will never show the manual capture button.
      ..allowManualCaptureAfterTimeout = -1

      // Indicates whether an screen with the VIZ result will be shown. Usually this is only used during development.
      ..shouldShowVIZResult = false

      // Use the following properties to control the quality of the resulting capture.
      // You might want to disable these during development of your app for faster testing.
      ..shouldRequireNoGlareOnDocument = true
      ..shouldRequireSharpImage = true
      ..shouldRequireNoFingerOnDocument = true;
  }

  /// Continues the VIZ flow to the NFC flow.
  void _continueFlowChain() async {
    ReadIDSession? session = _vizResult?.readIDSession;

    /// Retrieve the NFC access key used to access the identity document.
    NFCAccessKey? key = _vizResult?.nfcAccessKey;
    if (session == null || key == null) {
      return;
    }

    NFCOnlyFlow nfcFlow = NFCOnlyFlow(key, _vizResult?.documentInfo);
    nfcFlow
      ..baseUrl = _baseUrl
      ..accessKey = _accessKey
      ..readIDSession = session;

    _startReadID(nfcFlow, session);
  }

  /// Updates the UI to show information from the session, if there is any.
  void updateUI({required ReadIDResult? readIDResult}) {
    if (readIDResult == null) {
      _statusText = null;
      _image = null;
      setState(() {});
    }
    final vizResult = readIDResult?.vizResult;
    final nfcResult = readIDResult?.nfcResult;

    if (nfcResult != null) {
      // this is how to retrieve the data from the result, please check the documentation
      // for more details on where to find the data you are interested in
      _statusText = "${nfcResult.readIDSession?.documentContent?.secondaryIdentifier} "
          "${nfcResult.readIDSession?.documentContent?.primaryIdentifier}"
          "\n${Strings.verificationResult} ${nfcResult.readIDSession?.consolidatedIdentityData?.chipCloneDetection.value}";
      _image = nfcResult.faceImage;
      if (nfcResult.readIDSession != null) {
        releaseSession(nfcResult.readIDSession!);
      }
      _vizResult = null;
      _shouldShowFlowChainingButton = false;
    } else if (vizResult != null) {
      // keep a reference for potential flow chain
      _vizResult = vizResult;
      _shouldShowFlowChainingButton = true;
      _statusText = "${vizResult.readIDSession?.documentContent?.secondaryIdentifier} "
          "${vizResult.readIDSession?.documentContent?.primaryIdentifier}";
    } else {
      _statusText = Strings.statusNoResult;
    }

    setState(() {});
  }

  /// Always call [release] on the ReadIDSession when the session is complete to prevent memory leaks.
  void releaseSession(ReadIDSession session) {
    session.release();
  }

  void handleFailure(Failure failure) {
    handleResponse(failure, context);
  }

  void handleErrorResponse(PlatformException e) {
    showGenericDialog(
        context: context, title: Strings.genericErrorTitle, message: e.message ?? Strings.genericErrorMessage);
  }

  /// Call resumeReadID to make sure that the user can continue the session should your Flutter activity be killed on Android.
  void resumeReadID() async {
    await Future.delayed(const Duration(seconds: 1));

    ReadIDResult result = await _readidPlugin.resumeReadID();
    if (result.vizResult == null) {
      Fluttertoast.showToast(msg: "No previous VIZ result");
      debugPrint("No previous VIZ result");
    } else {
      debugPrint("Previous VIZ result: ${result.vizResult?.readIDSession?.documentContent?.secondaryIdentifier}");
      Fluttertoast.showToast(
          msg: "Welcome back ${result.vizResult?.readIDSession?.documentContent?.secondaryIdentifier}");
    }
    if (result.nfcResult == null) {
      debugPrint("No previous NFC result");
    } else {
      debugPrint("Previous NFC result");
      _image = result.nfcResult?.faceImage;
      updateUI(readIDResult: result);
    }
  }
}