469

I am collecting user input with a TextFormField and when the user presses a FloatingActionButton indicating they are done, I want to dismiss the on screen keyboard.

How do I make the keyboard go away automatically?

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  MyHomePageState createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller = new TextEditingController();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.send),
        onPressed: () {
          setState(() {
            // send message
            // dismiss on screen keyboard here
            _controller.clear();
          });
        },
      ),
      body: new Container(
        alignment: FractionalOffset.center,
        padding: new EdgeInsets.all(20.0),
        child: new TextFormField(
          controller: _controller,
          decoration: new InputDecoration(labelText: 'Example Text'),
        ),
      ),
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

void main() {
  runApp(new MyApp());
}
Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
  • 1
    Here is the [recommended solution](https://stackoverflow.com/a/54977554/8138591) in May 2021 – Son Nguyen May 31 '21 at 10:40
  • sir can you please go through the below question relating to flutter . below link is my question link in stack overflow ----- https://stackoverflow.com/questions/69640226/how-to-create-shapes-or-alphabets-by-joining-dots-join-dots-for-creating-shap ------- – Jithu Hari Oct 21 '21 at 07:52

31 Answers31

862

For Flutter version 2 or latest :

Since Flutter 2 with null safety this is the best way:

FocusManager.instance.primaryFocus?.unfocus();

Note: using old ways leads to some problems like keep rebuild states;


For Flutter version < 2 :

As of Flutter v1.7.8+hotfix.2, the way to go is:

FocusScope.of(context).unfocus();

Comment on PR about that:

Now that #31909 (be75fb3) has landed, you should use FocusScope.of(context).unfocus() instead of FocusScope.of(context).requestFocus(FocusNode()), since FocusNodes are ChangeNotifiers, and should be disposed properly.

-> DO NOT use ̶r̶e̶q̶u̶e̶s̶t̶F̶o̶c̶u̶s̶(̶F̶o̶c̶u̶s̶N̶o̶d̶e̶(̶)̶ anymore.

 F̶o̶c̶u̶s̶S̶c̶o̶p̶e̶.̶o̶f̶(̶c̶o̶n̶t̶e̶x̶t̶)̶.̶r̶e̶q̶u̶e̶s̶t̶F̶o̶c̶u̶s̶(̶F̶o̶c̶u̶s̶N̶o̶d̶e̶(̶)̶)̶;̶

Read more about the FocusScope class in the flutter docs.

AnasSafi
  • 5,353
  • 1
  • 35
  • 38
Pascal
  • 15,257
  • 2
  • 52
  • 65
  • The problem I have been experiencing is when leaving a text field to open up the camera, the keyboard is not always dismissed and overlaps the camera. This solution does in fact remove the focus, BUT then the focus returns to the text field and the problem persists. Moving the focus to an unused FocusNode solves this. – dfmiller Jan 16 '20 at 23:28
  • If this solution didn't fix your error, you can add "await Future.delayed(const Duration(milliseconds: 200))" before send to another page. – Eray Hamurlu Feb 01 '22 at 19:52
  • my issue was i put 'focusnode: FocusNode()' in ui. i removed it and put this code. Worked! – Vipin Krishna Sep 29 '22 at 08:46
  • Thanks for your great solution. But I have a question about it. If I use FocusManager.instance.primaryFocus?.unfocus(); solution, should I dispose of it in dispose method? – Aaydin Feb 01 '23 at 07:36
  • @Aaydin Not sure exactly what your use case is. I suggest you to just experiment / test and see the result ;-) – Pascal Feb 01 '23 at 09:33
337

Note: This answer is outdated. See the answer for newer versions of Flutter.

You can dismiss the keyboard by taking away the focus of the TextFormField and giving it to an unused FocusNode:

FocusScope.of(context).requestFocus(FocusNode());
cubuspl42
  • 7,833
  • 4
  • 41
  • 65
Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
  • Where would you implement this code? In the TextFormField; maybe after `onChanged:` or in the action of your custom button? – Charles Jr Dec 31 '17 at 00:23
  • @CharlesJr I do it in the action of my button. – Duncan Jones Jun 23 '18 at 07:05
  • you can use my package if you want :) https://pub.dartlang.org/packages/keyboard_actions – diegoveloper Dec 09 '18 at 06:55
  • 31
    This is before Flutter v1.7.8 - see answer https://stackoverflow.com/a/56946311/8696915 which gives `FocusScope.of(context).unfocus()` – Richard Johnson Aug 09 '19 at 00:31
  • 4
    Don't do this if you have access to .unfocus() since it'll cause a memory leak. That new FocusNode() will never get cleaned up. See comment about .unfocus() – Michael Peterson Feb 20 '20 at 02:00
  • What I dont like about that is the creation of FocusNodes. and since you are not disposing it so its staying in memory which can result in memory leaks. The best alternative is FocusScope.of(context).unfocus(); – Maz Jul 10 '20 at 10:52
  • It triggers 2 renders – lolelo Nov 29 '21 at 14:16
