flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
162.22k stars 26.65k forks source link

Draggable + TextField interaction stopped working #124421

Closed borjandev closed 2 weeks ago

borjandev commented 1 year ago

Steps to Reproduce

  1. Switch to 90f8ac5b41129a21878fe1607a9fcf57576a840c on master channel or any commit which comes after 90f8ac5b41129a21878fe1607a9fcf57576a840c Execute flutter run on the code sample

Expected results: TextField is horizontally draggable, text inside TextField changes to "Wee.. I'm being dragged"

Actual results: TextField is not horizontally draggable, text inside TextField is still "Drag me horizontally"

Code sample ```dart import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; const defaultText = "Drag me horizontally"; const successText = "Wee.. I'm being dragged"; final dismissibleTextEditingController = TextEditingController(text: defaultText); void main() { debugPrintGestureArenaDiagnostics = true; runApp( MaterialApp( home: Scaffold( body: Container( padding: const EdgeInsets.all(50), child: Center( child: Dismissible( direction: DismissDirection.horizontal, onUpdate: (details) { dismissibleTextEditingController.text = successText; if (details.progress == 0.0) { dismissibleTextEditingController.text = defaultText; } }, key: const Key('Dismissible'), child: TextField( controller: dismissibleTextEditingController, ), ), ), ), ), ), ); } ```
Logs Successful Gesture Arena on a690c0485f8afc6200f8d1dc60e483aeed8f01a7. ``` flutter: Gesture arena 3 ❙ ★ Opening new gesture arena. flutter: Gesture arena 3 ❙ Adding: LongPressGestureRecognizer#6412b(debugOwner: _TextSelectionGestureDetectorState#09cd1, state: ready) flutter: Gesture arena 3 ❙ Adding: TapAndDragGestureRecognizer#a9a43(debugOwner: _TextSelectionGestureDetectorState#09cd1) flutter: Gesture arena 3 ❙ Adding: HorizontalDragGestureRecognizer#f173b(debugOwner: GestureDetector, start behavior: start) flutter: Gesture arena 3 ❙ Closing with 3 members. flutter: Gesture arena 3 ❙ Rejecting: LongPressGestureRecognizer#6412b(debugOwner: _TextSelectionGestureDetectorState#09cd1, state: possible) flutter: Gesture arena 3 ❙ Accepting: HorizontalDragGestureRecognizer#f173b(debugOwner: GestureDetector, start behavior: start) flutter: Gesture arena 3 ❙ Self-declared winner: HorizontalDragGestureRecognizer#f173b(debugOwner: GestureDetector, start behavior: start) ``` Unsuccessful Gesture Arena on commit 90f8ac5b41129a21878fe1607a9fcf57576a840c and subsequent commits ``` flutter: Gesture arena 1 ❙ ★ Opening new gesture arena. flutter: Gesture arena 1 ❙ Adding: LongPressGestureRecognizer#fec4e(debugOwner: _TextSelectionGestureDetectorState#1676e, state: ready) flutter: Gesture arena 1 ❙ Adding: TapAndHorizontalDragGestureRecognizer#dc538(debugOwner: _TextSelectionGestureDetectorState#1676e) flutter: Gesture arena 1 ❙ Adding: HorizontalDragGestureRecognizer#2bb6b(debugOwner: GestureDetector, start behavior: start) flutter: Gesture arena 1 ❙ Closing with 3 members. flutter: Gesture arena 1 ❙ Rejecting: LongPressGestureRecognizer#fec4e(debugOwner: _TextSelectionGestureDetectorState#1676e, state: possible) flutter: Gesture arena 1 ❙ Accepting: TapAndHorizontalDragGestureRecognizer#dc538(debugOwner: _TextSelectionGestureDetectorState#1676e) flutter: Gesture arena 1 ❙ Self-declared winner: TapAndHorizontalDragGestureRecognizer#dc538(debugOwner: _TextSelectionGestureDetectorState#1676e) ```

https://user-images.githubusercontent.com/64651636/230674533-5a53f23b-211c-411d-b2d8-3831cc7db6af.mov

https://user-images.githubusercontent.com/64651636/230674441-dedd85df-1543-4de1-bec5-581c2bc411b6.mov

borjandev commented 1 year ago

@chunhtai @Renzo-Olivares @xu-baolin @justinmc This seems to be caused by https://github.com/flutter/flutter/pull/123055

Since that pull request landed in master, and I assume passed your internal checks and tests, does that mean that this is intended behaviour?

If it is indeed intended, then is there some simple parameter that we can change in the Dismissible or the TextField widget in order to make the Dismissible win the GestureArena ?

Essentially the way that it was working before commit 90f8ac5b41129a21878fe1607a9fcf57576a840c was merged?

Renzo-Olivares commented 1 year ago

Thank you for reporting this @borjandev. Getting this behavior correct is really tricky since text selection dragging gestures on mobile devices were just recently introduced (they have been supported on desktop for a while). What you can do for now as a short-term solution is wrap your Dismissible in a MediaQuery with a custom gestureSettings like the code sample below.

What this does is changes the drag acceptance threshold for touch devices for the Dismissible making it 0.1 (by default it is 8.0) lower than the drag acceptance threshold of the TextField for touch devices which is also 8.0 by default. This way Dismissible can always beat the TextField.

The side-effects of this is that text selection dragging gestures will not work inside of the TextField that is within the Dismissible because it will always win. Regular tap gestures, and long press gestures will still work inside of that TextField.

I'll see if I can come up with a better solution / decide if this behavior is expected for this type of use case, but a full solution might be tricky given the constraints. Curious to hear others thoughts.

Edit: Updated context on the issue: https://github.com/flutter/flutter/issues/127017#issuecomment-1564992595

Code sample ```dart import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; const defaultText = "Drag me horizontally"; const successText = "Wee.. I'm being dragged"; final dismissibleTextEditingController = TextEditingController(text: defaultText); void main() { debugPrintGestureArenaDiagnostics = true; runApp( MaterialApp( home: Scaffold( body: Container( padding: const EdgeInsets.all(50), child: Center( child: MediaQuery( data: const MediaQueryData( gestureSettings: DeviceGestureSettings(touchSlop: 7.9) ), child: Dismissible( direction: DismissDirection.horizontal, onUpdate: (details) { dismissibleTextEditingController.text = successText; if (details.progress == 0.0) { dismissibleTextEditingController.text = defaultText; } }, key: const Key('Dismissible'), child: TextField( controller: dismissibleTextEditingController, ), ), ), ), ), ), ), ); } ```
borjandev commented 1 year ago

@Renzo-Olivares Thank you for the prompt response, and for the short-term solution!

Modifying the touchSlop is viable in my use case.

Looking forward to hear thoughts from others, but feel free to close this issue if needed, as even in the absence of an ideal full solution, I would accept the tradeoff of needing to modify the touchSlop, as it provides the necessary control to prioritise the widget which wins the GestureArena, which can be tailored to the specific app use case.

Renzo-Olivares commented 1 year ago

Great to hear that worked for you! I'll keep this open for discussion, and at the least I think I'll probably make a PR to add this use-case to the documentation somewhere so it is more discoverable to others.

darshankawar commented 1 year ago

This is replicable on latest master but not on latest stable.

Code sample: https://github.com/flutter/flutter/issues/124421#issue-1659166549

stable, master flutter doctor -v ``` [!] Flutter (Channel stable, 3.7.10, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.7.10 on channel stable at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 4b12645012 (6 days ago), 2023-04-03 17:46:48 -0700 • Engine revision ec975089ac • Dart version 2.19.6 • DevTools version 2.20.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. [!] Flutter (Channel master, 3.10.0-2.0.pre.29, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.10.0-2.0.pre.29 on channel master at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision f4cd897e7e (3 hours ago), 2023-04-10 10:09:21 +0800 • Engine revision 58ab7eaee6 • Dart version 3.0.0 (build 3.0.0-417.0.dev) • DevTools version 2.23.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Android toolchain - develop for Android devices (Android SDK version 30.0.3) • Android SDK at /Users/dhs/Library/Android/sdk ✗ cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/macos#android-setup for more details. [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13C100 • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.2) • IntelliJ at /Applications/IntelliJ IDEA.app • Flutter plugin version 65.1.4 • Dart plugin version 213.7228 [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.29.0 [✓] Connected device (3 available) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 15.3.1 19D52 • macOS (desktop) • macos • darwin-x64 • macOS 12.2.1 21D62 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. ```

Note: I didn't add regression label to this based on below comment as it looks like the recently introduced functionality specially on mobile devices is causing the reported behavior.

Getting this behavior correct is really tricky since text selection dragging gestures on mobile devices were just recently introduced (they have been supported on desktop for a while). What you can do for now as a short-term solution is wrap your Dismissible in a MediaQuery with a custom gestureSettings like the code sample below.

Sembauke commented 11 months ago

Hey @Renzo-Olivares,

Thanks for introducing this workaround, I believe I have a different case but is also related to this issue.

Currently when having a ListView and putting the scrollDirection to horizontal and adding a TextField as the child, the Textfield will no longer be draggable.

This problem strangely only occurs on IOS and not on Android. I would love to know if there is a similar work around for this use-case.

Thank you.

Renzo-Olivares commented 11 months ago

@Sembauke can you post a code sample? Thank you

blaneyneil commented 9 months ago

Responding here, despite not being related to TextField, because https://github.com/flutter/flutter/issues/127165 was closed in favor of this ticket.

On iOS, you can no longer (as of 3.10) trigger certain GestureDetectors on SelectableText. For instance, onHorizontalDragUpdate will not trigger if you swipe right on the text in the below code sample. However, if that ListView is still flinging/scrolling then it will indeed trigger. I included a lot of text so you can play with the vertical scrolling. If it's idle then the GestureDetector doesn't work, if it's ScrollDirection.forward or ScrollDirection.reverse then it does work.

blaneyneil commented 9 months ago
import 'package:flutter/material.dart';

class TermsScreen extends StatelessWidget {

  const TermsScreen({super.key});

  @override
  Widget build(BuildContext context) {

    return GestureDetector(
      onHorizontalDragUpdate: (DragUpdateDetails dragUpdateDetails) {
        if (dragUpdateDetails.delta.dx > 20) {
          Navigator.pop(context);
        }
      },

      child: Scaffold(
        body: Container(
          padding: const EdgeInsets.symmetric(horizontal: 24.0),
          child: ListView(
            children: const <Widget>[

              SelectableText.rich(
                TextSpan(
                  style: TextStyle(color: Colors.black, fontSize: 17.0),
                  children: [

                    TextSpan(
                      text: "Notice that widgets are passed as arguments to other widgets. The Scaffold widget takes a number of different widgets as named arguments, each of which are placed in the Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in widgets for the leading widget, and the actions of the title widget. This pattern recurs throughout the framework and is something you might consider when designing your own widgets.",
                      style: TextStyle(color: Colors.black, fontSize: 17.0),
                    ),

                    TextSpan(
                      text: "Notice that widgets are passed as arguments to other widgets. The Scaffold widget takes a number of different widgets as named arguments, each of which are placed in the Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in widgets for the leading widget, and the actions of the title widget. This pattern recurs throughout the framework and is something you might consider when designing your own widgets.",
                      style: TextStyle(color: Colors.black, fontSize: 17.0),
                    ),

                    TextSpan(
                      text: "Notice that widgets are passed as arguments to other widgets. The Scaffold widget takes a number of different widgets as named arguments, each of which are placed in the Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in widgets for the leading widget, and the actions of the title widget. This pattern recurs throughout the framework and is something you might consider when designing your own widgets.",
                      style: TextStyle(color: Colors.black, fontSize: 17.0),
                    ),

                    TextSpan(
                      text: "Notice that widgets are passed as arguments to other widgets. The Scaffold widget takes a number of different widgets as named arguments, each of which are placed in the Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in widgets for the leading widget, and the actions of the title widget. This pattern recurs throughout the framework and is something you might consider when designing your own widgets.",
                      style: TextStyle(color: Colors.black, fontSize: 17.0),
                    ),

                    TextSpan(
                      text: "Notice that widgets are passed as arguments to other widgets. The Scaffold widget takes a number of different widgets as named arguments, each of which are placed in the Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in widgets for the leading widget, and the actions of the title widget. This pattern recurs throughout the framework and is something you might consider when designing your own widgets.",
                      style: TextStyle(color: Colors.black, fontSize: 17.0),
                    ),

                    TextSpan(
                      text: "Notice that widgets are passed as arguments to other widgets. The Scaffold widget takes a number of different widgets as named arguments, each of which are placed in the Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in widgets for the leading widget, and the actions of the title widget. This pattern recurs throughout the framework and is something you might consider when designing your own widgets.",
                      style: TextStyle(color: Colors.black, fontSize: 17.0),
                    ),

                  ],
                ),
              ),

            ],
          ),
        ),

      ),
    );

  }
}
Nirajn2311 commented 9 months ago

@Sembauke can you post a code sample? Thank you

@Renzo-Olivares We got it working with your workaround. Thanks

dmytrohurskyi commented 6 months ago

Looks like the workaround stopped working after recent flutter updates. Any update on this? Does this workaround work for you on the latest stable Flutter?

@Renzo-Olivares can you help here?

flutter-triage-bot[bot] commented 6 months ago

This issue is assigned to @Renzo-Olivares but has had no recent status updates. Please consider unassigning this issue if it is not going to be addressed in the near future. This allows people to have a clearer picture of what work is actually planned. Thanks!

dmytrohurskyi commented 5 months ago

@Renzo-Olivares this needs flutter team's attention

muzzah commented 3 months ago

Just adding my comment here that this is causing me issues as well. Does the flutter team have a work around here that we could use considering the above workaround does not work anymore? Is a fix incoming?

github-actions[bot] commented 1 day ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.