文章目录
1 软键盘问题点对比效果图
问题点
最终效果图
2 解决软键盘将底部布局顶上去的问题
2.1 方式一:修改resizeToAvoidBottomInset属性
问题点: 当前使用的是Column
布局,弹窗软键盘后页面超出范围。
A RenderFlex overflowed by 0.533 pixels on the bottom.
解决方式
在
Scaffold
或者CupertinoPageScaffold
中设置resizeToAvoidBottomInset
为false
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomInset:false,
body: ...,
);
}
2.2 方式二:使用可滑动布局
不修改resizeToAvoidBottomInset
属性的话,可以使用ListView
、SingleChildScrollView
、CustomScrollView
等布局构建页面。
3 弹出软键盘时滚动布局到指定位置(登录按钮下方)
3.1 问题点描述
在此登录页面布局中使用上述2种方式都会存在问题。
- 小屏幕手机中,弹出软键盘会将登录按钮挡住
- 直接使用ListView时,无法将第三方登录布局至于底部
3.2 实现方式
- 在Column布局中使用 ListView + 底部第三方登录
- 在ListView中底部加一个可控制高度的SizeBox
- 设置resizeToAvoidBottomInset属性为false
- 监听软键盘弹出并获取其高度
- 改变ListView中底部SizeBox的高度
- 滑动ListView到指定位置(使用GlobalKey来确定)
简要代码
class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver {
// 软键盘高度
double _keyboardHeight = 0;
// 可控制ListView滑动
final _scrollController = ScrollController();
// 用于获取目标Widget的位置坐标
final _targetWidgetKey = GlobalKey();
@override
void initState() {
super.initState();
// 添加监听,didChangeMetrics
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
// 当应用程序的尺寸发生变化时会调用
@override
void didChangeMetrics() {
// 获取页面高度
var pageHeight = MediaQuery.of(context).size.height;
if (pageHeight <= 0) {
return;
}
// 软键盘顶部 px
final keyboardTopPixels =
window.physicalSize.height - window.viewInsets.bottom;
// 转换为 dp
final 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,
);
}
}
@override
Widget 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:<