Skip to content

Repository files navigation

VeryGoodBooking — Bridging Ecosystems Demo

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.

Architecture

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 BookingCubit drives PassengerStepper, FareSummary, and SeatMap across all targets.
  • booking_core is platform-agnostic — no web imports.
  • Host owns navigation — Flutter emits a typed BookingDraft on continue; the host decides what to do with it (Widgetbook shows the JSON payload).

Prerequisites

Setup

dart pub get && dart run melos bootstrap
dart run melos run analyze
dart run melos run test

Run mobile

cd apps/booking_mobile
flutter run

The app preloads the demo JFK → SFO flight (S2). Use the AppBar menu to simulate seat error (S7) or reset the demo.

Run web embed (Widgetbook)

cd apps/booking_web_embed
flutter run -d chrome --web-port=8080

Open http://localhost:8080/ (web/index.html).

Or use the VS Code launch config "booking_web_embed" in .vscode/launch.json.

Run Angular host

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 start

Open http://localhost:4200/.

Production bundle (Flutter + Angular):

dart run melos run build:angular

See apps/booking_angular/README.md for details.

Run React host

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 dev

Open http://localhost:5173/.

Production bundle (Flutter + React):

dart run melos run build:react

See apps/booking_react/README.md for details.

Demo states (web Widgetbook)

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.

Host ↔ Flutter API

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

CustomEvents (per-view and app-wide)

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.

CI

Pull requests run melos run analyze and melos run test via .github/workflows/main.yaml.

Package READMEs

About

A demo app for the talk "Bridging Ecosystems: Embedding multiple Flutter components into any web app" at Fluttercon 26

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages