Saltar al contenido principal
Captura en Flutter

Abre la captura desde una app Flutter con un platform channel.

Markdown

Tu backend crea el proceso en Human Plugin con un flujo Journey y un callbackUri que usa el esquema de URL de tu app. Un MethodChannel abre el link de la respuesta en ASWebAuthenticationSession en iOS y en Custom Tabs en Android. No se necesita ningún paquete externo.

Conserva el id de Human Plugin de la respuesta de la creación. Tu backend lee el resultado con él.

Requisitos previos​

  • Flutter 3 o posterior
  • Xcode 14 o posterior para iOS
  • Android Studio para Android
  • Un esquema de URL único para tu app, como myapp

1. Crea el proceso con tu esquema​

Tu backend envía el esquema como callbackUri:

{
"flow": "humantoken",
"clientId": "client-a",
"correlationId": "order-1001",
"credential": { "type": "DUI_TYPE_NG_BVN", "value": "<document number>" },
"callbackUri": "myapp://callback"
}

y devuelve id y link a la app.

2. Crea el servicio en Dart​

// lib/services/capture_service.dart
import 'package:flutter/services.dart';

class CaptureService {
static const _channel = MethodChannel('capture_plugin');

/// Devuelve true cuando la captura terminó, false cuando la persona la cerró.
Future<bool> open(String link) async {
try {
final result = await _channel.invokeMethod<String>('openSession', {'url': link});
return result != null;
} on PlatformException {
return false;
}
}
}

3. Implementa el plugin de iOS​

// ios/Runner/CapturePlugin.swift
import Flutter
import AuthenticationServices

class CapturePlugin: NSObject, FlutterPlugin, ASWebAuthenticationPresentationContextProviding {
private var session: ASWebAuthenticationSession?

static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "capture_plugin", binaryMessenger: registrar.messenger())
let instance = CapturePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}

func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
guard call.method == "openSession",
let args = call.arguments as? [String: Any],
let urlString = args["url"] as? String,
let url = URL(string: urlString) else {
result(FlutterMethodNotImplemented)
return
}

let newSession = ASWebAuthenticationSession(
url: url,
callbackURLScheme: "myapp"
) { callbackURL, error in
self.session = nil
if let callbackURL = callbackURL {
result(callbackURL.absoluteString)
} else {
result(FlutterError(code: "SESSION_ERROR",
message: error?.localizedDescription ?? "Session cancelled",
details: nil))
}
}
newSession.presentationContextProvider = self
newSession.prefersEphemeralWebBrowserSession = true
newSession.start()
session = newSession
}

func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first else {
return ASPresentationAnchor()
}
return window
}
}

Regístralo en AppDelegate.swift:

CapturePlugin.register(with: self.registrar(forPlugin: "CapturePlugin")!)

4. Implementa el plugin de Android​

Agrega la dependencia a android/app/build.gradle:

implementation("androidx.browser:browser:1.5.0")
// android/app/src/main/kotlin/<your.package>/CapturePlugin.kt
package com.yourapp

import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

class CapturePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware {
private var channel: MethodChannel? = null
private var binding: ActivityPluginBinding? = null
private var pendingResult: MethodChannel.Result? = null

override fun onAttachedToEngine(flutterBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterBinding.binaryMessenger, "capture_plugin")
channel?.setMethodCallHandler(this)
}

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method == "openSession") {
val url = call.argument<String>("url") ?: return result.error("MISSING_URL", "URL is required", null)
pendingResult = result
val activity = binding?.activity ?: return result.error("NO_ACTIVITY", "No activity", null)
CustomTabsIntent.Builder().build().launchUrl(activity, Uri.parse(url))
} else {
result.notImplemented()
}
}

fun handleRedirect(uri: Uri) {
if (uri.scheme == "myapp") {
pendingResult?.success(uri.toString())
pendingResult = null
}
}

override fun onAttachedToActivity(binding: ActivityPluginBinding) { this.binding = binding }
override fun onDetachedFromActivity() { binding = null }
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { this.binding = binding }
override fun onDetachedFromActivityForConfigChanges() { binding = null }
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel = null }
}

Regístralo y gestiona el regreso en MainActivity.kt:

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine.plugins.add(CapturePlugin())
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let { uri ->
if (uri.scheme == "myapp") {
val plugin = flutterEngine?.plugins?.get(CapturePlugin::class.java) as? CapturePlugin
plugin?.handleRedirect(uri)
}
}
}

5. Registra tu esquema​

En Info.plist:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>

6. Inicia la captura​

final ended = await CaptureService().open(link);

if (ended) {
// La captura terminó. Pide a tu backend que lea el resultado
// con GET /v1/processes/{id}, usando el id de Human Plugin.
} else {
// La persona cerró la sesión.
}

Resumen​

ArchivoPropósito
lib/services/capture_service.dartServicio Dart con MethodChannel
ios/Runner/CapturePlugin.swiftiOS: ASWebAuthenticationSession
ios/Runner/AppDelegate.swiftRegistro del plugin
android/.../CapturePlugin.ktAndroid: Custom Tabs
android/.../MainActivity.ktRegistro del plugin y gestión del regreso
Info.plistEsquema de URL de iOS
AndroidManifest.xmlIntent filter de Android