Skip to content

Taps on TextField prefix/suffix #39376

Closed

Description

@MRL-00

I'm experiencing weird issues with the latest stable version of Flutter: 1.7.8+hotfix.4.
I have a simple textfield widget that has an onTap() action. The textfield widget also has a prefix icon which has its own onTap() action. When I press the prefix icon, it completes everything inside the prefix onTap() but it also then goes and completes everything in the textfield onTap().

I've been able to replicate it with the below code. Both onTap methods print outputs to the console. When the prefix icon is clicked, it is outputting the icon onTap as well as the textfield onTap actions.

Replicated in below code

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  void _incrementCounter() {
    setState(() {
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              color: Colors.red,
              child: TextField(
                onTap: () {
                  print('Hello this is a print out');
                },
                decoration: InputDecoration(
                  hintText: 'Hint text',
                  border: InputBorder.none,
                  contentPadding: EdgeInsets.only(left: 25.0, top: 16.0),
                  prefixIcon: IconButton(
                    icon: Icon(Icons.hotel, color: Colors.green, size: 40.0,),
                    onPressed: () {
                      print("Hi this is a second print out");
                    },
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

Flutter Doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Mac OS X 10.14.6 18G87, locale en-NZ)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 10.3)
[✓] iOS tools - develop for iOS devices
[✓] Android Studio (version 3.4)
[✓] VS Code (version 1.37.1)
[✓] Connected device (1 available)

• No issues found!

I have switched back to the master channel in the mean time to get around this issue but would be good to have it fixed.
Thanks.

Activity

  1. added
    a: text inputEntering text in a text field or keyboard related problems
    p: material_uimaterial_ui package in flutter/packages
    on Aug 28, 2019
  2. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Aug 28, 2019
  3. MRL-00 commented on Sep 24, 2019

    @MRL-00
    Author

    @HansMuller @mikhail-tokarev Hey guys, is there any idea on when this might be fixed? It is also happening now on the 1.9.1+hotfix 2 version.

  4. HansMuller commented on Sep 24, 2019

    @HansMuller
    Contributor
  5. justinmc commented on Sep 24, 2019

    @justinmc
    Contributor

    There is a discussion at the end of #36324 about this problem, and a work in progress PR to fix it #38722. I'm hoping to get some time to finish that up soon.

  6. self-assigned this
    on Sep 24, 2019
  7. JZ-at-TP commented on Oct 8, 2019

    @JZ-at-TP

    Any update on this? I'de love to update my Flutter version but can't with this outstanding.

  8. justinmc commented on Oct 9, 2019

    @justinmc
    Contributor

    @JZ-at-TP I haven't had time to circle back to that WIP PR, and I won't for at least a week or two. If this is a blocker for you (or anyone else reading this), and you want to take over the PR yourself, I would be happy to help you through it. Otherwise I will hopefully be pushing commits to that PR in a few weeks to finish it up.

  9. MRL-00 commented on Oct 11, 2019

    @MRL-00
    Author

    Hey @justinmc I would be happy to look at doing the PR. Can you point me in the right direction on where to look? You can reach me at [email protected] if you want to touch base via email.

  10. justinmc commented on Oct 11, 2019

    @justinmc
    Contributor

    @MattL-NZ Thanks so much! I'll email you with some details.

  11. changed the title [-]Flutter Stable Version 1.7.8+hotfix.4 issue with TextField[/-] [+]Taps on TextField prefix/suffix[/+] on Oct 22, 2019
  12. MRL-00 commented on Jan 21, 2020

    @MRL-00
    Author

    Hi @justinmc, just thought i would check in and see how you are progressing with this issue? Have you been able to get something working?

  13. justinmc commented on Jan 22, 2020

    @justinmc
    Contributor

    Sorry, I haven't had time to finish #38722, but I plan to revisit it when I can!

  14. Lecarvalho commented on Feb 2, 2020

    @Lecarvalho

    Do we have a workaround for instant?

  15. 8 remaining items

  16. MarsGoatz commented on Dec 26, 2020

    @MarsGoatz

    TextField working properly without bugs is bare minimum TBH and writing hacks for these bare minimum widgets is just another level crazy.

    Hoping a fix will be out soon!

  17. ilkercankaya commented on Jan 15, 2021

    @ilkercankaya

    @MattL-NZ,
    You have to place a simple check inside onTap function. Please check below

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // Focus nodes are necessary
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
      final String title;
      final textFieldFocusNode = FocusNode();
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      void _incrementCounter() {
        setState(() {});
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.red,
                  child: TextField(
                    focusNode: widget.textFieldFocusNode,
                    onTap: () {
                      if (widget.textFieldFocusNode.canRequestFocus) {
                        print('Hello this is a print out');
                      }
                    },
                    decoration: InputDecoration(
                      hintText: 'Hint text',
                      border: InputBorder.none,
                      contentPadding: EdgeInsets.only(left: 25.0, top: 16.0),
                      prefixIcon: IconButton(
                        icon: Icon(
                          Icons.hotel,
                          color: Colors.green,
                          size: 40.0,
                        ),
                        onPressed: () {
                          // Unfocus all focus nodes
                          widget.textFieldFocusNode.unfocus();
    
                          // Disable text field's focus node request
                          widget.textFieldFocusNode.canRequestFocus = false;
    
                          // Do your stuff
                          print("Thanks for the solution");
    
                          //Enable the text field's focus node request after some delay
                          Future.delayed(Duration(milliseconds: 100), () {
                            widget.textFieldFocusNode.canRequestFocus = true;
                          });
                          print("Hi this is a second print out");
                        },
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    

    Instead of giving 100ms as delay, try

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
          textFieldFocusNode.canRequestFocus = true;
        });
    

    It seems to work and is a smaller window.

  18. drekka commented on Jan 18, 2021

    @drekka

    Not sure if this will help, but here's what I ended up with to stop the field from taking focus when I tap a suffix button.

    I did it by creating an extension which I used to generate input decorations. When a suffix button is tapped, it deactivates the ability of field to accept focus, then calls the action, passing it a closure to restore the field's focus ability. This allows the button to perform any amount of async work before calling the completion.

    /// Defines the action for a suffix icon.
    typedef IconAction = void Function(void Function());
    
    extension InputDecorationFactory on InputDecoration {
      /// Factory that creates an input decoration set with the apps standard look and feel.
      static InputDecoration formFieldDecoration({@required String label, @required String hint, IconData suffixIcon, IconAction action, FocusNode focusNode}) {
        return InputDecoration(
          labelText: label,
          hintText: hint,
          enabledBorder: UnderlineInputBorder(
            borderSide: BorderSide(color: Colors.black12),
          ),
        ).withSuffixIcon(suffixIcon, focusNode: focusNode, action: action);
      }
    
      /// Adds a suffix icon to an input decoration.
      InputDecoration withSuffixIcon(IconData suffixIcon, {IconAction action, FocusNode focusNode}) {
        // If there is no icon there's nothing to change.
        if (suffixIcon == null) {
          return this;
        }
    
        // If there's just an icon then add it and return.
        if (action == null) {
          return this.copyWith(suffixIcon: Icon(suffixIcon));
        }
    
        // otherwise setup the action and focus node.
        return this.copyWith(
          suffixIcon: IconButton(
            icon: Icon(suffixIcon),
            onPressed: () => _textFormFieldIconAction(focusNode: focusNode, action: action),
          ),
        );
      }
    
      // Deactivate the fields ability to focus then restore it through a closure passed to the buttons function. This allows
      // the button to call async functionality.
      // This fixes a know Flutter issue where fields pick up on a suffix button's tap and take focus, causing the keyboard to display at the same time as any widgets presented by the button.
      void _textFormFieldIconAction({FocusNode focusNode, IconAction action}) {
        focusNode?.unfocus();
        focusNode?.canRequestFocus = false;
        action(() {
          focusNode?.canRequestFocus = true;
        });
      }
    }
  19. bayramaliunuvar commented on Mar 12, 2021

    @bayramaliunuvar

    all you have to do is define a variable.
    bool vadeTarihiClear=false;

    SuffixIcon
    decoration: InputDecoration( suffixIcon: IconButton( icon: Icon(clearButtonIcon), onPressed: () { vadeTarihiClear = true; Future.delayed(Duration(milliseconds: 100), () { vadeTarihiClear = false; }); })),

    TextField ontap
    onTap: () async { if (vadeTarihiClear) return;
    milliseconds: 100 enough

  20. drekka commented on Mar 23, 2021

    @drekka

    I learned long ago o never trust code that relies on time. Too many bugs come out of it and sometimes some bad user experiences. Not saying that won’t work. It’s just been my experience to avoid such code.

  21. LongCatIsLooong commented on Mar 24, 2021

    @LongCatIsLooong
    Contributor

    internal link: b/183372823.

  22. cpboyd commented on Apr 29, 2021

    @cpboyd

    This also applies to other uses of touchable components on top of widgets that utilize onTapDown, e.g. CupertinoContextMenu.

    Putting an IconButton on top will result in the CupertinoContextMenu's internal onTapDown still triggering.

  23. wwwqyhme commented on May 24, 2021

    @wwwqyhme
    1. set textfield's decoration to null
    2. wrap your textfield with InputDecorator
    AnimatedBuilder(
                  animation: Listenable.merge(<Listenable>[focusNode]),
                  builder: (BuildContext context, Widget? child) {
                    return InputDecorator(
                      decoration:
                          effectiveDecoration.copyWith(errorText: state.errorText),
                      isFocused: focusNode.hasFocus,
                      isEmpty: state.value == null,
                      child: child,
                    );
                  },
                  child: textField,
                );
              }
  24. LongCatIsLooong commented on May 26, 2021

    @LongCatIsLooong
    Contributor

    @justinmc @chunhtai as I understand it the tap "leaks" because of the use of the _TransparentTapGestureRecognizer in the text field right? The text field wants to handle double/triple taps but single taps should be handled by the selection handle, but as a result taps that landed on something other than the selection handle are now leaking to the text field as well.

    After testing on my android devices and iphone I have a different interpretation of the tap gesture:

    iOS Android
    selection handle The handle can be dragged but DOES NOT HANDLE TAPS.
    I tested this on a simulator so I can precisely land my taps on the tiny selection handle. It turns out the current behavior might result from less accurate pointer devices
    The handle can be dragged and tapped. Tap down brings up a magnifier and the selection toolbar and tap up dismisses the magnifier & the toolbar. Tapping in quick succession performs multiple single taps. https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/Editor.java;drc=master;bpv=1;bpt=1;l=5455
    so it does not yield the pointer to the text field when the gesture is a double tap.
    selection rect Tapping expands the selection to the corresponding text boundary (single - word edge, double - word, triple - paragraph). If the target selection is already part of the current selection, it keeps the selection as is and toggles the text selection toolbar. tap gestures seem to do the same as on regular text

    So I guess we can retire the _TransparentTapGestureRecognizer class. And maybe deprecate the onSelectionHandleTapped callback on EditableTexts, etc. too (can't deprecate that. Android still uses it to toggle the toolbar).

    I think one of the main challenges to implement the iOS selection highlight gesture behavior would be that if the entire document is selected the user won't be able to tap somewhere that doesn't have a glyph to collapse the selection to a caret (since the current API only tells us the text position to the closest glyph: GetGlyphPositionAtCoordinate always return a valid position in the text even if the given coordinate is far away from every glyph).

    EDIT

    It seems I was wrong about iOS selection handle not handling taps. I did the test again and found out they DO handle taps in aUITextField, but do not in a UITextView.
    Neither UITextView nor UITextField handles taps on the selection handle. UITextFields just have a large effective "selectionRect" (larger than the visually highlighted area) so the taps are actually handled by the selection rect. And UITextViews simply do not toggle the text selection toolbar when the user taps on the selected area (so the "selection rect" behavior above only applies to UITextField).

  25. LongCatIsLooong commented on May 26, 2021

    @LongCatIsLooong
    Contributor

    This has caused several really annoying issues (e.g., #78918), and I'd like to fix this before continuing with #79414 (it's difficult to port some of the hacks to the new implementation). I'm going to work on it ASAP.

  26. added
    r: fixedIssue is closed as already fixed in a newer version
    on Jun 8, 2021
  27. github-actions commented on Jul 31, 2021

    @github-actions

    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.

  28. locked as resolved and limited conversation to collaborators on Jul 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

a: text inputEntering text in a text field or keyboard related problemsfound in release: 1.20Found to occur in 1.20frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagesr: fixedIssue is closed as already fixed in a newer version

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions