Skip to content

Conversation

@davidhicks980
Copy link
Contributor

@davidhicks980 davidhicks980 commented Aug 29, 2025

This PR implements CupertinoMenuAnchor, CupertinoMenuItem, CupertinoLargeMenuDivider using RawMenuAnchor.

Resolves #60298, notDmDrl/pull_down_button#26, #137936.

This repo is a rough draft. Some of the tests are failing due to code changes. Comments may not accurately reflect the latest version of CupertinoMenuAnchor. Subjective decisions had to be made due to limits of the Flutter engine. This PR was extracted from a multi-layer menu, so extraneous code may still be present.

One large decision that I'd like feedback on is whether CupertinoMenuAnchor should receive a single panel widget rather than individual children.

I'll be busy this weekend, so excuse my lack of response to questions/comments.

Screen.Recording.2025-08-29.at.7.17.12.AM.mov

Dartpad: https://dartpad.dev/8c6bba779b6a00e95582b61b132292bc

@dkwingsmt

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: cupertino flutter/packages/flutter/cupertino repository d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos labels Aug 29, 2025
@dkwingsmt dkwingsmt self-requested a review September 3, 2025 18:03
@davidhicks980
Copy link
Contributor Author

davidhicks980 commented Sep 7, 2025

@dkwingsmt Heads up, a somewhat large change to menu item layout will land soon, so you may want to avoid reviewing that section for a day or so.

@davidhicks980 davidhicks980 marked this pull request as ready for review October 18, 2025 17:13
@flutter-dashboard
Copy link

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@davidhicks980
Copy link
Contributor Author

Oh @dkwingsmt this should be ready for review

@dkwingsmt
Copy link
Contributor

Awesome! I'll review it after the Thanksgiving.

Copy link
Contributor

@dkwingsmt dkwingsmt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM. Mostly minor suggestions and doc improvements. Thank you!

/// The following constant represents a division in text scale factor beyond which
/// we want to change how the menu is laid out.
///
/// This explanation was ported from CupertinoDialog.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean CupertinoAlertDialog? (Maybe use cupertino/dialog.dart instead since the said explanation does not technically belong to the class.)

Copy link
Contributor Author

@davidhicks980 davidhicks980 Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, whoops. Done! Good catch.

}
}

// The font size at which text scales linearly on the iOS 18.5 simulator.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reworded the doc here and mentioned "unit" here so that followup explanations are easier to understand. LMK what you think.

Suggested change
// The font size at which text scales linearly on the iOS 18.5 simulator.
// Base font size used for text-scaling calculations.
//
// On iOS the text scale changes in increments of 1/17 (≈5.88%), as
// observed on the iOS 18.5 simulator. Each step (1/17 of the base font size)
// is referred to as one "unit" in the following calculations.
// The font size of 17.0 pt is referred to as the "base font size".

If you agree, remove the relevant parts in the doc of _normalizeTextScale.

Copy link
Contributor Author

@davidhicks980 davidhicks980 Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Done, except I removed the "The font size of 17.0 pt is referred to as the 'base font size'" since we already say "Base font size used for text-scaling calculations." Lmk if I should add it back in.

Comment on lines 228 to 229
final double units =
textScaler.scale(_kCupertinoMobileBaseFontSize) - _kCupertinoMobileBaseFontSize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

Suggested change
final double units =
textScaler.scale(_kCupertinoMobileBaseFontSize) - _kCupertinoMobileBaseFontSize;
final double units = _normalizeTextScale(textScaler);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! My b!

Comment on lines 57 to 58
/// * Max "regular" scale factor ≈ 23/17 ≈ 1.352... (6 units)
/// * Min "accessible" scale factor ≈ 28/17 ≈ 1.647... (11 units)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// * Max "regular" scale factor ≈ 23/17 ≈ 1.352... (6 units)
/// * Min "accessible" scale factor ≈ 28/17 ≈ 1.647... (11 units)
/// * Max "regular" scale factor ≈ 23/17 ≈ 1.352... (normalized text scale: 6)
/// * Min "accessible" scale factor ≈ 28/17 ≈ 1.647... (normalized text scale: 11)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

///
/// The returned value is positive when the text scale factor is larger than the
/// base font size, negative when smaller, and zero when equal.
double _normalizeTextScale(TextScaler textScaler) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: move this to just below _kCupertinoMobileBaseFontSize so that it's clearer what "normalized text scale" is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

///
/// On all platforms except web, this color is applied to the divider before
/// the [color] is applied, and is used to give the appearance of the divider
/// cutting into the background.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you further explain what "cutting into the background" means?

size: Size(double.infinity, displacement),
painter: _AliasedLinePainter(
overlayColor: CupertinoDynamicColor.resolve(overlayColor, context),
border: BorderSide(width: 0.0, color: CupertinoDynamicColor.resolve(color, context)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to define it with border instead of simply another color?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Color works. Should be done!

/// The horizontal space in which the [trailing] widget can be placed.
final double? trailingWidth;

/// The alignment of the center point of the leading widget within the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why do we want to align the leading/trailing widgets this special way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment on the _AlignMidpoint widget. This matches how iOS lays out it's leading and trailing widgets: https://developer.apple.com/documentation/uikit/uiview/centerxanchor. AFAIK there's currently no way to simply achieve this behavior with regular Flutter widgets.

/// When the inner menu button is disabled, [longPressToOpenDuration] should
/// be set to [Duration.zero] to prevent the menu from opening on long-press.
///
/// Defaults to [Duration.zero], which disables the behavior.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we remove disabling in this PR, so that we don't have to break this behavior later?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would provide some method of disabling. If a user wants to long-press-and-drag the menu anchor around the screen, they may want to disable longPressToOpen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up removing longPressToOpenDuration, adding enableLongPressToOpen, and adding a _CupertinoMenuAnchorState.longPressToOpenDuration constant. I figure we can always add a way to customize the duration if needed.

}

/// Handles swiping events for a [_SwipeRegion].
// This class was adapted from _DragAvatar.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend not mentioning connections like this, because such connections can be outdated in the future as one of them is updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@dkwingsmt dkwingsmt self-requested a review December 17, 2025 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: cupertino flutter/packages/flutter/cupertino repository framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pull-Down Menus for iOS 14

2 participants