如何在Flutter中点击TextField/屏幕其他位置后隐藏软键盘?

204

目前,我知道使用此代码隐藏软键盘的方法,可以通过任何小部件的onTap方法实现。

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

我想通过在TextField之外的任何地方点击或屏幕上的任何位置来隐藏软键盘。 在Flutter中是否有任何方法可以实现这一点?


但是,我希望通过在TextField之外的任何地方点击或屏幕上的任何位置来隐藏软键盘。 Flutter中是否有任何方法可以实现这一点?

3
您可以将整个屏幕用https://docs.flutter.io/flutter/widgets/GestureDetector-class.html包裹起来,然后在“onTap: () => FocusScope.of(context).requestFocus(new FocusNode());”中调用上面的代码。 - Günter Zöchbauer
1
感谢 @GünterZöchbauer。是否有触摸事件方法,因为轻按不会解决我的问题。键盘在 onTap 方法中隐藏。我需要经常隐藏键盘,因为我触摸屏幕。 - Ammy Kang
我正在使用TabBar,并在每个选项卡屏幕上拥有搜索视图框。当我从一个选项卡滑动到另一个选项卡时,如果屏幕上有键盘或者SearchView的TextField中有文本,则不会滑动到另一个选项卡,而是返回到相同的选项卡。主要是在键盘弹出时存在选项卡滑动问题,否则选项卡滑动正常。 - Ammy Kang
我该如何添加“Listener”小部件,你能给个例子吗? - Ammy Kang
2
MaterialApp( builder: (context, child) => GestureDetector( onTap: (){ FocusManager.instance.primaryFocus?.unfocus(); }, child: child, )) - Avnish Nishad
显示剩余6条评论
29个回答

399
你的做法不对,尝试使用这个简单的方法来隐藏软键盘。你只需要在整个屏幕中使用GestureDetector方法并在onTap方法中写入这段代码即可。{{}}
FocusScope.of(context).requestFocus(new FocusNode());

以下是完整的示例:

new Scaffold(
   body: new GestureDetector(
      onTap: () {
         FocusScope.of(context).requestFocus(new FocusNode());
      },
      child: new Container(
         //rest of your code write here
      ),
   ),
)

更新(2021年5月)

return GestureDetector(
   onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
   child: Scaffold(
      appBar: AppBar(
         title: Text('Login'),
      ),
      body: Body(),
   ),
);

即使您触摸AppBar,这也能正常工作,new 在Dart 2中是可选的FocusManager.instance.primaryFocus将返回当前在小部件树中具有主要焦点的节点。

使用null安全在Dart中进行条件访问


21
实际上,只有添加了 behavior: HitTestBehavior.translucent, 参数时才能确保 onTap 回调函数总是被触发。在没有这个参数的情况下,我发现某些轻击并不能正常工作。 - AndrewS
6
这个回答已经过时,请查看更新后的回答,针对v1.17.1(至少是这个版本)在这里https://dev59.com/h1cO5IYBdhLWcg3w-V_0#61986983。 - dbyuvaraj
1
有时手势检测器在整个屏幕上无法工作,那么就将Scaffold包装在GestureDetector小部件中。 - Avnish Nishad
1
手势检测器应该直接放在 Scaffold 的 body 中,否则它将无法工作。 - Alexa289
2
onTapDown比onTap更好。onTapDown: (_) => FocusManager.instance.primaryFocus?.unfocus() - Hardik
显示剩余3条评论

55

更新

从2019年5月开始,FocusNode现在有一个unfocus方法:

取消所有对焦的请求。

无论这个节点是否曾经请求过焦点,这种方法都是安全的。

如果为您的文本字段声明了FocusNode,请使用unfocus

final focusNode = FocusNode();

// ...

focusNode.unfocus();

我的原始回答建议使用detach方法-只有在您需要完全摆脱您的FocusNode时才使用。如果您打算继续使用它-请改用unfocus

如果您没有明确声明FocusNode,则针对当前上下文的FocusScope请使用unfocus

FocusScope.of(context).unfocus();
< hr />

请查看原回答的修订历史。


“detach” 不再可用,我相信。我尝试调用它,但是它不起作用 - 它说没有这个名称的函数。 - Rodrigo Vieira
@RodrigoVieira 谢谢您指出这一点。我已经从我的帖子中删除了过时的信息。 - George Zvonov

39
只是一个小的侧记:
如果你使用ListView,它的keyboardDismissBehavior属性可能会引起兴趣:
ListView(
  keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
  children: [],
)

