When you're working with TextField or TextFormField, keyboard keeps poping up from the bottom, and you would like to know when it pops up and goes back down.
Unfortunatly, there's no way of knowing only with the TextField. It's possible to know keyboard popup with using onTap, but there's no event for closing keyboard.
To know if keyboard is up, you'll need to check if the TextField is no longer focused, OR...
MediaQuery.of(context).viewInsets.bottom != 0
You can check if the view has changed. Keyboard changes the view when it shows up, so when it's visible, the value will be above 0. Otherwise, it'll be positive value.
Now, we know how to check visibility of a keyboard, it's time to use it.
@override
Widget build(BuildContext context) {
keyboardVisiblity = MediaQuery.of(context).viewInsets.bottom != 0
return widget;
}
You can just use that line of code inside build() method of a widget.
But, I thought I'd make a custom widget for it.
Like the GestureDetector, I made my version of a detector widget for keyboard.
class KeyboardDetector extends StatefulWidget {
const KeyboardDetector({
Key? key,
this.onOpenKeyboard,
this.onCloseKeyboard,
required this.child,
}) : super(key: key);
final Function()? onOpenKeyboard;
final Function()? onCloseKeyboard;
final Widget child;
@override
State<KeyboardDetector> createState() => _KeyboardDetectorState();
}
This custom widget has events for opening keyboard, and closing it.
Also, it needs to be wrapping the widgets that calls keyboard to detect it just like the GestureDetector, so it has a child.
class _KeyboardDetectorState extends State<KeyboardDetector> with WidgetsBindingObserver{
bool previousState = false;
@override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
// TODO: implement dispose
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeMetrics(){
bool currentState = MediaQuery.of(context).viewInsets.bottom != 0;
if(previousState != currentState){
previousState = currentState;
if(currentState){
print('open');
widget.onOpenKeyboard?.call();
}
else{
print('close');
widget.onCloseKeyboard?.call();
}
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
I've used the WidgetsBindingObserver to observe the change in the view. In the above code, there's didChangeMetrics(), and it's called when the screen dimensions changes. It's called several times while keyboard is poping up, so it's important to call the events only when the value changes.
Here's how it's used.
return KeyboardDetector(
onOpenKeyboard: (){ setState((){ _isKeyboardVisible = true;} ); },
onCloseKeyboard: (){ setState((){ _isKeyboardVisible = false; }); },
child: Scaffold(
appBar: AppBar(
...
),
body: ...
TextField(
controller: _textController,
decoration: InputDecoration(
fillColor: Colors.transparent,
hintText: 'title'
),
),
bottomSheet: Offstage(
offstage: !_isKeyboardVisible,
child: ...
),
),
);