90

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.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
user2785693
  • 935
  • 1
  • 8
  • 7

4 Answers4

144

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.

starball
  • 20,030
  • 7
  • 43
  • 238
Hemanth Raj
  • 32,555
  • 10
  • 92
  • 82
  • 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. – user2785693 Mar 18 '18 at 18:09
  • 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 – Qiong Wu Apr 04 '18 at 14:22
  • before showing the dialog just invoke [`unfocus`](https://docs.flutter.io/flutter/widgets/FocusNode/unfocus.html) method on the same focus node. Do it only if you did not wanted the textfield to be focused when you come back. – Hemanth Raj Apr 04 '18 at 14:41
  • 4
    The focus node should also be cleaned up in the stateful widget's dispose(). See the example on https://api.flutter.dev/flutter/widgets/FocusNode-class.html. – stpch Mar 20 '20 at 18:29
  • 1
    don't forget to dispose it in dispose(); – Hesam Jan 08 '21 at 23:42
  • _focus.removeListner, and _focus.dispose in the dispose() – Indy9000 Aug 07 '21 at 18:32
66

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'),
    )
  )
)
avikam
  • 1,018
  • 9
  • 11
  • 1
    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.. – TGLEE May 02 '21 at 06:53
  • 1
    On android, if you press the system backbutton while in a TextField then [onFocusChange] will not be triggered. – Marwin Lebensky May 27 '21 at 14:34
  • but when we trigger a popup the focuscope trigger against on focus, so my datepicker open against. – Yogi Arif Widodo Nov 22 '21 at 11:57
23

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

TextField(
  onTap: () {
    print('Editing stated $widget');
  },
)
Ankur Prakash
  • 1,417
  • 4
  • 18
  • 31
  • 7
    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. – Günter Zöchbauer Feb 08 '19 at 08:19
3

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,
  ),
);
Al Walid Ashik
  • 1,545
  • 20
  • 32