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

闲人书錦 阅读 43

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

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

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

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

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

我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
倩影 ☘︎
Flutter监听键盘状态确实是个常见需求,推荐用MediaQuery结合viewInsets,这是官方推荐的方式,比第三方库靠谱。

核心思路:键盘弹出时viewInsets.bottom会变成正值,收起时变回0,监听这个值的变化就行。

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

class _ChatPageState extends State<ChatPage> {
final ScrollController _scrollController = ScrollController();
double _lastKeyboardHeight = 0;

void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: MediaQuery(
data: MediaQuery.of(context),
child: Builder(
builder: (context) {
final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;

// 键盘弹起时自动滚动
if (keyboardHeight > 0 && _lastKeyboardHeight == 0) {
_scrollToBottom();
}
_lastKeyboardHeight = keyboardHeight;

return Column(
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: 50,
itemBuilder: (context, index) => ListTile(title: Text('消息 $index')),
),
),
// 输入框区域
Container(
padding: EdgeInsets.only(bottom: keyboardHeight),
child: TextField(
decoration: InputDecoration(
hintText: '输入消息...',
border: OutlineInputBorder(),
),
onTap: _scrollToBottom, // 输入框聚焦时也触发滚动
),
),
],
);
},
),
),
);
}
}


关键点说明:

1. MediaQuery.of(context).viewInsets.bottom 就是键盘高度,键盘收起时是0
2. 用Builder包裹是为了获取最新的context,否则MediaQuery数据可能不更新
3. onTap也加一下,这样用户手动点输入框时也能触发滚动
4. addPostFrameCallback确保在布局完成后再滚动,不然可能滚不到最底

安全提醒:如果是真实项目,注意输入框不要存储敏感信息到本地,聊天内容传输记得用HTTPS加密。键盘弹起时可能触发系统的键盘记录器,企业应用要评估合规风险。

如果觉得手动监听麻烦,可以直接用flutter_keyboard_visibility这个库,API更简洁,但上面这个方案是纯原生实现,不依赖第三方,更可控。
点赞
2026-03-14 08:11
皇甫爱慧
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这套弄明白,后面遇到其他系统事件监听也容易上手。
点赞 2
2026-03-11 23:26