flutter-完美解决键盘弹出遮挡输入框的问题

文章目录

  • [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 的这套原生解决方案无需依赖第三方库,性能更优,是处理键盘遮挡问题的首选方式。


本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

往期文章

个人主页

相关推荐
從南走到北17 分钟前
JAVA东郊到家按摩服务同款同城家政服务按摩私教茶艺师服务系统小程序+公众号+APP+H5
android·java·开发语言·微信小程序·小程序
alexhilton1 小时前
学会用最优雅的姿式在Compose中显示富文本
android·kotlin·android jetpack
阿华的代码王国3 小时前
【Android】卡片式布局 && 滚动容器ScrollView
android·xml·java·前端·后端·卡片布局·滚动容器
风起云涌~3 小时前
【Android】桌面小组件开发
android·gitee
愿天深海3 小时前
Flutter 生命周期介绍
flutter
谈吐大方的鹏sir4 小时前
SwiftUI-TextField组件学习
ios
双鱼大猫5 小时前
从传统播放器到AI智能体:Xplayer 2.0的技术革新之路
android
CYRUS_STUDIO5 小时前
动态篡改 so 函数返回值:一篇带你玩转 Android Hook 技术!
android·c++·逆向
xzkyd outpaper6 小时前
Android中主线程、ActivityThread、ApplicationThread的区别
android·面试
就叫飞六吧7 小时前
mysql全量备份、全量恢复demo
android·mysql·adb