braintree into flutter web using JS got this error "options.selector or options.container must reference a valid DOM node"

942

Technical Disclaimer: flutter-web is in beta and I would not recommend it to be used with any payment service. This might lead to critical issues and not advisable.

The HtmlElementView widget adds all its elements into shadowdom which is not directly accessible for the global javascript context. Check this issue here in github.

The solution would be to pass the DivElement reference to the external js function. For e.g. in your case

Create the div element out side the build method and hold a reference, like in initSate


DivElement paymentDiv;

@override
initState(){

// always call before rest of the logic.
super.initState();

var htmlL = """<div id="checkout-message"></div>
        <div id="dropin-container"></div>
    <button id="submit-button">Submit payment</button>""";
paymentDiv=  DivElement()
          ..appendHtml(htmlL)
          ..style.border = 'none');
// remaining logic

}

Then in your build/other method pass this element for the registerViewFactory method.

// ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory(
        'payment-container',
        (int viewId) => paymentDiv;

Set you JS interop to accept dynamic parameter.

@JS()
external String payment(dynamic auth);

Rewrite you Javascript to directly work with this element reference. e.g

function payment(auth){
    var button = auth;
    // Remaining logic
}
Share:
942
Shudipto Trafder
Author by

Shudipto Trafder

Android App Developer, Flutter Developer, and Deep learning enthusiast

Updated on December 18, 2022

Comments

  • Shudipto Trafder
    Shudipto Trafder over 1 year

    Trying to implement Braintree payment gateway into flutter web. Still no SDK for flutter web from Braintree. So trying to implement their javascript SDK.

    Here is my js file

    function payment(auth){
        var button = document.querySelector('submit-button');
        console.log(auth);
        console.log(button);
        braintree.dropin.create({
          authorization: auth,
          container: 'dropin-container'
        }, function (createErr, instance) {
            console.log(createErr);
            console.log(instance);
             button.addEventListener('click', function () {
                instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
                  // Submit payload.nonce to your server
                  return payload.nonce
                });
             });
        });
    }
    

    Calling this js function from dart. Here is the complete dart code.

    @JS()
    library my_script;
    
    import 'dart:html';
    import 'dart:js_util';
    
    import 'package:carbonbins/model/model.dart';
    import 'package:carbonbins/pages/navigation.gr.dart';
    import 'package:carbonbins/utils/image_helper.dart';
    import 'package:flutter/material.dart';
    import 'dart:ui' as ui;
    
    import 'package:js/js.dart';
    import 'package:js/js.dart' as js;
    
    @JS()
    external void initBraintree(auth);
    
    @JS()
    external String payment(auth);
    
    class PaymentPage extends StatefulWidget {
      final UserModel userModel;
    
      PaymentPage({@required this.userModel});
    
      @override
      _PaymentPageState createState() => _PaymentPageState();
    }
    
    class _PaymentPageState extends State<PaymentPage> {
    
      String auth = "sandbox_.....";
    
      void getButton() {
        var htmlL = """<div id="checkout-message"></div>
            <div id="dropin-container"></div>
        <button id="submit-button">Submit payment</button>""";
    
        // ignore: undefined_prefixed_name
        ui.platformViewRegistry.registerViewFactory(
            'payment-container',
            (int viewId) => DivElement()
              ..appendHtml(htmlL)
              ..style.border = 'none');
    
        print(HtmlElementView(
          viewType: "dropin-container",
        ));
      }
    
      void setupDropin() {
        print(auth);
        var status = payment(auth);
        print("Status: $status");
      }
    
      @override
      void initState() {
        getButton();
        setupDropin();
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            width: MediaQuery.of(context).size.width,
            child: Column(
              children: <Widget>[
                SizedBox(
                  height: 100,
                ),
                Container(
                  width: 500.0,
                  height: 300.0,
                  child: HtmlElementView(
                    viewType: "payment-container",
                  ),
                )
              ],
            ),
          ),
        );
      }
    

    When I run this code, I see only the submit button in the screen. Got this error from web console,

    "options.selector or options.container must reference a valid DOM node."
    

    How can I integrate the Braintree payment into the flutter web?

    or any other international payment gateway that works in flutter web.

    • Daniel N.
      Daniel N. about 3 years
      were you able to get this working? I have been having trouble getting the actual payment nonce to return
    • Shudipto Trafder
      Shudipto Trafder about 3 years
      hey @DanielN. basically, I switched to another framework, but now flutter in the stable channel, and ready for production, so you can try now.
  • Danny Sims
    Danny Sims over 3 years
    How do you pass the element reference of the button from paymentDiv to the payment JS function?
  • Harkirat singh
    Harkirat singh almost 3 years
    The container should be empty prior to creation of dropin instance