launch static method

MorphFlight launch(
  1. BuildContext context, {
  2. required Object from,
  3. required MorphTargetSpec target,
  4. required MorphContentBuilder builder,
  5. MorphMotion? motion,
  6. bool barrierDismissible = true,
  7. double maxScrimOpacity = 0.45,
  8. Color scrimColor = Colors.black,
  9. Color shadowColor = const Color(0x99000000),
  10. VoidCallback? onDismissRequested,
  11. String? semanticLabel,
  12. bool routeMode = false,
  13. 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;
}