文章目录

  • 1 软键盘问题点对比效果图
  • 2 解决软键盘将底部布局顶上去的问题
    • 2.1 方式一:修改resizeToAvoidBottomInset属性
    • 2.2 方式二:使用可滑动布局
  • 3 弹出软键盘时滚动布局到指定位置(登录按钮下方)
    • 3.1 问题点描述
    • 3.2 实现方式
  • 4 登录页面完整代码

1 软键盘问题点对比效果图

问题点

最终效果图


2 解决软键盘将底部布局顶上去的问题

2.1 方式一:修改resizeToAvoidBottomInset属性

问题点: 当前使用的是Column布局,弹窗软键盘后页面超出范围。

A RenderFlex overflowed by 0.533 pixels on the bottom.

解决方式

Scaffold或者CupertinoPageScaffold中设置resizeToAvoidBottomInset为false

  @overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: Colors.white,resizeToAvoidBottomInset:false,body: ...,);}

2.2 方式二:使用可滑动布局

不修改resizeToAvoidBottomInset属性的话,可以使用ListViewSingleChildScrollViewCustomScrollView等布局构建页面。

3 弹出软键盘时滚动布局到指定位置(登录按钮下方)

3.1 问题点描述

在此登录页面布局中使用上述2种方式都会存在问题。

  • 小屏幕手机中,弹出软键盘会将登录按钮挡住
  • 直接使用ListView时,无法将第三方登录布局至于底部


3.2 实现方式

  1. 在Column布局中使用 ListView + 底部第三方登录
  2. 在ListView中底部加一个可控制高度的SizeBox
  3. 设置resizeToAvoidBottomInset属性为false
  4. 监听软键盘弹出并获取其高度
  5. 改变ListView中底部SizeBox的高度
  6. 滑动ListView到指定位置(使用GlobalKey来确定)

简要代码

class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver {// 软键盘高度double _keyboardHeight = 0;// 可控制ListView滑动final _scrollController = ScrollController();// 用于获取目标Widget的位置坐标final _targetWidgetKey = GlobalKey();@overridevoid initState() {super.initState();// 添加监听,didChangeMetricsWidgetsBinding.instance.addObserver(this);}@overridevoid dispose() {WidgetsBinding.instance.removeObserver(this);super.dispose();}// 当应用程序的尺寸发生变化时会调用@overridevoid didChangeMetrics() {// 获取页面高度var pageHeight = MediaQuery.of(context).size.height;if (pageHeight <= 0) {return;}// 软键盘顶部  pxfinal keyboardTopPixels =window.physicalSize.height - window.viewInsets.bottom;// 转换为 dpfinal keyboardTopPoints = keyboardTopPixels / window.devicePixelRatio;// 软键盘高度final keyboardHeight = pageHeight - keyboardTopPoints;setState(() {_keyboardHeight = keyboardHeight;});if (keyboardHeight <= 0) {return;}// 获取目标位置的坐标RenderBox? renderBox =_targetWidgetKey.currentContext?.findRenderObject() as RenderBox?;if (renderBox == null) {return;}// 转换为全局坐标final bottomOffset =renderBox.localToGlobal(Offset(0, renderBox.size.height));final targetDy = bottomOffset.dy;// 获取要滚动的距离// 即被软键盘挡住的那段距离 加上 _scrollController.offset 已经滑动过的距离final offsetY =keyboardHeight - (pageHeight - targetDy) + _scrollController.offset;// 滑动到指定位置if (offsetY > 0) {_scrollController.animateTo(offsetY,duration: kTabScrollDuration,curve: Curves.ease,);}}@overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: Colors.white,// 避免底部布局被软键盘顶上来resizeToAvoidBottomInset: false,body: GestureDetector(behavior: HitTestBehavior.opaque,// 点击空白位置关闭软键盘onTap: () => FocusManager.instance.primaryFocus?.unfocus(),child: Column(children: [Expanded(child: ListView(controller: _scrollController,children: [...// 一系列输入框Widget...// 弹出的软键盘位于此Widget之下Row(key: _targetWidgetKey,mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [...],),// 动态变换高度,保证ListView可滑动SizedBox(height: _keyboardHeight)],),),/// 底部的布局Row(children: const [...],),],),),);}
}

