31

How can I listen whether the keyboard is showing up or hiding?

I tried this example How to listen to keyboard on screen Flutter?

 void _listener(){
    if(_myNode.hasFocus){
      // keyboard appeared 
    }else{
      // keyboard dismissed
    }
}

FocusNode _myNode = new FocusNode()..addListener(_listner);

TextField _myTextField = new TextField(
        focusNode: _mynNode,
        ...
        ...
    );

But unfortunately it doesn't work. Any ideas how it could be possible to listen to the keyboard change?

It seems like it works when I press "Done" on the keyboard. But if i press back on my phone it won't go to "keyboard dismissed" because the focus still exists.. Any help?

GreenTigerEye
  • 5,917
  • 9
  • 22
  • 33

8 Answers8

26

KeyboardVisibilityBuilder

Listening for keyboard show/hide events can be achieved with WidgetsBindingObserver mixin. I prepared KeyboardVisibilityBuilder widget that handles the behavior for you. The usage is quite similar to AnimatedBuilder:

return KeyboardVisibilityBuilder(
  builder: (context, child, isKeyboardVisible) {
    if (isKeyboardVisible) {
      // build layout for visible keyboard
    } else {
      // build layout for invisible keyboard
    }
  },
  child: child, // this widget goes to the builder's child property. Made for better performance.
);

KeyboardVisibilityBuilder implementation:

/// Calls `builder` on keyboard close/open.
/// https://stackoverflow.com/a/63241409/1321917
class KeyboardVisibilityBuilder extends StatefulWidget {
  final Widget child;
  final Widget Function(
    BuildContext context,
    Widget child,
    bool isKeyboardVisible,
  ) builder;

  const KeyboardVisibilityBuilder({
    Key key,
    this.child,
    @required this.builder,
  }) : super(key: key);

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

class _KeyboardVisibilityBuilderState extends State<KeyboardVisibilityBuilder>
    with WidgetsBindingObserver {
  var _isKeyboardVisible = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom;
    final newValue = bottomInset > 0.0;
    if (newValue != _isKeyboardVisible) {
      setState(() {
        _isKeyboardVisible = newValue;
      });
    }
  }

  @override
  Widget build(BuildContext context) => widget.builder(
        context,
        widget.child,
        _isKeyboardVisible,
      );
}
Andrey Gordeev
  • 30,606
  • 13
  • 135
  • 162
  • Using mixin is a good idea, as we can also use it with provider – Fayaz Dec 12 '20 at 19:23
  • 2
    I use this to validate input fields whenever the keyboard is closed, instead of when the Apply button is pressed – Ber Feb 01 '21 at 15:04
  • Also we can give a void Function(bool isKeyboardOpened)? onKeyboardVisibilityChange; parameter to KeyboardVisibilityBuilder widget and, with this parameter, we can can it on didChangeMetrics function, like; widget.onKeyboardStatusChange?.call(newValue); with this way, we can give a function to widget for update different things with this parameter. in this way we can notify some other things based keyboards visibility. – leylekseven Jan 02 '23 at 16:45
9

Not sure how reliable this is, but there's this property on MediaQueryData:

  /// The number of physical pixels on each side of the display rectangle into
  /// which the application can render, but over which the operating system
  /// will likely place system UI, such as the keyboard, that fully obscures
  /// any content.
  final EdgeInsets viewInsets;

Checking if viewInsets.vertical is greater than zero in the build() method gave me correct results:

  @override
  Widget build(BuildContext context) {

    bool isKeyboardShowing = MediaQuery.of(context).viewInsets.vertical > 0;

    return SafeArea(
      child: Scaffold(
        body: Column(
          children: <Widget>[
            Text(isKeyboardShowing ? 'YES!' : 'NO!'),
            TextField(),
          ],
        ),
      ),
    );
  }

It's probably a good idea to combine this with other checks (e.g. input focus), to avoid false positives.

Vinicius Braz Pinto
  • 8,209
  • 3
  • 42
  • 60
  • Do phones with notches give a false positive here? – stantronic Nov 14 '19 at 12:20
  • 1
    Notches don't affect viewInsets, as the MediaQueryData page explains. Notches would be included in `.padding` and `.viewPadding`. That said, you probably should be using `.bottom`, not `.vertical`. The latter adds any top offset representing top area completely obscured by the system. https://api.flutter.dev/flutter/widgets/MediaQueryData/viewInsets.html also states: ```When a mobile device's keyboard is visible viewInsets.bottom corresponds to the top of the keyboard.```. – qix May 24 '22 at 13:47
5

There is a package which mets your requirement completely. Please check that: flutter_keyboard_visibility

In there, by using StreamSubscription, you can listen whether the keyboard becomes goes up/down and react based on that.

Utku A.
  • 738
  • 8
  • 8
5

what I need is a listener so I convert Andrey Gordeev code to

    import 'package:flutter/cupertino.dart';

class KeyboardVisibilityListener extends StatefulWidget {
  final Widget child;
  final void Function(
    bool isKeyboardVisible,
  ) listener;

  const KeyboardVisibilityListener({
    Key? key,
    required this.child,
    required this.listener,
  }) : super(key: key);

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

class _KeyboardVisibilityListenerState extends State<KeyboardVisibilityListener>
    with WidgetsBindingObserver {
  var _isKeyboardVisible = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final bottomInset = WidgetsBinding.instance!.window.viewInsets.bottom;
    final newValue = bottomInset > 0.0;
    if (newValue != _isKeyboardVisible) {
      _isKeyboardVisible = newValue;
      widget.listener(_isKeyboardVisible);
    }
  }

  @override
  Widget build(BuildContext context) => widget.child;
}

and I used it to unfocus the input field when hiding the keyboard

home: KeyboardVisibilityListener(
        listener: (isKeyboardVisible) {
          if (!isKeyboardVisible) {
            FocusManager.instance.primaryFocus?.unfocus();
          }
        },
        child: Scaffold(
          key: scaffoldKey,
          body: layoutp.currentItem.page,
        ),
      ),
2
if (MediaQuery.of(context).viewInsets.bottom > 0.0) {
   // keyboard on the screen
}

Simple explanation: MediaQuery to learn the size of the current media. This class use as MediaQueryData media = MediaQuery.of(context);. If any view appears on the screen MediaQuery.of(context).viewInsetsgive some value of the height of that view. As keyboard appears from the bottom on the screen so I use MediaQuery.of(context).viewInsets.bottom and this gives me the height of the keyboard taken on my screen. When the keyboard doesn't appear this value is 0.And this solution definitely works.

Nazmul81
  • 150
  • 1
  • 3
  • 13
0

Did you pick up the spelling mistake?

FocusNode _myNode = new FocusNode()..addListener(_listner);

Should be:

FocusNode _myNode = new FocusNode()..addListener(_listener);
0

A widget that calls a callback whenever the user presses or releases a key on a keyboard.

A RawKeyboardListener is useful for listening to raw key events and hardware buttons that are represented as keys. Typically used by games and other apps that use keyboards for purposes other than text entry.

For text entry, consider using a EditableText, which integrates with on-screen keyboards and input method editors (IMEs).

const RawKeyboardListener({
Key key,
@required FocusNode focusNode,
@required ValueChanged<RawKeyEvent> onKey,
@required Widget child
})

Creates a widget that receives raw keyboard events.

For text entry, consider using a EditableText, which integrates with on-screen keyboards and input method editors (IMEs).

Implementation

  const RawKeyboardListener({
  Key key,
  @required this.focusNode,
  @required this.onKey,
  @required this.child,
}) : assert(focusNode != null),
     assert(child != null),
     super(key: key);
Scaphae Studio
  • 503
  • 3
  • 3
0

You can use this library keyboard_visibility_pro

KeyboardVisibility(
      // it will notify
        onChanged: (bool visible) {
          print(visible);
        },
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const <Widget>[TextField()],
          ),
        ),
      ),
Masum Billah Sanjid
  • 1,029
  • 1
  • 7
  • 19