Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dismissing keyboard programatically #7247

Closed
mikemimik opened this issue Dec 13, 2016 · 80 comments · Fixed by #31909

Comments

@mikemimik
Copy link
Contributor

@mikemimik mikemimik commented Dec 13, 2016

It's not clear how to accomplish this.

@eseidelGoogle

This comment has been minimized.

Copy link
Contributor

@eseidelGoogle eseidelGoogle commented Dec 13, 2016

@Hixie or @mpcomplete presumably this is a docs bug?

@Hixie

This comment has been minimized.

Copy link
Contributor

@Hixie Hixie commented Dec 13, 2016

Assuming this means how to do it from code, I guess so.

@mikemimik

This comment has been minimized.

Copy link
Contributor Author

@mikemimik mikemimik commented Dec 13, 2016

When you select a InputFormField the keyboard automatically gets requested and is shown. There is actually no way to make it dismiss from the UI and it's unclear or not possible to do it from current widgets.

@Hixie

This comment has been minimized.

Copy link
Contributor

@Hixie Hixie commented Dec 13, 2016

From the UI it's easy, just press the Android OS-level down arrow button (replaces the back button when the keyboard is up).

@mikemimik

This comment has been minimized.

Copy link
Contributor Author

@mikemimik mikemimik commented Dec 13, 2016

I'm working in iOS and there is no such OS-level trigger. I tested in the simulator and on device, I get the same standard keyboard.

@chinmaygarde

This comment has been minimized.

Copy link
Member

@chinmaygarde chinmaygarde commented Dec 13, 2016

TextInputConnection.close should do what you need.

@mikemimik

This comment has been minimized.

Copy link
Contributor Author

@mikemimik mikemimik commented Dec 15, 2016

@chinmaygarde The .close() method is not static on that class, which means I need to access it on an instance member. However the constructor is private, so I can't create an instance member.

@mikemimik

This comment has been minimized.

Copy link
Contributor Author

@mikemimik mikemimik commented Dec 15, 2016

I figured out how to do it. In discussion with @eseidelGoogle, posed the question if unfocusing might do the trick.

Inside a section of code which has access to the context you just need to have Focus.clear(context); and it will dismiss the keyboard.

@mikemimik

This comment has been minimized.

Copy link
Contributor Author

@mikemimik mikemimik commented Dec 15, 2016

I'm come across an error with the about implementation. As you'll see from the below images, I'm using code to dismiss the keyboard by calling Focus.clear(context);.

I'm not sure how unfocusing dismisses the keyboard, as in, precisely what flutter is doing under the hood.

The effect of being 'focused' is still in effect on the InputFormField widget.

The code to achieve these images is in this repo.

Unselected input:

simulator screen shot dec 15 2016 3 21 07 am

Selected input:

simulator screen shot dec 15 2016 3 21 19 am

Dismissed keyboard:

simulator screen shot dec 15 2016 3 22 12 am

@sethladd

This comment has been minimized.

Copy link
Contributor

@sethladd sethladd commented Dec 17, 2016

@mikemimik did you ever get this to work? What was your recipe?

@Hixie Hixie added this to the No Milestone Necessary milestone Feb 27, 2017
@mikemimik

This comment has been minimized.

Copy link
Contributor Author

@mikemimik mikemimik commented Apr 11, 2017

@sethladd Will cycle back to this on the weekend. Apologies for being so terribly behind on responding.

@pedia

This comment has been minimized.

Copy link

@pedia pedia commented Nov 3, 2017

I found HACK way:

import 'package:flutter/services.dart';

and call

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

@jonasbark

This comment has been minimized.

Copy link

@jonasbark jonasbark commented Nov 30, 2017

This is a better way, as it also clears the focus:
FocusScope.of(context).requestFocus(new FocusNode());
from
https://stackoverflow.com/a/44991969/1068346

@Hixie Hixie removed the dev: docs label Dec 14, 2017
@kentcb

This comment has been minimized.

Copy link
Contributor

@kentcb kentcb commented Mar 20, 2018

Another problem here (even with the hacks) is knowing when the keyboard has been dismissed. The requestFocus method does not return a Future, so there's no easy way to wait for the dismissal.

This is a problem for me because I want the keyboard to dismiss so I can accurately determine where to show a menu. I use RenderBox to figure this out, but the keyboard remaining on screen screws up measurements. If I stick an await new Future.delayed(...) after dismissing the keyboard and before the measurement code, it works as long as the delay is long enough (who knows what long enough really is though...)

@Sun3

This comment has been minimized.

Copy link