效果图


4 登录页面完整代码

带删除和眼睛按钮的输入框控件

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';class UserTextField extends StatefulWidget {final TextEditingController controller;final TextInputType? keyboardType;final String? placeholder;final bool usedInPassword;final Widget? suffixWidget;final int? maxLength;const UserTextField({Key? key,required this.controller,this.keyboardType,this.placeholder,this.usedInPassword = false,this.suffixWidget,this.maxLength,}) : super(key: key);@overrideState<StatefulWidget> createState() => _UserTextFieldState();
}class _UserTextFieldState extends State<UserTextField> {var _showClearIcon = false;var _showEyeIcon = false;late bool _obscurePassword;@overridevoid initState() {super.initState();_obscurePassword = widget.usedInPassword;widget.controller.addListener(() {var isNotEmpty = widget.controller.text.isNotEmpty;setState(() {_showEyeIcon = isNotEmpty;_showClearIcon = isNotEmpty;});});}@overrideWidget build(BuildContext context) {return CupertinoTextField(controller: widget.controller,keyboardType: widget.keyboardType,onChanged: (_) {},placeholder: widget.placeholder,style: const TextStyle(color: Colors.black),placeholderStyle: const TextStyle(color: Colors.grey),maxLength: widget.maxLength,padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),decoration: BoxDecoration(color: Colors.white,border: Border.all(color: Colors.grey, width: 0.5),borderRadius: BorderRadius.circular(26),),obscureText: _obscurePassword,obscuringCharacter: "*",suffix: widget.suffixWidget ??(widget.usedInPassword ? _buildPasswordEyeIcon() : _buildClearIcon()),);}Widget _buildClearIcon() {return _showClearIcon? CupertinoButton(padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),child: const Icon(Icons.clear, size: 18),onPressed: () => widget.controller.clear(),): const SizedBox(width: 8.0);}Widget _buildPasswordEyeIcon() {return _showEyeIcon? CupertinoButton(padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),child: Icon(_obscurePassword ? Icons.visibility_off : Icons.visibility,size: 18,),onPressed: () {setState(() => _obscurePassword = !_obscurePassword);},): const SizedBox(width: 8.0);}
}

登录页面

