Integrate Captcha V3 into a Flutter application

更新时间:
复制 MD 格式

Use the mobile HTML5 version of Alibaba Cloud Captcha 2.0 in your Flutter hybrid app by loading it inside a WebView component. This approach avoids native SDK dependency conflicts and lets you apply Captcha updates without rebuilding the app.

How it works

The integration connects three components:

  1. HTML5 page (client side): The HTML5 business page loads Captcha 2.0, presents the verification challenge, and passes the resulting captchaVerifyParam token to Flutter through a JavaScript channel.

  2. Application server (server side): Your server receives the token from Flutter and calls the VerifyIntelligentCaptcha API operation to confirm the result is genuine.

  3. Flutter app: A WebView component loads the HTML5 business page. A JavaScript channel bridges the HTML5 page and your Dart code, so verification tokens flow directly into Flutter.

Important

Server-side verification is mandatory. Without it, the client-side token can be forged and the CAPTCHA provides no protection.

Prerequisites

Before you begin, ensure that you have:

  • Activated Alibaba Cloud Captcha 2.0

  • Created a verification scenario with Integration Method set to Webview+H5 (for Apps And WeChat Mini Programs)

  • Flutter 2.0 or later

  • The webview_flutter plugin installed

Step 1: Integrate Captcha 2.0 on the HTML5 client side

Follow Integrate with Captcha using the V3 architecture for web and HTML5 clients to add Captcha 2.0 to your HTML5 business page.

Note

If your service uses the V2 architecture, see Integrate with Captcha using the V2 architecture for web and HTML5 clients instead.

In the Captcha success callback, post captchaVerifyParam to the Flutter JavaScript channel named testInterface:

// Success callback — called after the user passes the CAPTCHA challenge
function success(captchaVerifyParam) {
    console.log(captchaVerifyParam);

    // Send the verification token to Flutter via the JavaScript channel.
    if (window.testInterface) {
        window.testInterface.postMessage(captchaVerifyParam);
    }
}

Step 2: Integrate Captcha 2.0 on the server side

On your application server, integrate the server-side software development kit (SDK) for Captcha 2.0 and call the VerifyIntelligentCaptcha API operation to perform secondary authentication. For details, see Server-side integration.

Important

Always perform server-side verification before granting access. Skipping this step allows attackers to forge CAPTCHA tokens and bypass human verification entirely.

Step 3: Set up the Flutter app

Add the webview_flutter dependency

In pubspec.yaml, add webview_flutter under dependencies:

dependencies:
  flutter:
    sdk: flutter
  webview_flutter: ^4.13.0  # Use the latest version

Install the dependency:

flutter pub get

Configure Android network permissions

Add the following permissions to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<application
  ...
  android:usesCleartextTraffic="true"
  ...
  >

Create the Captcha WebView page

Create captcha_page.dart. The key configuration points are:

  • JavaScriptMode.unrestricted — required for Captcha 2.0 scripts to execute.

  • addJavaScriptChannel('testInterface', ...) — receives captchaVerifyParam from the HTML5 success callback.

  • loadHtmlString(htmlContent, baseUrl: 'https://your-domain.com') — loads the local HTML5 page with a trusted origin.

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/services.dart';

class CaptchaPage extends StatefulWidget {
  const CaptchaPage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<CaptchaPage> createState() => _CaptchaPageState();
}

class _CaptchaPageState extends State<CaptchaPage> {
  WebViewController? _controller;
  String _verifyResult = "Waiting for verification";
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _initWebView();
  }

  Future<void> _initWebView() async {
    // Load the local HTML file.
    String htmlContent = await rootBundle.loadString('assets/index.html');

    // Configure the WebView controller.
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      // Add a JavaScript channel to receive captchaVerifyParam from the HTML5 page.
      ..addJavaScriptChannel('testInterface', onMessageReceived: (JavaScriptMessage message) {
        // Print the complete verification parameters to the console.
        print('Received Captcha parameters: ${message.message}');

        // Forward captchaVerifyParam to your server for secondary authentication.
        setState(() {
          _verifyResult = "Verified";
        });
      })
      ..setNavigationDelegate(
        NavigationDelegate(
          onPageFinished: (url) {
            setState(() {
              _isLoading = false;
            });
          },
        ),
      )
      // Load the local HTML file. You can also load an HTML5 URL.
      ..loadHtmlString(htmlContent, baseUrl: 'https://your-domain.com');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: _controller == null
          ? const Center(child: CircularProgressIndicator())
          : Column(
        children: [
          Expanded(
            child: Stack(
              children: [
                WebViewWidget(controller: _controller!),
                if (_isLoading)
                  const Center(
                    child: CircularProgressIndicator(),
                  ),
              ],
            ),
          ),
          Container(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Text('Verification status: $_verifyResult', style: const TextStyle(fontSize: 16)),
                const SizedBox(height: 10),
                ElevatedButton(
                  onPressed: () {
                    if (_controller != null) {
                      _controller!.runJavaScript('initCaptcha()');
                      setState(() {
                        _verifyResult = "Captcha reset";
                      });
                    }
                  },
                  child: const Text('Reset Captcha'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Use the Captcha page in main.dart

import 'package:flutter/material.dart';
import 'captcha_page.dart';  // Import the Captcha page.

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Alibaba Cloud Captcha Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const CaptchaPage(title: 'Alibaba Cloud Captcha Demo'),
    );
  }
}

Register the HTML5 asset

In pubspec.yaml, add the HTML5 file path under flutter:

flutter:
  assets:
    - assets/index.html

Verify the integration

Run your Flutter app and trigger the CAPTCHA challenge. If the verification completes and your app receives the captchaVerifyParam token, the client-side integration is working. Confirm that your server successfully calls VerifyIntelligentCaptcha and returns the expected result.

Troubleshooting

The CAPTCHA widget does not load

JavaScript must be enabled. Confirm that setJavaScriptMode(JavaScriptMode.unrestricted) is set on the WebViewController. Without it, Captcha 2.0 scripts cannot execute.

The JavaScript channel receives no message

The channel name in your HTML5 success callback (window.testInterface) must exactly match the name passed to addJavaScriptChannel in Dart ('testInterface'). A mismatch silently drops messages.

The page loads but CAPTCHA challenges fail

Confirm that Android network permissions are set in AndroidManifest.xml (INTERNET, ACCESS_NETWORK_STATE, ACCESS_WIFI_STATE) and that android:usesCleartextTraffic="true" is present if your HTML5 page or Captcha endpoint uses HTTP.

Download the demo

Flutter app integration demo

What's next