showSnapSheet<T> function

Future<T?> showSnapSheet<T>(
  1. BuildContext context, {
  2. required SnapSheetBuilder builder,
  3. double initialScrollOffset = 0,
})

Opens a sheet the reader can drag between snapSheetDetents, starting at the shortest. Where a sheet cannot have detents the content keeps its own scroll controller and initialScrollOffset is the caller's own business.

The sheet follows the view across the mobile breakpoint: it reopens in the other form, and the result is whichever form the reader closes.

Implementation

Future<T?> showSnapSheet<T>(
  BuildContext context, {
  required SnapSheetBuilder builder,
  double initialScrollOffset = 0,
}) {
  final completer = Completer<T?>();

  void open({required bool isMobile}) {
    var crossed = false;
    var popped = false;

    // The mobile layout drops a desktop page's navigator, sheet and all.
    void reopenIfDropped() {
      if (crossed || popped) {
        return;
      }
      crossed = true;
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (context.mounted) {
          open(isMobile: context.isMobileView);
        } else {
          completer.complete();
        }
      });
    }

    Widget home(BuildContext sheetContext, ScrollController? controller) {
      return _SnapSheetHome(
        isMobile: isMobile,
        onCross: () {
          if (crossed || !sheetContext.mounted || !context.mounted) {
            return;
          }
          if (ModalRoute.of(sheetContext)?.isCurrent != true) {
            return;
          }
          crossed = true;
          Navigator.of(sheetContext).pop();
          open(isMobile: !isMobile);
        },
        onDispose: reopenIfDropped,
        child: builder(sheetContext, controller),
      );
    }

    final barrierColor = context.colorScheme.modalScrim;
    final Future<T?> closed;
    if (isMobile) {
      final navigator = sheetNavigatorOf(context);
      closed = navigator.push(
        SnapSheetRoute<T>(
          builder: home,
          initialScrollOffset: initialScrollOffset,
          sheetBarrierColor: barrierColor,
          barrierLabel: UiLocalizations.of(context).close,
        ),
      );
    } else {
      closed = _showSideSheet<T>(
        context,
        builder: (sheetContext) => home(sheetContext, null),
      );
    }
    unawaited(
      closed.then((value) {
        popped = true;
        if (!crossed) {
          completer.complete(value);
        }
      }),
    );
  }

  open(isMobile: context.isMobileView);
  return completer.future;
}