¿Cómo puedo descartar el teclado en pantalla?

139

Estoy recopilando la entrada del usuario con un TextFormFieldy cuando el usuario presiona un para FloatingActionButtonindicar que ha terminado, quiero cerrar el teclado en pantalla.

¿Cómo hago para que el teclado desaparezca automáticamente?

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
fuente
Collin, La segunda respuesta de @Pascal debería ser la respuesta actualizada y corregida.
Jack

Respuestas:

227

A partir de Flutter v1.7.8 + hotfix.2, el camino a seguir es:

FocusScope.of(context).unfocus()

Comente sobre relaciones públicas sobre eso:

Ahora que ha aterrizado # 31909 (be75fb3), debe usar en FocusScope.of(context).unfocus()lugar de FocusScope.of(context).requestFocus(FocusNode()), ya que los FocusNodes son ChangeNotifiersy deben eliminarse correctamente.

-> NO lo use ̶r̶e̶q̶u̶e̶s̶t̶F̶o̶c̶u̶s̶(̶F̶o̶c̶u̶s̶N̶o̶d̶e̶(̶)̶más.

 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
fuente
1
Me funcionó en el hotfix v1.7.8 + hotfix.4
Rajesh Jr.
2
funciona para mí, esta respuesta debe estar enmascarada como la correcta
user3087360
@kine, ¿podrías darnos más detalles? Según la documentación, esta es la forma de hacerlo (consulte api.flutter.dev/flutter/widgets/FocusNode/unfocus.html )
Pascal
@Pascal Sí, he leído la documentación y esta habría sido mi solución preferida, pero en mi aplicación no siempre funciona (un formulario bastante complejo con varios TextFieldsy otros widgets de edición). No sé por qué, pero a veces unfocusno tiene ningún efecto. Reemplazarlo con la solución aceptada (sin otros cambios) soluciona el problema. Tal vez esté relacionado con la ubicación desde la que unfocusse llama. Necesito llamarlo, por ejemplo, desde CheckBoxListTile.onChangedpara descartar el teclado cuando el usuario interactúa con un widget de casilla de verificación y desde otras ubicaciones similares. No recuerdo la ubicación exacta del problema.
Kine
@kine Gracias por la explicación. Dar un ejemplo al equipo de Flutter sería increíble ;-)
Pascal
207

Nota: esta respuesta está desactualizada. Vea la respuesta para las versiones más recientes de Flutter .

Puede descartar el teclado quitando el foco del TextFormFieldy dándolo a un no utilizado FocusNode:

FocusScope.of(context).requestFocus(FocusNode());
Collin Jackson
fuente
¿Dónde implementaría este código? En TextFormField; tal vez después onChanged:o en la acción de su botón personalizado?
Charles Jr
@CharlesJr Lo hago con la acción de mi botón.
Duncan Jones
puedes usar mi paquete si quieres :) pub.dartlang.org/packages/keyboard_actions
diegoveloper
17
Esto es antes de Flutter v1.7.8 - vea la respuesta stackoverflow.com/a/56946311/8696915 que daFocusScope.of(context).unfocus()
Richard Johnson
1
No hagas esto si tienes acceso a .unfocus () ya que provocará una pérdida de memoria. Ese nuevo FocusNode () nunca se limpiará. Ver comentario sobre .unfocus ()
Michael Peterson
70

La solución con FocusScope no me funciona. Encontré otro:

import 'package:flutter/services.dart';

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

Resolvió mi problema.

Andrey Turkovsky
fuente
Llamar a esto no me oculta el teclado. ¿Debería funcionar tanto en Android como en iOS?
Jardo
Me funcionó en iOS (12.1, iPhone 5s) y Android (Pixel 3)
Jannie Theunissen
¿Dónde pones esto? Intenté agregarlo como el primer bit de código en la aplicación y no hizo nada.
Justin808
¿Qué quieres decir con "el primer bit de código"? Insértelo en el buildmétodo, por ejemplo.
Andrey Turkovsky
1
¿Hay un equivalente para esto para el teclado numérico?
Daniel Maksimovich
19

Para Flutter 1.17.3 (canal estable a junio de 2020), use

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

Ninguna de las soluciones anteriores no me funciona.

Flutter sugiere esto: coloque su widget dentro del nuevo GestureDetector () en el que el toque ocultará el teclado y onTap use FocusScope.of (contexto) .requestFocus (nuevo 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
fuente
12

El siguiente código me ayudó a ocultar el teclado

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

prueba esto en el gesto del grifo

karan champaneri
fuente
Gracias ... tomé esta solución
Ajay Kumar
6

Como en Flutter todo es un widget, decidí envolver el SystemChannels.textInput.invokeMethod('TextInput.hide');y el FocusScope.of(context).requestFocus(FocusNode());enfoque en un módulo de utilidad corto con un widget y un mixin en él.

Con el widget, puede envolver cualquier widget (muy conveniente cuando se usa un buen soporte de IDE) con el 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(),
    );
  }
}

Con el mixin, puede activar la ocultación del teclado de cualquier estado o widget en cualquier interacción:

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

Simplemente cree un keyboard_hider.dartarchivo y el widget y el mixin estarán listos para usar:

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
fuente
4

Puedes usar el unfocus()método de la FocusNodeclase.

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.
fuente
1
¡Hola! Sería mejor si pudieras editar tu respuesta para agregar un poco más de contexto a tu respuesta.
grooveplex
1
La mejor solución con la menor cantidad de codificación
Val
Sí, quita el foco si toca un widget específico. No puedo agregarlo a todos los widgets de mi aplicación.
Dpedrinha
Tocar un widget es solo una forma de llamar al unfocusmétodo. Si no desea enviar spam a su aplicación, puede utilizar una forma diferente, como el TextFormFieldevento de cambio o un patrón reactivo, depende de la arquitectura de su aplicación.
Evandro Ap. S.
4

Parece diferentes enfoques para diferentes versiones. Estoy usando Flutter v1.17.1 y lo siguiente me funciona.

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

intente usar un controlador de edición de texto. al principio,

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

y en el evento de prensa,

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

esto descartará el teclado.

CHANDUKA SAMARASINGHE.
fuente