Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >当永恒的软键盘问题遇到Flutter

当永恒的软键盘问题遇到Flutter

作者头像
烧麦程
发布于 2022-05-10 12:39:36
发布于 2022-05-10 12:39:36
3.8K00
代码可运行
举报
文章被收录于专栏:半行代码半行代码
运行总次数:0
代码可运行

移动端开发的同学可能或多或少都遇到过软键盘的问题。不是被遮住布局就是布局顶不上去。那么使用 Flutter 的时候,遇到软键盘出来的时候又会遇到什么问题呢?最近在练习使用 Flutter,顺便撸个自己的 APP,遇到了这个问题,把自己的实践顺便拿出来分享一下。

从场景开始说起

我的场景是一个从底部弹出的 Dialog,Dialog 里主要就是一个 TextField 输入框。如图:

这个时候当 TextInput 获得输入焦点的时候,情况出现了:

这里会直接类似这种报错。

贴一下异常堆栈看一下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
════════ Exception caught by rendering library ═════════════════════════════════════════════════════
The following assertion was thrown during layout:
A RenderFlex overflowed by 17 pixels on the bottom.

The relevant error-causing widget was: 
  Column file:///Users/chenglei/fataccount/lib/keepAccount/add_keep_account.dart:48:22
The overflowing RenderFlex has an orientation of Axis.vertical.
The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and black striped pattern. This is usually caused by the contents being too big for the RenderFlex.

哦!原来是布局溢出了,再仔细看看,会发现,当键盘弹出来的时候,正常布局就是在键盘的上面,留给dialog 可以用的就只有一点点高度了,自然就 over 了。

Google解决法

搜索了一下,发现 Flutter 中关于这个问题有一个属性可以解决,在所在页面的 Scaffold 设置一个 resizeToAvoidBottomInset 属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
resizeToAvoidBottomInset: false

看一下效果:

我们可以看到,布局确实不溢出了,但是我们的 Dialog 也看不到了。我们看下 resizeToAvoidBottomInset 的注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// If true the [body] and the scaffold's floating widgets should size
  /// themselves to avoid the onscreen keyboard whose height is defined by the
  /// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
  ///
  /// For example, if there is an onscreen keyboard displayed above the
  /// scaffold, the body can be resized to avoid overlapping the keyboard, which
  /// prevents widgets inside the body from being obscured by the keyboard.
  ///
  /// Defaults to true.

大概意思就是这个属性 true 的时候,布局会根据键盘高度去调整,避免自己被键盘挡住。那么是 false 的时候,就不会调整了。像我的这种在底部的输入框,就直接被键盘遮住了。

解决思路

那么既然底部对话框里面有输入框的时候,resize布局和不resize布局都不合适的时候,那么就只能考虑调整对话框自己的位置了。也就是,当键盘没弹出的时候,输入框在下面,键盘出来的时候,输入框在键盘的上方。底部对话框再怎么样,也不能被输入框顶到屏幕外面去吧。

这时候就有问题了:

  • 如何监听键盘弹出和收回
  • 如何根据键盘弹出收回来调整对话框的高度

根据上文 resizeToAvoidBottomInset 的注释,我们可以找到一个有用的信息, 键盘高度是可以从

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
MediaQueryData.viewInsets

里面获取的。至于怎么监听键盘呢,其实 Google 一下也很简单,套用一下别人的思路:

界面的布局大小发生变化的时候,键盘高度不是0,我们就认为键盘弹出,反之键盘已经被收回。

至于如何监听界面大小变化了呢?

给你的 StatefulWidgetState 继承一个 WidgetsBindingObserver, 在 didChangeMetrics 方法里面就可以收到应用界面大小变化的回调了。

实践

既然大体思路有了,那么我们一步步来实践完成 Dialog 高度的兼容。

didChangeMetrics 回调里面,我们在当前 frame 结束的时候根据不同的高度来设置对话框的高度, 这里我准备了一个 initHeight 来表示对话框的初始高度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@override
  void didChangeMetrics() {
    super.didChangeMetrics();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      var bottom = MediaQuery.of(context).viewInsets.bottom;
      if (bottom == 0) {
        // 键盘收起来
        setState(() {
          this.height = initHeight;
        });
      } else {
        setState(() {
          this.height = initHeight + bottom;
        });
      }
    });
  }

至于初始高度的获取,也很简单,在 init 的时候,获取当前 Widget 的高度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      initHeight = context.size.height;
    });
  }

这个时候运行一下,就会发现当键盘弹出的时候,输入框在键盘的上方。

但是在以为已经大功告成的时候,遇到了一个新问题,输入框的高度是可以随着输入的时候按了回车键之后变化的。我们直接按几个换行:

输入框的高度变大了,Dialog的高度没有变,输入框的下半部分仍然会被遮住。纠结了一会,想想还是再优化一下吧,似乎也不是很复杂。随时拿到输入框的高度,把高度的变化通知给 Dialog 就可以了。

优化

