Problem
MacosPopupButton renders its collapsed trigger at a hard-coded height with a hard-coded internal padding, which makes it impossible to visually align the control with a sibling MacosTextField or to give the up/down caret breathing room from the trigger's right edge.
Specifically, in lib/src/buttons/popup_button.dart:
const double _kPopupButtonHeight = 20.0; // line 17
// Inside the collapsed trigger's build:
Container(
padding: const EdgeInsets.only(left: 8.0, right: 2.0),
height: _kPopupButtonHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
innerItemsWidget,
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: SizedBox(
height: _kPopupButtonHeight - 4.0,
width: _kPopupButtonHeight - 4.0,
child: CustomPaint(painter: _UpDownCaretsPainter(...)),
),
),
],
),
)
Three constants are fixed by the library and cannot be overridden from user code:
_kPopupButtonHeight (trigger container height, 20px).
- The Container's
EdgeInsets.only(left: 8, right: 2) — particularly the 2px on the right, which pins the caret against the trigger's right border.
- The caret's own
Padding(left: 8) between label and caret.
Why external wrappers don't help
Wrapping the widget in SizedBox(height: N) makes the outer box taller but leaves the native trigger at 20px centered inside. Wrapping in Padding(right: X) or Row(children: [Expanded(child: MacosPopupButton(...)), SizedBox(width: X)]) doesn't visibly push the caret inward: the internal Row uses MainAxisSize.min, so it shrink-wraps; the Container still paints its decoration at the stretched parent width, but the caret's position is set by spaceBetween against the Row's intrinsic content — the right: 2 of the inner Container is applied inside that paint region, so the caret ends up ~2px from the painted right edge regardless of the outer constraints.
Which means: short of forking the widget, users cannot visually match MacosPopupButton to a sibling MacosTextField (common in settings-style forms) or to Apple's own System Settings popups, which have ~28–32px trigger height.
Real-world use case — same-width controls, mismatched heights
A settings-style form where two popups and a text input all span the same content width should render at the same height. Right now:
Field A ┌──────────────────────────┐
│ Selected option ▾ │ ← 20px tall (MacosPopupButton)
└──────────────────────────┘
Field B ┌──────────────────────────┐
│ Selected option ▾ │ ← 20px tall
└──────────────────────────┘
Field C ┌──────────────────────────┐
│ │
│ Typed value │ ← 32px tall (MacosTextField)
│ │
└──────────────────────────┘
Width matches across all three. Height does not — the two popups are noticeably shorter than the text field, and stacked together the eye reads it as visual inconsistency rather than a style choice.
Caret-inset subproblem, same diagram
Zooming in on the right edge of a popup's collapsed trigger:
┌──────────────────────────┐
│ Selected option ▾ │ ← caret ~2px from border; visually stuck
└──────────────────────────┘
┌──────────────────────────┐
│ Selected option ▾ │ ← desired: caret ~6–8px from border
└──────────────────────────┘
The AppKit convention (and what macOS System Settings ships) is closer to the second drawing. The EdgeInsets.only(right: 2.0) in the hardcoded container padding makes the first drawing unavoidable.
Proposed API
Two non-exclusive options, either works:
Option A — Add ControlSize support (matches #408)
MacosPopupButton(
controlSize: ControlSize.regular, // or .small / .large
...
)
ControlSize.regular bumps the trigger height to ~28px, .large to ~32px, matching the AppKit convention, with the inner padding and caret inset scaled proportionally.
Option B — Explicit size/padding overrides
MacosPopupButton(
buttonHeight: 32,
triggerPadding: EdgeInsets.only(left: 10, right: 8),
caretLeftPadding: 10,
...
)
Option A is more idiomatic and composable with the existing enum that #408 is tracking for other widgets. Option B is a larger override API but ships faster.
Versions
macos_ui: 2.2.2 (reproducible on dev at HEAD as of this filing)
flutter: 3.41.x stable
- macOS 15.7.4 (Intel)
References
- Umbrella issue for
ControlSize adoption: #408
ControlSize enum introduction: #386
- Original popup button PR: #177
Happy to contribute a PR if the maintainers agree on the API direction (either option works — likely Option A to align with #408).
Problem
MacosPopupButtonrenders its collapsed trigger at a hard-coded height with a hard-coded internal padding, which makes it impossible to visually align the control with a siblingMacosTextFieldor to give the up/down caret breathing room from the trigger's right edge.Specifically, in
lib/src/buttons/popup_button.dart:Three constants are fixed by the library and cannot be overridden from user code:
_kPopupButtonHeight(trigger container height, 20px).EdgeInsets.only(left: 8, right: 2)— particularly the 2px on the right, which pins the caret against the trigger's right border.Padding(left: 8)between label and caret.Why external wrappers don't help
Wrapping the widget in
SizedBox(height: N)makes the outer box taller but leaves the native trigger at 20px centered inside. Wrapping inPadding(right: X)orRow(children: [Expanded(child: MacosPopupButton(...)), SizedBox(width: X)])doesn't visibly push the caret inward: the internalRowusesMainAxisSize.min, so it shrink-wraps; the Container still paints its decoration at the stretched parent width, but the caret's position is set byspaceBetweenagainst the Row's intrinsic content — theright: 2of the inner Container is applied inside that paint region, so the caret ends up ~2px from the painted right edge regardless of the outer constraints.Which means: short of forking the widget, users cannot visually match
MacosPopupButtonto a siblingMacosTextField(common in settings-style forms) or to Apple's own System Settings popups, which have ~28–32px trigger height.Real-world use case — same-width controls, mismatched heights
A settings-style form where two popups and a text input all span the same content width should render at the same height. Right now:
Width matches across all three. Height does not — the two popups are noticeably shorter than the text field, and stacked together the eye reads it as visual inconsistency rather than a style choice.
Caret-inset subproblem, same diagram
Zooming in on the right edge of a popup's collapsed trigger:
The AppKit convention (and what macOS System Settings ships) is closer to the second drawing. The
EdgeInsets.only(right: 2.0)in the hardcoded container padding makes the first drawing unavoidable.Proposed API
Two non-exclusive options, either works:
Option A — Add
ControlSizesupport (matches #408)ControlSize.regularbumps the trigger height to ~28px,.largeto ~32px, matching the AppKit convention, with the inner padding and caret inset scaled proportionally.Option B — Explicit size/padding overrides
Option A is more idiomatic and composable with the existing enum that
#408is tracking for other widgets. Option B is a larger override API but ships faster.Versions
macos_ui: 2.2.2 (reproducible ondevat HEAD as of this filing)flutter: 3.41.x stableReferences
ControlSizeadoption: #408ControlSizeenum introduction: #386Happy to contribute a PR if the maintainers agree on the API direction (either option works — likely Option A to align with #408).