dartway_studio_binding

The app half of the DartWay Studio wiring: one widget that connects a Flutter app to Studio's live preview.

dartway_studio_bridge is the wire — the protocol both sides depend on. This package is the side that speaks it: it attaches the host, reports the route, the features mounted on screen, the session and the language, and executes what Studio asks for.

Why it is a package

None of this refers to any domain. It is protocol wiring nobody edits, and it used to be scaffolded into every project by copy-paste — a binding widget, a persona sign-in flow, a session adapter and a route adapter. That drifts: the guard that stops an empty passport flashing in Studio between screens once lived in one copy and was missing from the other, and neither copy knew the other existed.

It is not part of the core, because the screen identity Studio binds to is the route's declared name — a dartway_router concept, and a dependency that has no business on apps that never open Studio. It is not part of the bridge either: the bridge's version means the wire, and host-side changes bumping it would turn "the bridge changed, catch up" into noise.

Use

Mount it in MaterialApp.builder — outside the router subtree, so the route is observed through the router delegate rather than a GoRouterState that only exists below the router.

MaterialApp.router(
  routerConfig: router.router,
  builder: (context, child) => DwStudioBinding(
    core: dw,
    manifest: appStudioManifest,
    router: ref.watch(appRouterProvider),
    describeUser: (profile) =>
        DwStudioUser(identifier: profile.phone, label: profile.firstName),
    locale: DwStudioLocale(
      provider: appLocaleProvider,
      select: (code) =>
          ref.read(appLocaleProvider.notifier).selectLanguageCode(code),
    ),
    validateAccessToken: studioSignedAccessValidator(
      const String.fromEnvironment('STUDIO_APP_ORIGIN'),
    ),
    child: child ?? const SizedBox.shrink(),
  ),
);

The widget renders child untouched and stays inert outside an iframe, so a production build carries it at no cost and needs no flag to turn it off.

What the project still supplies

Four things, because the framework cannot know them:

manifest What the app declares about itself: its zones and their screens
describeUser Which profile field is the identity Studio matches its personas against, and which one names the user on screen
locale The app's own language provider and switcher — omit it and Studio's language switcher stays inert, which is right for an app that ships one language
validateAccessToken Who may drive this app. Pass studioSignedAccessValidator(...); left null, any Studio is accepted — right on a laptop, wide open in production

Screen passports stay in the project too. Build them from routes rather than from path strings, so a path refactor cannot leave a passport pointing at a screen that no longer exists:

final scheduleStudioSpec = dwStudioSpecForRoute(
  AppNavigationZone.schedule,
  title: 'Schedule',
  purpose: 'What is on this week.',
);

Features

A widget that is a product feature implements DwFeature and declares its DwFeatureSpec next to itself. The binding reports the ones currently mounted, re-scanning over a short window after each route change — a screen is not on screen at the moment the route naming it changes.

Only the last re-scan may report an empty screen. An early one often lands between screens and finds nothing; that is "not ready yet", not "has no features", and Studio takes these reports at face value.

Libraries

dartway_studio_binding
Binds a DartWay app to DartWay Studio.