Flutter comment_tree:^0.3.0多评论树ListView渲染异常问题
问题原因
渲染失败核心原因是**Expanded组件嵌套不符合Flutter布局约束规则**,和comment_tree库本身无关:
- 你封装的
Comments组件根布局是Column,内部直接用Expanded包裹CommentTreeWidget,而Expanded仅能作为Flex类组件(Column、Row、Flex)的直接子级、且父组件有明确高度约束时才能正常生效。 - 把
Comments放入ListView、或SingleChildScrollView内的Column时,这类滚动组件会给子组件传递无界高度约束,此时Expanded会尝试抢占无限高度的剩余空间,直接触发布局溢出、渲染白屏。 - 放入
GridView能正常渲染,是因为GridView的每个子项都会通过gridDelegate计算出固定宽高约束,Expanded能拿到明确高度边界,所以不会报错。 - 你当前
CommentPage的写法本身也有问题:在SingleChildScrollView的Column里直接写Expanded,同样会触发无界高度下的布局错误,这也是你注释里标注加了循环渲染行就无法加载的直接原因。
修复方案
1. 改造Comments组件,移除不合理的Expanded嵌套
CommentTreeWidget本身会根据内容自适应高度,不需要外层套Expanded,直接去掉Comments组件里多余的Column和Expanded包裹即可,修改后的Comments组件build方法代码:
@override Widget build(BuildContext context) { return Container( padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: CommentTreeWidget<Comment, Comment>( commentLi[0], [ commentLi[0], commentLi[1], ], treeThemeData: TreeThemeData(lineColor: Colors.blue, lineWidth: 3), avatarRoot: (context, data) => const PreferredSize( child: CircleAvatar( radius: 18, backgroundColor: Colors.grey, backgroundImage: NetworkImage(AppConstants.User_Url)), preferredSize: Size.fromRadius(18), ), avatarChild: (context, data) => const PreferredSize( child: CircleAvatar( radius: 12, backgroundColor: Colors.grey, backgroundImage: NetworkImage(AppConstants.User_Url)), preferredSize: Size.fromRadius(12), ), contentChild: (context, data) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(12)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( '${data.userName}', style: Theme.of(context) .textTheme .caption ?.copyWith( fontWeight: FontWeight.w600, color: Colors.black), ), SizedBox( height: 4, ), Text( '${data.content}', style: Theme.of(context) .textTheme .caption ?.copyWith( fontWeight: FontWeight.w300, color: Colors.black), ), ], ), ), DefaultTextStyle( style: Theme.of(context).textTheme.caption.copyWith( color: Colors.grey[700], fontWeight: FontWeight.bold), child: Padding( padding: EdgeInsets.only(top: 4), child: Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 8, ), Text('5d'), SizedBox( width: 8, ), Text('Like'), SizedBox( width: 24, ), Text('Reply'), ], ), ), ) ], ); }, contentRoot: (context, data) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(12)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Tina Chuhan', style: Theme.of(context).textTheme.caption.copyWith( fontWeight: FontWeight.w600, color: Colors.black), ), SizedBox( height: 4, ), Text( '${data.content}', style: Theme.of(context).textTheme.caption.copyWith( fontWeight: FontWeight.w300, color: Colors.black), ), ], ), ), DefaultTextStyle( style: Theme.of(context).textTheme.caption.copyWith( color: Colors.grey[700], fontWeight: FontWeight.bold), child: Padding( padding: EdgeInsets.only(top: 4), child: Row( mainAxisSize: MainAxisSize.min, children: const [ SizedBox( width: 8, ), Text('5d'), SizedBox( width: 8, ), Text('Like'), SizedBox( width: 24, ), Text('Reply'), ], ), ), ) ], ); }, ), ); }
所有内部嵌套的Column、Row都要加上mainAxisSize: MainAxisSize.min,避免子组件抢占多余高度。
2. 改造CommentPage布局,移除滚动组件内的非法Expanded
根据需求二选一即可:
写法1:评论列表滚动、底部输入框固定
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("评论")), body: Column( children: [ Expanded( child: ListView.builder( padding: EdgeInsets.symmetric(vertical: 12), itemCount: commentLi.length, itemBuilder: (context, index) { return Comments(commentList: commentLi); }, ), ), NewMessage() ], ), ); }
写法2:评论和输入框一起整体滚动
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("评论")), body: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ for (int i = 0; i < 3; i++) Comments(commentList: commentLi), Comments(commentList: commentLi), NewMessage() ], ), ), ); }
额外注意点
- 你当前Comments组件里写死了用内部
commentLi的第一条当根评论、前两条当子评论,没有使用传入的widget.commentList参数,后续对接真实接口数据时记得替换成传入的评论数据,不然所有评论树渲染的内容都会重复。 - 所有放在滚动组件(ListView、SingleChildScrollView、GridView等)里的子组件,不要在外层随意套
Expanded,Expanded只适用于有明确高度约束的Flex布局直接子级。
内容的提问来源于stack exchange,提问作者Himanshu Flutter
相关产品推荐
相关产品推荐

