launch static method
- BuildContext context, {
- required Object from,
- required MorphTargetSpec target,
- required MorphContentBuilder builder,
- MorphMotion? motion,
- bool barrierDismissible = true,
- double maxScrimOpacity = 0.45,
- Color scrimColor = Colors.black,
- Color shadowColor = const Color(0x99000000),
- VoidCallback? onDismissRequested,
- String? semanticLabel,
- bool routeMode = false,
- OverlayState? overlay,
Launches (or retargets) the flight for from. Prefer the
showMorph* entry points: they also resolve MorphTheme defaults,
this method does not.
Implementation
static MorphFlight launch(
BuildContext context, {
required Object from,
required MorphTargetSpec target,
required MorphContentBuilder builder,
MorphMotion? motion,
bool barrierDismissible = true,
double maxScrimOpacity = 0.45,
Color scrimColor = Colors.black,
Color shadowColor = const Color(0x99000000),
VoidCallback? onDismissRequested,
String? semanticLabel,
bool routeMode = false,
// The shuttle's home. Defaults to the root overlay above [context];
// a route passes its navigator's own overlay explicitly, because
// the navigator's context sits ABOVE that overlay.
OverlayState? overlay,
}) {
final MorphScopeState scope = MorphScope.of(context);
// liveFlightOf, not flightOf: retargeting a stray flight whose tag
// was disposed would drive show/hide on a defunct State.
final MorphFlight? existing = scope.liveFlightOf(from);
if (existing != null) {
assert(
!routeMode || existing.routeContentKey != null,
'showMorphRoute(from: $from): an overlay flight is already live '
'for this tag. A route cannot adopt an overlay flight mid-air - '
'the content ownership chains differ. Close the overlay first, '
'or retarget it with showMorph.',
);
if (motion != null) {
existing.controller.motion = motion;
}
if (onDismissRequested != null) {
existing.onDismissRequested = onDismissRequested;
}
if (semanticLabel != null) {
existing.semanticLabel = semanticLabel;
}
if (routeMode) {
existing.enableRouteMode();
}
// RETARGET CONTRACT: the existing flight keeps its target,
// builder, barrier and scrim - the content lives in the shuttle
// and cannot be swapped mid-air. Only motion, dismissal routing
// and the semantic label are updated.
existing
..open()
.._addHistoryEntry(context);
return existing;
}
final MorphFlight flight =
MorphFlight._(
scope: scope,
tag: scope.tagOf(from),
target: target,
builder: builder,
barrierDismissible: barrierDismissible,
maxScrimOpacity: maxScrimOpacity,
scrimColor: scrimColor,
shadowColor: shadowColor,
// The NEAREST overlay: the flight belongs to the world its
// scope lives in. A nested navigator (a tab, an embedded
// device mockup) keeps its flights inside itself; in a
// single-navigator app this is the root overlay anyway.
overlay: overlay ?? Overlay.of(context),
motion: motion ?? .normal,
disableAnimations:
MediaQuery.maybeDisableAnimationsOf(context) ?? false,
)
..onDismissRequested = onDismissRequested
..semanticLabel = semanticLabel;
if (routeMode) {
flight.enableRouteMode();
}
scope.adoptFlight(flight);
flight
..open()
.._addHistoryEntry(context);
return flight;
}