3

I need to set up a captcha for my Flutter web app, but all the packages that currently exist for captcha in Flutter do not support web. To solve this issue I first tried to render the ReCaptcha box inside dart using an IFrameElement and an HTMLElementView, but the issue I get there is that when I push the code onto my domain, it says that I have given an invalid domain for the site key.

enter image description here

I can't understand why the site key would be invalid, I have checked and double checked the site key to make sure it matches the one for the domain I put in the ReCaptcha console. Is it possible that the IFrame is not allowing communication to the ReCaptcha API? Here is the code for that.

import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'dart:html' as html;
import 'dart:js' as js;

class platformViewRegistry {
  static registerViewFactory(String viewId, dynamic cb) {
    // ignore:undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory(viewId, cb);
  }
}

class TestPlugin extends StatefulWidget {
  TestPlugin();

  _TestPluginState createState() => _TestPluginState();
}

class _TestPluginState extends State<TestPlugin> {
  String createdViewId = 'map_element';

  @override
  void initState() {
    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory(
        createdViewId,
        (int viewId) => html.IFrameElement()
          ..width = MediaQuery.of(context).size.width.toString()
          ..height = MediaQuery.of(context).size.height.toString()
          ..srcdoc = """<!DOCTYPE html><html>
  <head>
    <title>reCAPTCHA</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
  </head>
  <body style='background-color: aqua;'>
    <div style='height: 60px;'></div>
    <form action="?" method="POST">
      <div class="g-recaptcha" 
        data-sitekey="mySitekey"
        data-callback="captchaCallback"></div>

    </form>
    <script>
      function captchaCallback(response){
        //console.log(response);
        alert(response);
        if(typeof Captcha!=="undefined"){
          Captcha.postMessage(response);
        }
      }
    </script>
  </body>
</html>"""
          ..style.border = 'none');

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 10),
      decoration: BoxDecoration(
          color: Colors.white,
          border: Border.all(color: Colors.grey[300], width: 1),
          borderRadius: BorderRadius.all(Radius.circular(5))),
      width: 200,
      height: 200,
      child: Directionality(
        textDirection: TextDirection.ltr,
        child: HtmlElementView(
          viewType: createdViewId,
        ),
      ),
    );
  }
}

Is it possible to set up a separate HTML site on a different domain, and have Flutter web display and interface through that site to fill out the captcha? Would the captcha still work if it were through an interface like that?

Thank you in advance for your help.

Landon Stahl
  • 51
  • 2
  • 4

2 Answers2

2

I found this solution for reCaptcha v2:

Create a folder called html in the root folder of your project and in that folder create the file recaptcha.html.

<html>
  <head>
    <title>reCAPTCHA</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer> 
</script>
  </head>
  <body style='background-color: aqua;'>
    <div style='height: 60px;'></div>
    <form action="?" method="POST">
      <div class="g-recaptcha" 
        data-sitekey="xxxxxxxxxxxxxxxxxxxxxxx"
        data-callback="captchaCallback"></div>

    </form>
    <script>
      function captchaCallback(response){
        //console.log(response);
        alert(response);
        if(typeof Captcha!=="undefined"){
          Captcha.postMessage(response);
        }
      }
    </script>
  </body>
</html>

Then add the new folder html on your pubspec.yaml file under assets

  assets:
    - fonts/
    - html/

Finally, change the initState() of your _TestPluginState to:

    ui.platformViewRegistry.registerViewFactory(
      createdViewId,
      (int viewId) => html.IFrameElement()
        ..style.height = '100%'
        ..style.width = '100%'
        ..src = '/assets/html/recaptcha.html'
        ..style.border = 'none',
    );

IMPORTANT: Do not forget to change ..srcdoc to ..src

J Lincho
  • 21
  • 5
  • 1
    If success, where do i put the code so that my app can proceed to next page? – ali May 30 '22 at 08:50
  • i have the same question as @ali. how do you get captcha token from html back to dart? any suggestions? – MSquare Sep 13 '22 at 14:41
  • this worked for me to get captcha token back to dart: `void initState() { // ignore: UNDEFINED_PREFIXED_NAME ui.platformViewRegistry.registerViewFactory( createdViewId, (int viewId) => html.IFrameElement() . . . ..src = '/assets/html/recaptcha.html' ..style.border = 'none'); html.window.onMessage.listen((msg) { Navigator.of(context).pop(msg.data); //msg.data is captcha token }); super.initState(); }` and in recaptcha.html in function captchaCallback(response) include: `window.parent.postMessage(response, "*");` – MSquare Sep 14 '22 at 16:04
1

You can try g_recaptcha_v3 package

Note:

  • it support reCAPTCHA V3 only and not V2
  • its for Flutter Web only
Bharath
  • 1,036
  • 10
  • 13