restage_codegen 2.0.0
restage_codegen: ^2.0.0 copied to clipboard
Write your UI as real Flutter widgets — your own design system, not a dialect — and restage_codegen compiles it into the blobs the Restage SDK ships over the air.
Changelog #
2.0.0 #
A condition in a screen may negate (!), combine with && and ||, and
compare a String state field, parameter, or context value to a literal, both
as a ternary condition and bound directly to a boolean input such as
Visibility(visible:). A collection-if whose condition is known only at run
time lowers to a conditional element in a list input of any type, with else
and nesting, so children: [if (isPro) Badge()] no longer needs a
Visibility wrapper. An element whose condition does not hold is absent
rather than empty, matching Flutter: a Column's spacing, a Wrap's runs,
and a Stack's indices all count the children that are there. Both work
inside a custom widget: a conditional element is an inlining mechanism the
build implements, and a String or int constructor parameter is a comparable
reference, so an equality chain may key on a parameter as well as on a state
field.
A widget slot typed PreferredSizeWidget lowers through a runtime adapter
instead of a downcast, so Scaffold(appBar: ...) and AppBar(bottom: ...)
reach the delivered screen. The slot content's height travels beside the slot,
because a rendered slot value cannot report a size of its own: the build reads
it from the authored app bar, tab bar or PreferredSize, sums a bar with the
widget under it, and refuses a widget whose height it cannot read.
Size.fromHeight(...) lowers to its bare height at a slot that surfaces a size
as a height alone.
A ternary comparing Theme.of(context).brightness against Brightness.light
or Brightness.dark lowers to a branch on the published brightness token, so a
screen can choose its own dark-mode values. That comparison is a condition like
any other: it negates, composes, binds to a boolean input, and gates a
collection-if.
A text-theme field read — Theme.of(context).textTheme.titleLarge?.fontSize,
with or without the null-aware step — lowers to its published binding. A whole
style read into a TextStyle slot, style: Theme.of(context).textTheme.titleLarge,
binds each published field of that style, and a trailing copyWith overrides
the fields it names with literals, other theme reads, or values in scope.
copyWith on a theme style is limited to the fields the theme publishes. An
enum-valued theme token binds only to a slot whose shape names that same enum,
and the published fallback-family list and decoration token bind to their own
slot types.
Screens read the device they render on. MediaQuery.sizeOf(context),
MediaQuery.paddingOf(context), MediaQuery.devicePixelRatioOf(context),
defaultTargetPlatform, Theme.of(context).platform, and
Localizations.localeOf(context).languageCode lower to the published device
data, and a platform or locale-subtag comparison in a ternary lowers to a match
on it. A numeric comparison on device data is refused: the artifact's data
language has no comparison operators. kIsWeb and the dart:io Platform.isX
getters lower to the matching platform key, and a device comparison used as a
ternary condition now inlines inside a custom widget instead of refusing.
MediaQuery.sizeOf(context).shortestSide and .longestSide, and
MediaQuery.orientationOf(context), lower too, and an orientation comparison
lowers to a match on the token. A device match is a condition like any other:
it negates, composes, binds to a boolean input, and gates a collection-if.
MediaQuery.viewPaddingOf(context).<side> and
MediaQuery.of(context).viewPadding.<side> lower to the published view
padding. Both inset families lower to the value published at the mount point in
every position: the read resolves at the screen's build context, which sits
above every consumer the screen authors. A read inside a custom widget refuses,
because the call sites that compose it decide its context.
A named widget list on a catalog widget lowers under its own name, so
AppBar.actions reaches the delivered screen. The canonical children slot is
unchanged.
The Measurement ledger accepts a reviewed introduction: an entry under
acceptedIntroductions naming a structural key declares the node new, so a
retired node with the same fingerprint is not proposed as its prior. An entry
is honoured once and retires itself. The ledger's acceptedIntroductions key
is always written and required on read.
Breaking: typed survey flows emit schema version 2. Their generated flow
documents include surveyQuestionOrder, preserving the authored order of
declared survey answer identifiers.
Breaking: one generated handle per annotated class. The build now emits a
single top-level <className>Ref for every annotated screen and flow, instead
of two different shapes depending on which frontend produced it.
| Source | Handle | Type |
|---|---|---|
@Screen(id:, surface:) on WelcomeScreen |
welcomeScreenRef |
SurfaceScreenRef<WelcomeScreenEvent> |
@Screen() on QuestionScreen |
questionScreenRef |
NeutralFlowScreenRef |
@FlowGraph(surface:) on FirstRunFlow |
firstRunFlowRef |
SurfaceFlowRef<FirstRunResult> |
For neutral @Screen() screens and for flows, the previous
<ClassName>Descriptor holder is still emitted as a @Deprecated alias whose
ref forwards to the new handle, so existing source compiles through 2.x. It
is removed at 3.0.
A categorized @Screen(surface:) is the exception: its holder is deleted
outright rather than deprecated, because the reference it carried was the
in-flow neutral one described below, which a categorized screen no longer
generates at all.
A flow's buildFlow() accepts either spelling for its screen references
(welcomeScreenRef or the deprecated WelcomeScreenDescriptor.ref), and both
resolve to the same screen.
A categorized @Screen(surface:) no longer emits an in-flow neutral
reference; a screen used as a flow step is a neutral @Screen().
The authored flow base class the compiler recognises is unchanged:
RestageFlow. A source that extends the host widget RestageFlowGraph by
mistake is already an analyzer error (the widget is a final class); the
compiler detects that supertype specifically to say which class was wanted.
Generated Dart changes; generated delivery artifacts do not.
Also in 2.0.0 #
- Generate
registerRestageWidgets()as the app startup helper.registerRestageCustomerWidgets()remains a deprecated forwarding alias through 2.x and is removed at 3.0. - Preserve generated catalog wire IDs across class renames and source moves.
A likely rename now stops before allocation and prints the exact append-only
renameordeprecateevent needed to state the author's intent. Stable source and class identity detect moves even when properties change in the same edit; deprecated identities are never reused. - Each generated output roster entry records the condition under which its path is written: for every lowering of its source, or only when the lowering produces one particular thing. Paths are reserved before the translator decides what a source lowers to, so this is how a consumer of the ledger tells which reservations are written together. Only paywall reservations are conditional; every screen and flow reservation is written for every lowering. The roster remains an output-ownership ledger, and the publication manifest remains the produced-output truth.
- Derive custom catalog properties, requiredness, order, constructor defaults, and descriptions from resolved constructor/Dartdoc facts across RFW, A2UI, and Widgetbook.
- Derive callback admission from resolved constructor shape: void return with zero arguments or one required positional payload in the target vocabulary. The exact callback property name is the event identity; no event declaration or rename annotation is used.
- Resolve only the sources that can declare something. All seven builders
that scan a whole package (the five applied automatically, plus the A2UI
catalog and Widgetbook story builders you opt into) now read raw source
first and resolve just the files spelling a Restage annotation, so a package
that declares nothing pays no analysis for them, the difference that matters
on large codebases, where resolving every file also made every builder depend
on every file. What you write is discovered as before: an annotation written
on the declaration, behind an import prefix, in a
part, or through aconstortypedefalias declared in another file in the same package all reach the same place they always did. An alias declared in a different package is the one shape that is not followed: these builders scan only the package they are building, so they never see that declaration. - A syntax error in a file that spells no Restage annotation is no longer
reported by these builders. They no longer analyse such files; the Dart
toolchain reports the error either way. The deprecated screen path
lib/<onboarding|message|survey>/screens/<id>.dartkeeps its diagnostics: the per-file screen builders still analyse a library there annotated or not, and the two opt-in package-wide builders still select it. - Write the two roster ledger files into a package only once it declares a Restage source, or reports a problem with one, instead of into every dependent.
- Decode the surface publication compiler handoff once per package per build rather than once per Dart library.
- Consume
a2ui.Configusage/write-back metadata. - Generate ordinary native Widgetbook v4 story source for custom widgets in
the same
build_runnerinvocation, including custom structured values and read-onlydescription/usagesidebar metadata. - Emit the A2UI Dart catalog and standalone document together under
lib/generated/, with no root-level compatibility aliases. - Derive every custom
WidgetandList<Widget>constructor input as an independently named child-bearing property across the RFW, A2UI, and Widgetbook targets; several exact names may coexist on one class. - Breaking generated A2UI layout: nest every custom widget and opaque
native-screen constructor input under one required
propsobject while leaving protocolidandcomponenton the envelope. Exact source names, including envelope-collision names, are preserved without aliases. - Share one analyzer-backed
ScreenSourceadmission contract between RFW and native A2UI/Widgetbook siblings; invalid path, ID, part, or source count fails before any sibling output is written. - Let one
@RestageWidgetopt out of selected package-enabled targets and let one safely omissible constructor input use@Ignorefor selected targets, without suppressing diagnostics or properties in its siblings. - Breaking.
paywallPurchaseandpaywallPriceForare rejected withIssueCode.unsupportedCommerceAuthoring; codegen no longer lowers either former authoring form into commerce events or product-data references.paywallEventremains available for generic custom events, while commerce operations belong in explicit host-controlled code.
1.3.0 #
- Give generated A2UI catalogs a deterministic content-derived catalog ID and carry that same identity through the generated Dart and standalone document.
- Project typed constraints onto literal schema arms, preserve controlled literal, path, and call value sources, and keep application-side runtime validation responsible for resolved values.
- Preserve nested structured-data descriptions and emit deterministic definition/reference documentation.
- Generate native Widgetbook v4 story inputs for custom
@RestageWidgets during the ordinarybuild_runnerinvocation, without auxiliary authoring. - Generate a typed
<Screen>Surfacewidget for each screen whose constructor takes host values. It mirrors the authored constructor, serializes the values, and keeps the authored widget as its fallback. - Lower constructor reads and
forover a context list from exact path and type evidence. - Resolve const identifiers at property and collection slots through their declarations.
- Accept a finite prelude of
finalorconstlocals before the returned widget expression; an unread local with side effects is refused. - Lower static collection elements: finite
forelements, conditionals that fold to a constant, and non-null spreads. - Lower value-callback closures at event slots on catalog widgets, segmented controls, and inlined custom widgets.
1.2.0 #
- Compile custom
@RestageWidgetcode into a standalone A2UI catalog: the custom-only builder emits an A2UI document + generated Dart for the app's own widgets, alongside the built-in catalog. - Carry producer-facing metadata into generated A2UI catalogs: widget and
property descriptions, plus the new optional
usagesteering text, emit into the document's system-prompt fragments. - Support general-delivery flows:
@FlowSource(delivery: FlowDeliveryMode.general)generates flows with untypedMapresults, checked by build-time validators. - Add message and survey screen/flow builders: flow and screen codegen is now surface-parameterized, so message and survey surfaces reuse the onboarding builders.
- Encode opaque lists of structured values in the custom catalog (pairs
with
rfw_catalog_schema1.1.0 /rfw_catalog_compiler1.1.0).
1.1.0 #
- Emit the custom widget catalog (
catalog.json) so registered custom widgets resolve in authored surfaces. - Additive codegen support for upcoming surface work; no breaking changes.
1.0.4 #
- Carry each component's full data schema in the standalone A2UI catalog document, not only the component discriminator, so a consumer reading the document alone (without the generated Dart) sees every component's fields and can generate rich payloads against it.
- Suppress
unused_elementin the generated A2UI catalog so a catalog that references only some of the shared helpers analyzes clean in the consumer's project.
1.0.3 #
- Emit a rich A2UI catalog for a custom
@RestageWidgetwhose property is typed as a data class: nested data classes, lists of objects, String-keyed maps, and named records each generate agenuischema that reconstructs and renders the value, with a fail-safe on a missing required value. - Infer a structured property's required-ness from the widget's default constructor, so a value the constructor requires is marked required even when the annotation omits it.
- Exclude a custom widget carrying a structured property from the RFW catalog/factory build (a non-fatal, logged exclusion); it renders via the A2UI emit target; native (RFW) rendering of custom structured data is a tracked future capability.
1.0.2 #
- Lower the
analyzerceiling to>=10.0.0 <13.0.0.NamedExpressionwas removed andArgumentList.argumentschanged toNodeList<Argument>in analyzer 13.0.0 (not 14.0.0 as the 1.0.1 note stated), so the previous<14.0.0constraint admitted analyzer 13.x, which this package's argument-list lowering does not compile against. Thebuild_runnertoolchain resolves analyzer 12.x anyway, so this matches what consumers actually use.
1.0.1 #
- Widen the
analyzerdependency constraint to>=10.0.0 <14.0.0: raise the floor to a verified-compiling version and admit analyzer 13.x. The ceiling stays below 14.0.0 because analyzer 14 removedNamedExpressionand reshaped argument lists, which this package's lowering relies on; thebuild_runnertoolchain (build) likewise does not yet support analyzer 14. - Add an example.
1.0.0 #
- Initial release of the Restage build-time code generator: the
build_runnerbuilders that compile Flutter-authored surfaces (annotated source classes and hand-authored.rfwtxt) into.rfwtxt/.rfwblobs, capability manifests, flow documents, and generated screen/flow descriptors. Includes structured-type decomposition, constant folding, theme-binding lowering, capability derivation, and the optional A2UI (genui) emit target.