Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use js library in flutter web

I need widget with bpmn.js view: https://github.com/bpmn-io/bpmn-js

Used HtmlElementView:

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry
        .registerViewFactory('bpmn_view', (int viewId) => element);

    return Column(
      children: <Widget>[
        Expanded(
            child: HtmlElementView(key: UniqueKey(), viewType: "bpmn_view")),
      ],
    );

With js:

    const html = '''
    <div id="canvas">canvas</div>
    <script>
      (function () {
        window.addEventListener('view_bpmn', function (e) {
           var bpmnJS = new BpmnJS({
               container: "#canvas"
           });

           bpmnJS.importXML(e.details);
         }, false);
      }());
    </script>
    ''';

    element.setInnerHtml(html,
        validator: NodeValidatorBuilder.common()..allowElement('script'));

enter image description here

But I get error when it execute:

VM4761 bpmn-viewer.development.js:18864 Uncaught TypeError: Cannot read property 'appendChild' of null
    at Viewer.BaseViewer.attachTo (VM4761 bpmn-viewer.development.js:18864)
    at Viewer.BaseViewer._init (VM4761 bpmn-viewer.development.js:18911)
    at Viewer.BaseViewer (VM4761 bpmn-viewer.development.js:18454)
    at new Viewer (VM4761 bpmn-viewer.development.js:19082)
    at <anonymous>:3:25
    at main.dart:185
    at future.dart:316
    at internalCallback (isolate_helper.dart:50)

And I can't set selector for BpmnJS like:

 var bpmnJS = new BpmnJS({
               container: "document.querySelector('flt-platform-view').shadowRoot.querySelector('#canvas')";
           });

How can I make it work?

like image 852
Vitaliy Vostrikov Avatar asked Feb 18 '20 07:02

Vitaliy Vostrikov


People also ask

Can I use JavaScript in Dart?

The Dart web platform supports calling JavaScript using the js package, also known as package:js. For help using the js package, see the following: Documentation for the js package: pub.

How do I add an external library to Flutter?

You can go to the pubspec. yaml file and add dependencies ,under dependencies and then packages get will do the work. or you can run flutter pub get in the terminal.

Is Flutter using js?

As an alternative to JavaScript or React Native, Google created the Flutter framework for cross-platform mobile application development. Flutter uses the Dart programming language which is not known by many developers. However, in contrast to React Native apps, apps built with Flutter look slick and much more native.

Can I use node js with Flutter?

In a matter of minutes and without a single line of code, Buddy allows you to connect Node. js and Build Flutter app.


1 Answers

Since BpmnJS container parameter accepts DOMElement type value, we can pass querySelector's result directly:

    _element = html.DivElement()
      ..id = 'canvas'
      ..append(html.ScriptElement()
        ..text = """
        const canvas = document.querySelector("flt-platform-view").shadowRoot.querySelector("#canvas");
        const viewer = new BpmnJS({ container: canvas });
        """);

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry
        .registerViewFactory('bpmn-view', (int viewId) => _element);

BpmnJS module should be attached to index.html file (in your project's top-level web folder):

<!DOCTYPE html>
<head>
  <title>BpmnJS Demo</title>
  <script defer src="main.dart.js" type="application/javascript"></script>
  <script src="https://unpkg.com/[email protected]/dist/bpmn-navigated-viewer.development.js"></script>
</head>
...

Here is full code:

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

class BpmnDemo extends StatefulWidget {
  @override
  _BpmnDemoState createState() => _BpmnDemoState();
}

class _BpmnDemoState extends State<BpmnDemo> {
  html.DivElement _element;

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

    _element = html.DivElement()
      ..id = 'canvas'
      ..append(html.ScriptElement()
        ..text = """
        const canvas = document.querySelector("flt-platform-view").shadowRoot.querySelector("#canvas");
        const viewer = new BpmnJS({ container: canvas });
        const uri = "https://cdn.staticaly.com/gh/bpmn-io/bpmn-js-examples/dfceecba/url-viewer/resources/pizza-collaboration.bpmn";
        fetch(uri).then(res => res.text().then(xml => viewer.importXML(xml)));
        """);

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry
        .registerViewFactory('bpmn-view', (int viewId) => _element);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
          child: HtmlElementView(key: UniqueKey(), viewType: "bpmn-view")),
    );
  }
}

UPDATE:

This example shows how to load a diagram from dart code and uses dart:js library:

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

class BpmnDemo extends StatefulWidget {
  @override
  _BpmnDemoState createState() => _BpmnDemoState();
}

class _BpmnDemoState extends State<BpmnDemo> {
  html.DivElement _element;
  js.JsObject _viewer;

  @override
  void initState() {
    super.initState();
    _element = html.DivElement();
    _viewer = js.JsObject(
      js.context['BpmnJS'],
      [
        js.JsObject.jsify({'container': _element})
      ],
    );
    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory('bpmn-view', (int viewId) => _element);
    loadDiagram('assets/pizza-collaboration.bpmn');
  }

  loadDiagram(String src) async {
    final bundle = DefaultAssetBundle.of(context);
    final xml = await bundle.loadString(src);
    _viewer.callMethod('importXML', [xml]);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(child: HtmlElementView(key: UniqueKey(), viewType: "bpmn-view")),
    );
  }
}

UPDATE 2:

Certain complications with calling methods from js library can arise when HtmlElementView uses IFrame element. In this case we can try two options:

  1. Store IFrame context on dart side and then use callMethod with saved context.
  2. Use postMessage method to communicate with IFrame
import 'dart:ui' as ui;
import 'dart:js' as js;
import 'dart:html' as html;
import 'package:flutter/material.dart';

class IFrameDemoPage extends StatefulWidget {
  @override
  _IFrameDemoPageState createState() => _IFrameDemoPageState();
}

class _IFrameDemoPageState extends State<IFrameDemoPage> {
  html.IFrameElement _element;
  js.JsObject _connector;

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

    js.context["connect_content_to_flutter"] = (content) {
      _connector = content;
    };

    _element = html.IFrameElement()
      ..style.border = 'none'
      ..srcdoc = """
        <!DOCTYPE html>
          <head>
            <script>
              // variant 1
              parent.connect_content_to_flutter && parent.connect_content_to_flutter(window)
              function hello(msg) {
                alert(msg)
              }

              // variant 2
              window.addEventListener("message", (message) => {
                if (message.data.id === "test") {
                  alert(message.data.msg)
                }
              })
            </script>
          </head>
          <body>
            <h2>I'm IFrame</h2>
          </body>
        </html>
        """;

    // ignore:undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory(
      'example',
      (int viewId) => _element,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: Icon(Icons.filter_1),
            tooltip: 'Test with connector',
            onPressed: () {
              _connector.callMethod('hello', ['Hello from first variant']);
            },
          ),
          IconButton(
            icon: Icon(Icons.filter_2),
            tooltip: 'Test with postMessage',
            onPressed: () {
              _element.contentWindow.postMessage({
                'id': 'test',
                'msg': 'Hello from second variant',
              }, "*");
            },
          )
        ],
      ),
      body: Container(
        child: HtmlElementView(viewType: 'example'),
      ),
    );
  }
}

like image 132
Spatz Avatar answered Sep 29 '22 04:09

Spatz