Flutter SDK
Flutter plugin built on the native iOS / Android SDKs, exposing a single Dart CaptchalaClient for both mobile platforms (with Linux / macOS / Windows desktop support via embedded WebView).
Demo on GitHub
📦
Captcha-La/flutter-demo — full runnable example with every integration step.
Install
Add the package from pub.dev:
# pubspec.yaml
dependencies:
captchala: ^1.3.2
http: ^1.2.0 # used by the demo's token-fetch helpersflutter pub get
flutter run # auto-picks the foreground device
# or:
flutter run -d <device-id>For iOS / macOS the first build runs pod install automatically. Make sure ios/Podfile declares platform :ios, '13.0' (or higher).
Quick start
import 'package:flutter/material.dart';
import 'package:captchala/captchala.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String _status = 'Tap to verify';
bool _verifying = false;
Future<void> _runVerify() async {
setState(() { _verifying = true; _status = 'Fetching server_token...'; });
// 1. Fetch a one-shot server_token from YOUR backend.
final initialToken = await fetchServerTokenFromYourBackend();
// 2. Build config from your business action.
final config = CaptchalaConfig(
appKey: 'YOUR_APP_KEY',
action: 'login', // login, register, pay, …
lang: 'en', // en, zh-CN, zh-TW, ja, ko, ms, vi, id
theme: 'light', // 'light' | 'dark'
enableVoice: true,
enableOfflineMode: true,
maskClosable: false,
serverToken: initialToken,
);
// 3. Initialize, register callbacks, then call verify().
final client = await CaptchalaClient.instance.initialize(config);
client.setCallbacks(
onSuccess: (r) async {
// Send r.passToken to YOUR backend for validation.
if (!mounted) return;
setState(() { _status = 'OK: ${r.passToken}'; _verifying = false; });
},
onFail: (e) { if (mounted) setState(() { _status = 'fail: ${e.code}'; _verifying = false; }); },
onError: (e) { if (mounted) setState(() { _status = 'error: ${e.code} ${e.message}'; _verifying = false; }); },
onClose: () { if (mounted && _verifying) setState(() { _status = 'closed'; _verifying = false; }); },
onServerTokenExpired: () => fetchServerTokenFromYourBackend(),
);
await client.verify();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(mainAxisSize: MainAxisSize.min, children: [
ElevatedButton(
onPressed: _verifying ? null : _runVerify,
child: Text(_verifying ? 'Verifying...' : 'Verify with CAPTCHA'),
),
const SizedBox(height: 12),
Text(_status, style: const TextStyle(fontFamily: 'monospace')),
]),
),
);
}
}API surface
| Symbol | Purpose |
|---|---|
CaptchalaClient.instance | Singleton accessor for the plugin. |
CaptchalaConfig(...) | Plain Dart record: appKey, action, lang, theme, enableVoice, enableOfflineMode, maskClosable, serverToken. |
await client.initialize(config) | Apply config. Returns the same client instance so you can chain setCallbacks. |
setCallbacks(...) | Register onSuccess, onFail, onError, onClose, onServerTokenExpired callbacks. |
await client.verify() | Open the native CAPTCHA view (Android: Activity over Flutter; iOS: UIViewController over Flutter). |
CaptchalaResult | Provided to onSuccess. Fields: passToken, challengeId, ttl, isOffline, isClientOnly. |
Server-side validation
Forward result.passToken (or result.token) to your backend and validate it against the CaptchaLa API. Never expose X-App-Secret in client code.
POST https://apiv1.captcha.la/v1/validate
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
Content-Type: application/json
{ "pass_token": "<result.passToken>", "client_ip": "<end-user IP>" }See the API Reference for the full validation endpoint and X-App-Key / X-App-Secret flow.
Troubleshooting
iOS pod install fails
Inios/Podfilesetplatform :ios, '13.0'(or higher). Runflutter clean && flutter pub get && cd ios && pod installafter switching the plugin version.Android
minSdkVersionmismatch
Bumpandroid/app/build.gradletominSdkVersion 21. The native Android SDK will refuse to compile below that.Callbacks fire on background isolate
Always guardsetStatewithif (mounted)before mutating UI state inside callbacks. The demo wraps every callback that updates state with this check.Desktop targets (Linux / Windows / macOS)
Linux requires GTK3 + WebKitGTK + libcurl. Windows needs the Edge WebView2 Runtime (Win 10 1809+). macOS desktop uses the system WebView automatically.
Requirements
- Flutter 3.10+ / Dart 3.0+
- Android:
minSdkVersion 21(Android 5.0+) - iOS: 13.0+ with CocoaPods
- Desktop (optional): GTK3 + WebKitGTK on Linux, WebView2 Runtime on Windows 10 1809+