Bringing Flutter to the Web: A Guide to Element Embedding

5 min read

Shaikh Afroz Avatar

Shaikh Afroz

Mobile Engineer

#flutter #web #element-embedding
Hero Image

At the recent Google I/O event, we were introduced to a host of exciting new features. Among them, the Flutter team gave us an exclusive sneak peek into their upcoming Flutter Forward event.

In this post, we’ll take a closer look at Element Embedding—what it is, how it works, and how it can supercharge your web applications.

On a personal note, I had the privilege of speaking at the Flutter Forward event about Element Embedding. Feel free to check out the video below to dive deeper into the topic.

What is Element Embedding?

What is Element Embedding? Element Embedding allows you to seamlessly integrate your Flutter application into a traditional web environment. This means you can embed Flutter within existing web frameworks like React, Vue, or Angular, and enable communication between Flutter and web technologies.

This hybrid approach brings the best of both worlds—giving you the native capabilities of Flutter, while still leveraging web styling via CSS and managing application state with JavaScript. The result is a powerful combination that enhances the flexibility and performance of your web applications.

To see it in action, be sure to check out the Flutter Element Embedding Demo.

🌐

Check out Google Demo on Element Embedding. Google IO demo

How it Actually Works ?

Maintaining state between Flutter and JavaScript is achieved through interoperability, which facilitates communication between different programming languages. This allows you to pass data, trigger functions, and synchronize state changes seamlessly across both environments.

On the Flutter side, the process involves using the JSExport annotation to expose specific properties and methods to JavaScript. You can then create callbacks to handle interactions between Flutter and JavaScript.

On the JavaScript side, you can access and manipulate the properties and methods that are made available from Flutter, enabling seamless bi-directional communication.

how

To get started, we first need to initialize Flutter on the web side. In your index.html, you will specify the Flutter entry point and identify the target <div> element where the Flutter app will be embedded. Here’s how you can do it:

Set up the HTML structure: Define a div that will serve as the container for your Flutter app. Initialize Flutter: In your script, load Flutter’s entry point and specify the target element where it should render.

index.html
<script defer>
window.addEventListener("load", function (ev) {
// Embed flutter into div#flutter_target
let target = document.querySelector("#flutter_target");
// Load the entrypoint
_flutter.loader.loadEntrypoint({
onEntrypointLoaded: async function (engineInitializer) {
let appRunner = await engineInitializer.initializeEngine({
hostElement: target,
});
await appRunner.runApp();
},
});
});
</script>

Once that’s done we call an Immediately invoked function expression, through which we access the Window object and call our flutter functions.

window._stateSet = function () {
window._stateSet = function () {
console.log("Call _stateSet only once");
};
let appState = window._appState;
let valueField = document.querySelector("#value");
let updateState = function () {
valueField.value = appState.count;
};
//Register a callback to update the HTML field from Flutter
appState.addHandler(updateState);
//Render the first value -- 0
updateState();
let incrementButton = document.querySelector("#increment");
incrementButton.addEventListener("click", (event) => {
appState.increaseCount();
});
}

On the Flutter Side, in the initState we use js and js_util packages to access utility functions that give us access to the javascript window object

@override
void initState() {
super.initState();
final export = js_util.createDartExport(this);
js_util.setProperty(js_util.globalThis, '_appState', export);
js_util.callMethod<void>(js_util.globalThis, '_stateSet', []);
}

Once that’s done, we can annotate any functions with @js.JSExport() and then call the function in the immediately invoked function expression

@js.JSExport()
void randomFunction(){
setState((){
// Do Something Here !!
});
}

Check out the whole Source Code and an Example with Rive I made for Element Embedding

Element Embedding opens up exciting possibilities for integrating Flutter with traditional web applications. By enabling seamless interaction between Flutter and web technologies like JavaScript, React, Vue, and Angular, it allows developers to take advantage of the best features from both ecosystems. Whether you’re looking to leverage Flutter’s native capabilities or enhance your web app with dynamic, interactive Flutter components, this approach offers a powerful solution.

As we’ve seen, setting up Element Embedding is straightforward, involving basic configuration on both the Flutter and JavaScript sides. With the power of interoperability, you can create rich, hybrid experiences that combine the flexibility of web development with the performance and native feel of Flutter.

If you’re curious to explore more, I encourage you to try it out with the demo and the source code linked earlier. Happy coding!

Afroz