Flutter弹出键盘时ListView子组件被意外销毁问题如何解决?
问题原因
默认情况下Flutter的Scaffold开启了resizeToAvoidBottomInset属性,软键盘弹出时会压缩当前页面的可用高度,这个尺寸变化会通过根节点的MediaQuery通知到路由栈里的所有页面。处于下层的列表页也会因此触发重绘,而ListView.builder默认采用懒加载机制,仅会保留当前可视区域内的组件,视口高度变小后超出可视范围的列表项就会被销毁,对应你看到的dispose日志。
解决方案
方案1:列表项状态保活(推荐)
适用于需要保留列表滚动位置、列表行临时状态的场景,给列表行的State混入AutomaticKeepAliveClientMixin即可让组件滑出视口/视口变化时也不会被销毁:
修改match_chat_row.dart中的_MatchChatRowState定义:
class _MatchChatRowState extends State<MatchChatRow> with AutomaticKeepAliveClientMixin { // 开启保活 @override bool get wantKeepAlive => true; @override void dispose(){ print("Disposed ROW! ${widget.content}"); super.dispose(); } @override Widget build(BuildContext context) { super.build(context); // 必须调用父类方法 // 原有build逻辑保持不变 final rowHeight = 100.0; final rowPadding = 10.0; return InkWell( // ... 原有代码 ); } }
方案2:禁止聊天页随软键盘调整大小
适用于聊天页不需要跟随软键盘上移布局的场景,直接给聊天页的Scaffold添加resizeToAvoidBottomInset: false属性即可,软键盘弹出时不会触发全局尺寸变化,下层列表自然不会重绘销毁组件:
修改chat_screen.dart中的Scaffold定义:
Scaffold( // 加这行禁止页面随软键盘压缩 resizeToAvoidBottomInset: false, appBar: AppBar( title: Text("Hi") ), // 原有其他代码保持不变 )
注意:如果采用方案2,软键盘弹出时会直接覆盖当前页面内容,你需要自行处理输入框的避让逻辑,避免输入框被软键盘遮挡。
内容的提问来源于stack exchange,提问作者Andreas Lymbouras
相关产品推荐
相关产品推荐

