Skip to content

Conversation

@justinmc
Copy link
Contributor

@justinmc justinmc commented Aug 7, 2019

Description

This PR implements the ContextMenu widget.

ContextMenu(
  child: FittedBox(
    fit: BoxFit.cover,
    child: Image.asset('images/my_image.jpg'),
  ),
  actions: <ContextMenuSheetAction>[
    ContextMenuSheetAction(
      child: const Text('My action'),
      onPressed: () { ... },
    ),
    // More ContextMenuSheetActions...
  ],
);
native Flutter
context_menu_small

Related Issues

Closes #34728

Tests

  • ContextMenuSheetAction
    • tapping
    • hold to change color
  • ContextMenu
    • default state without being interacted with
    • opening
    • all different ways to close

@justinmc justinmc added framework flutter/packages/flutter repository. See also f: labels. f: cupertino flutter/packages/flutter/cupertino repository work in progress; do not review labels Aug 7, 2019
@justinmc justinmc self-assigned this Aug 7, 2019
@fluttergithubbot
Copy link
Contributor

It looks like this pull request may not have tests. Please make sure to add tests before merging. While there are exceptions to this rule, if this patch modifies code it is probably not an exception.

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

@justinmc justinmc force-pushed the ios13-context-menus branch from 4a9fef5 to 346bd20 Compare August 7, 2019 22:02
@justinmc justinmc force-pushed the ios13-context-menus branch from 346bd20 to 2cc3813 Compare August 7, 2019 22:03
@justinmc justinmc force-pushed the ios13-context-menus branch from 0afdd9d to 5d534ee Compare August 8, 2019 23:10
@justinmc justinmc force-pushed the ios13-context-menus branch from d1abc7b to 96e9f0c Compare August 12, 2019 22:21
@justinmc justinmc force-pushed the ios13-context-menus branch from 88e574e to 4f75892 Compare August 13, 2019 17:31
@justinmc justinmc force-pushed the ios13-context-menus branch from 867363b to 45fd739 Compare August 14, 2019 16:52
@justinmc justinmc force-pushed the ios13-context-menus branch from 171b77a to d5ddf75 Compare August 15, 2019 16:56
@justinmc justinmc force-pushed the ios13-context-menus branch from 3c16d3d to 904447f Compare October 22, 2019 17:44
@justinmc
Copy link
Contributor Author

@HansMuller @LongCatIsLooong Ready for re-review. I've renamed to CupertinoContextMenu, changed preview to previewBuilder, and made a bunch of smaller requested changes.

One question I had: Is there a callback type that I can reuse instead of defining the PreviewBuilder and _PreviewBuilderChildless types ?

Copy link
Contributor

@HansMuller HansMuller left a comment

Choose a reason for hiding this comment

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

This looks good; just some small feedback plus: need to document/test ModalRoute.filter

/// A function that produces the preview when the CupertinoContextMenu is open.
///
/// Called every time the animation value changes.
typedef PreviewBuilder = Widget Function(
Copy link
Contributor

Choose a reason for hiding this comment

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

This should have a name that's a little more specific because inadvertant collisions. Maybe ContextMenuPreviewBuilder

/// Widget build(BuildContext context) {
/// return Scaffold(
/// key: scaffoldKey,
/// body: Center(
Copy link
Contributor

Choose a reason for hiding this comment

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

My mistake, what you had is fine.

/// The widget that can be "opened" with the [CupertinoContextMenu].
///
/// When the [CupertinoContextMenu] is long-pressed, the menu will open and
/// this widget (or the widget returned by [previewBuilder], if provided) will
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this may be revealing a detail that's not important: that the child/previewBuilder will be moved to the overlay. Can we explain what it means to "open" the menu just in terms of the route we're going to push? That approach jibes with using Navigator.pop() to close the menu.


/// The actions that are shown in the menu.
///
/// This parameter cannot be null, and items in the List must be
Copy link
Contributor

Choose a reason for hiding this comment

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

can't be null or empty

/// sharp corners when in the page.
///
/// In addition to the current [BuildContext], the function is also called
/// with an [Animation] and the [child]. The animation is used internally to
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of "is used internally", you could explain that the animation goes from 0-1 when the menu opens, 1-0 when it closes.

/// // it's closed. It uses the given animation to animate the corners
/// // in sync with the opening animation.
/// child: ClipRRect(
/// borderRadius: BorderRadius.circular(64.0 * animation.value),
Copy link
Contributor

Choose a reason for hiding this comment

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

NICE

// child in the screen.
_ContextMenuOrientation get _contextMenuOrientation {
final Rect childRect = _getRect(_childGlobalKey);
final double screenWidth = MediaQuery.of(context).size.width;
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the answer here?


// A floating copy of the CupertinoContextMenu's child.
//
// When the child is pressed, but before the CupertinoContextMenu opens, it does
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the explanation!

}) : _filter = filter,
super(settings: settings);

final ui.ImageFilter _filter;
Copy link
Contributor

Choose a reason for hiding this comment

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

Doc needed

This new route feature should also get its own test.

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 test to check that CupertinoContextMenu is able to add a BackdropFilter using this filter param, which was easiest since ModalRoute is abstract. Let me know if you had something else in mind.

