102

In Android, we can call setOnFocusChangeListener(), do something in onFocusChanged() method, but flutter does not provider onFocus() interface like onTap() in GestureDetector or onKey() in RawKeyboardListener.

I have read flutter api about focus, https://api.flutter.dev/flutter/widgets/FocusManager-class.html but I can't find a way to realize my request, anyone who can give me a hand?

coder.john
  • 1,235
  • 2
  • 12
  • 11
  • 1
    I agree - its hard to understand the intent from the documentation. Example would be great as well. – Ride Sun Jan 31 '18 at 22:45
  • [Haider Abbas](https://stackoverflow.com/users/4729459) posted an [Answer](https://stackoverflow.com/a/67089822) saying "You may use MouseRegion() events like [https://api.flutter.dev/flutter/widgets/MouseRegion-class.html](https://api.flutter.dev/flutter/widgets/MouseRegion-class.html) onEnter, onExit" – Scratte Apr 19 '21 at 07:44

4 Answers4

182

In addtion you can use Focus widget.

Focus(
  child: TextFormField(...),
  onFocusChange: (hasFocus) {
    if(hasFocus) {
      // do stuff
    }
  },
)
GoodSp33d
  • 6,252
  • 4
  • 35
  • 67
BambinoUA
  • 6,126
  • 5
  • 35
  • 51
  • 13
    Certainly cleaner than having to set up and maintain a `FocusNode` instance. And for this post there is a proper example. Seems this could be the accepted answer now. Nice, Thanks – Gene Bo May 21 '20 at 22:45
  • 7
    This doesn't fire if you tap back. – TimSim Jul 11 '20 at 18:53
  • @TimSim It did when I tested it (with both TextFormField and TextField). – Andy King Jul 15 '20 at 16:19
  • 2
    It's definitely cleaner but depends on the use case. If we have more than one input field maintaining a focus node for each would be better. – Hassan Hammad Aug 24 '21 at 02:18
  • Almost a must if you're working with blocs and re-usable widgets! – nyphur Nov 21 '21 at 15:43
  • `onFocusChange` will fire when you lose focus, too. So the variable `hasFocus` is misleading and should be renamed or replace with `_` if you don't use it. – chemturion Aug 11 '22 at 22:25
  • @chemturion It will fire, but `hasFocus` will evaluate to `true` if the node has focus and `false` if it does not. The name is appropriate. Booleans are typically named with the naming convention has-, is-, did-, etc. – keysmusician Jul 19 '23 at 23:37
109

Using FocusNode

Here is a fully correct answer. addListener shall be in initState(), not build(), because that would result in adding a listener every time a widget is built and you most likely don't want that.

import 'package:flutter/material.dart';

class SomeWidget extends StatefulWidget {
  @override
  _SomeWidgetState createState() => _SomeWidgetState();
}

class _SomeWidgetState extends State<SomeWidget> {
  final _focusNode = FocusNode();
    
  @override
  void initState() {
    super.initState();
    _focusNode.addListener(() {
      print("Has focus: ${_focusNode.hasFocus}");
    });
  }
    
  @override
  Widget build(BuildContext context) {
    return TextField(focusNode: _focusNode);
  }
    
  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }
}

Using flutter_hooks package

If you're by any chance using the flutter_hooks package, there is a dedicated useFocusNode hook.

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class SomeWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final focusNode = useFocusNode();
    useEffect(() {
      focusNode.addListener(() {
        print("Has focus: ${focusNode.hasFocus}");
      });
      return; // You need this return if you have missing_return lint
    }, [focusNode]);

    return TextField(focusNode: focusNode);
  }
}
Albert221
  • 5,223
  • 5
  • 25
  • 41
  • Thanks @Albert221, I was going through this problem of receiving multiple calls, now I managed to solve. – Luan Martins Gepfrie Jul 26 '19 at 12:25
  • 11
    *Note:* a node focus listener is called twice: when it acquires focus and when it loses the focus. Inside the callback `node.hasFocus` would be `true` or `false` respectively – Kirill Karmazin Dec 09 '19 at 12:09
  • @Albert221 Do we also need to call the remove listener in the dispose method ? – raphire Jul 28 '22 at 10:16
  • 2
    @raphire no, it's not needed. Notifying listeners in a `ChangeNotifer` (which a `FocusNode` is) is illegal after its disposal – Albert221 Jul 29 '22 at 07:23
  • So... why is the second final focusNode in the build method if it's not supposed to be there?... Or it IS supposed to be there, if it's a HookWidget?! – Karolina Hagegård Aug 29 '22 at 06:32
  • 2
    @KarolinaHagegård the `useFocusNode` is a hook, that returns the `FocusNode`, but also manages its lifecycle. It's like the first snippet with `initState` and `dispose`, but hidden inside a hook as you can see here: https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/lib/src/focus.dart#L50-L79 – Albert221 Aug 29 '22 at 08:12
45

I suppose you are looking for FocusNode class. Use addListener method to add a listener function that listens to focus change.

Example
declare and define FocusNode

 var focusNode = FocusNode();
  @override
  void initState() {
    focusNode.addListener(() {
      print(focusNode.hasFocus);
    });
    super.initState();
  }

Use focus node in textfield

TextField(
            focusNode: focusNode,
          ),

Output

when textfield is focus you will get true else false

Kunchok Tashi
  • 2,413
  • 1
  • 22
  • 30
Hemanth Raj
  • 32,555
  • 10
  • 92
  • 82
-2
class FocusChange {
     static void fieldFocusChange(BuildContext context, FocusNode 
     currentFocusNode,
     FocusNode nextFocusNode) {
     currentFocusNode.unfocus();
     FocusScope.of(context).requestFocus(nextFocusNode);
    }
}
ajaybadole
  • 121
  • 1
  • 1
  • 6
  • 2
    Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Apr 17 '23 at 02:03