85

I know that general answer to unfocusing is to use this piece of code: FocusScope.of(context).requestFocus(new FocusNode());

But when TextField has custom focusNode, this code doesn't seem to work.

SystemChannels.textInput.invokeMethod('TextInput.hide'); still works, but it only removes the keyboard - field itself is still selected.

The code (irrelevant parts removed):

class RegisterScreen extends StatelessWidget {
  final phoneNumberTEC = TextEditingController();
  final passwordTEC = TextEditingController();
  final passwordFocusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return this.keyboardDismisser(
      context: context,
      child: Scaffold(
        appBar: new AppBar(
          title: new Text("Register"),
        ),
        body: this.page(context),
        resizeToAvoidBottomPadding: false,
      ),
    );
  }

  Widget keyboardDismisser({BuildContext context, Widget child}) {
    final gesture = GestureDetector(
      onTap: () {
        this.passwordFocusNode.unfocus();
        FocusScope.of(context).requestFocus(new FocusNode());
        SystemChannels.textInput.invokeMethod('TextInput.hide');
      },
      child: child,
    );
    return gesture;
  }

  Widget page(BuildContext context) {
    return Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Container(
            padding: new EdgeInsets.all(16.0),
            child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  this.phoneNumberTextField(context),
                  this.passwordTextField(context),
                ]
            ),
          ),
          // cutting irrelevant widgets out
          )
        ]
    );
  }

  Widget phoneNumberTextField(BuildContext context) {
    return TextField(
      controller: this.phoneNumberTEC,
      decoration: InputDecoration(hintText: "Phone number"),
      onSubmitted: (string) {
        FocusScope.of(context).requestFocus(this.passwordFocusNode);
      },
    );
  }

  Widget passwordTextField(BuildContext context) {
    return TextField(
      controller: this.passwordTEC,
      decoration: InputDecoration(hintText: "Password"),
      obscureText: true,
      focusNode: this.passwordFocusNode,
      onSubmitted: (string) {
        this.performRegister(context);
      },
    );
  }

}
desudesudesu
  • 2,185
  • 1
  • 15
  • 20
  • So testing this, I enter in a value into Phone number, press enter, enter a value into Password, press enter, the soft keyboard dismisses and the Password field is no longer focused. Is this not what you want? [Gif](https://i.imgur.com/JwlYtVS.gif) – soupjake Nov 26 '18 at 13:24
  • @SnakeyHips I want both this behavior and losing focus by clicking outside of the keyboard. Right now, clicking outside of the keyboard works only for first textfield, which has no custom focusNode. – desudesudesu Nov 26 '18 at 13:28
  • I don't seem to have any problems doing that? [Gif](https://i.imgur.com/ubQ5S2s.gif) – soupjake Nov 26 '18 at 13:32
  • @SnakeyHips comment out ```SystemChannels.textInput.invokeMethod('TextInput.hide');``` -- because it hides the keyboard but doesn't end editing textfield (cursor is still blinking there). – desudesudesu Nov 26 '18 at 13:33
  • Still working fine for me? I did have to remove `this.performRegister(context);` because that method wasn't provided within your code. Maybe that method is causing the issue? – soupjake Nov 26 '18 at 13:35

13 Answers13

152

Here is a similar answer to @kasiara's answer but another way.

FocusScope.of(context).unfocus();
_textEditingController.clear();
Blasanka
  • 21,001
  • 12
  • 102
  • 104
  • 5
    As per https://stackoverflow.com/questions/63666298/widgets-tree-rebuild-using-focusscope-ofcontext-unfocus This can cause widget tree rebuilds which in practice leads to some pretty odd side-effects. The suggested answer to that question works- use `FocusManager.instance.primaryFocus?.unfocus();` instead. – Ben Roberts Jan 13 '23 at 06:06
  • @BenRoberts Thanks! there was no FocusManager.instance at that time when this answer provided. I will check the doc for more info and update answer. – Blasanka Feb 19 '23 at 17:41
41

In my case I had a TextFormField which I wanted to remove focus from and hide the keyboard if possible. The following code does both and clears TextField content.

FocusScope.of(context).requestFocus(new FocusNode()); //remove focus
WidgetsBinding.instance.addPostFrameCallback((_) => _textEditingController.clear()); // clear content

where _textEditingController is just a TextEditingController(text: "") held in widget's State.

  • 12
    Is it ok to create undisposed `FocusNode`s every time ?? – Ramesh-X Jan 28 '20 at 04:34
  • 1
    @Ramesh-X i did some testing with this code : https://gist.github.com/llpinokio/b58d80501fcd7f9a3ac139bfe6e5a2a0 , and after watching the devtools memory tab, and triggering the garbage collector manually, the gc can collect those objects and delete them, but it does so in various times, one of them is when pushingReplacement screens in the app. So, in conclusion i think it's best to create a dummy focusNode and dispose it in the current stateful widget. – giuliano-oliveira Jun 02 '20 at 21:20
  • 1
    No, it's not @Ramesh-X, create and dispose it later using a variable – Ely Dantas Jul 23 '20 at 16:34
  • 11
    WidgetsBinding.instance.focusManager.primaryFocus?.unfocus(); It's unfocus all focuses without creating new one☝️ – OnlyTarg Jul 05 '21 at 14:15
  • I'm using this in the parent page to clear focus in it's children: ```dart final focusNodeForUnfocussing = FocusNode(); FocusScope.of(context).requestFocus(focusNodeForUnfocussing); focusNodeForUnfocussing.unfocus(); focusNodeForUnfocussing.dispose(); ``` – SEG.Veenstra Aug 18 '22 at 08:09
21

This works for the new Flutter version:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return Listener(
      onPointerDown: (_) {
        FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus) {
          currentFocus.focusedChild?.unfocus();
        }
      },
      child: MaterialApp(
        title: 'Flutter Demo',
        home: Scaffold(body: SafeArea(child: TextField())),
      ),
    );
  }
}
molundb
  • 13
  • 4
