Flutter中解决键盘遮挡TextField问题

1,520 阅读2分钟

记录一下开发过程中的问题

在开发过程中可能会遇到这样的场景,一个列表上有很多个输入框组件,可以编辑,也可以上下滑动,底部还有个固定的按钮,一般都是通过Stack、Positioned层叠定位布局固定到底部实现的,会遇到2种情况:

第一种:自带的这种,Scaffold默认会打开重新计算View高度的设置,即resizeToAvoidBottomInset默认为true,这种方式会有个不好地方,就是当键盘弹出来的时候会把底部按钮顶上去,原生就很容易解决这种问题。在flutter可以通过显示隐藏来解决或者问题,但是这种方式在收起键盘的时候在安卓机上有明显的卡顿留白,很难受

Stack(
  fit: StackFit.expand,
  children: [
    _content(),
    Positioned(
      left: 0,
      right: 0,
      bottom: 0,
      child: Visibility(
        visible: MediaQuery.of(context).viewInsets == EdgeInsets.zero,
        child: _bottomLayout(),
      ),
    ),
  ],
)

第二种:在Scaffold中设置resizeToAvoidBottomInset为false,让页面在弹出收起键盘的时候不重新绘制,这个时候又有了新的问题,底部按钮正常固定在底部,但是输入框会被键盘遮挡住,就需要做处理让其滚动到键盘上方。我是这么解决的,给每个item设置GlobalKey和FocusNode,设置GlobalKey是为了获取输入框在获取item所在屏幕的坐标

Scaffold(
  resizeToAvoidBottomInset: false,
)

计算item所在位置

extension GlobalKeyExtension on GlobalKey {
  Rect? get globalPaintBounds {
    final renderObject = currentContext?.findRenderObject();
    var translation = renderObject?.getTransformTo(null).getTranslation();
    if (translation != null && renderObject?.paintBounds != null) {
      return renderObject?.paintBounds.shift(Offset(translation.x, translation.y));
    } else {
      return null;
    }
  }
}

FocusNode是为了监听TextField获取焦点并计算所在位置

focusNode.addListener(() {
  offsetY=containerKey.globalPaintBounds?.bottom ?? 0;
});

接下来with WidgetsBindingObserver添加监听,实现didChangeMetrics方法,didChangeMetrics这个方法是在页面尺寸发生变化时会调用,可以在这个方法里面做列表的滚动处理,然后通过ScrollController的animateTo方法让item滚动到键盘上方

@override
void didChangeMetrics() {
  // 获取页面高度
  var pageHeight = MediaQuery.of(getCurContext()).size.height;
  if (pageHeight <= 0) {
    return;
  }
  final keyboardTopPixels = window.physicalSize.height - window.viewInsets.bottom;
  final keyboardTopPoints = keyboardTopPixels / window.devicePixelRatio;
  // 计算出软键盘高度
  final keyboardHeight = pageHeight - keyboardTopPoints;
  if(offsetY < (pageHeight-keyboardHeight || keyboardHeight <= 0)){
    return;
  }
  //要滑动高度
  final scrollHeight =keyboardHeight - (pageHeight - offsetY) + scrollController.offset;
  if (scrollHeight > 0) {
    scrollController.animateTo(
      scrollHeight, duration: Duration(microseconds: 200),curve: Curves.ease,
    );
  }
}

如有不足之处,还请指正,谢谢!