151

Solution with FocusScope doesn't work for me. I found another:

import 'package:flutter/services.dart';

SystemChannels.textInput.invokeMethod('TextInput.hide');

It solved my problem.

Andrii Turkovskyi
  • 27,554
  • 16
  • 95
  • 105
102

For Flutter 1.17.3 (stable channel as of June 2020), use

FocusManager.instance.primaryFocus.unfocus();
suztomo
  • 5,114
  • 2
  • 20
  • 21
39

Following code helped me to hide keyboard

   void initState() {
   SystemChannels.textInput.invokeMethod('TextInput.hide');
   super.initState();
   }
poonam
  • 391
  • 3
  • 6
  • 1
    For me, this worked better than the solution to add FocusScope.of(context).unfocus() as that solution put me into a loop of the keyboard being shown and hidden – Varun Aug 19 '20 at 01:35
  • This works the best for me. I had a keyboard open from another screen and I think focus was in a weird state so only this method worked. Thank YOU!!!! – Eradicatore Feb 28 '21 at 14:45
  • This is much more elegant that the previous solutions. And most importantly, this can be wrapped in override `dispose` method to guarantee whenever the widget is disposed (e.g. very useful in dialog) , the on screen keyboard input will be closed appropriately. – chungonion May 03 '21 at 06:39
30

To dismiss the keyboard (1.7.8+hotfix.2 and above) just call the method below:

FocusScope.of(context).unfocus();

Once the FocusScope.of(context).unfocus() method already check if there is focus before dismiss the keyboard it's not needed to check it. But in case you need it just call another context method: FocusScope.of(context).hasPrimaryFocus

Cassio Seffrin
  • 7,293
  • 1
  • 54
  • 54
27

Looks like different approaches for different version. I am using Flutter v1.17.1 and the below works for me.

onTap: () {
    FocusScopeNode currentFocus = FocusScope.of(context);
    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
       currentFocus.focusedChild.unfocus();
    }
}
dbyuvaraj
  • 487
  • 1
  • 6
  • 20
23
GestureDetector(
          onTap: () {
            FocusScope.of(context).unfocus();
          },
          child:Container(
    alignment: FractionalOffset.center,
    padding: new EdgeInsets.all(20.0),
    child: new TextFormField(
      controller: _controller,
      decoration: new InputDecoration(labelText: 'Example Text'),
    ),
  ), })

try this on tap gesture

21

None of the above solutions don't work for me.

Flutter suggests this - Put your widget inside new GestureDetector() on which tap will hide keyboard and onTap use FocusScope.of(context).requestFocus(new FocusNode())