import 'dart:ui';import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'user_text_field.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return const CupertinoApp(theme: CupertinoThemeData(primaryColor: Colors.red,scaffoldBackgroundColor: Colors.white,),debugShowCheckedModeBanner: false,home: LoginPage(),);}
}class LoginPage extends StatefulWidget {const LoginPage({Key? key}) : super(key: key);@overrideState<StatefulWidget> createState() => _LoginPageState();
}class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver {final _emailController = TextEditingController();final _passwordController = TextEditingController();final _codeController = TextEditingController();// 软键盘高度double _keyboardHeight = 0;// 可控制ListView滑动final _scrollController = ScrollController();// 用于获取目标Widget的位置坐标final _targetWidgetKey = GlobalKey();@overridevoid initState() {super.initState();// 添加监听,didChangeMetricsWidgetsBinding.instance.addObserver(this);}@overridevoid dispose() {WidgetsBinding.instance.removeObserver(this);super.dispose();}// 当应用程序的尺寸发生变化时会调用@overridevoid didChangeMetrics() {// 获取页面高度var pageHeight = MediaQuery.of(context).size.height;if (pageHeight <= 0) {return;}// 软键盘顶部  pxfinal keyboardTopPixels =window.physicalSize.height - window.viewInsets.bottom;// 转换为 dpfinal keyboardTopPoints = keyboardTopPixels / window.devicePixelRatio;// 软键盘高度final keyboardHeight = pageHeight - keyboardTopPoints;setState(() {_keyboardHeight = keyboardHeight;});if (keyboardHeight <= 0) {return;}// 获取目标位置的坐标RenderBox? renderBox =_targetWidgetKey.currentContext?.findRenderObject() as RenderBox?;if (renderBox == null) {return;}// 转换为全局坐标final bottomOffset =renderBox.localToGlobal(Offset(0, renderBox.size.height));final targetDy = bottomOffset.dy;// 获取要滚动的距离// 即被软键盘挡住的那段距离 加上 _scrollController.offset 已经滑动过的距离final offsetY =keyboardHeight - (pageHeight - targetDy) + _scrollController.offset;// 滑动到指定位置if (offsetY > 0) {_scrollController.animateTo(offsetY,duration: kTabScrollDuration,curve: Curves.ease,);}}@overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: Colors.white,resizeToAvoidBottomInset: false,body: GestureDetector(behavior: HitTestBehavior.opaque,onTap: () => FocusManager.instance.primaryFocus?.unfocus(),child: Column(children: [Expanded(child: ListView(controller: _scrollController,children: [SafeArea(child: Align(alignment: Alignment.centerRight,child: CupertinoButton(onPressed: () {},child: const Icon(CupertinoIcons.clear, size: 24),),),),const Padding(padding: EdgeInsets.fromLTRB(16, 16, 16, 16),child: Text('你好,\n欢迎使用Flutter App',style: TextStyle(fontSize: 24,color: Colors.black,fontWeight: FontWeight.w500,),),),const SizedBox(height: 40),Padding(padding: const EdgeInsets.symmetric(horizontal: 16),child: UserTextField(controller: _emailController,keyboardType: TextInputType.emailAddress,placeholder: '请输入邮箱',),),const SizedBox(height: 16.0),Padding(padding: const EdgeInsets.symmetric(horizontal: 16),child: UserTextField(controller: _passwordController,keyboardType: TextInputType.visiblePassword,usedInPassword: true,placeholder: '请输入密码',),),const SizedBox(height: 16.0),Padding(padding: const EdgeInsets.symmetric(horizontal: 16),child: UserTextField(controller: _codeController,keyboardType: TextInputType.number,placeholder: '请输入6位验证码',),),const SizedBox(height: 16.0),CupertinoButton(padding: const EdgeInsets.all(16),child: Container(height: 44,width: double.infinity,alignment: Alignment.center,decoration: const BoxDecoration(color: Colors.red,borderRadius: BorderRadius.all(Radius.circular(22)),),child: const Text('登录',style: TextStyle(color: Colors.white),),),onPressed: () {},),Row(key: _targetWidgetKey,mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [CupertinoButton(minSize: 24,alignment: Alignment.topCenter,padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 0),onPressed: () {},child: const Text('忘记密码?',style: TextStyle(fontSize: 14),),),CupertinoButton(minSize: 24,alignment: Alignment.topCenter,padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 0),onPressed: () {},child:const Text('立即注册', style: TextStyle(fontSize: 14)),),],),SizedBox(height: _keyboardHeight)],),),Row(children: const [SizedBox(width: 16),Expanded(child: Divider()),SizedBox(width: 8),Text('其它登录方式',style: TextStyle(fontSize: 13, color: Colors.grey),),SizedBox(width: 8),Expanded(child: Divider()),SizedBox(width: 16),],),Row(crossAxisAlignment: CrossAxisAlignment.center,mainAxisAlignment: MainAxisAlignment.center,children: [CupertinoButton(onPressed: () {},child: const Icon(Icons.facebook, size: 44),),const SizedBox(width: 32),CupertinoButton(onPressed: () {},child: const Icon(Icons.apple, size: 44),),],),const SizedBox(height: 12),],),),);}
}

