Skip to content

Conversation

@xhzq233
Copy link
Contributor

@xhzq233 xhzq233 commented Jul 21, 2023

Fixes #70716, #81057
Related ##52226

Why the conflict

If we put a TapGestureRecognizer at CupertinoContextMenu 's subtree.

For simplicity, we name the TapGestureRecognizer inside CupertinoContextMenu as tg2, the TapGestureRecognizer at CupertinoContextMenu 's subtree as tg1, and the press duration as t1.

When kPressTimeout+_previewLongPressTimeout/2 (500ms) < t1 < kPressTimeout+_previewLongPressTimeout (900ms), this moment

  1. Although tg2 was rejected, _ContextMenuRoute was still opened.

  2. And the onTap method of tg1 is triggered

The problem is that the GestureRecognizer in this ContextMenu is TapGestureRecognizer instead of LongPressGestureRecognizer, and Tap does not claim to victory, but GestureArena selects the deepest Tap to win.

  1. When t1 is greater than 500ms, it means that it has passed PrimaryPointerGestureRecognizer.deadline plus half of the openController's duration, means that _ContextMenuRoute is about to be opened. After GestureArena received pointer up event, tg1 competed with tg2. The default "child-victory" worked, and the onTap of tg1 was called, so both were triggered.

  2. When t1 is less than 900ms, because route is opened at this time, and both will be rejected by arena.

Solution

The reason is that TapGestureRecognizer will not declare victory by itself. Add the following code.

// call this when animation's value first reaches [_midpoint]

_tapGestureRecognizer.resolve(GestureDisposition.accepted);

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • All existing and new tests are passing.

@github-actions github-actions bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. f: cupertino flutter/packages/flutter/cupertino repository labels Jul 21, 2023
@xhzq233
Copy link
Contributor Author

xhzq233 commented Jul 23, 2023

I'm not sure ask who to review my code. Can you please give me some help? @MitchellGoodwin

@MitchellGoodwin MitchellGoodwin self-requested a review July 24, 2023 17:33
@MitchellGoodwin
Copy link
Contributor

@xhzq233 it looks like the failing check has to do with a plugin that this PR should not be interacting with. Would you mind rebasing to the current master branch to see if that fixes the check?

@xhzq233
Copy link
Contributor Author

xhzq233 commented Jul 25, 2023

@xhzq233 it looks like the failing check has to do with a plugin that this PR should not be interacting with. Would you mind rebasing to the current master branch to see if that fixes the check?

Merged. Now it has become a Windows build check error.= =

@MitchellGoodwin
Copy link
Contributor

Merged. Now it has become a Windows build check error.= =

Looks like there was some other issues. Another rebase to current master might work.

await gesture.down(tester.getCenter(find.byKey(const Key('container'))));
await tester.pump();
// simulate a press with specified duration
for (int i = 0; i < 100; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you try await tester.pump(const Duration(milliseconds: pressDuration)); first? Is this for loop more consistent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I tried it first. But _openAnimation won't reach midPoint since a pump only triggers one frame.

I need more frames to drive _openAnimation to reach the midPoint, so the route can be opened.

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 my main concern is that this loop just looks a little forced. Would pumpFrames work for this case?

Copy link
Contributor Author

@xhzq233 xhzq233 Aug 2, 2023

Choose a reason for hiding this comment

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

According to https://github.com/flutter/flutter/blob/2c71881f505b0dc0997e959b3ac37664d221fad8/packages/flutter_test/lib/src/widget_tester.dart#L704C3-L719C4

pumpFrames use the same methodology as me, and it will pump another widget. So I think it probably isn't necessary to use pumpFrames.

Copy link
Contributor

Choose a reason for hiding this comment

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

Does it really matter that 100 frames need to be pumped, not just 1 frame over the same duration? I wouldn't think that would affect the gesture.

Copy link
Contributor Author

@xhzq233 xhzq233 Aug 3, 2023

Choose a reason for hiding this comment

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

It aims to simulate the actual situation: the user keeps pressing and requesting frames. If there is only one frame, the animation is mutant and cannot drive the value of the animation controller.

Maybe the value of 100 is unreasonable. This is what I came up with casually.

await tester.pumpAndSettle();

// judge whether _ContextMenuRouteStatic present on the screen
final Finder static = find.byWidgetPredicate(
Copy link
Contributor

Choose a reason for hiding this comment

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

static is a protected variable in Dart. Could you change the name of this to something else?

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 👍🏻

@justinmc justinmc changed the title Context menu improvement CupertinoContextMenu improvement Jul 27, 2023
Copy link
Contributor

@MitchellGoodwin MitchellGoodwin left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you for answering my questions and putting this together.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

LGTM with nits 👍

});
});

