fitGlassNavStripGroups function
Fits a strip's groups into available points of height.
Natively, a strip that runs out of room keeps what fits from the top and collapses the rest into a ••• menu at the end: whole groups while they fit, then as many items of the next as still do — split off into a group of their own, a lone one standing as a circle — and everything after that becomes an entry in the menu. A GlassBarItem.menu among them contributes its own entries.
Shared with GlassPinnedBarChrome's in-route strip; this library is not exported from the package barrel.
Implementation
List<GlassNavBarGroup> fitGlassNavStripGroups(
List<GlassNavBarGroup> groups,
double available,
) {
double extentOf(int count, GlassBarItemBackground background) =>
count == 1 || background != GlassBarItemBackground.shared
? count * GlassVerticalBarMetrics.controlExtent
: count * GlassVerticalBarMetrics.itemExtent;
var total = 0.0;
for (final group in groups) {
if (total > 0) total += GlassVerticalBarMetrics.spacing;
total += extentOf(group.items.length, group.background);
}
if (total <= available) return groups;
// Room for everything that stays, with the ••• circle held back at the end.
final budget = available -
GlassVerticalBarMetrics.controlExtent -
GlassVerticalBarMetrics.spacing;
final kept = <GlassNavBarGroup>[];
final overflow = <GlassBarActionItem>[];
var used = 0.0;
for (final group in groups) {
if (overflow.isNotEmpty) {
overflow.addAll(group.items);
continue;
}
final gap = kept.isEmpty ? 0.0 : GlassVerticalBarMetrics.spacing;
final extent = extentOf(group.items.length, group.background);
if (used + gap + extent <= budget) {
kept.add(group);
used += gap + extent;
continue;
}
var fits = 0;
while (fits < group.items.length &&
used + gap + extentOf(fits + 1, group.background) <= budget) {
fits++;
}
if (fits > 0) {
kept.add(GlassNavBarGroup(
items: group.items.sublist(0, fits),
background:
fits == 1 ? GlassBarItemBackground.separate : group.background,
axis: Axis.vertical,
));
used += gap + extentOf(fits, group.background);
}
overflow.addAll(group.items.sublist(fits));
}
final entries = <Widget>[];
for (final item in overflow) {
if (item is GlassBarMenuItem) {
if (entries.isNotEmpty) entries.add(const GlassMenuDivider());
entries.addAll(item.menuItems);
continue;
}
if (!item.hasTap) continue;
entries.add(GlassMenuItem(
title: item.label ?? '',
icon: switch (item) {
GlassBarIconItem(:final icon) => icon,
GlassBarSheetItem(:final icon) => icon,
_ => null,
},
enabled: item.enabled,
onTap:
item is GlassBarSheetItem ? () => item.onPresent(null) : item.onTap,
));
}
if (entries.isEmpty) return kept;
return [
...kept,
GlassNavBarGroup(
items: [
GlassBarMenuItem(
icon: const Icon(CupertinoIcons.ellipsis),
menuItems: entries,
id: _overflowItemId,
background: GlassBarItemBackground.separate,
),
],
background: GlassBarItemBackground.separate,
axis: Axis.vertical,
),
];
}