import "package:material_ui/material_ui.dart"; import "package:nexus/helpers/extensions/global_rect.dart"; import "package:nexus/helpers/extensions/is_desktop.dart"; extension ShowAdaptiveMenu on BuildContext { /// Shows [items] in a popup menu on desktop, or a bottom sheet on mobile. /// /// On desktop, the menu opens at [globalPosition] if given (e.g. where the /// user right clicked), or over the widget this context belongs to. Future showAdaptiveMenu( List items, { Offset? globalPosition, }) async { if (!isDesktop) { await showModalBottomSheet( context: this, isScrollControlled: true, showDragHandle: true, builder: (context) => SafeArea( child: SingleChildScrollView( padding: const .only(bottom: 8), // PopupMenuItems must be inside something with the menu role child: Semantics( role: .menu, explicitChildNodes: true, child: ListTileTheme.merge( contentPadding: const .symmetric(horizontal: 24), minTileHeight: 48, titleTextStyle: Theme.of(context).textTheme.titleMedium, child: Column(mainAxisSize: .min, children: items), ), ), ), ), ); return; } final overlay = Overlay.of(this).context.findRenderObject() as RenderBox; final anchor = switch (globalPosition) { final position? => position & Size.zero, null => globalRect, }; await showMenu( context: this, constraints: .loose(.infinite), position: .fromRect(anchor, Offset.zero & overlay.size), items: items, ); } }