195

Currently, I know the method of hiding the soft keyboard using this code, by onTap methods of any widget.

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

But I want to hide the soft keyboard by clicking outside of TextField or anywhere on the screen. Is there any method in flutter to do this?

Ammy Kang
  • 11,283
  • 21
  • 46
  • 68
  • 3
    You can wrap your whole screen in a https://docs.flutter.io/flutter/widgets/GestureDetector-class.html and call above code in `onTap: () => FocusScope.of(context).requestFocus(new FocusNode());` – Günter Zöchbauer Aug 02 '18 at 11:56
  • 1
    Thanks @GünterZöchbauer. is there any touch event method, as tapping is not gonna resolve my issue. Keyboard is hiding on onTap method. I need to hide the keyboard frequently as I touch the screen. – Ammy Kang Aug 02 '18 at 12:33
  • Sorry, I don't follow. why would tapping not solve your issue? You are tapping on the background or similar. When that happens, you call ...requestFocus... – Günter Zöchbauer Aug 02 '18 at 12:36
  • I am working with TabBar and have search view box on each Tab screens. when I swipe from one tab to another then it doesn't swipe to another tab and comes back to same tab if keyboard is up on the screen or if there is text in TextField of SearchView. am having tab swiping issue mainly when keyboard is up otherwise tab swiping working fine. – Ammy Kang Aug 02 '18 at 12:44
  • How does that prevent you from applying my suggestion? – Günter Zöchbauer Aug 02 '18 at 12:46
  • keyboard is hiding on onTap but still facing same issue, may be keyboard is hiding little late. – Ammy Kang Aug 02 '18 at 12:48
  • I see. Sorry, don't know how to work around that. – Günter Zöchbauer Aug 02 '18 at 12:49
  • Try to use a `Listener` widget for access to direct touch events. – boformer Aug 02 '18 at 13:02
  • How can I add `Listener` widget, can you please gimme an example? – Ammy Kang Aug 02 '18 at 13:16
  • try the package I created :) https://pub.dartlang.org/packages/keyboard_actions – diegoveloper Dec 09 '18 at 06:55
  • 1
    MaterialApp( builder: (context, child) => GestureDetector( onTap: (){ FocusManager.instance.primaryFocus?.unfocus(); }, child: child, )) – Avnish Nishad Jan 30 '23 at 05:45

28 Answers28

390

You are doing it in the wrong way, just try this simple method to hide the soft keyboard. you just need to wrap your whole screen in the GestureDetector method and onTap method write this code.

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

Here is the complete example:

new Scaffold(
   body: new GestureDetector(
      onTap: () {
         FocusScope.of(context).requestFocus(new FocusNode());
      },
      child: new Container(
         //rest of your code write here
      ),
   ),
)

Updated (May 2021)

return GestureDetector(
   onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
   child: Scaffold(
      appBar: AppBar(
         title: Text('Login'),
      ),
      body: Body(),
   ),
);

This will work even when you touch the AppBar, new is optional in Dart 2. FocusManager.instance.primaryFocus will return the node that currently has the primary focus in the widget tree.

Conditional access in Dart with null-safety

kalucki23
  • 172
  • 4
  • 15
Daizy Arora
  • 3,932
  • 1
  • 7
  • 3
  • 20
    Actually only with a `behavior: HitTestBehavior.translucent,` parameter `onTap` is always called. It didn't work on some taps for me without this parameter. – AndrewS Jul 16 '19 at 21:22
  • 6
    This answer is outdated. Please check the updated answer for v1.17.1 (at least :)) here https://stackoverflow.com/a/61986983/705842 – dbyuvaraj May 24 '20 at 14:02
  • 1
    Sometime gesture detector does not work on the whole screen then just wraps the scaffold inside GestureDetector Widget. – Avnish Nishad Jul 23 '20 at 14:48
  • 1
    Gesture detector should be on Scaffold body directly, otherwise it won't work – Alexa289 May 22 '21 at 23:07
  • 2
    onTapDown is better than onTap. `onTapDown: (_) => FocusManager.instance.primaryFocus?.unfocus()` – Hardik Feb 21 '22 at 16:31
  • @Hardik "better" in what terms? – Konstantin Kozirev Aug 06 '22 at 18:11
  • @KonstantinKozirev in onTapDown, it triggers the function as soon as we touch the screen unlike onTap where it triggers the function on lifting the finger making a tap gesture. – Hardik Aug 07 '22 at 11:26
  • Please update the accepted answer to the latest and better solutions: * `TextField` now has an `onTapOutside` callback parameter * If you don't have control of the text field and you cannot pass any parameter you can use `TextFieldTapRegion` widget as a wrapper. – Gyuri Majercsik Jun 26 '23 at 07:11
54

Updated

Starting May 2019, FocusNode now has unfocus method:

Cancels any outstanding requests for focus.