/// Below is an example of using `previewBuilder` to show an image tile that's
/// similar to each tile in the iOS iPhoto app's context menu. Several of
/// these could be used in a GridView for a similar effect.
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
///

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch


class _CupertinoContextMenuState extends State<CupertinoContextMenu> with TickerProviderStateMixin {
final GlobalKey _childGlobalKey = GlobalKey();
double _childOpacity = 1.0;
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can use an Offstage and a TickerMode instead, like the Hero widget does?

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'm trying this out.

Actually @goderbauer I got this code from you awhile back. I was asking about animating to a final laid out state, and you suggested rendering the first frame offstage and sent me this gist: https://gist.github.com/goderbauer/b136e871fac33960dbd539ea1930ff97

Any idea if there is an advantage to Offstage and TickerMode instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah I think it's because PopupRoute is already using Offstage internally, and so this widget is just managing the offstage variable.

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong Oct 30, 2019

Choose a reason for hiding this comment

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

What I originally meant was to use Offstage + TickerMode to hide the original child instead of Opacity. Should we stop the animation in the original child when it's hidden, by adding a TickerMode parent? As for Offstage vs Opacity they seem interchangeable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh I see, sorry! That makes more sense and is probably more performant. I'll change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually after changing it something broke. I was getting the size of the Opacity widget using a GlobalKey, and I couldn't do that with an Offstage widget (even when it was onstage). It gave me weird values and broke the tests.

My compromise is to use a TickerMode and an Opacity widget together, but let me know if you know why getting the size wasn't working.

///
/// This parameter cannot be null, and items in the List must be
/// [CupertinoContextMenuAction]s.
final List<CupertinoContextMenuAction> actions;
Copy link
Contributor

Choose a reason for hiding this comment

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

Might have overlooked something, but CupertinoContextMenu's implementation does not seem to make use of the fact that actions are CupertinoContextMenuActions. Is it possible to relax the type constraint to List<Widget>? According to the sketch file the actions can be grouped, and group separators look quite different from regular ones, so I wonder if we can make this a list of Widgets.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah there is nothing preventing us from using List<Widget> here. I did it just to encourage users to always use CupertinoContextMenuActions, but I also didn't realize there were group separators. Does Flutter usually allow any type of widget when it can?

Copy link
Contributor

Choose a reason for hiding this comment

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

There's an issue for this: #12078. Also I tried adding a context menu to a UISegmentedControl, and it turns out other than group separators there can also be headers (similar to action sheet headers)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If that's more in line with how we usually do it then changing it sounds good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh sorry I posted my last comment before Github updated to show yours. That issue definitely confirms that this should be agnostic so I'm changing it now 👍

flex: 2,
child: IntrinsicHeight(
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
Copy link
Contributor

Choose a reason for hiding this comment

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

Per xcode the value is 13.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you!

@codecov
Copy link

codecov bot commented Oct 23, 2019

Codecov Report

Merging #37778 into master will decrease coverage by 0.14%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #37778      +/-   ##
==========================================
- Coverage   61.77%   61.62%   -0.15%     
==========================================
  Files         199      195       -4     
  Lines       19276    19016     -260     
==========================================
- Hits        11907    11718     -189     
+ Misses       7369     7298      -71
Flag Coverage Δ
#flutter_tool 61.62% <0%> (-0.15%) ⬇️
Impacted Files Coverage Δ
...tter_tools/lib/src/build_system/targets/linux.dart 56.71% <0%> (-32.84%) ⬇️
...tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart 7.69% <0%> (-17.89%) ⬇️
...ages/flutter_tools/lib/src/commands/build_aot.dart 39.79% <0%> (-14.28%) ⬇️
...lutter_tools/lib/src/android/android_workflow.dart 63.92% <0%> (-9.13%) ⬇️
...flutter_tools/lib/src/android/android_builder.dart 75% <0%> (-8.34%) ⬇️
...ckages/flutter_tools/lib/src/base/async_guard.dart 88.88% <0%> (-5.56%) ⬇️
packages/flutter_tools/lib/src/android/gradle.dart 79.73% <0%> (-3.1%) ⬇️
...ges/flutter_tools/lib/src/build_runner/web_fs.dart 34.71% <0%> (-2.44%) ⬇️
.../flutter_tools/lib/src/commands/build_fuchsia.dart 81.81% <0%> (-2.19%) ⬇️
packages/flutter_tools/lib/src/ios/xcodeproj.dart 89.71% <0%> (-2.09%) ⬇️
... and 55 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bf45897...4a0f41c. Read the comment docs.

final Widget child;

/// The actions that are shown in the menu.
///
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be a good place to say that typically the actions are [CupertinoContextMenuAction]s.

Copy link
Contributor

@HansMuller HansMuller left a comment

Choose a reason for hiding this comment

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

LGTM

@justinmc
Copy link
Contributor Author

justinmc commented Nov 4, 2019

Closing in favor of: #43918

@justinmc justinmc closed this Nov 4, 2019
@justinmc justinmc deleted the ios13-context-menus branch December 4, 2019 00:44
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

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.

[iOS 13] Context Menus

6 participants