Visitar URL original
Fix: menu positioning in CupertinoNavigationBar on Flutter 3.35.x · TimeFinderApp/pull_down_button@8c5ee8d · GitHub
Skip to content

Commit 8c5ee8d

Browse files
committed
Fix: menu positioning in CupertinoNavigationBar on Flutter 3.35.x
Fixes menu positioning and animation issues when PullDownButton is used in CupertinoNavigationBar's trailing widget on Flutter 3.35.0+. Root cause: Flutter 3.35.0 introduced a regression where CupertinoNavigationBar wraps children with incomplete MediaQueryData (only textScaler set, causing size to default to Size(0,0)). Solution: Pass overlay context for MediaQuery lookups instead of using the button's context. The overlay always has complete MediaQueryData. Changes: - Add mediaQueryContext parameter to getRect() - Add overlayContext parameter to animationAlignment() - Pass navigator.overlay.context for both MediaQuery lookups This fix is forward-compatible and will continue working when Flutter's fix lands in stable. Related: - flutter/flutter#174771 (MediaQuery returns Size(0,0) in nav bar) - flutter/flutter#174673 (Flutter's fix, not yet in stable) - notDmDrl#74 (Original bug report)
1 parent 9ea5979 commit 8c5ee8d

3 files changed

Lines changed: 53 additions & 44 deletions

File tree

