Flutter avoid focusing on TextField when clicking button inside

Viewed 659

I have a widget that looks like this:

address input widget

The code looks like this:

TextField(
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    hintText: 'Enter your address or ZIP code',
    hintStyle: TextStyle(fontSize: 18),
    suffixIcon: IconButton(
      icon: Icon(Icons.gps_fixed),
      onPressed: () {
        ...
      },
    ),
  ),
),

The behavior I want is that when the user clicks on the GPS button, instead of having the keyboard pop up, the TextField should become temporarily read-only and be filled with the current address after we get a response from some API.

The problem is, the way I wrote the code causes the TextField to gain focus when the GPS button is clicked. The click handler then disables the TextField to do async query, which causes the keyboard to disappear immediately. This creates a very janky animation.

Is there a way to make the TextField NOT gain focus when the GPS button is clicked?

2 Answers

This suffixIcon for the textfield is a part of it, If you click the suffixIcon, it's also performing click on the textfield. This is the reason why you are getting your keyboard poped up.

Solution: Wrap your textfield into a Stack, and make sure IconButton is the last child of it. This performs click on your IconButton instead of gaining focus in textfield.

Stack(
  alignment: Alignment.centerRight,
  children: <Widget>[
    TextField(),
    IconButton(
      icon: Icon(Icons.add),
      onPressed: () {
        
      },
    ),
  ],
)

It is a known bug, issue has ben opened on github , and closed due to inactivity... You can take a look at that issue, there are some workarounds posted by users, they mainly revolve around stacks. I'm guessing that is where Suman's answer stems from :).

I propose a different approach, simply separate the button from the TextField. Something like this:

  Row(
      children: <Widget>[
        Expanded(
            flex: 1,
            child: TextField(
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                hintText: 'Enter your address or ZIP code',
                hintStyle: TextStyle(fontSize: 18),
              ),
            )),
        Expanded(
          flex: 0,
          child: IconButton(
              icon: Icon(Icons.gps_fixed),
              tooltip: 'Get a gpx fix',
              onPressed: () {
                print("do something");
              }),
        ),
      ],
    )
 );

result_image

I personally prefer to have the button outside the text field because IMHO it conveys that it is a button more so than when having it inside. When inside it looks like it is just an icon that describes the field. Anyhow that is just personal preference, you can add border around both elements if that is what you prefer by wrapping both in a container and placing the decoration on the container.

Related