I've recently had to hide the keyboard on scroll in my app. Once the user has finished typing, they typically scroll down. By hiding the keyboard, they gain a lot of real estate.
First try: ScrollController
My view uses a SingleChildScrollView
to show scrollable content. My first try was to introduce a ScrollController
and listen to it:
ScrollController scrollController;
@override
void initState() {
scrollController = ScrollController();
scrollController.addListener(hideKeyboard);
super.initState();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: scrollController,
child: Column(...)
);
}
hideKeyboard() {
FocusScope.of(context).requestFocus(FocusNode());
}
And it worked great!... Most of the time.
There are times where it will hide the keyboard even though I am not actively scrolling. For example if I select a slightly hidden TextField, the keyboard will briefly appear, and then disappear as the TextField scrolls into view.
Second try: GestureDetector
@override
Widget build(BuildContext context) {
return GestureDetector(
onVerticalDragDown: (_) {
hideKeyboard();
},
child: SingleChildScrollView(...));
}
I've been wondering if I should change the behavior
of the GestureDetector
. The doc says it defaults to HitTestBehavior.deferToChild
, which means that the GestureDetector
will handle the event if the touch event hits one of its children. That's what we want. So we can keep the default.
Also, I initially used onVerticalDragStart
. The doc says it'll trigger when "[it] has begun to move vertically." but it didn't work. I had to use onVerticalDragDown
. My guess is that the scroll is done its child, the SingleChildScrollView
, not by the GestureDetector
itself, so the vertical drag will technically never "start".