Como posso descartar o teclado na tela?

139

Estou coletando a entrada do usuário com um TextFormFielde quando o usuário pressiona a FloatingActionButtonindicando que terminou, desejo descartar o teclado na tela.

Como faço o teclado desaparecer automaticamente?

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
fonte
Collin, A segunda resposta de @Pascal deve ser a resposta atualizada e corrigida.
Jack

Respostas:

233

A partir do Flutter v1.7.8 + hotfix.2, o caminho a seguir é:

FocusScope.of(context).unfocus()

Comente sobre RP sobre isso:

Agora que # 31909 (be75fb3) pousou, você deve usar em FocusScope.of(context).unfocus()vez de FocusScope.of(context).requestFocus(FocusNode()), uma vez que FocusNodes são ChangeNotifiers, e devem ser descartados corretamente.

-> NÃO use ̶r̶e̶q̶u̶e̶s̶t̶F̶o̶c̶u̶s̶(̶F̶o̶c̶u̶s̶N̶o̶d̶e̶(̶)̶mais.

 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̶(̶)̶)̶;̶
Pascal
fonte
1
Trabalhou para mim no hotfix v1.7.8 + hotfix.4
Rajesh Jr.
2
funcione para mim, esta resposta deve ser mascarada como a correta
user3087360
@kine você pode explicar? De acordo com a documentação, esta é a maneira de fazer (consulte api.flutter.dev/flutter/widgets/FocusNode/unfocus.html )
Pascal
@Pascal Sim, li a documentação e esta teria sido a minha solução preferida, mas no meu aplicativo nem sempre funciona (uma forma bastante complexa com vários se TextFieldoutros widgets de edição). Não sei porquê, mas às vezes unfocusnão surte efeito. Substituí-lo pela solução aceita (sem outras alterações) corrige o problema. Talvez esteja relacionado ao local de onde unfocusé chamado. Eu preciso chamá-lo, por exemplo, de CheckBoxListTile.onChangedpara dispensar o teclado quando o usuário interage com um widget de caixa de seleção e de outros locais semelhantes. Não me lembro do local exato do problema.
vacas
@kine Obrigado pela explicação. Fornecer um exemplo para a equipe do Flutter seria incrível ;-)
Pascal
210

Nota: esta resposta está desatualizada. Veja a resposta para versões mais recentes do Flutter .

Você pode dispensar o teclado retirando o foco do TextFormFielde dando-o a um não utilizado FocusNode:

FocusScope.of(context).requestFocus(FocusNode());
Collin Jackson
fonte
Onde você implementaria este código? No TextFormField; talvez depois onChanged:ou na ação de seu botão personalizado?
Charles Jr
@CharlesJr Eu faço isso na ação do meu botão.
Duncan Jones
você pode usar meu pacote se quiser :) pub.dartlang.org/packages/keyboard_actions
diegoveloper
17
Isso é antes do Flutter v1.7.8 - consulte a resposta stackoverflow.com/a/56946311/8696915 que dáFocusScope.of(context).unfocus()
Richard Johnson
1
Não faça isso se você tiver acesso a .unfocus (), pois isso causará um vazamento de memória. Esse novo FocusNode () nunca será limpo. Ver comentário sobre .unfocus ()
Michael Peterson
70

A solução com FocusScope não funciona para mim. Eu encontrei outro:

import 'package:flutter/services.dart';

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

Isso resolveu meu problema.

Andrey Turkovsky
fonte
Chamar isso não esconde o teclado para mim. Deve funcionar em Android e iOS?
Jardo
Funcionou para mim no iOS (12.1, iPhone 5s) e Android (Pixel 3)
Jannie Theunissen
Onde você coloca isso? Eu tentei apenas adicioná-lo como o primeiro pedaço de código no aplicativo e não fez nada.
Justin808
O que você quer dizer com "o primeiro pedaço de código"? Insira-o no buildmétodo, por exemplo.
Andrey Turkovsky
1
existe um equivalente para isso para o teclado numérico?
Daniel Maksimovich
20

Para Flutter 1.17.3 (canal estável em junho de 2020), use

FocusManager.instance.primaryFocus.unfocus();
suztomo
fonte
12

Nenhuma das soluções acima não funciona para mim.

Flutter sugere isso - Coloque seu widget dentro do novo GestureDetector () no qual o toque ocultará o teclado e, no toque, use FocusScope.of (contexto) .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
fonte
12

