Skip to main content

okyc_flutter 0.1.0 copy "okyc_flutter: ^0.1.0" to clipboard
okyc_flutter: ^0.1.0 copied to clipboard

retracted

OKYC identity verification for Flutter: the hosted KYC/KYB flow in a secure, full-screen WebView with native permissions, file picking and results.

example/lib/main.dart

import 'dart:convert';
import 'dart:io';

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

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'OKYC Flutter example',
      theme: ThemeData(colorSchemeSeed: const Color(0xFF3B5BDB), useMaterial3: true),
      home: const ExampleHome(),
    );
  }
}

/// The Android emulator reaches the host machine at 10.0.2.2; the iOS simulator shares the host network.
String get _devHost => Platform.isAndroid ? '10.0.2.2' : 'localhost';

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

  @override
  State<ExampleHome> createState() => _ExampleHomeState();
}

class _ExampleHomeState extends State<ExampleHome> {
  final _url = TextEditingController();
  final _api = TextEditingController(text: 'http://$_devHost:4000');
  final _log = <String>[];
  bool _creating = false;
  String? _result;

  @override
  void dispose() {
    _url.dispose();
    _api.dispose();
    super.dispose();
  }

  Future<void> _createSandboxSession() async {
    setState(() => _creating = true);
    try {
      final url = await SandboxSessions(Uri.parse(_api.text.trim())).create();
      _url.text = url;
    } catch (e) {
      if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Could not create a session: $e')));
    } finally {
      if (mounted) setState(() => _creating = false);
    }
  }

  Future<void> _start() async {
    setState(() {
      _log.clear();
      _result = null;
    });
    final result = await Okyc.start(
      context,
      OkycConfig(sessionUrl: _url.text.trim()),
      onEvent: (e) => setState(() => _log.add('${e.type.name} ${e.payload.isEmpty ? '' : e.payload}')),
    );
    setState(() {
      _result = switch (result) {
        OkycCompleted(:final sessionId, :final status) => 'Completed · $status · $sessionId\n'
            'Fetch the verified result on your server: GET /v1/sessions/$sessionId',
        OkycCancelled(:final sessionId) => 'Cancelled${sessionId == null ? '' : ' · $sessionId'}',
        OkycFailed(:final code, :final message) => 'Failed · $code · $message',
      };
    });
  }

  @override
  Widget build(BuildContext context) {
    final t = Theme.of(context);
    return Scaffold(
      appBar: AppBar(title: const Text('OKYC Flutter example')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          TextField(
            key: const Key('session-url'),
            controller: _url,
            decoration: const InputDecoration(
              labelText: 'Session URL',
              hintText: 'https://verify.okyc.stream/s/ses_…#t=st_…',
              border: OutlineInputBorder(),
            ),
            keyboardType: TextInputType.url,
            autocorrect: false,
            maxLines: 3,
            minLines: 1,
          ),
          const SizedBox(height: 12),
          FilledButton.icon(
            key: const Key('start'),
            onPressed: _start,
            icon: const Icon(Icons.verified_user_outlined),
            label: const Text('Start verification'),
          ),
          const SizedBox(height: 24),
          Text('Local development', style: t.textTheme.titleSmall),
          const SizedBox(height: 8),
          TextField(
            controller: _api,
            decoration: const InputDecoration(labelText: 'Local API', border: OutlineInputBorder(), isDense: true),
            keyboardType: TextInputType.url,
            autocorrect: false,
          ),
          const SizedBox(height: 8),
          OutlinedButton.icon(
            key: const Key('create-sandbox'),
            onPressed: _creating ? null : _createSandboxSession,
            icon: _creating
                ? const SizedBox.square(dimension: 16, child: CircularProgressIndicator(strokeWidth: 2))
                : const Icon(Icons.science_outlined),
            label: const Text('Create sandbox session'),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 4),
            child: Text(
              'Creates a throwaway sandbox account on the local API and a test session. '
              'In production your server creates sessions with its secret key.',
              style: t.textTheme.bodySmall?.copyWith(color: t.colorScheme.onSurfaceVariant),
            ),
          ),
          if (_result != null) ...[
            const SizedBox(height: 24),
            Text('Result', style: t.textTheme.titleSmall),
            const SizedBox(height: 4),
            SelectableText(_result!, key: const Key('result')),
          ],
          if (_log.isNotEmpty) ...[
            const SizedBox(height: 16),
            Text('Events', style: t.textTheme.titleSmall),
            const SizedBox(height: 4),
            for (final line in _log) Text(line, style: t.textTheme.bodySmall?.copyWith(fontFamily: 'monospace')),
          ],
        ],
      ),
    );
  }
}

/// Local-development helper: signs up a throwaway dashboard user, creates a tenant and a sandbox session.
/// Never ship this — production apps get the session URL from their own backend.
class SandboxSessions {
  SandboxSessions(this.api);
  final Uri api;
  final _client = HttpClient()..connectionTimeout = const Duration(seconds: 10);
  final _cookies = <String, String>{};

  Future<String> create() async {
    try {
      await _post('/api/auth/sign-up/email', {
        'email': 'flutter+${DateTime.now().millisecondsSinceEpoch}@example.test',
        'password': 'sandbox-${DateTime.now().microsecondsSinceEpoch}',
        'name': 'Flutter example',
      });
      final tenant = await _post('/dash/v1/tenants', {'name': 'Flutter example'});
      final workflows = tenant['workflows'] as Map<String, dynamic>;
      final session = await _post(
        '/dash/v1/test-sessions',
        {'workflow_id': workflows['kyc']},
        headers: {'x-okyc-tenant': tenant['id'] as String, 'x-okyc-mode': 'test'},
      );
      var url = Uri.parse(session['url'] as String);
      if (url.host == 'localhost' && _devHost != 'localhost') url = url.replace(host: _devHost);
      return url.toString();
    } finally {
      _client.close();
    }
  }

  Future<Map<String, dynamic>> _post(String path, Object body, {Map<String, String> headers = const {}}) async {
    final req = await _client.postUrl(api.resolve(path));
    req.headers
      ..contentType = ContentType.json
      // The dashboard API only accepts calls from the dashboard origin.
      ..set('origin', 'http://localhost:5174');
    if (_cookies.isNotEmpty) req.headers.set('cookie', _cookies.entries.map((e) => '${e.key}=${e.value}').join('; '));
    headers.forEach(req.headers.set);
    req.write(jsonEncode(body));
    final res = await req.close();
    for (final c in res.cookies) {
      _cookies[c.name] = c.value;
    }
    final text = await res.transform(utf8.decoder).join();
    if (res.statusCode >= 300) throw HttpException('$path → ${res.statusCode} $text');
    return jsonDecode(text) as Map<String, dynamic>;
  }
}
0
likes
0
points
441
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

OKYC identity verification for Flutter: the hosted KYC/KYB flow in a secure, full-screen WebView with native permissions, file picking and results.

Homepage

Topics

#kyc #identity-verification #webview #okyc

License

unknown (license)

Dependencies

flutter, url_launcher, webview_flutter, webview_flutter_android, webview_flutter_wkwebview

More

Packages that depend on okyc_flutter

Packages that implement okyc_flutter