class Home extends StatelessWidget {
@override
  Widget build(BuildContext context) {
    var widget = new MaterialApp(
        home: new Scaffold(
            body: new Container(
                height:500.0,
                child: new GestureDetector(
                    onTap: () {
                        FocusScope.of(context).requestFocus(new FocusNode());
                    },
                    child: new Container(
                        color: Colors.white,
                        child:  new Column(
                            mainAxisAlignment:  MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.center,

                            children: [
                                new TextField( ),
                                new Text("Test"),                                
                            ],
                        )
                    )
                )
            )
        ),
    );

    return widget;
}}
aamitarya
  • 719
  • 6
  • 11
21

As in Flutter everything is a widget, I decided to wrap the FocusScope.of(context).unfocus(); approach in a short utility widget.

Just create the KeyboardHider widget:

import 'package:flutter/widgets.dart';

/// A widget that upon tap attempts to hide the keyboard.
class KeyboardHider extends StatelessWidget {
  /// Creates a widget that on tap, hides the keyboard.
  const KeyboardHider({
    required this.child,
    Key? key,
  }) : super(key: key);

  /// The widget below this widget in the tree.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () => FocusScope.of(context).unfocus(),
      child: child,
    );
  }
}

Now, you can wrap any widget (very convenient when using a good IDE) with the KeyboardHider widget, and then when you tap on something, the keyboard will close automatically. It works well with forms and other tappable areas.

class SimpleWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return KeyboardHider(
      /* Here comes a widget tree that eventually opens the keyboard,
       * but the widget that opened the keyboard doesn't necessarily
       * takes care of hiding it, so we wrap everything in a
       * KeyboardHider widget */
      child: Container(),
    );
  }
}
Vince Varga
  • 6,101
  • 6
  • 43
  • 60
21

For me, the Listener above App widget is the best approach I've found:

Listener(
  onPointerUp: (_) {
    FocusScopeNode currentFocus = FocusScope.of(context);
    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
      currentFocus.focusedChild.unfocus();
    }
  },
  child: MaterialApp(
    title: 'Flutter Test App',
    theme: theme,
    ...
  ),
)
Ilya Iksent
  • 1,425
  • 17
  • 15
  • This worked for me in iOS, I don't know it is right way to do it. – Niraj Jan 11 '21 at 13:23
  • 1
    It worked in iOS, But we loose the paste functionality ! As soon as we tap & hold in textbox, paste option appears but hides when we try to tap on it. – Tushar Pol Aug 20 '22 at 17:55
  • Indeed this makes the paste functionality no longer work. Use the approach with `GestureDetector` to fix this – edwardmp Jan 16 '23 at 22:18
19

This may simplify the case. Below code will work only if keyboard is open

if(FocusScope.of(context).isFirstFocus) {
 FocusScope.of(context).requestFocus(new FocusNode());
}
Vamsi Krishna
  • 353
  • 3
  • 12
11

You can use unfocus() method from FocusNode class.

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  MyHomePageState createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller = new TextEditingController();
  FocusNode _focusNode = new FocusNode(); //1 - declare and initialize variable

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.send),
        onPressed: () {
            _focusNode.unfocus(); //3 - call this method here
        },
      ),
      body: new Container(
        alignment: FractionalOffset.center,
        padding: new EdgeInsets.all(20.0),
        child: new TextFormField(
          controller: _controller,
          focusNode: _focusNode, //2 - assign it to your TextFormField
          decoration: new InputDecoration(labelText: 'Example Text'),
        ),
      ),
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

void main() {
  runApp(new MyApp());
}
Evandro Ap. S.
  • 111
  • 1
  • 5
  • 1
    Hi! It would be best if you could [edit] your answer to add just a little more context to your answer. – grooveplex Jan 31 '19 at 20:03
  • Yes, it removes the focus if tapping on an specific widget. Can't add it to every widget on my app. – Dpedrinha Sep 13 '19 at 01:58
  • Tapping on a widget is just one way to call `unfocus` method. If you don't want to spam this on your app you can use a different way, like the `TextFormField` change event or a reactive pattern, it depends on your app's architecture. – Evandro Ap. S. Sep 13 '19 at 18:28
8

To summarize, this is a working solution for Flutter 1.17:

Wrap your Widget like this:

GestureDetector(
        onTap: FocusScope.of(context).unfocus,
        child: YourWidget(),
);
Valentin Seehausen
  • 665
  • 1
  • 8
  • 10
7

if you use CustomScrollView, just put,

keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
9Dragons
  • 137
  • 1
  • 4
6

You can wrap your widget with "GestureDetector", then assign "FocusScope.of(context).unfocus()" to its onTap function

GestureDetector(
 onTap: () => FocusScope.of(context).unfocus(),
 child: child,
);
Aykut Acikgoz
  • 85
  • 1
  • 2
5

Call this function when you needed

  void hideKeyboard(BuildContext context) {
      FocusScopeNode currentFocus = FocusScope.of(context);
      if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
        FocusManager.instance.primaryFocus?.unfocus();
      }
    }
Masum Billah Sanjid
  • 1,029
  • 1
  • 7
  • 19
5

To dismiss keyboard when TextField or some widget loses focus you can do this:

TextField(
      onTapOutside: (event) {
        FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
          FocusManager.instance.primaryFocus?.unfocus();
        }
      },
);
Bl4ck
  • 63
  • 1
  • 6
3
_dismissKeyboard(BuildContext context) {
   FocusScope.of(context).requestFocus(new FocusNode());
}

@override
Widget build(BuildContext context) {

return new GestureDetector(
    onTap: () {
    this._dismissKeyboard(context);
    },
    child: new Container(
    color: Colors.white,
    child: new Column(
        children: <Widget>[/*...*/],
    ),
    ),
 );
}
Amit Prajapati
  • 13,525
  • 8
  • 62
  • 84
2

You can also declare a focusNode for you textfield and when you are done you can just call the unfocus method on that focusNode and also dispose it

class MyHomePage extends StatefulWidget {
  MyHomePageState createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller = new TextEditingController();

/// declare focus
  final FocusNode _titleFocus = FocusNode();

  @override
  void dispose() {
    _titleFocus.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.send),
        onPressed: () {
          setState(() {
            // send message
            // dismiss on screen keyboard here

            _titleFocus.unfocus();
            _controller.clear();
          });
        },
      ),
      body: new Container(
        alignment: FractionalOffset.center,
        padding: new EdgeInsets.all(20.0),
        child: new TextFormField(
          controller: _controller,
          focusNode: _titleFocus,
          decoration: new InputDecoration(labelText: 'Example Text'),
        ),
      ),
    );
  }
}
2

If your keyboard still won't turn off , don't forget add focusNode to TextField. The above information was helpful, but forgetting to add focusNode bothered me a bit. Here an example.

TextField(
          focusNode: FocusNode(),
          textController: _controller,
          autoFocus: false,
          textStyle: TextStyle(fontSize: 14),
          onFieldSubmitted: (text) {},
          onChanged: (text) {},
          hint: 'Enter the code',
          hintColor: CustomColors.mediumGray,
          suffixAsset: _voucherController.text.length == 7
              ? Assets.ic_approved_voucher
              : null,
          isIcon: false,
          isObscure: false,
          maxLength: 7,
        )



closeKeyboard(BuildContext context) {
    var currentFocus = FocusScope.of(context);
    if (!currentFocus.hasPrimaryFocus) {
      currentFocus.unfocus();
    }
  }

  @override
  Widget build(BuildContext context) {
    _keyboardVisible = MediaQuery.of(context).viewInsets.bottom != 0;
    size = MediaQuery.of(context).size;
    return GestureDetector(
      onTap: () {
        closeKeyboard(context);
      },
      child: Scaffold(
        backgroundColor: Colors.white,
        body: Container(
            width: double.maxFinite,
            height: double.maxFinite,
            child: _buildUI(vm)),
      ),
    );
  }
Kishan Viramgama
  • 893
  • 1
  • 11
  • 23
Yasin Ege
  • 605
  • 4
  • 14
2