testWidgets('Is gesture conflicts of ctx_menu resolved',
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: What is ctx_menu? Maybe change this to: testWidgets('Conflicting gesture detectors',

int? onPointerDownTime;
int? onPointerUpTime;
bool insideTapTriggered = false;
// 500ms < duration < 900ms
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Maybe explain where these values come from here.

final TestGesture gesture = await tester.createGesture();
await gesture.down(tester.getCenter(find.byKey(const Key('container'))));
await tester.pump();
// simulate a press with specified duration
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Capital letter at the beginning and period at the end of your comments here and below.

(Widget w) => '${w.runtimeType}' == '_ContextMenuRouteStatic',
);
// can't present together
if (insideTapTriggered) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this an if, does it happen differently on different platforms?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is used to detect whether the click gesture is detected by GestureDetector, so there will be no difference between different platforms.

await gesture.down(tester.getCenter(find.byKey(const Key('container'))));
await tester.pump();
// simulate a press with specified duration
for (int i = 0; i < 100; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it really matter that 100 frames need to be pumped, not just 1 frame over the same duration? I wouldn't think that would affect the gesture.

@xhzq233
Copy link
Contributor Author

xhzq233 commented Aug 3, 2023

Done. It is my first time contributing to Flutter. Thank you for your review and guidance.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

Renewing my LGTM and merging. Thanks for fixing this!

@justinmc justinmc merged commit ff5b0e1 into flutter:master Aug 7, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 8, 2023
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Aug 8, 2023
flutter/flutter@ad0aa8d...436df69

2023-08-08 [email protected] Roll Flutter Engine from 9c83d90b01bd to 146c4c9487fc (6 revisions) (flutter/flutter#132112)
2023-08-08 [email protected] Roll Flutter Engine from c27109291e22 to 9c83d90b01bd (5 revisions) (flutter/flutter#132108)
2023-08-08 [email protected] Roll Flutter Engine from be085f6699b6 to c27109291e22 (3 revisions) (flutter/flutter#132086)
2023-08-08 [email protected] Revert "Replace TextField.canRequestFocus with TextField.focusNode.canRequestFocus" (flutter/flutter#132104)
2023-08-08 [email protected] [Impeller] add drawVertices and drawAtlas benchmarks. (flutter/flutter#132080)
2023-08-07 [email protected] Adds more documentations around ignoreSemantics deprecations. (flutter/flutter#131287)
2023-08-07 [email protected] [web] New HtmlElementView.fromTagName constructor (flutter/flutter#130513)
2023-08-07 [email protected] Move mock canvas to flutter_test (flutter/flutter#131631)
2023-08-07 [email protected] Add static_path_tessellation macrobenchmark (flutter/flutter#131837)
2023-08-07 [email protected] [web] Remove usage of `ui.webOnlyInitializePlatform()` (flutter/flutter#131344)
2023-08-07 [email protected] Roll Flutter Engine from 39a575f65d50 to be085f6699b6 (1 revision) (flutter/flutter#132069)
2023-08-07 [email protected] Android context menu theming and visual update (flutter/flutter#131816)
2023-08-07 [email protected] Roll Flutter Engine from 39ce1c097bce to 39a575f65d50 (2 revisions) (flutter/flutter#132064)
2023-08-07 [email protected] CupertinoContextMenu improvement (flutter/flutter#131030)
2023-08-07 [email protected] Roll Flutter Engine from 5b47c0577060 to 39ce1c097bce (3 revisions) (flutter/flutter#132057)
2023-08-07 [email protected] Slider should check `mounted` before start interaction (flutter/flutter#132010)
2023-08-07 [email protected] Roll Packages from ce53da1 to d7ee75a (7 revisions) (flutter/flutter#132058)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems 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.

GestureDetector inside CupertinoContextMenu may trigger if the touch is released before CupertinoContextMenu completes transition

3 participants