How to hide soft input keyboard on flutter after clicking outside TextField/anywhere on screen?
Asked Answered
O

31

212

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?

Oleneolenka answered 2/8, 2018 at 11:49 Comment(11)
You can wrap your whole screen in a docs.flutter.io/flutter/widgets/GestureDetector-class.html and call above code in onTap: () => FocusScope.of(context).requestFocus(new FocusNode());Frisk
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.Oleneolenka
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...Frisk
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.Oleneolenka
How does that prevent you from applying my suggestion?Frisk
keyboard is hiding on onTap but still facing same issue, may be keyboard is hiding little late.Oleneolenka
I see. Sorry, don't know how to work around that.Frisk
Try to use a Listener widget for access to direct touch events.Assess
How can I add Listener widget, can you please gimme an example?Oleneolenka
try the package I created :) pub.dartlang.org/packages/keyboard_actionsTuinenga
MaterialApp( builder: (context, child) => GestureDetector( onTap: (){ FocusManager.instance.primaryFocus?.unfocus(); }, child: child, ))Orizaba
K
407

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

Kangaroo answered 4/3, 2019 at 6:13 Comment(9)
Actually only with a behavior: HitTestBehavior.translucent, parameter onTap is always called. It didn't work on some taps for me without this parameter.Bon
This answer is outdated. Please check the updated answer for v1.17.1 (at least :)) here https://mcmap.net/q/74005/-how-can-i-dismiss-the-on-screen-keyboardColenecoleopteran
Sometime gesture detector does not work on the whole screen then just wraps the scaffold inside GestureDetector Widget.Orizaba
Gesture detector should be on Scaffold body directly, otherwise it won't workBikini
onTapDown is better than onTap. onTapDown: (_) => FocusManager.instance.primaryFocus?.unfocus()Carey
@Carey "better" in what terms?Ebracteate
@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.Carey
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.Sfax
This solution does not work for on Flutter for Web. The Textfield loses focus right on a tapCalandra
G
62

The Most Simplest Solution 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.

Grieg answered 1/3, 2023 at 11:3 Comment(7)
Just solved using this approach.Pathological
There are many ways to achieve this behavior but this answer is the most solid one.Ovaritis
This is the cleanest and most straightforward approach, yet virtually invisible among GestureDetector answers. An editor should put this answer all the way up.Vamp
Such a simple solution, much better than the alternatives, and it actually works! Thank you!Tyner
Very clean and better approach. Should be the right answer! I think wrapping something into a gesture detector without the real need is not the right way of doing this.Cachexia
I had edited your title to correct your "The Most Simplest Solutions" wrong English, but if you enjoy it like that, it's ok :)Eatables
this parameter exists even in TextFormField :)Paphlagonia
S
55

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.

Supine answered 23/3, 2019 at 14:40 Comment(2)
detach is no longer available, I believe. I tried calling it, but it does not work - it says there's no function with that name.Blaspheme
@RodrigoVieira thank you for pointing that out. I removed outdated information from my post.Supine
B
41

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
)

Buerger answered 18/8, 2020 at 18:40 Comment(3)
your small side note is big!Shuman
Thanks ! awesome note. Perfect solution for Searchdelegate.Renitarenitent
Works also for SingleChildScrollViewSinge
T
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(
       -
       -
       -
        )
   )
Titrate answered 3/8, 2018 at 9:27 Comment(1)
Just for the records: Creating FocusNodes on demand without disposing them is likely to cause memory leaks...Aidaaidan
J
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'),
        ),
      )
    );
  }
Jube answered 7/3, 2019 at 16:19 Comment(2)
Somehow the hittestbehaviour is needed for it to work in iOS.Jillianjillie
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.Arleen
P
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(...)
      ),
    );
  }

Pittsburgh answered 23/7, 2019 at 5:49 Comment(2)
I use Singlechildscrollview and if the empty spaces below are touched it doesn't work. Is there any solution for it?Trover
@Trover the answer is: put your GestureDetecfor before Scaffold. Enjoy =)Blume
D
14

*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.

Devlen answered 7/9, 2022 at 9:38 Comment(1)
This is the one that worked best for me. It behaves how you would expect when you scroll or tap anywhere.Names
B
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)

