0

I have a TextField which has an attribute prefixIcon that accepts a widget. I passed a GestureDetector so that I can do something onTap event of it. But the problem I am facing is as soon as I tap it, though it calls onTap event it but along with that it also focussed the TextField that further launches the keyboard.

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

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body:MyWidget,
    );
  }
}

class MyWidget extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return MyWidgetState();
  }
}

class MyWidgetState extends State<MyWidget>
{
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 200,
      width: 200,
      padding: EdgeInsets.all(20),
      child: TextField(
        decoration: InputDecoration(
          prefixIcon: GestureDetector(
            child: Container(color: Colors.greenAccent, width: 25, height: 25,),
            onTap: () => print("hmm"),
          ),
        ),
      ),
    );
  }
}

So I am trying to find a way by which tapping on prefixIcon widget (here GestureDetector doesn't focus TextField). How can I achieve that functionality?

Nimish Bansal
  • 1,719
  • 4
  • 20
  • 37

2 Answers2

2

I was facing a similar problem, here I explained the problem and a possible solution: Flutter DropdownButton inside TextFormField as prefix

In your case, instead of DropdownButton is a Container, so you could do this:

Wrap(
  children: <Widget>[
    Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16.0),
        boxShadow: [BoxShadow()],
      ),
      child: Row(
        children: <Widget>[
          GestureDetector(
            child: Container(color: Colors.greenAccent, width: 25, height: 25,),
            onTap: () => print("hmm"),
          ),
          Flexible(
            child: TextField(),
          ),
        ],
      ),
    ),
  ],
)

Edit: Unfortunately the prefix of the TextField is intended to be used when the TextField is focused. I could come up with a workaround detecting the focus of the TextField and unfocusing it if the prefix was tapped, here is an example:

final _controller = TextEditingController(text: "Test");
final _focusNode = FocusNode();
var _prefixTapped = false;

@override
void initState() {
  super.initState();
  _focusNode.addListener(() {
    if (_focusNode.hasFocus & _prefixTapped) _focusNode.unfocus();
    _prefixTapped = false;
  });
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text("TextFieldWithGesturePrefix")),
    body: TextField(
      controller: _controller,
      focusNode: _focusNode,
      decoration: InputDecoration(
        prefixIcon: GestureDetector(
          child: Container(
            color: Colors.greenAccent,
            child: Text("Any widget"),
          ),
          onTap: () {
            _prefixTapped = true;
            _focusNode.unfocus();
            print("prefix tapped");
          },
        ),
      ),
    ),
  );
}
Pablo Barrera
  • 10,387
  • 3
  • 28
  • 49
  • This is also quite similar that I have tried as a workaround, but it won't be a better option in general since if you apply different styles, InputDecoration on TextField like border etc you have to imitate all that styles for the prefix. – Nimish Bansal Oct 17 '19 at 03:34
  • Thanks, that worked. But I would suggest few edits, was ListView actually required? I think that is not required. What do you think? Also instead of a prefix, we can use prefixIcon that will be visible even if TextField is not focussed. – Nimish Bansal Oct 17 '19 at 13:34
  • Nice workaround, Pablo. I've created a new solution for this myself that doesn't need to worry about the focusnode at all while still keeping the icon where it's supposed to be. – Adrian Murray Oct 17 '19 at 15:22
  • 1
    Great Adrian, I'll see it. Nimish I edited the changes you mentioned in the code and removed the notes to keep it simple and avoid confusions if anyone want to use this. – Pablo Barrera Oct 17 '19 at 16:26
1

You could wrap the TextField in a Row then add an icon before that that is tappable. Then it won't matter what the default behavior is.

Container(
  height: 200,
  width: 200,
  padding: EdgeInsets.all(20),
  child: Row(
    children: <Widget>[
      GestureDetector(
        child: Container(color: Colors.greenAccent, width: 25, height: 25,),
        onTap: () => print("hmm"),
      ),
      Expanded(child: TextField()),
    ],
  ),
)

I think I have a better solution for you since it doesn't require any manipulation of the FocusNode. Simply pass the two into a Stack, make use of the CompositedTransformTarget/Followers and overlay the decorator with the item you want. I've tested it and it works. It also makes it so the icon you want to place over the prefix input follows along the size of the textfield if that's what you want. Keeping things in sync.

class TestWidget extends StatelessWidget {

  final LayerLink link = LayerLink();

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[

        TextField(
          maxLines: null,
          decoration: InputDecoration(
            prefixIcon: CompositedTransformTarget(
              child: Container(color: Colors.transparent, width: 25, height: 25,),
              link: link,
              ),
          )
        ),



        CompositedTransformFollower(
          link: link,
          child: GestureDetector(
            child: Container(color: Colors.greenAccent, width: 25, height: 25,),
            onTap: () => Vibrate.feedback(FeedbackType.heavy),
          ),
        )
      ],
    );
  }
}
Adrian Murray
  • 2,170
  • 1
  • 13
  • 15
  • Actually this is something similar that I have tried as a workaround, but it won't be a better option in general since if you apply different styles, InputDecoration on TextField like border etc you have to imitate all that styles for the prefix. – Nimish Bansal Oct 17 '19 at 03:34
  • Here, I've added another solution that doesn't require too much effort and essentially replaces the prefix icon with whatever you want. – Adrian Murray Oct 17 '19 at 15:12
  • I didn't know about these widgets! Seems to be useful for some specific cases. I'll test it and see how it works, but at first sight I see that you need to create the widget twice in two places trying to keep its dimensions equally... – Pablo Barrera Oct 17 '19 at 16:31
  • This is my first go around with these widgets. I don't think you need to create the widget twice though. You could probably make this code into a class that takes a widget/prefixIcon parameter and passes the given size of that widget to a more lightweight widget in the actual prefixIcon's spot. Something like SizedBox.fromSize would probably suffice. Then the Rect that needs to be followed is created without much else. I just think that playing with the focus node might be a little sketchy incase the flutter team changes the behavior in the future. Unlikely, but a risk. – Adrian Murray Oct 17 '19 at 16:59
  • Yeah, I would try to simplify this approach to avoid repeating code and also see if it's possible to not wrap the TextField with Stack using Overlay so we don't have to alter too much the structure of the widgets for this, but I don't know how complex it could get. – Pablo Barrera Oct 17 '19 at 17:33
  • The code provided was only to show that it's possible to do. The repeating was simply done because I just copied and pasted his own code. I thought about using a CustomMultiChildLayout instead, or maybe even a Flow depending on if the icon was animated, but those are solutions for classes I'd intend on using a lot more than just once. I'd avoid an Overlay since then the icon would be placed on top of every widget on the screen, including AppBars and such. – Adrian Murray Oct 17 '19 at 18:20
  • @AdrianMurray Thanks. this solution worked too, but it(prefix) is somewhat misaligned. Thanks again for letting me know about the special widget CompositedTransformFollower. – Nimish Bansal Oct 18 '19 at 07:21
  • Some changes in width and height of Container inside CompositedTransformFollower are required. I have to double both of them so as to match them with Container in CompositedTransformTarget – Nimish Bansal Oct 18 '19 at 07:26