Flutter里怎么监听键盘弹起和收起事件?

闲人书錦 阅读 9

我正在用Flutter做聊天页面,想在键盘弹出时自动滚动到底部,但不知道怎么监听页面键盘的显示和隐藏。网上看到有人说用MediaQuery,但我试了好像没反应。有没有类似React里那种onFocus或者键盘事件的监听方式?

比如在React里我这样处理输入框聚焦:

const handleFocus = () => {
  inputRef.current?.scrollIntoView({ behavior: 'smooth' });
};

return (
  <input ref={inputRef} onFocus={handleFocus} />
);

Flutter里有对应的方案吗?试过WidgetsBindingObserver但不太会用,求指点!

我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
皇甫爱慧
Flutter监听键盘状态,WidgetsBindingObserver确实是正确思路,你没用对可能是漏了几个关键点。

核心逻辑是这样的:让State实现WidgetsBindingObserver,然后在didChangeMetrics()里检测viewInsets变化。

class ChatPage extends StatefulWidget {
@override
_ChatPageState createState() => _ChatPageState();
}

class _ChatPageState extends State with WidgetsBindingObserver {
final ScrollController _scrollController = ScrollController();

@override
void initState() {
super.initState();
// 关键步骤1:注册observer
WidgetsBinding.instance.addObserver(this);
}

@override
void dispose() {
// 关键步骤2:移除observer
WidgetsBinding.instance.removeObserver(this);
_scrollController.dispose();
super.dispose();
}

@override
void didChangeMetrics() {
super.didChangeMetrics();
// 关键步骤3:通过viewInsets判断键盘状态
final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom;
final pixelRatio = WidgetsBinding.instance.window.devicePixelRatio;
final keyboardHeight = bottomInset / pixelRatio;

if (keyboardHeight > 0) {
// 键盘弹起了,自动滚动到底部
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 200),
curve: Curves.easeOut,
);
}
});
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: _scrollController,
itemCount: messages.length,
itemBuilder: (context, index) => MessageBubble(message: messages[index]),
),
bottomNavigationBar: _buildInputBar(),
);
}

Widget _buildInputBar() {
return Container(
padding: EdgeInsets.only(
// 关键步骤4:用MediaQuery把输入框顶上来
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: TextField(
decoration: InputDecoration(hintText: '输入消息...'),
),
);
}
}


几个容易踩的坑提醒一下:

第一,addPostFrameCallback这个是必须的,因为键盘弹起时界面还没完全渲染好,直接scroll会失败。

第二,viewInsets.bottom单位是逻辑像素,需要除以devicePixelRatio才是真实高度。

第三,build方法里给输入框容器加padding-bottom用的是MediaQuery.of(context).viewInsets.bottom,这个不需你手动计算,Flutter会自动处理好。

如果觉得这套写法麻烦,可以直接用flutter_keyboard_visibility这个包,一行代码搞定:

import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';

@override
Widget build(BuildContext context) {
return KeyboardVisibilityBuilder(
builder: (context, isKeyboardVisible) {
if (isKeyboardVisible) {
// 键盘弹起了
_scrollToBottom();
}
return YourWidget();
},
);
}


不过原理都是一样的,建议先把WidgetsBindingObserver这套弄明白,后面遇到其他系统事件监听也容易上手。
点赞
2026-03-11 23:26