Axel
  • 4,365
  • 11
  • 63
  • 122
19

similar to @Blasanka , but more shorter. Put this inside a GestureDetector that wraps the whole page.

onTap: () {
      FocusScope.of(context).unfocus();
      new TextEditingController().clear();
    },
Nader
  • 191
  • 1
  • 4
13

you may have seen some people use this approach:

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

in this approach you are creating a FocusNode on the fly and not disposing it. while this also works but it's not recommended because FocusNode's are persistence objects and you should dispose them to avoid memory leaks:

@override
  void dispose() {
    focusNode.dispose();
    super.dispose();
    }

check out this link. the author has great explanation of how to do it in the best and right way.have a nice day:)

Moghaddam
  • 271
  • 5
  • 8
9

Comes out, I didn't manage the lifecycle of FocusNode properly: https://flutter.io/docs/cookbook/forms/focus

Thus, following code did work for me:

class RegisterScreen extends StatefulWidget {
  @override
  _RegisterScreenState createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {
  final phoneNumberTEC = TextEditingController();
  final passwordTEC = TextEditingController();
  FocusNode passwordFocusNode;

  @override
  void initState() {
    super.initState();

    this.passwordFocusNode = FocusNode();
  }

  @override
  void dispose() {
    this.passwordFocusNode.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return this.keyboardDismisser(
      context: context,
      child: Scaffold(
        appBar: new AppBar(
          title: new Text("Register"),
        ),
        body: this.page(context),
        resizeToAvoidBottomPadding: false,
      ),
    );
  }

  Widget keyboardDismisser({BuildContext context, Widget child}) {
    final gesture = GestureDetector(
      onTap: () {
        FocusScope.of(context).requestFocus(new FocusNode());
        debugPrint("!!!");
      },
      child: child,
    );
    return gesture;
  }

// ...

}

Thanks for @SnakeyHips for help - inability to reproduce the issue when it was clearly reproducible on my side gave me some thoughts :)

desudesudesu
  • 2,185
  • 1
  • 15
  • 20
7

Wrap the page Root Widget with GestureDetector Widget. That's work for me

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
      FocusScope.of(context).unfocus();
      new TextEditingController().clear();
    },
      child: _yourPageRootWidget(..)
  )
}
Ravindra S. Patil
  • 11,757
  • 3
  • 13
  • 40
Kasun Hasanga
  • 1,626
  • 4
  • 15
  • 35
6

just wrap your scaffold with GestureDetector,then put FocusScope.of(context).unfocus(); inside the onTap Function

6

Currently using the top accepted answers (FocusScope.of(context).unfocus(); or FocusScope.of(context).requestFocus(new FocusNode());) can ocassionally cause widget tree rebuilds and a can even make it so it's impossible to re-focus the widget you un-focus.

As an alternative, this appears to work as expected / not cause intermittent bugs.

FocusManager.instance.primaryFocus?.unfocus();

Ben Roberts
  • 199
  • 1
  • 8
2

You can use onTapOutside in TextFormField.

TextFormField(
onTapOutside: (event) => FocusScope.of(context).unfocus(),
...
)
Erisan Olasheni
  • 2,395
  • 17
  • 20
1

I modified @Axel answer because I detected that on a screen with scroll, like SingleChildScrollView, the behavior is not as desired, since when scrolling the screen the keyboard hides, and we don't always want this (in a form, for example), so , replacing the Listener with a GestureDetector this problem is solved. See below:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () {
          FocusScopeNode currentFocus = FocusScope.of(context);
          if (!currentFocus.hasPrimaryFocus) {
            currentFocus.focusedChild?.unfocus();
          }
        },
        child: MaterialApp(
        title: 'Flutter Demo',
        home: Scaffold(body: SafeArea(child: TextField())),
      ),
    );
  }
}
0
@override
Widget build(BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
return Material(
child: Scaffold(

Only add new function(page) header , ( back main screen auto close keyboard )

Dhaiyur
  • 498
  • 4
  • 14
alpc
  • 598
  • 3
  • 6
0

You can use it if you want to unfocus from the text field onTap: () { WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();}

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 14 '23 at 17:25