Flutter
Integrate EU CAPTCHA into your Flutter app using the eu_captcha_flutter package. The widget works on both Android and iOS from a single Dart codebase.
| Source | Platforms |
|---|---|
| github.com/Myra-Security-GmbH/eu-captcha-flutter | Android API 19+ · iOS 13+ |
Server-side verification is identical regardless of client platform — see Server-Side Verification. For native SDKs see Android SDK and iOS SDK.
Requirements
- Flutter 3.0+
- Dart SDK ≥ 3.0
- Android API 19 (KitKat) or later
- iOS 13 or later
Installation
Add the package as a git dependency in your pubspec.yaml:
dependencies:
flutter_inappwebview: ^6.0.0
eu_captcha_flutter:
git:
url: https://github.com/Myra-Security-GmbH/eu-captcha-flutter.git
Then run:
flutter pub get
Platform configuration
Android — add the internet permission to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
iOS — no additional configuration needed.
Basic usage
Add the EuCaptcha widget to your form. It renders at a fixed height of 70 logical pixels, constrained to a maximum width of 420:
import 'package:eu_captcha_flutter/eucaptcha.dart';
EuCaptcha(
sitekey: 'EUCAPTCHA_SITE_KEY',
onComplete: (token) {
// Challenge complete — submit token to your server as "eu-captcha-response"
submitForm(token);
},
onExpire: (_) {
// Token expired — disable submit until user completes again
setState(() => _submitEnabled = false);
},
onError: (_) {
// Network or server error during verification
showErrorSnackBar();
},
)
Full login form example
class _LoginPageState extends State<LoginPage> {
final _captchaKey = GlobalKey<EuCaptchaState>();
String _captchaToken = '';
bool _submitEnabled = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
// ... username and password fields ...
EuCaptcha(
key: _captchaKey,
sitekey: 'EUCAPTCHA_SITE_KEY',
theme: 'auto',
onComplete: (token) => setState(() {
_captchaToken = token;
_submitEnabled = true;
}),
onExpire: (_) => setState(() => _submitEnabled = false),
onError: (_) => setState(() => _submitEnabled = false),
),
FilledButton(
onPressed: _submitEnabled ? _submit : null,
child: const Text('Log in'),
),
],
);
}
Future<void> _submit() async {
final response = await http.post(
Uri.parse('https://yourserver.example/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'eu-captcha-response': _captchaToken}),
);
// handle response...
// Reset the widget after a failed attempt
_captchaKey.currentState?.reset();
}
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
sitekey |
String |
yes | — | Your public sitekey |
onComplete |
void Function(String) |
yes | — | Called with the token when the challenge completes |
onExpire |
void Function(String)? |
no | null |
Called when the token expires |
onError |
void Function(String)? |
no | null |
Called when a network or server error occurs |
theme |
String |
no | 'auto' |
'light', 'dark', or 'auto' (follows system theme) |
lang |
String |
no | 'en' |
BCP 47 language tag (e.g. 'de', 'fr') |
Resetting the widget
After a rejected form submission, reset the widget using a GlobalKey so the user can complete a new challenge:
final _captchaKey = GlobalKey<EuCaptchaState>();
// In your widget tree:
EuCaptcha(key: _captchaKey, sitekey: 'EUCAPTCHA_SITE_KEY', onComplete: ...)
// After a failed submission:
_captchaKey.currentState?.reset();
Server-side verification
When onComplete fires, POST the token to your server as the eu-captcha-response field and verify it against the Verification API.
For ready-made server libraries, see Server-Side Verification.