Flutter 解决App登录页面软键盘遮挡住登录按钮或顶起底部控件的问题相关推荐

  1. Android 软键盘弹出时把布局顶上去,控件乱套解决方法

    Android 软键盘弹出时把布局顶上去,控件乱套解决方法 参考文章: (1)Android 软键盘弹出时把布局顶上去,控件乱套解决方法 (2)https://www.cnblogs.com/zhuj ...

  2. android 软键盘遮住按钮,Android应用中出现软键盘遮挡住按钮如何解决

    Android应用中出现软键盘遮挡住按钮如何解决 发布时间:2020-11-20 16:25:47 来源:亿速云 阅读:110 作者:Leah Android应用中出现软键盘遮挡住按钮如何解决?相信很 ...

  3. 解决微信H5页面软键盘弹起后页面下方留白的问题(iOS端)

    解决微信H5页面软键盘弹起后页面下方留白的问题(iOS端) 参考文章: (1)解决微信H5页面软键盘弹起后页面下方留白的问题(iOS端) (2)https://www.cnblogs.com/zouw ...

  4. android 页面默认不弹软键盘_Android 软键盘的全面解析,让你不再怕控件被遮盖!...

    作者 | Vander丶 编辑 | 苏宓 微信公众号 | mobilehub 背景 Android软键盘这块从我入职到现在,是一个一直纠缠我的问题. 从布局挤压,到EditText显示不全,在到弹出时 ...

  5. edittext 软键盘上方_Android 软键盘的全面解析,让你不再怕控件被遮盖

    原标题:Android 软键盘的全面解析,让你不再怕控件被遮盖 作者 | Vander丶 编辑 | 苏宓 微信公众号 | mobilehub 背景 Android软键盘这块从我入职到现在,是一个一直纠 ...

  6. js手机键盘遮挡_iphone手机微信页面软键盘遮挡input输入框解决方法

    现象描述:iphone手机微信页面,用position: fixed;定位的input或textarea输入框,在获取输入焦点时,会被弹出的输入法软键盘遮挡,导致用户无法看到输入框,效果如图: 简化测 ...

  7. Android软键盘的全面解析,让你不再怕控件被遮盖

    本文转载自:https://blog.csdn.net/l540675759/article/details/74528641 背景 1.Android软键盘这块从我入职到现在,是一个一直纠缠我的问题 ...

  8. 仿微信、QQ聊天页面软键盘遮挡聊天对话的解决办法

    在刚制作的时候弹起来的软键盘会覆盖聊天记录 类似这样 然后我仔细研究了下代码 原代码: //底部框代码 <view class="cu-bar foot input"> ...

  9. 移动端h5页面软键盘弹出后 背景图片被顶上去

    移动端h5页面在软键盘弹出后,body的高度被压缩了,就导致原本高度100%的背景图被顶上去一截,需要把div的高度强行设回100%才能解决这个问题 <div class="app&q ...

最新文章

  1. 1.17 选择排序法
  2. java 实现set,Java--Set的三个具体实现类
  3. Java Signal实例
  4. 目标检测技术演化:从R-CNN到Faster R-CNN
  5. 连接池dbcp跟c3p0
  6. linux三剑客应用到工作中,LINUX 三剑客老大(AWK) 日常工作总结(示例代码)
  7. Android时间戳与字符串相互转换
  8. c语言中sprintf函数_在C / C ++中使用sprintf()函数
  9. 【气动学】基于matlab导弹拦截计算方法【含Matlab源码 982期】
  10. 电子书:《网页木马攻防实战》
  11. 《我的世界》Minecraft私服搭建100%成功
  12. STM32 CAN通讯过滤器使用总结及代码分析
  13. 保姆级/DOSBox使用MAC
  14. 免费邮箱客户端设置方法大全
  15. 2019年双十一购物数据分析报告
  16. UWB定位,新一代的精确定位技术
  17. 模式也能开盲盒,”盲返“模式带动电商平台共享经济
  18. 蓝屏代码page_fault_in_nonpaged_area (0x00000050)
  19. Jlink给目标板供电 外部电源给目标板供电
  20. 游戏手柄改typec接口

热门文章

  1. 语雀转换发布CSDN和语雀导出图片图片无法识别处理(针对typora)
  2. 【数据挖掘】天池挑战赛 新闻推荐
  3. Macbook M1开启允许任意来源应用
  4. 米家电磁炉显示e10_米家电磁炉深度使用解析 这才是我想要的
  5. YOLO系列(V1-V2-V3)
  6. java文件打包jar文件_把java文件打包成.jar (jar命令详解)
  7. iOS静态库中打包图片资源
  8. 【STM32F407】第2章 ThreadX FileX文件系统介绍
  9. 借CT阵营之力NFV让运营商网络变得更美
  10. 【工具】markdown