@Sun3 Sun3 commented Jun 13, 2018

What we really need especially on the iOS side is a way to add a Done button to the keyboard.

@eseidelGoogle

This comment has been minimized.

Copy link
Contributor

@eseidelGoogle eseidelGoogle commented Jun 13, 2018

@Sun3 would you be willing to file a bug for a Done button with an example from an iOS app you're seeking to replicate?

@zoechi

This comment has been minimized.

Copy link
Contributor

@zoechi zoechi commented Jun 14, 2018

@Sun3 @eseidel might be covered by #9573

@sahil311289

This comment has been minimized.

Copy link

@sahil311289 sahil311289 commented Jun 22, 2018

Did anyone try this:

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

I read it from another forum.

@manujbahl

This comment has been minimized.

Copy link

@manujbahl manujbahl commented Jul 19, 2018

It has stopped working for me now.
It used to work on the alpha channel but not anymore on the beta channel

@manujbahl

This comment has been minimized.

Copy link

@manujbahl manujbahl commented Jul 19, 2018

As requested here is the code.

main() {
    runApp(new Home());
}


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;
    }
}

Here is the flutter doctor result .

Manujs-MacBook-Pro:Examples MBahl$ flutter doctor -v
[✓] Flutter (Channel master, v0.5.5-pre.14, on Mac OS X 10.13.5 17F77, locale en-US)
    • Flutter version 0.5.5-pre.14 at /Users/MBahl/flutter/flutter
    • Framework revision b401e76554 (5 weeks ago), 2018-06-13 09:20:19 -0700
    • Engine revision c3976b3c71
    • Dart version 2.0.0-dev.61.0.flutter-c95617b19c

[✓] Android toolchain - develop for Android devices (Android SDK 28.0.1)
    • Android SDK at /Users/MBahl/Library/Android/sdk
    • Android NDK at /Users/MBahl/Library/Android/sdk/ndk-bundle
    • Platform android-28, build-tools 28.0.1
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 9.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 9.4.1, Build version 9F2000
    • ios-deploy 1.9.2
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 26.0.1
    • Dart plugin version 173.4700
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)

