Fluttercon talk demo: one shared Flutter codebase powers a native mobile app and multiple independent Flutter widgets embedded in a plain HTML host page via the Flutter Web Multi-View API, with type-safe JS interop.
packages/booking_core/ Shared models, BookingCubit, PriceEngine, 3 widgets
apps/booking_mobile/ Single-screen mobile app (runApp)
apps/booking_web_embed/ Multi-view web shell (runWidget) + web/index.html Widgetbook host
apps/booking_angular/ Angular host consuming the Flutter web embed build output
apps/booking_react/ React (Vite) host consuming the Flutter web embed build output
- One
BookingCubitdrives PassengerStepper, FareSummary, and SeatMap across all targets. booking_coreis platform-agnostic — no web imports.- Host owns navigation — Flutter emits a typed
BookingDrafton continue; the host decides what to do with it (Widgetbook shows the JSON payload).
- FVM with Flutter 3.32.8 (see
.fvm/version) - Dart ^3.8.0
dart pub get && dart run melos bootstrap
dart run melos run analyze
dart run melos run testcd apps/booking_mobile
flutter runThe app preloads the demo JFK → SFO flight (S2). Use the AppBar menu to simulate seat error (S7) or reset the demo.
cd apps/booking_web_embed
flutter run -d chrome --web-port=8080Open http://localhost:8080/ (web/index.html).
Or use the VS Code launch config "booking_web_embed" in .vscode/launch.json.
A second framework host lives at apps/booking_angular. It mounts the same Flutter embed with an Angular header and fare summary component.
# From repo root — build Flutter assets for /flutter/ subpath
dart run melos run build:flutter-for-hosts
cd apps/booking_angular
npm startOpen http://localhost:4200/.
Production bundle (Flutter + Angular):
dart run melos run build:angularSee apps/booking_angular/README.md for details.
A third framework host lives at apps/booking_react. It mounts the same Flutter embed with a React header and fare summary component.
# From repo root
dart run melos run build:flutter-for-hosts
cd apps/booking_react
npm run devOpen http://localhost:5173/.
Production bundle (Flutter + React):
dart run melos run build:reactSee apps/booking_react/README.md for details.
| State | How to trigger | Expected |
|---|---|---|
| S2 | Load index.html |
Seat map mounts; fare summary syncs; pax placeholder visible until a seat is selected |
| S3 | Select a seat, then change passengers in #pax |
Pax Flutter view mounts on first seat; fare and seat requirement update in #summary |
| S4 | Select some but not all seats | CTA still disabled |
| S5 | Select all required seats | CTA enabled |
| S6 | Click Continue on web component | Draft JSON panel appears at bottom of page |
| S7 | Toolbar → Simulate seat error | Seat map errors; pax/summary still work; retry restores map |
| S8 | Clear all seats (tap/chip × or Clear seats) | Pax Flutter view unmounts via app.removeView; placeholder returns |
Mobile covers S2–S5 and S7 on a single screen.
| API | Direction | Purpose |
|---|---|---|
window.booking.controller |
Flutter → host | Command surface + business state sync |
controller.onStateChanged(snapshot) |
Host sets listener | Real-time BookingSnapshot updates |
controller.getState() |
Host → Flutter | Latest snapshot (late subscribers) |
controller.requestContinue() |
Host → Flutter | Validate and emit draft |
controller.applyPromo(pct) |
Host → Flutter | Apply discount |
controller.clearSeats() |
Host → Flutter | Clear all selected seats (unmounts pax when empty) |
controller.simulateSeatError() |
Host → Flutter | S7 domain demo |
initialData on addView |
Host → Flutter | { slot, flightId, cabin, occupied, ... } |
window.booking.onContinue(draft) |
Flutter → host | Hand off completed booking draft |
Listen on each view's hostElement (e.g. #seatmap, #pax):
hostEl.addEventListener('flutter_booking_view_error', (e) => {
console.log(e.detail.code, e.detail.message);
});
hostEl.addEventListener('flutter_booking_view_ready', (e) => {
console.log(e.detail.viewId, e.detail.slot);
});Listen on window for app-level failures:
window.addEventListener('flutter_booking_app_error', (e) => {
console.log(e.detail.type, e.detail.message);
});Fare summary on web is <booking-fare-summary> (not a Flutter view). Seat map and passenger stepper are Flutter embeds.
Pull requests run melos run analyze and melos run test via .github/workflows/main.yaml.