====== Dismiss the keyboard after clicking out of the TextField =======

 @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => FocusScope.of(context).unfocus(),  //this will dismiss keyboard
      child: Scaffold(
        body: SafeArea(
               .........

====== Dismiss the keyboard when scrolling the screen =======

ListView(
            keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, //this will dismiss
            children: [
               ..........

The SingleChildScrollView widget also have this property.

Supun Sandaruwan
  • 1,814
  • 19
  • 19
2

You can use this:

FocusScope.of(context).requestFocus(FocusNode());

And you can use this onTap of GestureDetector or InkWell like this:

GestureDetector(
  onTap: () {
    // THIS FOCUS SCOPE WILL CLOSE THE KEYBOARD
    FocusScope.of(context).requestFocus(FocusNode());
    forgotPasswordAPI(emailController.text);
  },
}
 
gotnull
  • 26,454
  • 22
  • 137
  • 203
2

add this code inside build widget

    FocusScope.of(context).requestFocus(FocusNode());
Ayush Sth
  • 317
  • 6
  • 11
  • this solution is not recommended as it will cause a mess if you want to give textfield focus again. below solution is prefect with if statement. `closeKeyboard(BuildContext context) { var currentFocus = FocusScope.of(context); if (!currentFocus.hasPrimaryFocus) { currentFocus.unfocus(); } }` – Shoaib Kakal Mar 03 '23 at 13:38
1

FocusScope.of(context).unfocus() has a downside when using with filtered listView. Apart from so many details and concisely, use keyboard_dismisser package in https://pub.dev/packages/keyboard_dismisser will solve all the problems.

1

I have created this function to my base code, so far works well!!

void hideKeyword(BuildContext context) {
  FocusScopeNode currentFocus = FocusScope.of(context);
  if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
    currentFocus.focusedChild.unfocus();
  }
}
wilfredonoyola
  • 485
  • 5
  • 12
1

FocusScope.of(context).unfocus(); doesn't work.

This code works for me at flutter ver 2.2.3 and null safety.

WidgetsBinding.instance?.focusManager.primaryFocus?.unfocus()

Source: https://github.com/flutter/flutter/issues/20227#issuecomment-512860882

For example, put this code in MyAppState to apply hide keyboard when touch outside for whole app.

return GestureDetector(
  onTap: () =>
      WidgetsBinding.instance?.focusManager.primaryFocus?.unfocus(),
  child: MaterialApp(
    title: 'Flutter Demo',
    theme: getTheme(),
    home: _body(),
  ),
);
Quyen Anh Nguyen
  • 1,204
  • 13
  • 21
1

Use SystemChannels.textInput.invokeMethod('TextInput.hide');. It will close/dismiss the keyboard when the screen loads.

void initState() {
  super.initState();
  SystemChannels.textInput.invokeMethod('TextInput.hide');
}
Mateus Felipe
  • 1,071
  • 2
  • 19
  • 43
Hari
  • 117
  • 4
1

In flutter 3.7.2 worked, wrap you Scaffold inside GestureDetector to dismiss keyboard when tap somewhere outside.

GestureDetector(
   onTap: () {
      SystemChannels.textInput.invokeMethod<void>('TextInput.hide');
   },
   child: Scaffold(),
),
omega_mi
  • 243
  • 2
  • 11
0

try using a TextEditingController. at the begining,

    final myController = TextEditingController();
     @override
  void dispose() {
    // Clean up the controller when the widget is disposed.
    myController.dispose();
    super.dispose();
  }

and in the on press event,

onPressed: () {
            myController.clear();}

this will dismiss the keybord.

Chetan Goyal
  • 435
  • 5
  • 14
-1

If you use TextField(maxLines: null) and just want to show Done button ON the screen keyboard to hide it, the code below works.

TextField(
    keyboardType: TextInputType.text,
    maxLines: null,
)

Side note: why in the first place doesn't the keyboard show Done button? The reason is found in the implementation of TextField:

keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),
ynn
  • 3,386
  • 2
  • 19
  • 42