首先我需要随时能感知到输入框的高度,那么最实在的就是在输入的时候顺便监听一下输入框自己的 height,我选择自己封装了一个 Widget:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final ValueChanged<double> heightChange;
final ValueChanged<double> initHeight;

@override
  Widget build(BuildContext context) {
    return TextField(
      onChanged: (content) {
        _checkHeight(context);
      },
      keyboardType: TextInputType.multiline,
    );
  }


_checkHeight(BuildContext context) {
    if (widget.heightChange != null) {
      widget.heightChange(context.size.height);
    }
  }

这个时候我们就能自己判断到对话框是不是在输入内容的时候变化了高度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// 检查输入框的高度
/// 使用一个 lastInputHeight 变量来记录上一次的键盘高度
  _checkHeight(height) {
    if (height > lastInputHeight) {
      // 输入框行数多了
      initHeight += (height - lastInputHeight);
      setState(() {
        var shouldHeight = this.height + (height - lastInputHeight);
        this.height = shouldHeight;
      });
    } else {
      initHeight -= (lastInputHeight - height);
      setState(() {
        var shouldHeight = this.height - (lastInputHeight - height);
        this.height = shouldHeight;
      });
    }
    lastInputHeight = height;
  }

这个时候,我就完成了 “高度 = 键盘高度 + 对话框高度 + 对话框高度变化值” 的逻辑。这时候再来看看效果:

总结

总结一下这里遇到的几个很有用的知识点:

如何获取一个 Widget 的高度?

Flutter 因为是响应式的布局开发,和 Android 这种命令式开发一个很大的区别就是基本避免直接操作一个 ui 的元素,这时候会遇到 2 个问题

  • 如何获取宽高
  • build的时候元素还没渲染完毕,又如何获取宽高

Flutter 中我们可以使用 context 去获取:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
context.size.height

或者

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(context.findRenderObject() as RenderBox).size.height

虽然 build 的时候我们无法判断宽高,但是我们可以监听渲染完毕的时候。自然就能想到使用 WidgetsBindingaddPostFrameCallback 方法,在当前 frame 完成的时候去调用,肯定是可以拿到宽高的。这个就非常类似 Android 中的 View.post{} 了。


如何获取键盘高度*

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
MediaQuery.of(context).viewInsets.bottom;

这就是个简单的 API 问题了,严格来说这个获取的方式是系统底部的ui高度,但是基本 99% 的情况可以和软键盘划等号了。


WidgetsBindingObserver的使用

注册 Widget 层绑定的接口,各种行为的监听。包括:

didPushRoute : 路由跳转

didChangeMetrics : 应用旋转,屏幕大小变化

didChangeTextScaleFactor : 字体变化

didChangePlatformBrightness: 亮度变化

didChangeAppLifecycleState: 生命周期

didChangeLocales: 系统本地设置变化,例如时间,语言

didHaveMemoryPressure: 内存不足

didChangeAccessibilityFeatures: accessibility相关

我们可以根据自己的需求灵活利用这个接口去实现我们想要的功能。


