前往小程序,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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Vue 3 源码导读
https://juejin.im/post/5d977f47e51d4578453274b3 来源:掘金
前端达人
2019/10/10
1.5K0
Vue 3 源码导读
前端常见vue面试题合集
通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
bb_xiaxia1998
2022/11/09
7540
Vue3 深度解析
距离尤雨溪首次公开 Vue3 (vue-next)源码有一个多月了。青笔观察到,刚发布国庆期间,出现不少解读 Vue3 源码的文章。当然不少有追风蹭热之嫌,文章草草讲讲响应式原理,或者只是做了一些上层的导读,告诉读者应该先看哪再看哪。不能说这些文章就没有一点价值,它确实能够让你在短时间内,不用过多思考就能了解到一些 Vue3 重中之重的“干货”。但是过于干货的未必就是好的。因为干货通常是经过作者咀嚼过后的产物,大部分营养其实只被作者消化了。留给读者的只是一些看似很有料,实则没有营养的残渣。就像一块啃到只剩骨头的排骨。这样的文章通常适合于媒体传播,仅用于快速捕获眼球。但是对于想更细致了解 Vue3 的专业前端开发,这显然远远不够。
我是一条小青蛇
2019/11/20
5.2K0
「中文翻译」Vue3 的诞生之路
因时间有限,文中翻译不对的地方还请指出,望海涵。想感受原汁原味还请移步上方链接。致敬尤大!
童欧巴
2020/06/04
7120
Vue 3.0 源码分析-数据侦测
2020年前端大事件之一,Vue 3.0终于正式发布了。作为一个大的版本更新,Vue 3 与 Vue 2相比,实现原理,使用方式等均有着不小的改动。本文主要会介绍讲述二块内容,分别是Vue 3.0 的简要介绍,Vue 3.0 数据侦测源码分析。小伙伴们可以根据自己的需求,查看对应的内容,也欢迎各位一起探讨,一起学习。 Vue 3.0 简要介绍 Vue 3 的 “前世今生” 2016年,Vue 2.0 正式发布,时至今日,已经过去了四年的时光。诚然,在这四年中,Vue 2的社区建设一直呈现出一副蓬勃向上的态
QQ音乐前端团队
2021/01/11
7560
面试官:Vue3有了解过吗?能说说跟Vue2的区别吗?
「Vue 新版本的理念成型于 2018 年末,当时 Vue 2 的代码库已经有两岁半了。比起通用软件的生命周期来这好像也没那么久,但在这段时期,前端世界已经今昔非比了
@超人
2021/02/26
10.5K0
面试官:Vue3有了解过吗?能说说跟Vue2的区别吗?
《Vue3.0抢先学》系列之:网友们都惊呆了!
今天开始,我想给大家讲点新东西。大家不用大喊学不动,请放松心情随意观看,我也讲不出什么很深奥难学的东西,本系列文章都会是些比较浅显易懂的家常内容。
一斤代码
2019/10/29
8500
《Vue3.0抢先学》系列之:网友们都惊呆了!
写给小白(自己)的vue3源码导读!
目前社区有很多 Vue3 的源码解析文章,但是质量层次不齐,不够系统和全面,总是一个知识点一个知识点的解读,这样我在拜读中,会出现断层,无法将整个vue3的知识体系融合,于是只能自己操刀来了
用户7413032
2022/03/24
1.4K0
写给小白(自己)的vue3源码导读!
Vue2 源码解析
Vue.js 是一个渐进式 MVVM 框架,目前被广泛使用,也成为目前前端技术中颇具代表性的一个框架。
EchoROne
2022/08/15
1.2K0
Vue2 源码解析
第一篇:一文看懂 Vue.js 3.0 的优化
我们的课程是要解读 Vue.js 框架的源码,所以在进入课程之前我们先来了解一下 Vue.js 框架演进的过程,也就是 Vue.js 3.0 主要做了哪些优化。
越陌度阡
2023/08/09
4070
第一篇:一文看懂 Vue.js 3.0 的优化
Vue3 对比Vue2,你找到哪些变化?
希望本篇文章能帮你加深对 Vue 的理解,能信誓旦旦地说自己熟练Vue2/3。除此之外,也希望路过的朋友可以帮助我查漏补缺🤞。 内容混杂用法 + 原理 + 使用小心得,建议收藏,慢慢看。 区别 生命周期的变化 整体来看,变化不大,只是名字大部分需要 + on,功能上类似。使用上 Vue3 组合式 API 需要先引入;Vue2 选项 API 则可直接调用,如下所示。 // vue3 <script setup> import { onMounted } from 'vue' onMounted(
@超人
2022/04/14
1.2K0
快速了解Vue3的新特性
10月5日尤大大公布了 vue 3.0 的源码,目前还是 pre-alpha 状态,预计年后会发布正式版本,这个无疑是国庆期间前端圈最大的新闻了。虽然此前关于 vue 3.0众说纷纭,但是既然已经官宣了,而且明年有可能迁移至新的开发框架,是时候撸起袖子学习一波了。
用户9914333
2022/07/22
4790
快速了解Vue3的新特性
Vue2和Vue3的底层原理详解
Vue.js是一个流行的JavaScript框架,用于构建用户界面。Vue.js通过MVVM架构模式和响应式数据绑定来实现数据和UI的分离。Vue.js的底层原理在Vue2和Vue3中略有不同。
世间万物皆对象
2024/03/20
8440
前端工程师的vue面试题笔记
指令本质上是装饰器,是 vue 对 HTML 元素的扩展,给 HTML 元素增加自定义功能。vue 编译 DOM 时,会找到指令对象,执行指令的相关方法。
bb_xiaxia1998
2022/11/10
7250
前端高频vue面试题总结3
通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
bb_xiaxia1998
2023/01/05
1.2K0
Vue3源码解析,打造自己的Vue3框架
分析Vue3源码并尝试打造自己的Vue3框架是一个复杂但极具教育意义的项目。这不仅能帮助你深入理解Vue3的内部工作机制,还能提升你的JavaScript、TypeScript以及前端框架开发能力。以下是一个大致的步骤指南,帮助你开始这一挑战:
瘦瘦itazs和fun
2025/01/02
870
Vue篇(011)-vue3带来的新特性/亮点
2. Tree shaking support: 按需编译,体积比vue2.x更小;
齐丶先丶森
2022/05/12
1.2K0
Vue篇(011)-vue3带来的新特性/亮点
Vue.js 3.x 优化概览
Vue.js 从 1.x 到 2.0 版本,最大的升级就是引入了虚拟 DOM 的概念。
CherishTheYouth
2022/05/10
3.5K0
Vue.js  3.x 优化概览
Vue常识面试题
Web是World Wide Web的简称,中文译为万维网我们可以将它规划成如下的几个时代来进行理解
隔壁老陈
2023/03/09
2.2K0
Vue常识面试题
Vue3源码解析,打造自己的Vue3框架无密分享
随着前端技术的飞速发展,Vue.js 作为一款轻量级且功能强大的前端框架,受到了广大开发者的青睐。Vue 3 作为 Vue.js 的最新版本,带来了许多令人振奋的改进和优化。本文将深入探讨 Vue 3 的源码,并基于这些理解,指导读者如何打造自己的 Vue 3 框架。
爱学IT-学无止境
2024/06/25
3020
相关推荐
Vue 3 源码导读
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验