O código a seguir me ajudou a esconder o teclado

   void initState() {
   SystemChannels.textInput.invokeMethod('TextInput.hide');
   super.initState();
   }
poonam
fonte
8
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'),
    ),
  ), })

tente este gesto de toque

Karan Champaneri
fonte
Obrigado ... peguei esta solução
Ajay Kumar
6

Como no Flutter tudo é um widget, decidi envolver o SystemChannels.textInput.invokeMethod('TextInput.hide');e a FocusScope.of(context).requestFocus(FocusNode());abordagem em um pequeno módulo de utilitário com um widget e um mixin nele.

Com o widget, você pode envolver qualquer widget (muito conveniente ao usar um bom suporte IDE) com o KeyboardHiderwidget:

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(),
    );
  }
}

Com o mixin, você pode ativar a ocultação do teclado de qualquer estado ou widget em qualquer interação:

class SimpleWidget extends StatefulWidget {
  @override
  _SimpleWidgetState createState() => _SimpleWidgetState();
}

class _SimpleWidgetState extends State<SimpleWidget> with KeyboardHiderMixin {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: () {
        // Hide the keyboard:
        hideKeyboard();
        // Do other stuff, for example:
        // Update the state, make an HTTP request, ...
      },
    );
  }
}

Basta criar um keyboard_hider.dartarquivo e o widget e o mixin estarão prontos para uso:

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

/// Mixin that enables hiding the keyboard easily upon any interaction or logic
/// from any class.
abstract class KeyboardHiderMixin {
  void hideKeyboard({
    BuildContext context,
    bool hideTextInput = true,
    bool requestFocusNode = true,
  }) {
    if (hideTextInput) {
      SystemChannels.textInput.invokeMethod('TextInput.hide');
    }
    if (context != null && requestFocusNode) {
      FocusScope.of(context).requestFocus(FocusNode());
    }
  }
}

/// A widget that can be used to hide the text input that are opened by text
/// fields automatically on tap.
///
/// Delegates to [KeyboardHiderMixin] for hiding the keyboard on tap.
class KeyboardHider extends StatelessWidget with KeyboardHiderMixin {
  final Widget child;

  /// Decide whether to use
  /// `SystemChannels.textInput.invokeMethod('TextInput.hide');`
  /// to hide the keyboard
  final bool hideTextInput;
  final bool requestFocusNode;

  /// One of hideTextInput or requestFocusNode must be true, otherwise using the
  /// widget is pointless as it will not even try to hide the keyboard.
  const KeyboardHider({
    Key key,
    @required this.child,
    this.hideTextInput = true,
    this.requestFocusNode = true,
  })  : assert(child != null),
        assert(hideTextInput || requestFocusNode),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () {
        hideKeyboard(
          context: context,
          hideTextInput: hideTextInput,
          requestFocusNode: requestFocusNode,
        );
      },
      child: child,
    );
  }
}
Vince Varga
fonte
4

Você pode usar o unfocus()método da FocusNodeclasse.

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.
fonte
1
Oi! Seria melhor se você pudesse editar sua resposta para adicionar um pouco mais de contexto a ela.
grooveplex
1
A melhor solução com a menor quantidade de codificação
Val
Sim, ele remove o foco se tocar em um widget específico. Não consigo adicioná-lo a todos os widgets do meu aplicativo.
Dpedrinha
Tocar em um widget é apenas uma maneira de chamar o unfocusmétodo. Se você não deseja enviar spam para o seu aplicativo, pode usar uma maneira diferente, como o TextFormFieldevento de alteração ou um padrão reativo, que depende da arquitetura do seu aplicativo.
Evandro Ap. S.
4

Parece abordagens diferentes para versões diferentes. Estou usando o Flutter v1.17.1 e o que está abaixo funciona para mim.

onTap: () {
    FocusScopeNode currentFocus = FocusScope.of(context);
    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
       currentFocus.focusedChild.unfocus();
    }
}
dbyuvaraj
fonte
1
_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
fonte
0

tente usar um controlador de edição de texto. no começo,

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

e no evento na imprensa,

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

isso descartará o teclado.

CHANDUKA SAMARASINGHE.
fonte
0

Para resumir, esta é uma solução funcional para o Flutter 1.17:

Envolva seu widget assim:

GestureDetector(
        onTap: FocusScope.of(context).unfocus,
        child: YourWidget(),
);
Valentin Seehausen
fonte