This method is safe to call regardless of whether this node has ever requested focus.

Use unfocus if you have declared a FocusNode for your text fields:

final focusNode = FocusNode();

// ...

focusNode.unfocus();

My original answer suggested detach method - use it only if you need to get rid of your FocusNode completely. If you plan to keep it around - use unfocus instead.

If you have not declared a FocusNode specifically - use unfocus for the FocusScope of your current context:

FocusScope.of(context).unfocus();

See revision history for the original answer.

George Zvonov
  • 9,401
  • 5
  • 33
  • 37
31

Wrap whole screen in GestureDetector as

new Scaffold(

  body: new GestureDetector(
      onTap: () {
        // call this method here to hide soft keyboard
        FocusScope.of(context).requestFocus(new FocusNode());
      },
    child: new Container(
       -
       -
       -
        )
   )
  • Just for the records: Creating `FocusNode`s on demand without disposing them is likely to cause memory leaks... – SePröbläm Jun 28 '22 at 19:22
31

Just as a small side note:

If you use ListView its keyboardDismissBehavior property could be of interest:

ListView(
  keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
  children: [],
)

Additionally, you can also use TapRegion

TapRegion(
  onTapOutside: (event) => FocusScope.of(context).unfocus(),
  child: // your sub-tree that triggered the keyboard
)

omnesia
  • 1,992
  • 1
  • 15
  • 14
27

I've added this line

behavior: HitTestBehavior.opaque,

to the GestureDetector and it seems to be working now as expected.

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context).calculatorAdvancedStageTitle),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          FocusScope.of(context).requestFocus(new FocusNode());
        },
        child: Padding(
          padding: const EdgeInsets.only(
            left: 14,
            top: 8,
            right: 14,
            bottom: 8,
          ),
          child: Text('Work'),
        ),
      )
    );
  }
atereshkov
  • 4,311
  • 1
  • 38
  • 49
  • 1
    Somehow the hittestbehaviour is needed for it to work in iOS. – Daryl Wong Jul 28 '19 at 13:14
  • 1
    This messes with VoiceOver behavior. You may want to add `excludeFromSemantics: true` to make sure assistive technologies don't see the entire screen as a single tappable block. – Isaac Lyman Oct 26 '22 at 16:48
21

The Most Simplest Solutions Yet

I've found the easiest way to do it, now you can use onTapOutside in the TextField widget

 TextField(
       onTapOutside: (event) {
                  print('onTapOutside');
                    FocusManager.instance.primaryFocus?.unfocus();
                },)

This is called for each tap that occurs outside of the[TextFieldTapRegion] group when the text field is focused.

Sambhav jain
  • 1,467
  • 12
  • 19
19

As of Flutters latest version v1.7.8+hotfix.2, you can hide keyboard using unfocus() instead of requestfocus()

FocusScope.of(context).unfocus()

so whenever you tap in the body part keyboard gets hidden

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text("Login"),
      ),
      body: GestureDetector(
        onTap: () {
          FocusScope.of(context).unfocus();
        },
        child: Container(...)
      ),
    );
  }

Mahesh Jamdade
  • 17,235
  • 8
  • 110
  • 131
13

*Update sept 2022 :: on flutter 3.0.2

if you have complex screen, i recommend to use Listener instead. here i face issue before :

There is a lag/delay when catch the event on `GestureDetector` with `HitTestBehavior.opaque`?

documentation said:

Rather than listening for raw pointer events, consider listening for higher-level gestures using GestureDetector

GestureDetector listening to high-level gesture. I think its caused some delay or lagging.

my workaround:

Listener(
  behavior: HitTestBehavior.opaque,
  onPointerDown: (_) {
     FocusManager.instance.primaryFocus?.unfocus();
  },
  child: Scaffold()

this will be catch event when you tap anywhere.

pmatatias
  • 3,491
  • 3
  • 10
  • 30
10

If you want the behavior to be accessible on any screen in your app, wrap MaterialApp with GestureDetector:

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScopeNode currentFocus = FocusScope.of(context);

        if (!currentFocus.hasPrimaryFocus) {
          currentFocus.unfocus();
        }
      },
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

Checking hasPrimaryFocus is necessary to prevent Flutter from throwing an exception when trying to unfocus the node at the top of the tree.

(Originally given by James Dixon of the Flutter Igniter blog)

Charden Daxicen
  • 405
  • 5
  • 10
7
GestureDetector(
  onTap: () {
        FocusScope.of(context).requestFocus(FocusNode());
  },
  behavior: HitTestBehavior.translucent,
  child: rootWidget
)
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
7
onPressed: () {
    FocusScope.of(context).unfocus();
},

This works for me.

Nixon Kosgei
  • 788
  • 8
  • 13
7

This will work in the latest flutter version.