此外,您还可以使用TapRegion
TapRegion(
  onTapOutside: (event) => FocusScope.of(context).unfocus(),
  child: // your sub-tree that triggered the keyboard
)


9
你的小注释很重要! - Ali80
谢谢!太棒了。Searchdelegate的完美解决方案。 - Fuad All
也适用于SingleChildScrollView - Taio

39

至简的解决方案

我找到了最简单的方法, 现在你可以在TextField小部件中使用onTapOutside

 TextField(
       onTapOutside: (event) {
                  print('onTapOutside');
                    FocusManager.instance.primaryFocus?.unfocus();
                },)

当文本字段处于焦点状态时,每次在[TextFieldTapRegion]组之外发生点击时,都会调用此方法。

1
刚刚用这种方法解决了。 - Linesofcode
2
有很多方法可以实现这种行为,但是这个答案是最稳定的。 - V Nikoyan
6
这是最干净和最直接的方法,但在GestureDetector答案中几乎不可见。编辑应该把这个答案放在最前面。 - Ashkan Sarlak
1
这个解决方案真是太简单了,比其他选择好多了,而且还真的有效!谢谢你! - Preem Palver2
1
非常干净且更好的方法。应该是正确的答案!我认为在没有真正需要的情况下将某物包装成手势检测器并不是正确的做法。 - Isac Moura
我已经编辑了你的标题,纠正了你的“最简单的解决方案”错误的英语,但是如果你喜欢那样,没问题 :) - undefined

31
将整个屏幕用GestureDetector包装起来。
new Scaffold(

  body: new GestureDetector(
      onTap: () {
        // call this method here to hide soft keyboard
        FocusScope.of(context).requestFocus(new FocusNode());
      },
    child: new Container(
       -
       -
       -
        )
   )

仅供记录:在不释放它们的情况下按需创建FocusNode可能会导致内存泄漏... - SePröbläm

27

我添加了这行文字

behavior: HitTestBehavior.opaque,

对于GestureDetector的处理,现在看起来已经达到了预期的效果。

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

1
在iOS中,某种程度上需要使用hittestbehaviour才能使其正常工作。 - Daryl Wong
1
这会影响VoiceOver的行为。您可能需要添加 excludeFromSemantics: true 以确保辅助技术不会将整个屏幕视为单个可点击块。 - Isaac Lyman

19

从Flutter最新版本v1.7.8+hotfix.2开始,您现在可以使用unfocus()而不是requestfocus()来隐藏键盘。

FocusScope.of(context).unfocus()

因此,每当您点击身体部位时,键盘都会隐藏起来。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text("Login"),
      ),
      body: GestureDetector(
        onTap: () {
          FocusScope.of(context).unfocus();
        },
        child: Container(...)
      ),
    );
  }


我使用Singlechildscrollview,如果触摸下面的空白区域,它不起作用。有什么解决办法吗? - Dharaneshvar
1
@Dharaneshvar 答案是:在 Scaffold 之前放置你的 GestureDetecfor。享受 =) - Marcello Câmara

14

*2022年9月更新:在Flutter 3.0.2中

如果您有复杂的屏幕,请使用Listener代替。我之前遇到了这个问题:

在`GestureDetector`中捕获事件时出现延迟/滞后,原因是什么?

文档中提到:

与其监听原始指针事件,不如使用GestureDetector监听更高级别的手势。

GestureDetector监听高级手势。我认为这导致了一些延迟或滞后。

我的解决方法:

Listener(
  behavior: HitTestBehavior.opaque,
  onPointerDown: (_) {
     FocusManager.instance.primaryFocus?.unfocus();
  },
  child: Scaffold()

当您点击任何地方时,将触发此事件。


1
这是对我来说运作最佳的选项。当你滑动或点击任何区域时,它的表现符合你的期望。 - Boris Barroso

10

如果您希望在应用的任何屏幕上都能访问该行为,请使用GestureDetector包裹MaterialApp:

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

检查hasPrimaryFocus是否为true是必要的,以防止Flutter在尝试取消聚焦树顶部节点时抛出异常。

(原始内容由Flutter Igniter博客的James Dixon提供)


2
调用了“unfocus”,但没有任何反应。当前焦点未丢失。 - Bagusflyer
此版本重新绘制整个视图层次结构;无论在gestureDetector内部有什么。我认为问题在于currentFocus.unfocus()调用。使用上面选定的答案。 - jdog
也许可以尝试使用onPanDown而不是onTap,或者尝试其他手势。 - alireza easazade

7
onPressed: () {
    FocusScope.of(context).unfocus();
},

这对我来说没问题。

它添加在哪里?在GestureDetector中吗? - eqrakhattak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接