authme_ekyc_sdk 3.0.1 copy "authme_ekyc_sdk: ^3.0.1" to clipboard
authme_ekyc_sdk: ^3.0.1 copied to clipboard

Flutter plugin for the AuthMe eKYC SDK: OCR-based identity document recognition (Taiwan + 12 other regions), liveness detection, and NFC passport verification on iOS and Android.

example/lib/main.dart

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:authme_ekyc_sdk/authme_ui_config.dart';
import 'package:authme_ekyc_sdk/ekyc_feature.dart';
import 'package:authme_ekyc_sdk/ekyc_locale.dart';
import 'package:authme_ekyc_sdk/ekyc_sdk_plugin_bridge.dart';
import 'package:authme_ekyc_sdk_example/widget/color_setting_panel.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter AuthMe eKYC SDK Example',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const AuthMeHomePage(),
    );
  }
}

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

  @override
  State<AuthMeHomePage> createState() => _AuthMeHomePageState();
}

class FullScreenDisplayPage extends StatelessWidget {
  final Map<String, dynamic> data;
  final Map<String, String> images;

  const FullScreenDisplayPage({
    Key? key,
    required this.data,
    required this.images,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Details'),
        backgroundColor: Colors.black,
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView(
              children: [
                ...data.entries.map((entry) {
                  return ListTile(
                    title: Text(entry.key),
                    subtitle: Text(entry.value.toString()),
                  );
                }).toList(),
                const Divider(),
                if (images['frontCropImage'] != null)
                  Image.network(
                    images['frontCropImage']!,
                    fit: BoxFit.cover,
                    height: 150,
                  ),
                if (images['backCropImage'] != null)
                  Image.network(
                    images['backCropImage']!,
                    fit: BoxFit.cover,
                    height: 150,
                  ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _AuthMeHomePageState extends State<AuthMeHomePage> {
  final Color _buttonColor = const Color(0xFF00C1B6);
  final Color _defaultButtonColor = const Color(0xFFFFFFFF);
  String? _token;
  bool _isLoading = false;
  final AuthmeSdk _authMeSdk = AuthmeSdk();
  late StreamSubscription _resultSubscription;
  String _name = "";
  var timestamp = DateTime.now().millisecondsSinceEpoch;
  bool _isInitialized = false;

  AuthmeLocale _selectedLanguage = AuthmeLocale.zhHant;
  bool _needConfirm = true;
  bool _isResultPageDisplayable = true;
  bool _isResultEditable = true;

  ActionButtonConfig? _actionButtonConfig;
  ActionHintConfig? _actionHintConfig;
  CloseHintConfig? _closeHintConfig;
  BackHintConfig? _backHintConfig;
  StepButtonConfig? _stepButtonConfig;
  bool _showStatement = true;

  @override
  void initState() {
    super.initState();
    _resultSubscription = _authMeSdk.resultStream.listen(
      (result) {
        if (result.containsKey('result')) {
          final resultMap = result['result'];
          if (resultMap is Map) {
            // 取得主要資料層
            final data = resultMap['data'];
            // 取得圖片層
            final images = resultMap['images'];

            if (data is Map) {
              // 只修改主要資料層
              Map<String, dynamic> modifications = Map.from(data);

              // 修改需要更新的部分
              modifications['address'] = 'new address';

              // 確認結果時只傳送主要資料層的修改
              _authMeSdk
                  .confirmScanResult(modifications)
                  .then((_) {
                    log('Scan result confirmed:$modifications');
                  })
                  .catchError((error) {
                    log('Failed to confirm scan result: $error');
                  });

              // 如需檢視掃描圖片,可從 result['result']['images'] 取得 base64 字串自行處理
              log(
                'Scan images keys: ${images is Map ? images.keys.toList() : "none"}',
              );
            }
          }
        } else if (result.containsKey('error')) {
          // 處理錯誤
          log("錯誤: ${result['error']}");
        }
      },
      onError: (error) {
        log("Stream error: $error");
      },
    );
  }

  String normalizeBase64(String base64String) {
    return base64String.replaceAll('\n', '');
  }

  String fixBase64(String base64String) {
    while (base64String.length % 4 != 0) {
      base64String += '=';
    }
    return base64String;
  }

  // Provide your OAuth client credentials at compile time via:
  //   flutter run --dart-define=AUTHME_CLIENT_ID=xxx --dart-define=AUTHME_CLIENT_SECRET=yyy
  // Do NOT hardcode real production credentials in source — they get checked
  // into git and scanned by GitHub secret scanning. The example uses empty
  // strings as defaults so it will fail loudly until the integrator supplies
  // real values from their own AuthMe tenant.
  static const String _kClientId = String.fromEnvironment(
    'AUTHME_CLIENT_ID',
    defaultValue: '',
  );
  static const String _kClientSecret = String.fromEnvironment(
    'AUTHME_CLIENT_SECRET',
    defaultValue: '',
  );

  Future<void> _fetchToken({String eventName = 'mobile'}) async {
    setState(() {
      _isLoading = true;
    });

    if (_kClientId.isEmpty || _kClientSecret.isEmpty) {
      setState(() {
        _isLoading = false;
      });
      print(
        '[AuthmeSdk Example] AUTHME_CLIENT_ID / AUTHME_CLIENT_SECRET '
        'were not provided via --dart-define. Token fetch skipped. '
        'See main.dart comments for usage.',
      );
      return;
    }

    try {
      final response = await http.post(
        Uri.parse('https://auth.authme.com/connect/token'),
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: {
          'client_id': _kClientId,
          'client_secret': _kClientSecret,
          'grant_type': 'client_credentials',
          'scope':
              'AuthmeServices customer_id:FlutterTest$timestamp event_name:$eventName',
        },
      );
      log(response.toString());

      if (response.statusCode == 200) {
        final Map<String, dynamic> jsonResponse = json.decode(response.body);
        setState(() {
          _token = jsonResponse['access_token'];
          _isLoading = false;
        });
        _authMeSdk.refreshToken(_token!);
      } else {
        throw Exception('Failed to load token');
      }
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      print('Error fetching token: $e');
    }
  }

  Future<void> initSDK() async {
    try {
      var serverURL = "https://api.authme.com";
      var activateToken =
          "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmZWF0dXJlcyI6WyJvcmdfbmFtZT1hdXRobWUtd2ViZGVtbyJdLCJleHAiOjE3ODM5MDA4MDAsImlzcyI6ImF1dGhtZS5jb20iLCJuYmYiOjE3NjgxNzYwMDB9.kXWkLXlDSMVkyD2gHF4I77DuHkwEAl18b09H92NvYbzvk2P0013r5W9j40O1mOjZ91J3-XDjQd186mkqwOig_a-95Cz5xYAEse2A_nbsnO6cOS-NUWwD3LQL38mmKMulM0EerHiJwJfo7hXjSwwHyB416uWMGVxgPG6azsAO6gR8iycAzKI8eUFKZWA4Lvfa-sXwtUTSdSJMqNW9icSeGH7LUV2f2aUHi3i75y7GSVUH1wsEeVHOmsNFuzqH8f8innhzHKh2H0ICAyBxqnUbkooi4aiztL-muRD6eK6Nn2OTnqAeXw1390YUpTIKZ7Ya7V7WVw_-yLkr7Q1-DWIiXg";
      var activateCertificate =
          "-----BEGIN CERTIFICATE-----\nMIIEhjCCA26gAwIBAgIUAOZ+C1CekSjJ38NsRKS+qCvW8ZAwDQYJKoZIhvcNAQEL\nBQAwNzELMAkGA1UEBhMCVFcxDzANBgNVBAoTBkF1dGhtZTEXMBUGA1UEAwwOKi4o\nYXV0aG1lKS5jb20wHhcNMjUwNTA2MDgzODUzWhcNMzIwNzA5MDI0NzA2WjAzMQsw\nCQYDVQQGEwJUVzEPMA0GA1UEChMGQXV0aG1lMRMwEQYDVQQDEwphdXRobWUuY29t\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsCTrKRpifJosF9gAyd5I\nmU9Ay3uEVWHrx122ks+JBm9u+1oVtH+AefTK+VRyS0kQ+vrddbSD6gwHPU6zXWV7\n3ZQxv+Kb/6lSZT2q0zhE2yU/QS1bSMwRLJ2CVS5YOoP5kmV/sj4RUHSSGtJHc0VG\nCPvPcsGrN17spXEjAjAHWZRFlXnQ37goQdP/EXwFU/1dIcViDSUzgeQjYItfTDTQ\nJQWffSE4goss4+WolnjcZvMORI1zr4kvecYDbOthhlngmsUykOFkEXI4udVRwzZq\n/qeznv0tSlgDBy0Mi8KkIgMqMZM70WSbL9t2AuW/m0+JWYemVNdKuFDWi3oKz/17\nvQIDAQABo4IBjDCCAYgwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF\nBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFXxdO+tPGt85NoCnjDlz8+s3tEa\nMB8GA1UdIwQYMBaAFMVSKq0CAmAnU4FngTexJCHZnjwfMIGNBggrBgEFBQcBAQSB\ngDB+MHwGCCsGAQUFBzAChnBodHRwOi8vcHJpdmF0ZWNhLWNvbnRlbnQtNjJjY2E3\nOGItMDAwMC0yMTA1LWE3ZjEtZDRmNTQ3ZjgzZTc0LnN0b3JhZ2UuZ29vZ2xlYXBp\ncy5jb20vZDdjNjY2MDY0NWMwNTM5NTE1ZTIvY2EuY3J0MIGCBgNVHR8EezB5MHeg\ndaBzhnFodHRwOi8vcHJpdmF0ZWNhLWNvbnRlbnQtNjJjY2E3OGItMDAwMC0yMTA1\nLWE3ZjEtZDRmNTQ3ZjgzZTc0LnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20vZDdjNjY2\nMDY0NWMwNTM5NTE1ZTIvY3JsLmNybDANBgkqhkiG9w0BAQsFAAOCAQEAOR3gAZ1f\nQL3DVWAEFlH9gyKgOht74+xX/N3qmVzAl6TzEwwy70uxURbzItTbfPhsjBdeLcWj\n0jQ4SnedX4NJzck+lctq4UXFZ38cthUxKNX77Of5nuzpqpbrpCGxE3cLC+4u7F8g\n49AMFMopxDWeUgbdxDGoFgBH0jRs5fQYNfX2zrNklyxb2e9cZsOsMRbIsjBub5th\nlquYVZUe1BL9p9T5FrY+nKJLuB9RoXcRNCTyLClTFoK4e7v42qq5QhaFVIZoAPSw\nRByCxKsisPr0+G4h8Z0+edETImjiW+Fh+wJnAcMYqxfGD/J5LtSiYM5L9qkd0rP0\nUMpQuhXM1tAGxA==\n-----END CERTIFICATE-----\n\n";

      final uiConfig = AuthMeUIConfig(
        actionButton: _actionButtonConfig,
        actionHint: _actionHintConfig,
        closeHint: _closeHintConfig,
        backHint: _backHintConfig,
        stepButton: _stepButtonConfig,
        showStatement: _showStatement,
      );

      await _authMeSdk.initialize(
        serverURL,
        activateToken,
        activateCertificate,
        showStatement: _showStatement,
      );
      _authMeSdk.setLocale(AuthmeLocale.zhHant);
    } on PlatformException catch (e) {
      // 捕捉 PlatformException 並進行處理
      log("Failed to initialize AuthMe SDK: ${e.message}");
      // 顯示錯誤訊息給用戶,或進一步處理錯誤
    } catch (e) {
      // 捕捉其他潛在的例外
      log("An unexpected error occurred: ${e.runtimeType}");
    }
  }

  Future<void> _runFeature(AuthmeFeature feature) async {
    try {
      final eventName = feature == AuthmeFeature.TWIDFraud
          ? 'id-card-fraud'
          : 'mobile';
      await _fetchToken(eventName: eventName);
      if (_token == null) {
        log('Token 不可用');
        return;
      }
      await _authMeSdk.startFeature(
        _token!,
        feature,
        needConfirm: _needConfirm,
        isResultPageDisplayable: _isResultPageDisplayable,
        isResultEditable: _isResultEditable,
        requestCode: 999,
        captureTimeout: 0,
      );
    } on PlatformException catch (e) {
      log("Failed to start feature ${feature.name}: ${e.message}");
    } catch (e) {
      log("Unexpected error in ${feature.name}: ${e.runtimeType}");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text("Flutter AuthMe eKYC SDK Example"),
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : SingleChildScrollView(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    _colorPanel(),
                    const SizedBox(height: 20),
                    _buildSettingsCard(),
                    const SizedBox(height: 20),
                    _buildFeaturesCard(),
                  ],
                ),
              ),
            ),
    );
  }

  Widget _colorPanel() {
    if (Platform.isIOS) {
      return Card(
        margin: EdgeInsets.zero,
        color: Colors.white,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              ColorSettingsPanel(
                actionButtonConfig: _actionButtonConfig,
                actionHintConfig: _actionHintConfig,
                closeHintConfig: _closeHintConfig,
                backHintConfig: _backHintConfig,
                stepButtonConfig: _stepButtonConfig,
                showStatement: _showStatement,
                isInitialized: _isInitialized,
                onShowStatementChanged: (value) {
                  setState(() => _showStatement = value);
                },
                onActionButtonChanged: _updateActionButton,
                onActionHintChanged: _updateActionHint,
                onCloseHintChanged: _updateCloseHint,
                onBackHintChanged: _updateBackHint,
                onStepButtonChanged: _updateStepButton,
                // 新增回調處理
                onApplyConfig: () {
                  _authMeSdk.setUIConfig(
                    AuthMeUIConfig(
                      actionButton: _actionButtonConfig,
                      actionHint: _actionHintConfig,
                      closeHint: _closeHintConfig,
                      backHint: _backHintConfig,
                      stepButton: _stepButtonConfig,
                      showStatement: _showStatement,
                    ),
                  );

                  // 可選:顯示成功提示
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(
                      content: Text('UI 設定已更新'),
                      backgroundColor: Colors.green,
                    ),
                  );
                },
              ),
            ],
          ),
        ),
      );
    } else {
      return const SizedBox(height: 0);
    }
  }

  Widget _buildSettingsCard() {
    return Card(
      margin: EdgeInsets.zero,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text('設置', style: Theme.of(context).textTheme.titleLarge),
                _isLoading
                    ? const SizedBox(
                        width: 20,
                        height: 20,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : const SizedBox(),
              ],
            ),
            const SizedBox(height: 16),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('語言設定', style: Theme.of(context).textTheme.titleSmall),
                  const SizedBox(height: 8),
                  DropdownButtonFormField<AuthmeLocale>(
                    value: _selectedLanguage,
                    decoration: const InputDecoration(
                      contentPadding: EdgeInsets.symmetric(
                        horizontal: 12,
                        vertical: 8,
                      ),
                      border: OutlineInputBorder(),
                    ),
                    items: AuthmeLocale.values.map((language) {
                      return DropdownMenuItem(
                        value: language,
                        child: Text(language.displayName),
                      );
                    }).toList(),
                    onChanged: (AuthmeLocale? value) {
                      if (value != null) {
                        setState(() {
                          _selectedLanguage = value;
                        });
                        _updateLanguage(value);
                      }
                    },
                  ),
                ],
              ),
            ),
            SwitchListTile(
              title: const Text('需要確認'),
              value: _needConfirm,
              onChanged: (bool value) {
                setState(() => _needConfirm = value);
              },
            ),
            SwitchListTile(
              title: const Text('顯示結果頁面'),
              value: _isResultPageDisplayable,
              onChanged: (bool value) {
                setState(() => _isResultPageDisplayable = value);
              },
            ),
            SwitchListTile(
              title: const Text('允許編輯結果'),
              value: _isResultEditable,
              onChanged: (bool value) {
                setState(() => _isResultEditable = value);
              },
            ),
            const SizedBox(height: 8),
            const SizedBox(height: 8),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton(
                    onPressed: _isInitialized
                        ? null
                        : () async {
                            setState(() {
                              _isInitialized = true;
                            });
                            try {
                              initSDK();
                              ScaffoldMessenger.of(context).showSnackBar(
                                const SnackBar(
                                  content: Text('SDK 初始化成功'),
                                  backgroundColor: Colors.green,
                                ),
                              );
                            } catch (e) {
                              setState(() {
                                _isInitialized = false;
                              });
                              ScaffoldMessenger.of(context).showSnackBar(
                                SnackBar(
                                  content: Text('初始化失敗: $e'),
                                  backgroundColor: Colors.red,
                                ),
                              );
                            }
                          },
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      backgroundColor: _buttonColor,
                      disabledBackgroundColor: _buttonColor.withOpacity(0.6),
                    ),
                    child: const Text(
                      "初始化 SDK",
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton(
                    onPressed: _isInitialized && _token == null
                        ? () => _fetchToken()
                        : null,
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      backgroundColor: _buttonColor,
                      disabledBackgroundColor: _buttonColor.withOpacity(0.6),
                    ),
                    child: const Text(
                      "Get Token",
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildFeaturesCard() {
    return Card(
      margin: EdgeInsets.zero,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('功能', style: Theme.of(context).textTheme.titleLarge),
            const SizedBox(height: 16),
            _buildFeatureSection('台灣證件', [
              ('身分證', AuthmeFeature.TWID),
              ('身分證 + 防偽', AuthmeFeature.TWIDFraud),
              ('駕照', AuthmeFeature.TWDriverLicense),
              ('健保卡', AuthmeFeature.TWHealth),
              ('居留證', AuthmeFeature.TWNResident),
            ]),
            const SizedBox(height: 16),
            _buildFeatureSection('護照', [
              ('護照 OCR', AuthmeFeature.Passport),
              ('護照 NFC', AuthmeFeature.NFCPassport),
            ]),
            const SizedBox(height: 16),
            _buildFeatureSection('生物驗證', [('活體偵測', AuthmeFeature.Liveness)]),
            if (_name.isNotEmpty) ...[
              const SizedBox(height: 16),
              Text("Name: $_name", style: const TextStyle(fontSize: 24)),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildFeatureSection(
    String title,
    List<(String, AuthmeFeature)> features,
  ) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.only(bottom: 8),
          child: Text(
            title,
            style: Theme.of(context).textTheme.titleSmall?.copyWith(
              color: Colors.grey[700],
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
        GridView.count(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          crossAxisCount: 2,
          mainAxisSpacing: 8,
          crossAxisSpacing: 8,
          childAspectRatio: 2.5,
          children: features
              .map((f) => _buildFeatureButton(f.$1, () => _runFeature(f.$2)))
              .toList(),
        ),
      ],
    );
  }

  Widget _buildFeatureButton(String title, VoidCallback onPressed) {
    final bool isEnabled = _isInitialized && _token != null;
    final txtColor = isEnabled ? Colors.black : Colors.grey;

    return ElevatedButton(
      onPressed: isEnabled ? onPressed : null,
      style: ElevatedButton.styleFrom(
        backgroundColor: _defaultButtonColor,
        disabledBackgroundColor: _defaultButtonColor,
      ),
      child: Text(
        title,
        textAlign: TextAlign.center,
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
        style: TextStyle(color: txtColor),
      ),
    );
  }

  Future<void> _updateLanguage(AuthmeLocale language) async {
    try {
      await _authMeSdk.setLocale(language);
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('語言設定已更新'), backgroundColor: Colors.green),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('更新語言失敗: ${e.toString()}'),
          backgroundColor: Colors.red,
        ),
      );
    } finally {}
  }

  void _updateActionButton({Color? backgroundColor, Color? contentColor}) {
    setState(() {
      _actionButtonConfig = ActionButtonConfig(
        backgroundColor:
            backgroundColor ??
            _actionButtonConfig?.backgroundColor ??
            Colors.blue,
        contentColor:
            contentColor ?? _actionButtonConfig?.contentColor ?? Colors.white,
      );
    });
  }

  void _updateActionHint({Color? backgroundColor, Color? contentColor}) {
    setState(() {
      _actionHintConfig = ActionHintConfig(
        backgroundColor:
            backgroundColor ??
            _actionHintConfig?.backgroundColor ??
            Colors.white,
        contentColor:
            contentColor ?? _actionHintConfig?.contentColor ?? Colors.black,
      );
    });
  }

  void _updateCloseHint({Color? backgroundColor, Color? contentColor}) {
    setState(() {
      _closeHintConfig = CloseHintConfig(
        backgroundColor:
            backgroundColor ??
            _closeHintConfig?.backgroundColor ??
            Colors.white,
        contentColor:
            contentColor ?? _closeHintConfig?.contentColor ?? Colors.black,
      );
    });
  }

  void _updateBackHint({Color? contentColor}) {
    setState(() {
      _backHintConfig = BackHintConfig(
        contentColor:
            contentColor ?? _backHintConfig?.contentColor ?? Colors.black,
      );
    });
  }

  void _updateStepButton({Color? backgroundColor, Color? contentColor}) {
    setState(() {
      _stepButtonConfig = StepButtonConfig(
        backgroundColor:
            backgroundColor ??
            _stepButtonConfig?.backgroundColor ??
            Colors.blue,
        contentColor:
            contentColor ?? _stepButtonConfig?.contentColor ?? Colors.white,
      );
    });
  }

  @override
  void dispose() {
    _resultSubscription.cancel();
    _authMeSdk.dispose();
    super.dispose();
  }
}
0
likes
145
points
386
downloads

Documentation

API reference

Publisher

verified publisherauthme.com

Weekly Downloads

Flutter plugin for the AuthMe eKYC SDK: OCR-based identity document recognition (Taiwan + 12 other regions), liveness detection, and NFC passport verification on iOS and Android.

Homepage
Repository (GitHub)
View/report issues

Topics

#ekyc #ocr #identity-verification #liveness #nfc

License

unknown (license)

Dependencies

flutter, http, plugin_platform_interface

More

Packages that depend on authme_ekyc_sdk

Packages that implement authme_ekyc_sdk