GestureDetector(
  onTap: () {
    FocusScopeNode currentFocus = FocusScope.of(context);

    if (!currentFocus.hasPrimaryFocus &&
        currentFocus.focusedChild != null) {
      FocusManager.instance.primaryFocus.unfocus();
    }
  },
  child: MaterialApp(
    theme: ThemeData.dark().copyWith(
      primaryColor: Color(0xFF0A0E21),
      scaffoldBackgroundColor: Color(0xFF0A0E21),
    ),
    home: LoginUI(),
  ),
);
shubham
  • 611
  • 7
  • 16
5

If you want to do this "the right way", use Listener instead of GestureDetector.

GestureDetector will only work for "single taps", which isn't representative of all possible gestures that can be executed.

Listener(
  onPointerDown: (_) {
    FocusScopeNode currentFocus = FocusScope.of(context);
    if (!currentFocus.hasPrimaryFocus) {
      currentFocus.focusedChild.unfocus();
    }
  },
  child: MaterialApp(...),
);
Rudresh Narwal
  • 2,740
  • 3
  • 11
  • 21
4

Best for me.

I wrap since Material App because global outside touch

FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
  FocusManager.instance.primaryFocus.unfocus();
}

Resolve below,

I check Platform iOS only because Android can dismiss the keyboard the back button.

Listener(
  onPointerUp: (_) {
    if (Platform.isIOS) {
      FocusScopeNode currentFocus = FocusScope.of(context);
      if (!currentFocus.hasPrimaryFocus &&
          currentFocus.focusedChild != null) {
        FocusManager.instance.primaryFocus.unfocus();
      }
    }
  },
  child: MaterialApp(
    debugShowCheckedModeBanner: true,
    home: MyHomePage(),
    ...
  ),
),

So happy your coding.

Flutter version

enter image description here

bamossza
  • 3,676
  • 1
  • 29
  • 28
4
child: Form(
    child: Column(
        children: [
            TextFormField(
                decoration: InputDecoration(
                        labelText: 'User Name', hintText: 'User Name'),
                onTapOutside: (PointerDownEvent event) {
                    FocusScope.of(context).requestFocus(_unUsedFocusNode);
                },
            ),
        ],
    ),
),

define focus Node

FocusNode _unUsedFocusNode = FocusNode();

override the onTapOutside method in TextFromField

onTapOutside: (PointerDownEvent event) {
  FocusScope.of(context).requestFocus(_unUsedFocusNode);
},

Edit:

Note: it will work in sdk Flutter 3.6.0-0.1.pre Dart SDK 2.19.0-374.1.beta
Ritunjay kumar
  • 261
  • 3
  • 6
3

I just developed a small package to give any widget the kind of behavior you are looking for: keyboard_dismisser on pub.dev. You can wrap the whole page with it, so that the keyboard will get dismissed when tapping on any inactive widget.

drogel
  • 2,567
  • 2
  • 13
  • 22
3

With Flutter 2.5 GestureDetector.OnTap hasn't worked for me.

Only this worked:

return GestureDetector(
      //keyboard pop-down
      onTapDown: (_) => FocusManager.instance.primaryFocus?.unfocus(),
      behavior: HitTestBehavior.translucent,
      child: Scaffold(
3

You can use onTapOutside now.

TextField(
    onTapOutside: (b) {
        FocusManager.instance.primaryFocus?.unfocus();
    }, 
),
2

try this if you are on a stack

body: GestureDetector(
              onTap: () {
                FocusScope.of(context).requestFocus(new FocusNode());
              },
              child: Container(
                height: double.infinity,
                width: double.infinity,
                color: Colors.transparent,
                child: Stack(children: [
                  _CustomBody(_),
                  Positioned(
                      bottom: 15, right: 20, left: 20, child: _BotonNewList()),
                ]),
              ),
            ),
2

This will work

 Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return GestureDetector(
  onTap: () {
    FocusScopeNode focus = FocusScope.of(context);
    if (!focus.hasPrimaryFocus && focus.focusedChild != null) {
      focus.focusedChild.unfocus();
    }
  },
  child: MaterialApp(
    title: 'Flutter Demo',
2

so easy solution for beginner here is the smoothest solution for you while you need to hide-keyboard when user tap on any area of screen. hope its help you a lot.

Step - 1 : you need to create this method in Global class,

this method wrap you main widget into GestureDetector so when user tap outside the textfield it will hide keyboard automatically

Widget hideKeyboardWhileTapOnScreen(BuildContext context, {MaterialApp child}){

  return GestureDetector(
    onTap: () {
      if (Platform.isIOS) { //For iOS platform specific condition you can use as per requirement
        SystemChannels.textInput.invokeMethod('TextInput.hide');
        print("Keyboard Hide");
      }      
    },
    child: child,
  );
}

this method wrap you main widget into Listener so when user touch and scroll up it will hide keyboard automatically

Widget hideKeyboardWhileTapOnScreen(BuildContext context, {MaterialApp child}){
  return Listener(
    onPointerUp: (_) {
      if (Platform.isIOS) {
        FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus &&
            currentFocus.focusedChild != null) {
          FocusManager.instance.primaryFocus.unfocus();
          print("Call keyboard listner  call");
        }
      }
    },
    child: child,
  );
}

Step - 2 : here is how to use Global method

@override
Widget build(BuildContext context) {
 
  return hideKeyboardWhileTapOnScreen(context,
    child: MaterialApp(
        debugShowCheckedModeBanner: false, home: Scaffold(body: setAppbar())),
  );
}

Widget setAppbar2() {
  return MaterialApp(
    debugShowCheckedModeBanner: false,
    theme: ThemeData(primarySwatch: Colors.orange),
    home: Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(),
    ),
  );
}
2

The simplest way - just write some code in your MaterialApp's builder method:

void main() {
  runApp(const MyApp());
}

Widget build(BuildContext context) {
  return MaterialApp(
    home: const MyHomePage(),
    builder: (context, child) {
      // this is the key
      return GestureDetector(
        onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
        child: child,
      );
    },
 );
}

Then in all the pages it works.

无夜之星辰
  • 5,426
  • 4
  • 25
  • 48
1

It is true what maheshmnj said that from version v1.7.8+hotfix.2, you can hide keyboard using unfocus() instead of requestfocus().

FocusScope.of(context).unfocus()

But in my case I was still presented with a lot of layout errors, as the screen I navigated to was not capable of handling the layout.

════════ Exception Caught By rendering library ═════════════════════════════════
The following JsonUnsupportedObjectError was thrown during paint():
Converting object to an encodable object failed: Infinity
When the exception was thrown, this was the stack
#0      _JsonStringifier.writeObject  (dart:convert/json.dart:647:7)
#1      _JsonStringifier.writeMap  (dart:convert/json.dart:728:7)
#2      _JsonStringifier.writeJsonValue  (dart:convert/json.dart:683:21)
#3      _JsonStringifier.writeObject  (dart:convert/json.dart:638:9)
#4      _JsonStringifier.writeList  (dart:convert/json.dart:698:9)

This was handled by inserting "resizeToAvoidBottomInset: false" in the receiving screens Scaffold()

@override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,   // HERE
      appBar: AppBar(
        centerTitle: true,
        title: Text("Receiving Screen "),
      ),
      body: Container(...)
      ),
    );
  }
Bo Kristensen
  • 1,460
  • 11
  • 8
1

FocusScopeNode currentFocus = FocusScope.of(context);

    if (!currentFocus.hasPrimaryFocus) {
      currentFocus.unfocus();
    }

You should check here https://flutterigniter.com/dismiss-keyboard-form-lose-focus/

1

This is best

Scaffold(
body: GestureDetector(
  onTap: () {
   if (messageFocusNode.hasFocus) {
     messageFocusNode.unfocus();
 }
},
child: new Container(
   //rest of your code write here
    )
 )
Pinkesh Darji
  • 1,041
  • 10
  • 23
0

UPDATE NOVEMBER 2021

According to the new flutter webview documentation: Putting this piece of code inside the given full example will solve the keyboard dismiss the issue.

@override
   void initState() {
     super.initState();
         // Enable hybrid composition.
         if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
   }

Full example code:

 import 'dart:io';
 import 'package:webview_flutter/webview_flutter.dart';

 class WebViewExample extends StatefulWidget {
   @override
   WebViewExampleState createState() => WebViewExampleState();
 }

 class WebViewExampleState extends State<WebViewExample> {
   @override
   void initState() {
     super.initState();
         // Enable hybrid composition.
 if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
   }

   @override
   Widget build(BuildContext context) {
     return WebView(
       initialUrl: 'https://flutter.dev',
     );
   }
 }
codelone
  • 604
  • 8
  • 17
0

Wrap your material app with GestureDetector and use below code to hide keyboard from anywhere in app.

GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          FocusScopeNode currentFocus = FocusScope.of(context);

          if (!currentFocus.hasPrimaryFocus &&
              currentFocus.focusedChild != null) {
            FocusManager.instance.primaryFocus?.unfocus();
          }
        },
        child: MaterialApp(
          ...
        ),
      ),

It will hide keyboard from whole app

Shailendra Rajput
  • 2,131
  • 17
  • 26
0

You can dismiss the keyboard thoughout the app. Just by putting this code under builder in MaterialApp and if using Getx then under GetMaterialApp

MaterialApp(
         builder: (context, child) => GestureDetector( onTap: (){
       FocusManager.instance.primaryFocus?.unfocus();
    }, child: child, ))
Avnish Nishad
  • 1,634
  • 1
  • 17
  • 18