本篇文章我分享了最近一次使用 Flutter 遇到软件盘的时候的处理方法。虽然回头看看思路整体不算很难,但是因为不熟悉,解决这个问题还是一波三折,花了一晚上的时间。这里拿出来分享一下,如果有朋友有更好的解决思路,也欢迎交流分享。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半行代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Flutter软键盘原理
Flutter页面在软键盘弹出的时候,可以设置 Scaffold 的 resizeToAvoidBottomInset 属性来设置软键盘的处理。当这个值为true的时候,页面会进行重新布局。那么我们应
烧麦程
2022/05/10
1.6K0
鸿蒙开发:如何解决软键盘弹出后的间距
近日在查看github中的issue时,发现了一个问题,说的是当自定义弹窗中有TextInput组件时,触摸焦点弹起软键盘后,组件和软键盘之间有一个间距,看到问题后,“我就在想,自定义弹窗,所有的UI都是传入的,是不是在绘制的时候,这个间距是开发者加的呢?”,本着对问题负责的态度,于是就验证问题出现的原因,最后却发现这是鸿蒙系统的问题。
程序员一鸣
2025/05/15
830
鸿蒙开发:如何解决软键盘弹出后的间距
Flutter 中键盘弹起时,Scaffold 发生了什么变化
最近刚好有网友咨询一个问题,那就顺便借着这个问题给大家深入介绍下 Flutter 中键盘弹起时,Scaffold 的内部发生了什么变化,让大家更好理解 Flutter 中的输入键盘和 Scaffold 的关系。
GSYTech
2021/03/02
2.2K0
Flutter 中键盘弹起时,Scaffold 发生了什么变化
Flutter 上的一个 Bug 带你了解键盘与路由的另类知识点
事情是这样的,由于近期 Flutter 发布了 1.17 的稳定版,按照“惯例”开始着手把生产项目升级到 1.12.13+hotfix.9 版本,在升级适配完成之后,一个突如其来的 Bug 让我陷入了沉思。
GSYTech
2020/05/21
1.4K0
Flutter-Dart全局可拖动悬浮球
当我们全局都需要用到某个设定且随时需要根据需求改变时,那么全局悬浮球是一个最好的选择(可拖动),参考其他大佬的文章,优化封装了一个简易的悬浮球,记录一下0.0。
老猫-Leo
2023/12/11
1.2K0
解决Android软键盘在全屏下设置adjustResize无效的问题
adjustResize则是缩放可调整页面 所以要和ScrollView配合 但是如果界面设成全屏模式就不会生效
码客说
2019/10/22
7.1K1
移动端那些戳中你痛点的软键盘问题及解决方法
在ios手机中,当页面中包含有输入框时,点击输入框,键盘弹起,会让页面中被fixed的元素失效。所以造成了底部吸底和顶部吸顶的元素错位的问题。下面的视频中就出现了这个问题,吸顶元素被推到可视区之外去了,而吸底元素也被推到了键盘之上。
wade
2021/05/28
9.2K0
移动端那些戳中你痛点的软键盘问题及解决方法
Flutter 小技巧之 MediaQuery 和 build 优化你不知道的秘密
今天这篇文章的目的是补全大家对于 MediaQuery 和对应 rebuild 机制的基础认知,相信本篇内容对你优化性能和调试 bug 会很有帮助。
GSYTech
2022/06/28
1.3K0
你知道吗,Flutter内置了10多种show
builder通常返回Dialog组件,比如SimpleDialog和AlertDialog。
老孟Flutter
2020/03/19
2K0
Flutter的showModalBottomSheet 输入框被弹出的键盘挡住?
最近在做项目的时候有这样一个需求:用户对已购买的商品进行评价,如果用户给差评,就必须输入原因。并且输入框是从底部弹起的一个单独层。
用户1974410
2022/09/20
3.7K0
Flutter的showModalBottomSheet 输入框被弹出的键盘挡住?
DialogFragment自动弹出软键盘,消失时关闭软键盘
2、设置dialog的style属性 android:windowSoftInputMode
玖柒的小窝
2021/11/05
5.3K3
Android 软键盘遮挡解决
        链接:https://www.jianshu.com/p/9eb57a8ff5e5
android_薛之涛
2018/09/12
4K1
Android 软键盘遮挡解决
【Flutter 专题】124 日常问题小结 (三) 自定义 Dialog 二三事
针对日常不同的需求,我们时常需要自定义 Dialog,而和尚在尝试过程中遇到一些小问题,简单记录总结一下;
阿策小和尚
2021/06/08
1.2K0
【Flutter 专题】124 日常问题小结 (三) 自定义 Dialog 二三事
可能这些是你想要的H5软键盘兼容方案
最近一段时间在做 H5 聊天项目,踩过其中一大坑:输入框获取焦点,软键盘弹起,要求输入框吸附(或顶)在输入法框上。需求很明确,看似很简单,其实不然。从实验过一些机型上看,发现主要存在以下问题:
桃翁
2019/06/20
8.3K1
可能这些是你想要的H5软键盘兼容方案
Input被软键盘遮住
在业务开发过程中,移动端输入框被软键盘遮住的情况还是很容易出现的。在不同手机不同系统都有可能出现。
wade
2020/04/24
1K0
鸿蒙开发:一文了解软键盘相关
前段时间,简单写了备忘录小应用,有一个小功能,那就是,底部的一排样式编辑按钮,在键盘未弹出时,就在页面底部展示,当键盘弹起时,就悬浮在软键盘之上,方便在内容编辑时进行样式修改,就如下图红框中的编辑按钮。
程序员一鸣
2024/12/26
1490
鸿蒙开发:一文了解软键盘相关
Flutter 强大的MediaQuery控件
通常情况下,不会直接将MediaQuery当作一个控件,而是使用MediaQuery.of获取当前设备的信息,用法如下:
老孟Flutter
2020/03/30
8670
[javascript] 解决移动端手机浏览器软键盘遮挡输入框问题
在手机端经常有这样的需求 , 最下面是输入框类似微信输入框 , 我们一般设置成了position:fixed
唯一Chat
2021/02/25
1.7K0
[javascript] 解决移动端手机浏览器软键盘遮挡输入框问题
Flutter全局悬浮按钮
2.思路 1)悬浮按钮可以使用flutter提供的Overlay + OverlayEntry 组合实现
徐建国
2021/08/25
2.6K0
Android爬坑之旅:软键盘挡住输入框问题的终极解决方案
开发做得久了,总免不了会遇到各种坑。 而在Android开发的路上,『软键盘挡住了输入框』这个坑,可谓是一个旷日持久的巨坑——来来来,我们慢慢看。
用户2802329
2018/08/07
5.1K0
Android爬坑之旅:软键盘挡住输入框问题的终极解决方案
推荐阅读
相关推荐
Flutter软键盘原理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验