107

I have a Flutter TextField which gets covered by the soft keyboard when the field is selected. I need to scroll the field up and out of the way when the keyboard is displayed. This is a pretty common problem and a solution is presented in this StackOverflow post.

I think I have the ScrollController part figured out but how do I detect when the TextField has been selected? There doesn't appear to be any event method (e.g. onFocus(), onSelected(), onTap(), etc).

I tried wrapping the TextField in a GestureDetector but that didn't work either -- apparently the event was never captured.

new GestureDetector(
  child: new TextField(
    decoration: const InputDecoration(labelText: 'City'),
  ),
  onTap: () => print('Text Selected'),
),

This is such a basic requirement that I know there must be an easy solution.

4 Answers 4

160

I suppose you are looking for FocusNode.

To listen to focus change, you can add a listner to the FocusNode and specify the focusNode to TextField.

Example:

class TextFieldFocus extends StatefulWidget {
  @override
  _TextFieldFocusState createState() => _TextFieldFocusState();
}

class _TextFieldFocusState extends State<TextFieldFocus> {
  FocusNode _focus = FocusNode();

  TextEditingController _controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    _focus.addListener(_onFocusChange);
  }

  @override
  void dispose() {
    super.dispose();
    _focus.removeListener(_onFocusChange);
    _focus.dispose();
  }

  void _onFocusChange() {
    debugPrint("Focus: ${_focus.hasFocus.toString()}");
  }
  
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.white,
      child: new TextField(
        focusNode: _focus,
      ),
    );
  }
}

This gist represents how to ensure a focused node to be visible on the ui.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you so very much for this answer. I haven't attempted to utilize the more robust solution you offered in the gist but the simple solution below seems to work in my test case.
I have an issue with this solution, since I am showing an overlay on focusing the textfield. showing the overlay works like a charm, but as soon as the overlay is closed, the textfield is autofocused again, and the overlay pops up again
before showing the dialog just invoke unfocus method on the same focus node. Do it only if you did not wanted the textfield to be focused when you come back.
The focus node should also be cleaned up in the stateful widget's dispose(). See the example on api.flutter.dev/flutter/widgets/FocusNode-class.html.
don't forget to dispose it in dispose();
|
82

To be notified about a focus event, you can avoid manually managing widget's state, by using the utility classes FocusScope, Focus.

From the docs (https://api.flutter.dev/flutter/widgets/FocusNode-class.html):

Please see the Focus and FocusScope widgets, which are utility widgets that manage their own FocusNodes and FocusScopeNodes, respectively. If they aren't appropriate, FocusNodes can be managed directly.

Here is a simple example:

FocusScope(
  child: Focus(
    onFocusChange: (focus) => print("focus: $focus"),
    child: TextField(
      decoration: const InputDecoration(labelText: 'City'),
    )
  )
)

3 Comments

For me, this answer worked better than the accepted answer because the callback gets called only when the child widget 'TextField' is tapped for entering text. With 'addListener' method from the accpepted answer, it was not the case. Some of parents widget would trigger the callback too when tapped. I didn't quite understand why..
On android, if you press the system backbutton while in a TextField then [onFocusChange] will not be triggered.
but when we trigger a popup the focuscope trigger against on focus, so my datepicker open against.
25

The easiest and simplest solution is to add the onTap method on TextField.

TextField(
  onTap: () {
    print('Editing stated $widget');
  },
)

1 Comment

There are other means for a TextField to get focus, for example if it's the first TextField on a page and future Flutter versions will support tabbing through fields.
4

There is another way if your textfield needs to be disabled for some purpose like mine. for that case, you can wrap your textField with InkWell like this,

InkWell(
  onTap: () {
    print('clicked');
  },
  child: TextField(
    enabled: false,
  ),
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.