friendly_captcha_flutter
FriendlyCaptcha v2 widget for Flutter. Privacy-first, accessible, proof-of-work bot protection on Android, iOS, and Web (Wasm-compatible).
The package only renders the captcha and surfaces the solution token. Verification stays on your backend.
Install
flutter pub add friendly_captcha_flutter
Web setup
Add the FriendlyCaptcha SDK to web/index.html, ideally in <head> so it loads before Flutter boots:
<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@1/site.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@1/site.compat.min.js" async defer></script>
If you ship a strict Content-Security-Policy, allow script-src cdn.jsdelivr.net (or self-host the SDK and allow your own origin), connect-src https://api.friendlycaptcha.com (or https://eu-api.friendlycaptcha.com for FriendlyCaptchaApiEndpoint.eu), and worker-src 'self' blob: — the SDK uses a worker for the proof-of-work loop.
Mobile setup
Sitekeys are origin-bound and the SDK rejects data: / about:blank, so the embedded WebView serves its HTML under a synthetic origin (defaults to https://app.local). Either:
- Add
app.localto your sitekey's allowed-hostnames list in the FriendlyCaptcha dashboard, or - Override the origin once at app startup to match a hostname you already allow:
void main() {
FriendlyCaptcha.mobileOrigin = 'https://signup.yourdomain.com';
runApp(const MyApp());
}
No further Android/iOS setup is required — flutter_inappwebview handles the platform plumbing.
Basic usage
FriendlyCaptcha(
sitekey: 'FCMG...',
onResult: (result) {
if (result.isCompleted) {
submitToYourBackend(result.solution!);
}
},
)
onResult fires only on terminal events (completed, error, expired). Use the controller below if you need intermediate states.
Controller usage
final captcha = FriendlyCaptchaController();
@override
Widget build(BuildContext context) {
return Column(
children: [
FriendlyCaptcha(
sitekey: 'FCMG...',
controller: captcha,
onResult: (_) {},
),
ListenableBuilder(
listenable: captcha,
builder: (_, __) => FilledButton(
onPressed: captcha.hasSolution
? () => _submit(captcha.solution!).then((ok) {
if (!ok) captcha.reset();
})
: null,
child: const Text('Submit'),
),
),
],
);
}
Solutions are single-use and expire after roughly 10 minutes, so call controller.reset() after a failed submission to arm a fresh challenge.
Configuration
| Parameter | Type | Default | Notes |
|---|---|---|---|
sitekey |
String |
required | Sitekey from the FriendlyCaptcha dashboard. |
onResult |
ValueChanged<…Result> |
required | Fires only on completed / error / expired. |
controller |
FriendlyCaptchaController? |
null |
Drive start() / reset() and observe statechanges. |
theme |
FriendlyCaptchaTheme |
auto |
light, dark, or auto. |
startMode |
FriendlyCaptchaStartMode |
focus |
auto, focus, or none. |
apiEndpoint |
FriendlyCaptchaApiEndpoint |
global |
global or eu for data-residency. |
language |
String? |
null (auto) |
Force a UI language (e.g. de, fr). |
height |
double |
80 |
Pixel height of the embed. |
Plus the static FriendlyCaptcha.mobileOrigin (default https://app.local) covered above.
Server-side verification
This package is intentionally client-side only. After a solution token arrives via onResult, POST it to your own server and call POST https://api.friendlycaptcha.com/api/v2/captcha/siteverify with your secret API key — see the FriendlyCaptcha docs for the request format. The package deliberately ships no helper for this so secret keys never live in your app bundle.
Testing
For widget-level tests or end-to-end CI runs, use one of FriendlyCaptcha's automated-testing sitekeys (e.g. the always-pass key documented there) so you don't burn real captcha solves.
Platform support
| Platform | Status |
|---|---|
| Android | Supported via flutter_inappwebview. |
| iOS | Supported via flutter_inappwebview. |
| Web | Supported via dart:js_interop + package:web. Compiles under dart2wasm. |
| Desktop | Not supported. |
License
MIT — see LICENSE.
Libraries
- friendly_captcha_flutter
- FriendlyCaptcha v2 widget for Flutter. See
https://developer.friendlycaptcha.com/docs/v2/ for the upstream SDK and
README.mdfor setup instructions.