Flutter水平FirestoreListView内ExpansionTile布局报错排查
Flutter 水平FirestoreListView嵌套ExpansionTile报!_debugDoingThisLayout断言修复
问题场景
Flutter开发中遇到可稳定复现的渲染报错,多次调整代码仍重复触发,报错关联组件为ExpansionTile,对应代码位置为lib/category/main_category_widget.dart:26:34。
问题代码
child: FirestoreListView<MainCategory>( scrollDirection: Axis.horizontal, query: mainCategoryCollection(widget.selectedCat), itemBuilder: (context, snapshot) { MainCategory mainCategory = snapshot.data(); return ExpansionTile( title: Text(mainCategory.mainCategory!), children: [ SubCategoryWidget( selectedSubCat: mainCategory.mainCategory, ), ], ); }, ),
报错信息
渲染库在布局阶段捕获异常:
performLayout()执行期间抛出断言:
'package:flutter/src/rendering/object.dart': 断言失败,第1840行第12位: '!_debugDoingThisLayout': 结果不为true。
从异常捕获的RenderObject信息可以看到:
- 水平滚动视口传递给列表项的约束为
BoxConstraints(0.0<=w<=312.7, 0.0<=h<=Infinity),即垂直方向高度无上限 - 异常关联的RenderPositionedBox最终计算尺寸为
Size(312.7, 0.0),所有子组件尺寸标记为MISSING,布局流程中断 - 调用栈最终指向RenderViewport、RenderSliverList的布局计算逻辑,属于列表项布局约束不匹配导致的异常
根本原因
- 布局约束冲突:代码中给
FirestoreListView设置了scrollDirection: Axis.horizontal水平滚动,水平滚动列表会给每个子项传递垂直方向无界的约束,而ExpansionTile是默认垂直向下展开的组件,展开子组件时会尝试在垂直方向占满可用空间,在无界高度约束下无法完成尺寸计算 - 布局重入触发断言:
!_debugDoingThisLayout断言的触发逻辑是:组件正在执行布局计算的过程中,又被触发了新的布局请求,Flutter为了避免布局死循环直接抛出异常。这里就是ExpansionTile在无界约束下反复请求重新计算高度,最终触发了该断言 - 额外隐患:如果
SubCategoryWidget内部使用了Expanded、Spacer这类Flex布局下撑满剩余空间的组件,在无界约束下也会进一步加剧布局计算异常
修复方案
- 方案1:给水平列表项明确宽高约束(最快修复)
水平滚动的列表项必须指定明确宽度,同时如果嵌套垂直展开的组件,需要给组件外层包裹SizedBox或ConstrainedBox限定高度边界,避免无界约束:child: FirestoreListView<MainCategory>( scrollDirection: Axis.horizontal, query: mainCategoryCollection(widget.selectedCat), itemBuilder: (context, snapshot) { MainCategory mainCategory = snapshot.data(); // 给列表项加明确尺寸约束 return SizedBox( width: 160, // 水平列表项必须指定固定宽度 height: 400, // 限定垂直方向的最大高度,给ExpansionTile提供明确边界 child: ExpansionTile( title: Text(mainCategory.mainCategory!), children: [ SubCategoryWidget( selectedSubCat: mainCategory.mainCategory, ), ], ), ); }, ), - 方案2:调整交互结构(更合理)
默认ExpansionTile的交互是垂直向下展开,和水平滚动列表的使用场景不匹配,建议调整布局结构:用垂直滚动的列表作为主分类容器,主分类展开后,子分类用水平滚动的ListView承载,更符合移动端交互习惯,也不会出现约束冲突。 - 方案3:排查子组件布局问题
检查SubCategoryWidget内部实现,不要在无明确高度约束的父容器下使用Expanded、Spacer、shrinkWrap: false的滚动组件,这类组件都会在无界约束下触发布局异常。
内容的提问来源于stack exchange,提问作者Muhammad Faruq
相关产品推荐
相关产品推荐

