Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Flutter Web Declarative Bootstrap (PUBLICLY SHARED)

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

SUMMARY

Bootstrap Flutter Web through Custom Elements.


Make Flutter Web embedding better with modern
web development (frameworks and tooling).
Author: David Iglesias (@ditman)
Go Link: flutter.dev/go/web-declarative-bootstrap
Created: March/2023 / Last updated: March/2023

WHAT PROBLEM IS THIS SOLVING?


The current Flutter Web embedding uses an imperative process provided by the
flutter.js file. The current process creates global namespaces in the page, and
uses a "script" approach to initialization.

This style doesn't mix well with current web development practices that rely on a
more modular syntax (ESModules are a thing now!), and bundlers, and makes
Flutter Web a little bit "odd" to integrate in their Flutter/Lit/Angular projects.

This document proposes a declarative bootstrap mechanism that allows to hide the
initialization complexity behind standard DOM APIs, like Custom Element and ES
Modules.

PUBLICLY SHARED
PUBLICLY SHARED
BACKGROUND
Readers of this document need to have some knowledge of current Web
Development practices (common build workflows, bundlers, transpilers...), and/or
have experience attempting to integrate Flutter Web into another modern web
framework, like Angular or ReactJS.

Audience
The audience for this document is Flutter Web programmers, especially those who
want to combine Flutter web with other, complex, web frameworks, rather than
using a Flutter Web app controlling the whole browser window.

Glossary/Links
● Using custom elements - Web Components | MDN
● Custom Elements v1 - Reusable Web Components | web.dev
● Custom Element Best Practices | web.dev
● TBD - TBD

OVERVIEW
Non-goals
● TBD

USAGE EXAMPLES
The current version of Flutter bootstraps apps through *imperative* code:
programmers need to declare their HTML structure first, then run some additional
JavaScript so Flutter knows where to render into the previously defined HTML.

With a declarative approach, programmers would be able to initialize and run their
flutter apps in a way that is much more similar to any standard website, using
something that looks like regular HTML tags.

Compare the current approach with the proposed one in the table below
(equivalent bits of code highlighted in similar colors):

PUBLICLY SHARED
PUBLICLY SHARED

Current approach (Imperative) Proposed approach (Declarative)

Unset Unset
<html> <html>
<head> <head>
<script src="flutter.js" defer></script> <script src="flutter.js" defer></script>
</head> </head>
<body> <body>
<!-- website markup --> <flutter-app option="value">
<div id="view_1"></div> <!-- website markup -->
<!-- website markup --> <flutter-view id="view_1"></flutter-view>
<script> <!-- website markup -->
window.addEventListener("load", </flutter-app>
function(_) { </body>
_flutter.loader.loadEntrypoint({ </html>
option: 'value',
});
});
</script>
</body>
</html>

The proposed approach leverages Custom Elements, which are a part of the Web
Components API.

Custom Elements PoC


Custom Elements can be used to program new HTML tags, by extending the
existing ones. Custom Elements integrate well with the DOM, and can be used as
any other tag, except programmers can control their lifecycle, behavior and DOM/JS
interface, in a "reactive" manner.

In a nutshell: one can define a <custom-app> element that can contain any markup
(like <div>s do), but which exposes a special setColor method that can be called
on an instance of the custom element obtained with querySelector (or any other
DOM selection method).

Here's a PoC that does just that:

● https://jsfiddle.net/ditman/wzqban5u

It demonstrates how a custom-view element can be configured via DOM attributes,


and how clicking on it can trigger a method call in its parent custom-app.

In addition to allowing Flutter Web to provide an easy to use initialization for a


Flutter app, it's also remarkable that custom element markup can be used in any
page before they're even registered. The browser will render unknown elements

PUBLICLY SHARED
PUBLICLY SHARED
as containers, until the required JavaScript loads. Then, the elements are
"upgraded" (progressively enhanced) by the browser and their custom behavior
kicks in.

This is a desirable feature for Flutter Web. Programmers can declare what Flutter
Web app to load, and where in the DOM it'll render, but still retain control on when
the Flutter bundle actually starts to download (never before flutter.js is done
upgrading the custom elements!)

DETAILED DESIGN/DISCUSSION
This document proposes defining two new Custom Elements that Flutter Web
programmers may use to embed their apps as described above:

<flutter-app>
The flutter-app Custom Element takes care of getting the app ready to go. It
maps roughly to the existing loadEntrypoint function, and would receive the same
configuration.

When the flutter-app element is connected to the DOM, it:

● attempts to configure the Flutter Service Worker with the files for this flutter
app.
● downloads the engineInitializer object from the appropriate bundle
(main.dart.js)
● waits for a flutter-view element to connect to start rendering the app into
that view
● keeps a reference to the currently running app, so additional views can be
connected/disconnected to it.

If the flutter-app is ever disconnected from the DOM, we should attempt to do as


much cleanup as possible. (Current Flutter apps don't do this.)

<flutter-view>
The flutter-view element is equivalent to a <div> that is passed to Flutter as a
hostNode.

When the flutter-view element is connected to the DOM, it:

● locates its parent flutter-app element


● registers itself as an available view, with its given ID

PUBLICLY SHARED
PUBLICLY SHARED
The Flutter app can then start rendering content into this new container.

In a multi-window app, it is likely that multiple flutter-view elements will be


placed inside one flutter-app. (This is currently not supported by Flutter web,
which only understands a single view).

The rest of the Custom Element lifecycle callbacks (AKA "reactions") can be used to
mimic adding/removing additional windows to an existing app. The design of this
multi-window management API is still TBD.

ACCESSIBILITY
Since these widgets are only used to initialize and render the actual Flutter app, it's
likely that their ARIA role is none.

The Flutter app itself (contents *inside* the flutter-view elements) should
provide the appropriate accessibility information.

INTERNATIONALIZATION
This proposal doesn't affect internationalization.

INTEGRATION WITH EXISTING FEATURES


flutter.js
There's an opportunity to truly modularize flutter.js. Use it not only to define the
Custom Elements described above, but to also export the components that we use
to build the Flutter bootstrap so users can build their own (without hacking the
contents of the file).

main.dart.js
The current bootstrap process relies on known locations in JS
(_flutter.loader.didCreateEngineInitializer) so Dart can "handshake" and
tell the loading process when it's ready to rumble.

Ideally, the output of main.dart.js should be an ES Module so we can use import


when initializing a flutter-app:

PUBLICLY SHARED
PUBLICLY SHARED

JavaScript
import { engineInitializer } from "main.dart.js";

With that, the need of a common *known* location on a JavaScript namespace is


negated (and all the race conditions associated with it).

flutter_service_worker.js
The Service Worker implementation that Flutter Web vends by default is generated
at build-time and specific to a single Flutter app.

Once Flutter Web apps start to get integrated with more sophisticated host
websites, the two following problems may appear:

● The host site already contains its own Flutter service worker:
○ If we vended the building blocks of a flutter service worker,
programmers could combine their existing service worker with
Flutter's.
● The host site wants to embed more than one Flutter app.
○ A single service worker must be shared across all applications. This
would require the Flutter service worker to be much more general
than it is now; and configurable by any application that needs it.
● More than one of the above?

Browser support
Custom Elements is one part of the Web Components API. Flutter Web already
leverages other parts of that API (Shadow DOM and slots), so Custom Elements
should be available in all the browsers that Flutter Web supports.

flutter create
Once this feature lands, we might want to update the generated index.html file for
Flutter apps so they use the Custom Element-based embedding, rather than the
imperative one. (This is a product decision though).

OPEN QUESTIONS
● Do we need help from dart compilers to be able to export as ESModules?
● How does this play with the upcoming wasm/skwasm targets?

PUBLICLY SHARED
PUBLICLY SHARED
TESTING PLAN
There's a suite of tests that test that Flutter Web apps can be embedded into
different styles of index.html files. At least, a new test case should be added to
that test suite.

DOCUMENTATION PLAN
This would need updating/extending the following pages on the docsite:

● Customizing web app initialization | Flutter


● Build and release a web app | Flutter
● GitHub - flutter/put-flutter-to-work: A Flutter add-to-app demo you can try
with your own apps (?)

MIGRATION PLAN
If possible, the current imperative way of initializing a Flutter Web app should be
maintained alongside the new mechanism, to maintain backwards compatibility.

PUBLICLY SHARED

You might also like