Watch
1
0
Fork
You've already forked nexus
0
forked from Nexus/nexus

show bottom sheets / dialogs / menus based on screen size

Fixes #76
This commit is contained in:
Henry Hiles 2026-10-15 10:20:27 -04:00
commit 76d78596cd
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
12 changed files with 141 additions and 49 deletions

View file

@ -0,0 +1,55 @@
import "package:material_ui/material_ui.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<void> showAdaptiveMenu(
List<PopupMenuEntry> 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 => switch (findRenderObject()) {
final RenderBox box =>
box.localToGlobal(.zero, ancestor: overlay) & box.size,
_ => Rect.zero,
},
};
await showMenu(
context: this,
constraints: .loose(.infinite),
position: .fromRect(anchor, Offset.zero & overlay.size),
items: items,
);
}
}