widgets library

The opinionated widget layer: interesting, correct uses of the morph engine, shipped as ready recipes - press springs, liquid selection, glass tethers. Everything here is built strictly ON package:morph/foundation.dart and motor; the engine never depends on this layer, and taste knobs here are expected to move with the owner's app.

This layer is about USING morph well, not about reproducing a platform's look: there is no glass shader here and none is planned - surface shading of that kind is the platform's business, not this package's.

Classes

ChaseSpring
A critically damped spring chasing a MOVING target, integrated step by step by its owner's clock (a Ticker's elapsed deltas).
CupertinoMotion
A collection of spring motions that are commonly used in Cupertino apps.
CurvedMotion
A motion based on a fixed duration and curve.
MaterialSpringMotion
Material Design 3 spring motion tokens for expressive design system.
MorphAnchor
The declarative layer on top of the engine: a morph as a function of state, in the spirit of SwiftUI's matchedGeometryEffect, adapted to Flutter's rules.
MorphController
A single retargetable scalar spring from which all derived morph values are computed. An interruption (open during close and vice versa) is a new SpringSimulation starting from the old one's current value and velocity: the velocity carry-over automatically makes the interruption consistent for every derived property.
MorphFlight
One flight: the tag -> shuttle -> target trio. Lives from launch to close finalization. A repeated showMorph on the same tag does not create a new flight - it retargets this one, which makes interruption free for the caller.
MorphFlightScope
Gives overlay content access to its flight (e.g. MorphReveal finds it here) without threading the flight through builder parameters.
MorphFrame
An immutable snapshot of all render values for one flight frame.
An explicit pipe between two pieces by id - for distant pieces where proximity fusion is not enough. width defaults to half the smaller height of the pair.
MorphMass
A raw SDF mass poured into a skin - contentless geometry fused into the contour alongside the pieces (MorphSkin.extraMasses). A mass has no morph identity: it cannot fly, it only adds to the shape. Sealed: MorphMass.box and MorphMass.bridge are the whole vocabulary.
MorphMenuItem
An entry of showMorphMenu.
MorphMotion
A morph's motion profile: a pair of Motions from the motor package plus closeVelocityHint. Character can only be changed as a whole profile (a mid-flight profile swap retargets the simulation); custom profiles are built through the public constructor from any Motions (Cupertino presets, Material tokens, curves, custom springs).
MorphPageRoute<T>
The route half of showMorphRoute; usable directly with Navigator.push for named-route setups.
MorphPiece
A piece of a liquid group: explicit geometry (no tree measurement, like the rest of the morph system) plus live content on top of the skin. rect and radius are plain frame data: an animating consumer derives them from its own spring value.
MorphPieceChannel
A per-frame geometry channel for a MorphPiece: a translation plus per-axis scales over the piece's base MorphPiece.rect, written by the app (a drag, a tether, an orbit) and consumed by the skin's render object directly. A write repaints and re-traces the skin with no widget rebuild and no relayout - the same citizenship flight ticks have.
MorphReveal
Content choreography: a block unfolds on its own sub-range of the same spring's progress - fade, slide-in, scale, and a vertical "unsquash" from the anchor. Content does not just appear and "be"; it unpacks out of the morph's direction of growth.
MorphScope
The registry of tags and flights. Installed once near the top of the tree (not necessarily above the Navigator, but inside MaterialApp). A flight's shuttle renders in the NEAREST enclosing Overlay, so a nested navigator keeps its flights inside itself; in a single-navigator app that is the root overlay anyway. Also serves as the TickerProvider for all flights.
MorphScopeState
The registry behind MorphScope: tags by id, live flights by id, and the ticker provider all flights share.
MorphSharedElement
Shared elements inside a flight: a piece of content (an album cover, an avatar, an icon) that TRAVELS from its place in the source to its place in the target instead of riding the fade-through crossfade.
MorphSkin
A group of pieces with one shared "skin": the smooth union of their SDFs traced into a single Path behind live content. Nearby pieces fuse with a concave fillet on their own; the blend width comes from style or an explicit blend (low - a geometric joint, high - a gooey neck).
MorphSkinStyle
A named bundle of fusion knobs - in the spirit of MorphMotion presets. k and cell are distances in pixels: the presets are calibrated for button-scale UI (pieces of 40-150px); scenes at other scales should scale k along via an explicit MorphSkin.blend.
MorphSurface
The Material adapter of the widget layer: renders the visible surface of the nearest MorphTag FROM its declared spec (Material + InkWell with the spec's shape), so the button and the flight can never disagree about the surface. The tap handler receives a context UNDER the tag, which is why showMorph* can omit from: - the morph flies from the surface the finger is already on.
MorphSurfaceSpec
The morph's surface model as a value: everything the engine can interpolate about a surface - (shape, color, elevation). Declared once at a MorphTag (via MorphTag.spec) and readable by any descendant through MorphTag.specOf, so the visible surface is BUILT FROM the declaration instead of duplicating it - drift between "what the button looks like" and "what the morph believes" becomes structurally impossible, whatever rendering stack draws it.
MorphTag
The identity marker: registers its RenderBox in the scope and hands the engine a (rect, shape) pair. The shape is declared, never inferred from decoration. Prefer declaring the whole surface once via spec and rendering from specOf; the individual fields remain for terse cases.
MorphTagState
The registration and flight-side machinery of a MorphTag.
MorphTapTarget
The surface-less sibling of MorphSurface, for content inside a MorphPiece: the skin IS the surface there ("one mass - one shadow"), so this recipe adds only what the glass cannot - button semantics, a click cursor and the tap, with the same context-under-the-tag contract that lets showMorph* omit from:. A Material here would paint a second outline that splits from the mass the moment anything deforms it.
MorphTargetSpec
Description of a flight target: how to compute its rect from the current overlay size (re-evaluated every frame - the target is live; a window resize or the keyboard retargets it automatically) and what surface it has.
MorphTheme
Ambient engine defaults as a ThemeExtension: "how this app morphs", declared once instead of being threaded through every call site.
Motion
A motion pattern such as spring physics or duration-based curves.
SpringButton
Press physics on a real spring: pointer-down retargets the scale toward the pressed depth, release retargets back with bounce and velocity carry-over - interruptible mid-press like everything else in the system. No timelines, no curves.
Tug
A glass surface on a short tether - a weight on a rubber leash, not a soap bubble.

Enums

MorphDirection
Which end of the trajectory the spring currently targets.
MorphPhase
Semantic phases of a flight. Boundaries are computed from the clamped spring value; consumers react to the phase instead of comparing rawValue against magic numbers.
MorphSharedFade
How a shared pair blends during the flight.

Constants

morphDragCommitDistance → const double
Default commit threshold for MorphFlight.endDrag: releasing with a projected displacement farther than this (in px, any direction) commits the close flight. Distance-based and direction-agnostic: flinging the card anywhere reads as "throw it away".
morphDragCommitVelocity → const double
Default commit threshold on the release velocity (px/s): a hard fling commits even when the projected distance stays short.

Functions

computeMorphFrame({required double value, required Rect sourceRect, required Rect targetRect, required ShapeBorder sourceShape, required ShapeBorder targetShape, required Color sourceColor, required Color targetColor, required double maxScrimOpacity, double sourceElevation = 0, double targetElevation = 24}) MorphFrame
Computes every render value of a flight frame from one spring value - the whole visual contract in a single pure function.
maybeMorphAnchorRect(BuildContext context) Rect?
Like morphAnchorRect, but null when the context has no laid-out box - for callers that can fall back (a centered dialog instead of an anchored popover) rather than crash.
morphAnchorRect(BuildContext context) Rect
The rect of context's render box in the coordinate space of the enclosing Overlay - the space every flight, and therefore every MorphTargetSpec.rectFor, works in. Use it to capture popover anchors at tap time: a raw localToGlobal returns SCREEN coordinates, which silently drift from overlay coordinates the moment the morph lives inside a nested navigator.
morphBumpedRect(Rect rect, {required double value, required Offset impactAxis, required double bumpScale, required double bumpRecoil}) Rect
The same bump applied to geometry: the rect squashes and kicks around its own center. For consumers whose mass IS the visual (the liquid skin) a transform is not an option.
morphCloseHintScale(double travelPx) double
Scales closeVelocityHint by the pixel travel of the flight: value space normalizes distance, so without this multiplier a near and a far close would bounce identically. A far close lands heavier and springs more visibly.
morphDragArm(double distance) double
The arm ramp for the commit cue: 0 below morphDragCommitDistance, smoothstepping to 1 over the next 60 px. Past the threshold the card visibly "arms" - extra recede, thinner scrim, a slight lean toward home - so the hand knows a release will close. A pure, reversible function of the displacement: backing off disarms the same way.
morphDragRecede(double distance, {double dimension = 400}) double
How far the container has "receded into the hand" during a free drag: a smooth asymptotic fraction (0 at rest, approaching 1) of the displacement distance. Drives the subtle scale-down and the scrim dimming while dragging - both stay pure functions of the displacement, so interruption continuity holds by construction.
morphDragScale(double recede, double arm) double
The container scale of a live drag: a subtle recede into the hand, deepened by the arm cue. Shared by the shuttle and the settled route page for the same reason as morphDragScrimFactor.
morphDragScrimFactor(double recede, double arm) double
The scrim attenuation of a live drag: the factor the displacement multiplies into the scrim opacity - thinning with morphDragRecede and thinning further as morphDragArm rises. The one implementation of the composition, shared by the shuttle and the settled route page, so a drag dims identically on both sides of the second latch.
morphLandingBump({required double value, required Offset impactAxis, required double bumpScale, required double bumpRecoil}) → ({Offset kick, double scaleX, double scaleY})
The landing bump: one set of formulas for every consumer (the MorphTag content transform and the liquid-skin rect deformation). Full-wave: it lives on the FULL spring value - a dip below zero gives squash along the impact axis, a slight stretch across it, and a recoil kick; the return into positive plays the reverse. A degenerate axis falls back to vertical.
morphProjectValue(double value, double velocityPerSecond, {double decelerationRate = 0.998}) double
Momentum projection (WWDC18 "Designing Fluid Interfaces"): where the value would coast to under natural deceleration. The "close or return" decision is made from the projection, not from the current position.
morphRubberband(double x, {required double dimension, double coefficient = 0.55}) double
The canonical overdrag resistance formula (rubber-banding): f(x) = xdc / (d + c*x). Monotonic, asymptotically approaches d, resistance is felt from the very first pixel (slope c at zero). c = 0.55 is the classic iOS coefficient.
showMorph(BuildContext context, {Object? from, required MorphTargetSpec target, required MorphContentBuilder builder, MorphMotion? motion, bool barrierDismissible = true, double? maxScrimOpacity, Color? scrimColor, Color? shadowColor, VoidCallback? onDismissRequested, String? semanticLabel}) MorphFlight
The public entry point: all the plumbing (Ticker, listener, Overlay, ghost, and the handoff latch) is under the hood. If a flight for the tag already exists, the call retargets it instead of creating a new one (RETARGET CONTRACT: the existing flight keeps its target, builder, barrier and scrim; only motion, dismissal routing and the semantic label are updated).
showMorphDialog(BuildContext context, {Object? from, required MorphContentBuilder builder, double width = 440, double height = 360, MorphSurfaceSpec? surface, ShapeBorder? shape, Color? surfaceColor, MorphMotion? motion, bool barrierDismissible = true, double? maxScrimOpacity, Color? scrimColor, Color? shadowColor, VoidCallback? onDismissRequested, String? semanticLabel}) MorphFlight
The dialog adopts its look from the app's DialogTheme (shape, color) unless overridden explicitly.
showMorphMenu(BuildContext context, {Object? from, required List<MorphMenuItem> items, MorphSurfaceSpec? surface, MorphMotion? motion, double width = 250, double maxScrimOpacity = 0.2, String? semanticLabel}) MorphFlight
The iOS 26 staple as one call: a control becomes ITS OWN menu.
showMorphRoute<T>(BuildContext context, {Object? from, required MorphContentBuilder builder, MorphTargetSpec? target, MorphMotion? motion, bool barrierDismissible = true, bool useRootNavigator = false, double? maxScrimOpacity, Color? scrimColor, Color? shadowColor, String? semanticLabel}) Future<T?>
A real Navigator route whose visual transition is a morph flight.
showMorphSheet(BuildContext context, {Object? from, required MorphContentBuilder builder, double? height, double heightFactor = 0.5, double maxWidth = 560, MorphSurfaceSpec? surface, ShapeBorder? shape, Color? surfaceColor, MorphMotion? motion, bool barrierDismissible = true, double? maxScrimOpacity, Color? scrimColor, Color? shadowColor, VoidCallback? onDismissRequested, String? semanticLabel}) MorphFlight
The sheet adopts its look from the app's BottomSheetTheme (shape, color) unless overridden explicitly: a morph sheet inside a foreign design system looks native with zero configuration.
uniformMorphRadius(ShapeBorder shape, Size size) double?
The single radius of a shape when it can be expressed by one (Circle, Stadium, rrect/superellipse with equal corners); null for exotic shapes.

Typedefs

MorphContentBuilder = Widget Function(BuildContext context, MorphFlight flight)
Builds the target content of a flight; called once per flight and reused between spring ticks.