Scaffold类有个resizeToAvoidBottomInset特点,它的作用是当弹出软键盘的时候,能够主动调理body区域的高度,撑起body的内容,使其底部高度刚好为键盘的高度,这样一来就不至于让键盘掩盖内容。

Scaffold(
   /// .....
   /// 当弹出软键盘的时候,是否主动调理body区域,默许为true
   resizeToAvoidBottomInset: true,
   /// .....
)

resizeToAvoidBottomInset默许值为true,主动调理body的高度,设置为false则停止这一行为。当然resizeToAvoidBottomInset为ture,主动调理高度也是需求条件的:

1、body区域不能为一个滚动区域。

如使用了SingleChildScrollViewListView包裹,它就会失去主动调理的功用,道理很简单,由于一个非固定高度的组件它无法主动核算高度。

2、需求存在TextField组件

一般来说当存在TextField组件,才会呈现软键盘,resizeToAvoidBottomInset才会有作用。

body主动调理高度会存在一些组件重叠问题,引起界面紊乱。由于body的高度改动了,如果body中的组件依赖于高度,就会产生畸变。所以为了防止这个问题你能够挑选将resizeToAvoidBottomInset设置为false,只是软键盘遮挡问题依然存在。当然,仍是有处理的方法的:

这个时候你能够经过判别键盘是否弹起,给body的底部添加一个SizedBox高度,就能处理这个问题。但,这里存在三个技术问题:

1、我要怎样判别键盘是否弹起?

承继WidgetsBindingObserver类,重写didChangeMetrics方法,该方法可用于检测页面的改动,软键盘的弹出和收起都会触发应该事情。然后经过判别MediaQuery.of(context).viewInsets.bottom的值来确认键盘是否弹起。当它的值大于0时,说明键盘弹起,反之为收起。

class _KeyboardDemoState extends State<KeyboardDemo>
    with WidgetsBindingObserver {
  // 键盘是否翻开
  bool isKeyboardOpen = false;
  @override
  void initState() {
    super.initState();
    // 注册监听
    WidgetsBinding.instance?.addObserver(this);
  }
  @override
  void dispose() {
    super.dispose();
    // 移除监听
    WidgetsBinding.instance?.removeObserver(this);
  }
  /// 页面尺度改动时回调
  @override
  didChangeMetrics() {
    super.didChangeMetrics();
    // 在页面重新烘托完结之后,经过MediaQuery.of(context).viewInsets.bottom获取软键盘高度
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      isKeyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0;
    });
  }
  /// .....
}

2、我要怎样获取当时键盘的高度?

答案已经在上面说了,MediaQuery.of(context).viewInsets.bottom 就是获取当时软键盘的高度。

3、我要怎样封闭软键盘?

可能有人会说,这是个问题吗,直接点击软键盘上的三角图标不就行了。问出这个问题的人,估量是没有考虑过ios体系。

iOS中,体系默许的软键盘是没有收起的按钮的。这个时候咱们就要想个办法来处理软键盘收起问题。一般来说咱们都会在键盘以外的区域添加一个单击事情,然后使用FocusManager.instance.primaryFocus?.unfocus()将键盘封闭。

GestureDetector(
   /// 点击内容包括通明区域
   behavior: HitTestBehavior.opaque,
   onTap: () {
     /// 撤销当时软键盘焦点并收起
     FocusManager.instance.primaryFocus?.unfocus();
   }
}

那么,单击事情使用什么组件呢?这里仍是有些考究的,能够使用InkWell或者GestureDetectoronTap事情,但前者点击时会产生水波纹的效果,后者要注意点击区域,默许情况下GestureDetector的onTap单击事情点击区域是不包括通明区域的,需求将behavior特点设为HitTestBehavior.opaque,包括通明区域,不然你可能在空白区域点半天,也没见键盘收起。当然,我比较推荐使用GestureDetector来处理单击事情。

完好代码:

import 'package:flutter/material.dart';
class KeyboardDemo extends StatefulWidget {
  final String inquiryCode;
  const KeyboardDemo({
    Key? key,
    required this.inquiryCode,
  }) : super(key: key);
  @override
  State<KeyboardDemo> createState() => _KeyboardDemoState();
}
class _KeyboardDemoState extends State<KeyboardDemo>
    with WidgetsBindingObserver {
  // 键盘是否翻开
  bool isKeyboardOpen = false;
  @override
  void initState() {
    super.initState();
    // 注册监听
    WidgetsBinding.instance?.addObserver(this);
  }
  @override
  void dispose() {
    super.dispose();
    // 移除监听
    WidgetsBinding.instance?.removeObserver(this);
  }
  /// 页面尺度改动时回调
  @override
  didChangeMetrics() {
    super.didChangeMetrics();
    // 在页面重新烘托完结之后,经过MediaQuery.of(context).viewInsets.bottom获取软键盘高度
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      isKeyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: PreferredSize(
        preferredSize: const Size.fromHeight(5),
        child: Container(
          color: Colors.white,
        ),
      ),
      /// 当弹出软键盘的时候,是否主动调理body区域,默许为true
      resizeToAvoidBottomInset: false,
      body: GestureDetector(
        /// 点击内容包括通明区域
        behavior: HitTestBehavior.opaque,
        onTap: () {
          /// 撤销当时软键盘焦点并收起
          FocusManager.instance.primaryFocus?.unfocus();
        },
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            SizedBox(
              height: 200,
              child: Container(
                margin: const EdgeInsets.all(12),
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                    border: Border.all(
                  width: 1.r,
                  color: Colors.black,
                )),
                child: const TextField(
                  decoration: InputDecoration(
                    hintText: '请输入内容',
                    border: InputBorder.none,
                  ),
                ),
              ),
            ),
            Container(
              width: double.infinity,
              margin: EdgeInsets.only(
                  bottom: MediaQuery.of(context).viewInsets.bottom),
              child: ElevatedButton(
                onPressed: () {},
                child: const Text('确认'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}