‎lib/src/internals/extensions.dart‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@ extension RectExtension on BuildContext {
1717
///
1818
/// If [Rect]'s height is bigger than the screen size, additionally normalize
1919
/// [Rect] to help mitigate possible layout issues.
20-
Rect getRect({RenderObject? ancestor}) {
21-
final RenderBox renderBoxContainer = currentRenderBox;
22-
final MediaQueryData queryData = MediaQuery.of(this);
23-
final Size size = queryData.size;
20+
///
21+
/// The [mediaQueryContext] parameter can be provided to use a different
22+
/// context for MediaQuery lookups. This is useful when the button's
23+
/// context has incomplete MediaQuery data (e.g., Flutter 3.35.x nav bar
24+
/// bug where MediaQueryData only contains textScaler).
25+
Rect getRect({RenderObject? ancestor, BuildContext? mediaQueryContext}) {
26+
final renderBoxContainer = currentRenderBox;
27+
final queryContext = mediaQueryContext ?? this;
28+
final queryData = MediaQuery.of(queryContext);
29+
final size = queryData.size;
2430

2531
final rect = Rect.fromPoints(
2632
renderBoxContainer.localToGlobal(
@@ -43,12 +49,8 @@ extension RectExtension on BuildContext {
4349

4450
/// Apply some additional adjustments on [Rect] from [RectExtension.getRect] if
4551
/// [rect] is bigger than [size].
46-
Rect _normalizeLargeRect(
47-
Rect rect,
48-
Size size,
49-
EdgeInsets padding,
50-
) {
51-
const double minimumAllowedSize = kMinInteractiveDimensionCupertino * 2;
52+
Rect _normalizeLargeRect(Rect rect, Size size, EdgeInsets padding) {
53+
const minimumAllowedSize = kMinInteractiveDimensionCupertino * 2;
5254

5355
final bool topIsNegative = rect.top.isNegative;
5456
final double height = size.height;

‎lib/src/internals/route.dart‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,20 @@ class PullDownMenuRoute<VoidCallback> extends PopupRoute<VoidCallback> {
153153

154154
/// Attempt to predict an animation alignment for [RoutePullDownMenu] using
155155
/// a button's position.
156+
///
157+
/// The [overlayContext] parameter can be provided to use a different context
158+
/// for MediaQuery lookups. This is useful when the button's context has
159+
/// incomplete MediaQuery data (e.g., Flutter 3.35.x CupertinoNavigationBar
160+
/// bug where MediaQueryData only contains textScaler).
156161
static Alignment animationAlignment(
157162
BuildContext context,
158-
Rect buttonRect,
159-
) {
160-
final Offset buttonCenter = buttonRect.center;
163+
Rect buttonRect, {
164+
BuildContext? overlayContext,
165+
}) {
166+
final buttonCenter = buttonRect.center;
161167

162-
final Size size = MediaQuery.of(context).size;
168+
final queryContext = overlayContext ?? context;
169+
final size = MediaQuery.of(queryContext).size;
163170

164171
final double heightCenter = size.height / 2;
165172

@@ -199,12 +206,9 @@ enum _MenuHorizontalPosition {
199206

200207
/// Returns a [_MenuHorizontalPosition] for provided screen [size] and a
201208
/// [buttonRect].
202-
static _MenuHorizontalPosition get(
203-
Size size,
204-
Rect buttonRect,
205-
) {
206-
final double leftPosition = buttonRect.left;
207-
final double rightPosition = buttonRect.right;
209+
static _MenuHorizontalPosition get(Size size, Rect buttonRect) {
210+
final leftPosition = buttonRect.left;
211+
final rightPosition = buttonRect.right;
208212

209213
final double width = size.width;
210214
final double widthCenter = width / 2;

‎lib/src/pull_down_button.dart‎

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,17 @@ typedef PullDownMenuCanceled = void Function();
160160
/// the button is pressed.
161161
///
162162
/// Used by [PullDownButton.itemBuilder].
163-
typedef PullDownMenuItemBuilder =
164-
List<Widget> Function(
165-
BuildContext context,
166-
);
163+
typedef PullDownMenuItemBuilder = List<PullDownMenuEntry> Function(
164+
BuildContext context,
165+
);
167166

168167
/// Signature used by [PullDownButton] to build button widget.
169168
///
170169
/// Used by [PullDownButton.buttonBuilder].
171-
typedef PullDownMenuButtonBuilder =
172-
Widget Function(
173-
BuildContext context,
174-
Future<void> Function() showMenu,
175-
);
170+
typedef PullDownMenuButtonBuilder = Widget Function(
171+
BuildContext context,
172+
Future<void> Function() showMenu,
173+
);
176174

177175
/// Signature used by [PullDownButton] to create animation for
178176
/// [PullDownButton.buttonBuilder] when the pull-down menu is opened.
@@ -391,16 +389,22 @@ class _PullDownButtonState extends State<PullDownButton> {
391389
rootNavigator: widget.useRootNavigator,
392390
);
393391

394-
final RenderBox overlay = navigator.overlay!.context.currentRenderBox;
395-
Rect button = context.getRect(ancestor: overlay);
392+
final overlay = navigator.overlay!.context.currentRenderBox;
393+
var button = context.getRect(
394+
ancestor: overlay,
395+
mediaQueryContext: navigator.overlay!.context,
396+
);
396397

397398
if (widget.buttonAnchor != null) {
398399
button = _anchorToButtonPart(context, button, widget.buttonAnchor!);
399400
}
400401

401-
final Alignment animationAlignment =
402-
widget.animationAlignmentOverride ??
403-
PullDownMenuRoute.animationAlignment(context, button);
402+
final animationAlignment = widget.animationAlignmentOverride ??
403+
PullDownMenuRoute.animationAlignment(
404+
context,
405+
button,
406+
overlayContext: navigator.overlay!.context,
407+
);
404408

405409
final List<Widget> items = widget.itemBuilder(context);
406410

@@ -603,25 +607,24 @@ Rect _anchorToButtonPart(
603607
PullDownMenuAnchor.end => buttonRect.left,
604608
};
605609

606-
return Rect.fromLTRB(
607-
side,
608-
buttonRect.top,
609-
side,
610-
buttonRect.bottom,
611-
);
610+
return Rect.fromLTRB(side, buttonRect.top, side, buttonRect.bottom);
612611
}
613612

614613
/// Returns a barrier label for [PullDownMenuRoute].
615614
String _barrierLabel(BuildContext context) {
616615
// Use this instead of `MaterialLocalizations.of(context)` because
617616
// [MaterialLocalizations] might be null in some cases.
618-
final MaterialLocalizations? materialLocalizations =
619-
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
617+
final materialLocalizations = Localizations.of<MaterialLocalizations>(
618+
context,
619+
MaterialLocalizations,
620+
);
620621

621622
// Use this instead of `CupertinoLocalizations.of(context)` because
622623
// [CupertinoLocalizations] might be null in some cases.
623-
final CupertinoLocalizations? cupertinoLocalizations =
624-
Localizations.of<CupertinoLocalizations>(context, CupertinoLocalizations);
624+
final cupertinoLocalizations = Localizations.of<CupertinoLocalizations>(
625+
context,
626+
CupertinoLocalizations,
627+
);
625628

626629
// If both localizations are null, fallback to
627630
// [DefaultMaterialLocalizations().modalBarrierDismissLabel].

0 commit comments

Comments
 (0)