Embedding a Flutter web app into Liveview

Hello everyone, I’m a totally new to phoenix, LiveView and in general frontend/backend development. But I think that starting with Phoenix LiveView is already a good step :slight_smile:.

Just to make things a bit more spicy, I need to take a self contained flutter web app and display it with LiveView. I’m following the guideline flutter_embedding. After reading enough about JS and how to work with LiveView I decided to create a hook. I moved all the generate flutter web files in the js/vendor/flutter_web folder. The hook is mounting correctly but I have no idea how to proceed. I’m not able to display anything in my flutter_targer div.
I’m sure that I’m missing something trivial but my inexperience with the web world is not helping me. Can somebody please point me in the correct direction?

import "../vendor/flutter_web/flutter.js"

let Hooks = {}

Hooks.Video = {
    mounted(){
        console.log("Hook Mounted!", this.el);

        window.addEventListener("phx:page-loading-start", function (ev) {
            // Embed flutter into div#flutter_target
            let target = document.querySelector("#flutter_target");
            _flutter.loader.loadEntrypoint({
              onEntrypointLoaded: async function (engineInitializer) {
                let appRunner = await engineInitializer.initializeEngine({
                  hostElement: target,
                });
                await appRunner.runApp();
              },
            });
          });
    }
}

did you figure out how to run this hook? as in you need some HTML to make this code run: JavaScript interoperability — Phoenix LiveView v0.20.1

I’m using some html in the defmodule. This is how I defined it.

def render(assigns) do
    ~H"""
    <div class="mx-auto max-w-sm">
      <.header class="text-center">
        Text Example
      </.header>
      <div id="flutter_target" style="height: 200px; width: 200px" phx-hook="Video"></div>
    </div>
    """
end

def mount(_params, _session, socket) do
    {:ok, socket}
end

During the loading of the page, if I put a breakpoint, it seems that the line

let target = document.querySelector("#flutter_target");

is never reached.

I imagine you exported your hook and imported it in “app.js” and passed it to the Liveview socket.

This is the code that I wrote in app.js:

import "../vendor/flutter_web/flutter.js"

let Hooks = {}

Hooks.Video = {
    mounted(){
        console.log("Hook Mounted!", this.el);

        window.addEventListener("phx:page-loading-start", function (ev) {
            // Embed flutter into div#flutter_target
            let target = document.querySelector("#flutter_target");
            _flutter.loader.loadEntrypoint({
              onEntrypointLoaded: async function (engineInitializer) {
                let appRunner = await engineInitializer.initializeEngine({
                  hostElement: target,
                });
                await appRunner.runApp();
              },
            });
          });
    }
}

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
    params: {_csrf_token: csrfToken},
    hooks: Hooks
})

And in the console, I receive the message:
“Hook Mounted!”

<div id="flutter_target" phx-hook="Video"></div>

Instead of the document.querySelector you can use this.el. And then I would add phx-update="ignore" to the element with the Hook on it in the template. Or, if you want to receive updates, implement an update() function in the Hook.

Did you try without the “phx:page-loading-start” listener?

In case you want dynamic import, check this

1 Like

@ndrean thank you for the reading, for sure the dynamic import could be useful in the future.

If I drop the listener I get an error on the _flutter.loader… telling me “Uncaught ReferenceError: _flutter is not defined”

Now I don’t know if this is expected because something is not loaded, or there is an issue with my import flutter.js. The path is correct and at the end of the flutter.js file I can read _flutter.loader = new FlutterLoader();