[!] VS Code (version 1.25.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected devices (1 available)
    • Manuj's iPhone • 4b185da29e7eb1d7e37d22ce0a205e7c081ca5b8 • ios • iOS 11.3.1

! Doctor found issues in 1 category.

YES "stopped working" means --- doesn't release focus and the keyboard stays on screen?

@jonsamwell

This comment has been minimized.

Copy link

@jonsamwell jonsamwell commented Jul 19, 2018

This is working on the latest dev build (v0.5.7).

@manujbahl

This comment has been minimized.

Copy link

@manujbahl manujbahl commented Jul 19, 2018

just upgraded and still does not work ..

Flutter (Channel master, v0.5.8-pre.79, on Mac OS X 10.13.5 17F77, locale en-US)
@Shantanu-Kotambkar

This comment has been minimized.

Copy link

@Shantanu-Kotambkar Shantanu-Kotambkar commented Jul 24, 2018

  • @manujbahl FocusScope.of(context).requestFocus(new FocusNode()); , worked for me. Thanks for the solution.

  • Here's a tip @manujbahl you are on master channel, that's probably the problem you are facing! You should change your channel to Beta and upgrade your flutter installation. Tell us if you run in any trouble. Happy to help!

@ch-muhammad-adil

This comment has been minimized.

Copy link

@ch-muhammad-adil ch-muhammad-adil commented May 9, 2019

@gspencergoog method is not defined ,

Screenshot 2019-05-10 at 12 47 32 AM

@ch-muhammad-adil

This comment has been minimized.

Copy link

@ch-muhammad-adil ch-muhammad-adil commented May 9, 2019

Here i have uploaded a video of the problem happening with me at latest version of the flutter, It is happening only at full screen forms ,

https://drive.google.com/file/d/1iwXdrRa1rrs1fhe7tgeY7rwyLRLtyVA-/view?usp=sharing

@gspencergoog

This comment has been minimized.

Copy link
Contributor

@gspencergoog gspencergoog commented May 9, 2019

Correct, unfocus is not available on stable yet.

@ch-muhammad-adil

This comment has been minimized.

Copy link

@ch-muhammad-adil ch-muhammad-adil commented May 9, 2019

Here is another video I have created, I have used unfocus as well after switching to master channel, but it is also not working ,

https://drive.google.com/file/d/1GteaG0vFa32wxjwJE13ULE86OEhvkr6l/view?usp=sharing

Flutter channels:
beta
dev

  • master
    stable

[✓] Flutter (Channel master, v1.5.9-pre.196, on Mac OS X 10.14.4 18E226, locale en-PK)

[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
[✓] Android Studio (version 3.4)
[✓] Connected device (1 available)

@ch-muhammad-adil

This comment has been minimized.

Copy link

@ch-muhammad-adil ch-muhammad-adil commented May 9, 2019

Correct, unfocus is not available on stable yet.

it is not working , at master even. :(

@ch-muhammad-adil

This comment has been minimized.

Copy link

@ch-muhammad-adil ch-muhammad-adil commented May 9, 2019

Got resolved, I have been doing a mistake which causing problem to assign focus at multiple TextInputFields , My TextField are actually custom widgets created by me and in case if focusNode is null i was assign it a default FocusNode(), and this have been bringing issues.

focusNode: focusNode != null ? focusNode : FocusNode(),

so it was also making FocusScope.of(context).requestFocus(FocusNode()); to not work.

@gspencergoog

This comment has been minimized.

Copy link
Contributor

@gspencergoog gspencergoog commented May 10, 2019

I'm glad you were able to resolve it. Yes, indeed, constructing a "throw away" FocusNode isn't the way to do things anymore (at least on the master channel, soon on the others).

@C-gotoh

This comment has been minimized.

Copy link

@C-gotoh C-gotoh commented May 13, 2019

@gspencergoog What about

FocusScope.of(context).detach()

I have used this to remove the focus from a widget instead of creating a new FocusNode.
Will it be better to use the new ".unfocus()" method when it becomes available in stable?

@gspencergoog

This comment has been minimized.

Copy link
Contributor

@gspencergoog gspencergoog commented May 13, 2019

@C-gotoh, In the new implementation, detach has moved to a new FocusAttachment object which maintains the attachment of the node to the focus tree. It will indeed unfocus the node if the node is the primary focus, but will also detach the node from the focus tree, meaning that you will have to call attach on it again to get a new FocusAttachment object in order to keep using it.

So, if you're trying to detach the node because it's no longer needed, then call detach. If you're trying to just release focus on the node, but intend to keep it around, call unfocus.

@iStiLLinIceAge

This comment has been minimized.

Copy link

@iStiLLinIceAge iStiLLinIceAge commented May 17, 2019

Try this dude...

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text('Test'),
      ),
      body: new InkWell(
        splashColor: Colors.transparent,
        highlightColor: Colors.transparent,
        onTap: (){
          FocusScope.of(context).requestFocus(new FocusNode());
        },
        child: new Center(
          child: SingleChildScrollView(
            child: Column(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: new Text('Try this'),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: new TextFormField(),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: new TextFormField(),
                ),
              ],
            ),
          )
        ),   
      ),
    );
  }
@chornbe

This comment has been minimized.

Copy link

@chornbe chornbe commented May 30, 2019

Can you add a native bridge in Flutter / Dart? 'Cause this is trivial in native code. Surprised this didn't bubble up long before now.

@Xgamefactory

This comment has been minimized.

Copy link

@Xgamefactory Xgamefactory commented Jun 8, 2019

why issue has closed ?
after with both workarounds textfield not getging keyboard opened

@jamesdixon

This comment has been minimized.

Copy link

@jamesdixon jamesdixon commented Jul 18, 2019

Trying to hide the keyboard when tapping away from a form field. To do this, I tried wrapping my entire app in a GestureDetector and then calling FocusScope.of(context).unfocus().

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScope.of(context).unfocus();
      },
      child: MaterialApp(
        title: 'Scout',
        theme: theme,
        home: LoginScreen(),
        routes: routes,
      ),
    );
  }
}

Unfortunately, if you tap the body more than once, I get the following error:

flutter: The following assertion was thrown while handling a gesture:
flutter: Node has primary focus, but no enclosingScope.
flutter: 'package:flutter/src/widgets/focus_manager.dart':
flutter: Failed assertion: line 549 pos 14: 'scope != null'

Is this poor usage of unfocus()? Alternatives?

Thanks!

@gspencergoog

This comment has been minimized.

Copy link
Contributor

@gspencergoog gspencergoog commented Jul 18, 2019

What this means, I think, is that you're trying to unfocus the root node in the focus tree.

You can check to see if FocusScope.of(context) returns the root node by comparing it against WidgetsBinding.instance.focusManager.rootScope.

Perhaps we should make that a no-op instead of an assertion (for the root node).

@jamesdixon

This comment has been minimized.

Copy link

@jamesdixon jamesdixon commented Jul 18, 2019

@gspencergoog thanks for the reply! Very much appreciate it.