Braswell answered 5/1, 2020 at 10:32 Comment(3)
unfocus is called but nothing happen. The current focus not lost.Renell
This version redraws your entire view heirarchy; whatever is inside the gestureDetector. I believe the issue is the currentFocus.unfocus() call. Use the selected answer above.Delphadelphi
maybe try onPanDown instead of onTap or maybe another gestureForeshore
A
7
GestureDetector(
  onTap: () {
        FocusScope.of(context).requestFocus(FocusNode());
  },
  behavior: HitTestBehavior.translucent,
  child: rootWidget
)
Aret answered 7/4, 2019 at 11:57 Comment(0)
A
7
onPressed: () {
    FocusScope.of(context).unfocus();
},

This works for me.

Aubry answered 20/8, 2019 at 17:41 Comment(1)
where is it added? in GestureDetector?Romelda
H
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(),
  ),
);
Hebraist answered 6/10, 2020 at 4:37 Comment(0)
C
6

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(...),
);
Claire answered 23/9, 2020 at 12:55 Comment(0)
B
5
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
Basilisk answered 3/10, 2022 at 5:57 Comment(0)
D
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

Daley answered 24/1, 2021 at 15:16 Comment(2)
yeah, after a year, it works nicely on iOS...Devon
its worked for me on ios , as u said adroid has back button. ( flutter version 3.0.5 )Ferrol
A
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.

Anorthic answered 8/2, 2020 at 9:3 Comment(0)
K
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(
Krause answered 11/10, 2021 at 11:34 Comment(2)
Same. Only this works for me. Thanks.Romelda
This works thanks! But how do we make it work when the user presses the back button?Agnail
C
3

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.

Chamberlin answered 19/12, 2022 at 8:54 Comment(0)
D
3

You can use onTapOutside now.

TextField(
    onTapOutside: (b) {
        FocusManager.instance.primaryFocus?.unfocus();
    }, 
),
Detour answered 9/5, 2023 at 10:7 Comment(0)
S
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()),
                ]),
              ),
            ),
Serrate answered 13/9, 2020 at 17:13 Comment(0)
S
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',
Shorn answered 28/4, 2021 at 19:18 Comment(2)
Write some details.Northward
Write this code in your main.dart inside build function.So that it will work for the whole applicationShorn
B
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(),
    ),
  );
}
Bacciform answered 13/8, 2021 at 7:17 Comment(0)
D
2

In case the hiding keyboard uses this

 onTapOutside: (e) => FocusScope.of(context).unfocus(),

textformField

   TextFormField( 
                  
                  onTapOutside: (e) => FocusScope.of(context).unfocus(),
                  decoration: InputDecoration(
                                  labelText: 'Enter your name'), ),
Dirichlet answered 30/1, 2024 at 3:15 Comment(0)
G
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(...)
      ),
    );
  }
Gruchot answered 13/11, 2019 at 9:16 Comment(0)
R
1

FocusScopeNode currentFocus = FocusScope.of(context);

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

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

Rigsdaler answered 22/4, 2020 at 15:11 Comment(1)
It would be more useful if you could also add here the main points of your linked site.Ivories
K
1

This is best

Scaffold(
body: GestureDetector(
  onTap: () {
   if (messageFocusNode.hasFocus) {
     messageFocusNode.unfocus();
 }
},
child: new Container(
   //rest of your code write here
    )
 )
Kymry answered 4/12, 2020 at 6:49 Comment(0)
M
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',
     );
   }
 }
Mousetail answered 9/11, 2021 at 9:42 Comment(0)
H
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

Hindorff answered 13/12, 2022 at 7:28 Comment(0)
O
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, ))
Orizaba answered 30/1, 2023 at 5:38 Comment(0)
N
0

Add this in Refreshindicaor -> triggerMode: RefreshIndicatorTriggerMode.anywhere

Nonlinearity answered 13/10, 2023 at 11:1 Comment(0)
G
0
`return GestureDetector(
 onTap: () => 
 FocusManager.instance.primaryFocus?.unfocus(),
 child: Scaffold(
 appBar: AppBar(
 title: Text('Login'),
  ),
 body: Body(),
  ),
);`

This code effectively ensures that tapping anywhere outside the focused text field unfocuses it, including when tapping on the app bar. It's worth noting that in Dart 2, 'new' is optional for constructor calls. The usage of FocusManager.instance.primaryFocus returns the currently focused node in the widget tree.

Glynis answered 1/4, 2024 at 9:42 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.