文章目录
- [1. 前言](#1. 前言)
- [2. 借助 Scaffold 的特性自动调整](#2. 借助 Scaffold 的特性自动调整)
- [3. 使用 MediaQuery 精准控制抬升高度](#3. 使用 MediaQuery 精准控制抬升高度)
-
- [3.1. 底部抽屉内输入框的方案](#3.1. 底部抽屉内输入框的方案)
- [4. 注意事项](#4. 注意事项)
- [5. 总结](#5. 总结)
1. 前言
在 Flutter 的开发过程中,经常会碰到某一个页面有个 TextField 输入组件,点击的时候键盘会弹起来,这时候页面应该需要抬升和键盘一样的高度,好展示输入内容,不然输入内容就会被键盘遮挡。还有特殊场景,比如一个底部抽屉弹框,里面有 TextField 输入组件,此时也要抬升页面以方便输入,这种场景下,如何完美解决呢?
其实,Flutter 提供了原生的解决方案,通过 Scaffold 组件的特性结合 MediaQuery 动态获取键盘高度,就能轻松实现输入框随键盘抬升的效果。本文将从基础场景到特殊场景,详细讲解这一方案的实现原理和具体操作。
2. 借助 Scaffold 的特性自动调整
Scaffold 是 Flutter 页面布局的基础组件,它内置了处理键盘弹出的机制,当键盘弹出时,会自动调整页面布局以避免输入框被遮挡,其核心在于resizeToAvoidBottomInset
属性(默认值为 true)。
当resizeToAvoidBottomInset: true
时,Scaffold 会根据键盘的高度自动调整自身尺寸,将底部被键盘遮挡的部分向上抬升,确保输入框可见。如下代码,当点击 TextField 弹出键盘时,Scaffold 会自动向上收缩,使输入框位于键盘上方,避免被遮挡:
dart
Scaffold(
resizeToAvoidBottomInset: true,
body: Column(
mainAxisAlignment: MainAxisAlignment.end, // 输入框在页面底部
children: [
TextField(
decoration: const InputDecoration(
hintText: '请输入内容',
border: OutlineInputBorder(),
),
),
],
),
)
若需要关闭这一特性(例如某些自定义布局场景),可将其设为 false,此时键盘弹起,页面不会被挤压。
3. 使用 MediaQuery 精准控制抬升高度
对于特殊场景(如底部抽屉、自定义弹窗中的输入框),仅依赖 Scaffold 的自动调整可能无法满足需求。此时,可通过 MediaQuery.of(context).viewInsets.bottom 动态获取键盘高度,手动调整输入框所在容器的底部内边距,实现精准抬升。如下代码:
dart
Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom, // 该底部内边距等于键盘高度
),
child: TextField(
decoration: const InputDecoration(
hintText: '请输入内容',
border: OutlineInputBorder(),
),
),
)
原理解析
MediaQuery.of(context).viewInsets 用于获取屏幕边缘被系统 UI 遮挡的区域尺寸,其中 bottom 属性在键盘弹出时会返回键盘的高度(单位为逻辑像素),键盘收起时则为 0。
利用这一特性,给输入框所在的容器添加底部内边距,值等于键盘高度,即可让容器随键盘同步抬升。
3.1. 底部抽屉内输入框的方案
如下代码:
dart
class ButtomPopupPageState extends State<ButtomPopupPage> {
/// 焦点
final FocusNode myFocusNode = FocusNode();
/// 文字
final TextEditingController inputText = TextEditingController();
/// 输入框滚动控制器
final ScrollController inputController = ScrollController();
/// 打开弹窗
void handleShowPopup() {
showModalBottomSheet(
context: context,
clipBehavior: Clip.antiAlias,
barrierColor: Colors.black.withAlpha(127),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
backgroundColor: const Color(0xFF232323),
isScrollControlled: true, // 允许抽屉高度自适应内容
enableDrag: false,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom), // 适配键盘高度
child: Container(
height: 238,
color: Colors.white,
padding: EdgeInsets.all(16),
child: Column(
children: [
Container(
height: 110,
clipBehavior: Clip.antiAlias,
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
color: const Color(0xFFEEF4FA),
borderRadius: BorderRadius.circular(16),
),
child: TextField(
expands: true,
scrollController: inputController,
controller: inputText,
focusNode: myFocusNode,
// 设置为 null 表示多行输入
maxLines: null,
maxLength: 1000,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.done,
autofocus: false,
decoration: InputDecoration(
isCollapsed: true,
contentPadding: EdgeInsets.zero,
hintText: '请输入......',
// 去除输入框底部的字符计数
counterText: '',
hintStyle: TextStyle(color: Color(0xff728BA4)),
border: InputBorder.none,
),
cursorColor: Color(0xff1E1E1E),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Color(0xff1E1E1E),
),
onChanged: (text) {},
onTapOutside: (PointerDownEvent _) {
// 点击输入框外界主动失去焦点
myFocusNode.unfocus();
},
),
),
// 确定按钮
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Container(
alignment: Alignment.center,
height: 56,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
color: Color(0xFF4165F1),
),
child: Text('确认'),
),
),
],
),
),
);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: Container(
alignment: Alignment.center,
decoration: const BoxDecoration(color: Colors.white),
child: TextButton(
onPressed: handleShowPopup,
child: const Text(
'点击弹出',
style: TextStyle(color: Colors.black, fontSize: 20),
),
),
),
),
],
);
}
}
4. 注意事项
- 确保布局结构合理,输入框所在容器的高度能够随内边距调整(如使用 Column(mainAxisSize: MainAxisSize.min))。
- 在底部抽屉中必须设置 isScrollControlled: true,否则可能因高度限制导致适配失效。
5. 总结
核心在于利用系统提供的布局特性和动态高度获取:
- 基础场景:直接使用 Scaffold,依赖其默认的 resizeToAvoidBottomInset: true 特性,即可实现页面自动抬升,适用于大多数常规页面。
- 特殊场景:对于底部抽屉、自定义弹窗等独立布局,通过 MediaQuery.of(context).viewInsets.bottom 获取键盘高度,给输入框容器添加对应底部内边距,实现精准适配。
通过这两种方案的结合,无论是常规页面还是特殊弹窗中的输入框,都能完美避开键盘遮挡,为用户提供流畅的输入体验。Flutter 的这套原生解决方案无需依赖第三方库,性能更优,是处理键盘遮挡问题的首选方式。
本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~
往期文章
- vue中ref的详解以及react的ref对比
- css使用aspect-ratio制作4:3和9:16和1:1等等比例布局
- Web前端页面开发阿拉伯语种适配指南
- flutter-使用extended_image操作图片的加载和状态处理以及缓存和下载
- flutter-制作可缩放底部弹出抽屉评论区效果
- flutter-实现Tabs吸顶的PageView效果
- Vue2全家桶+Element搭建的PC端在线音乐网站
- 助你上手Vue3全家桶之Vue3教程
- 超详细!vue组件通信的10种方式
- 超详细!Vuex手把手教程
- 使用nvm管理node.js版本以及更换npm淘宝镜像源
- vue中利用.env文件存储全局环境变量,以及配置vue启动和打包命令
个人主页