It seems that when tapping outside of the form field, what's returned by FocusScope.of(context) is the root scope, and therefore, unfocus() is never called. This is all assuming I understood you correctly 😄

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScopeNode currentScope = FocusScope.of(context);
        FocusScopeNode rootScope =
            WidgetsBinding.instance.focusManager.rootScope;

        if (currentScope != rootScope) {
          currentScope.unfocus();
        }
        // FocusScope.of(context).requestFocus(FocusNode());
      },
      child: MaterialApp(
        title: 'Scout',
        theme: theme,
        home: LoginScreen(),
        routes: routes,
      ),
    );
  }
}

Is there a method to get the previously focused FocusNode?

@deepakpawade

This comment has been minimized.

Copy link

@deepakpawade deepakpawade commented Jul 20, 2019

@jamesdixon and @gspencergoog thanks .. the code solved my problem...
But hovering over focusManager and rootScope says the following respectively:
Rarely used directly. Instead, consider using [FocusScope.of] to obtain the [FocusScopeNode] for a given [BuildContext].
and
This field is rarely used directly. To find the nearest [FocusScopeNode] for a given [FocusNode], call [FocusNode.nearestScope].
How to do the way they say??

@jamesdixon

This comment has been minimized.

Copy link

@jamesdixon jamesdixon commented Aug 2, 2019

As an update to my previous comment, I've found that the following works as expected using the unfocus() method.

    return GestureDetector(
      onTap: () {
        FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus) {
          currentFocus.unfocus();
        }
      },
      child: ...
    );
@jifalops

This comment has been minimized.

Copy link

@jifalops jifalops commented Aug 4, 2019

I've tried several things here such as

    FocusScopeNode currentFocus = FocusScope.of(context);
    if (currentFocus != WidgetsBinding.instance.focusManager.rootScope &&
        !currentFocus.hasPrimaryFocus) {
      currentFocus.unfocus();
    }

and

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

The only thing that has worked for me is to use FocusScope.of(context).requestFocus(FocusNode()), but it throws a concurrent modification exception if you call that in response to a text field being tapped.

So the final working solution was:

Future.microtask(() => FocusScope.of(context).requestFocus(FocusNode()));
@jamesdixon

This comment has been minimized.

Copy link

@jamesdixon jamesdixon commented Aug 4, 2019

@jifalops have you tried without currentFocus != WidgetsBinding.instance.focusManager.rootScope?

I just wrote an article on this topic that contains the full code. I'm curious if there's something different about your approach as this works for me.

@jifalops

This comment has been minimized.

Copy link

@jifalops jifalops commented Aug 4, 2019

Yes I tried it without checking the rootScope, but from what I gather, calling unfocus() on the root scope causes an exception. My use case involves hiding the keyboard when the text field gains focus, which is a bit more stringent than most use cases.

@jifalops

This comment has been minimized.

Copy link

@jifalops jifalops commented Aug 4, 2019

@jamesdixon That is a clever way to unfocus any input in the app when tapping outside of them.

@jamesdixon

This comment has been minimized.

Copy link

@jamesdixon jamesdixon commented Aug 4, 2019

@jifalops thanks! out of curiosity, what's your use case for hiding the keyboard when the form field gains focus?

@jifalops

This comment has been minimized.

Copy link

@jifalops jifalops commented Aug 4, 2019

To show the date/time picker dialogs instead of initially going into edit mode. https://pub.dev/packages/datetime_picker_formfield

@jamesdixon

This comment has been minimized.

Copy link

@jamesdixon jamesdixon commented Aug 4, 2019

@JamesMcIntosh

This comment has been minimized.

Copy link

@JamesMcIntosh JamesMcIntosh commented Aug 28, 2019

@jifalops @jamesdixon If unfocus() is not working then pay VERY close attention to which BuildContext you have passed in to the FocusScope.of(context).
In particular I made the mistake of using the BuildContext passed into a build method rather than the context supplied from a Builder function, my particular case the function was building up a MaterialPageRoute so the context was well out of expected scope by the time the builder was called i.e.

MaterialPageRoute<void> buildRoute(BuildContext context, Widget child) {
  return MaterialPageRoute<void>(
    builder: (BuildContext builderContext) => GestureDetector(
      onTap: () {
        FocusScopeNode currentFocus = FocusScope.of(context); // SHOULD BE builderContext
        if (!currentFocus.hasPrimaryFocus) {
          currentFocus.unfocus();
        }
      },
      child: child,
    ),
  );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Widget Documentation
Specific documentation requests
You can’t perform that action at this time.