DraggableScrollableSheet内嵌TabBarView报ParentDataWidget错误如何解决
实现目标
在DraggableScrollableSheet组件内实现粘滞效果的TabBar,最终达成与参考动效一致的滚动交互体验。
参考动效:
问题现象
页面运行时,每次触发滚动都会抛出Incorrect use of ParentDataWidget.错误。曾尝试调整组件结构:将Container包裹的TabBar、Expanded包裹的TabBarView移出StickyHeader组件,外层改用Column包裹,仍无法解决报错。
当前临时规避方案是给包裹Expanded的Container设置2000的固定高度,但不符合业务需求:TabBarView不能设置固定高度,需要支持内容自适应的无限滚动;如果去掉固定高度仅保留Expanded,就会直接触发上述ParentDataWidget相关错误。
报错根因
Incorrect use of ParentDataWidget报错的直接原因是Expanded组件使用不符合规则:
- Expanded是Flex体系的专属子组件,必须作为
Column/Row/Flex的直接子节点才能正常生效 - 原代码将Expanded作为Container的子节点放在StickyHeader的content属性下,上层没有Flex类父组件,无法识别Expanded传递的ParentData,必然触发报错
- 额外结构问题:TabBarView本身需要父组件传递明确的纵向高度约束,原代码将其放在ListView(传递无限高度约束的滚动组件)内部,即使修复Expanded位置,也会出现高度计算冲突,无法实现自适应高度滚动。
修复方案
- 移除所有错误位置的Expanded组件,从根源解决ParentData不匹配问题
- 放弃ListView+第三方StickyHeader的嵌套结构,直接通过Column布局划分区域,Expanded仅放在Column的直接子节点位置给TabBarView提供合法高度约束
- TabBarView内部的滚动列表直接复用
DraggableScrollableSheet传递的滚动控制器,实现滑动联动,无需给TabBarView写死固定高度,天然支持内容自适应的无限滚动
修复后可直接运行的代码
return Scaffold( body: Stack( children: [ Container( height: double.infinity, width: width, decoration: BoxDecoration( color: redMain, ), ), DraggableScrollableSheet( initialChildSize: 0.82, minChildSize: 0.82, builder: (BuildContext context, myScrollController) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20)), color: Colors.white, ), child: Column( children: [ // 顶部非粘滞内容区 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 34), child: Text('Featured', style: Title1), ), Container( color: Colors.yellow, height: 100, margin: EdgeInsets.only(bottom: 22), ), ], ), // 粘滞TabBar TabBar( controller: _tabController, labelColor: Colors.red, unselectedLabelColor: Colors.grey, tabs: [ Tab(child: Text("All")), Tab(child: Text("News")), ], ), // TabBarView占满剩余高度,无需写死固定值 Expanded( child: TabBarView( controller: _tabController, children: <Widget>[ // 第一个Tab页,内容自适应滚动 ListView.builder( controller: myScrollController, padding: EdgeInsets.zero, itemCount: 100, itemBuilder: (context, index) => Container( width: double.infinity, color: index.isEven ? Colors.green[100] : Colors.green, height: 80, alignment: Alignment.center, child: Text('All 标签页第$index项'), ), ), // 第二个Tab页,内容自适应滚动 ListView.builder( controller: myScrollController, padding: EdgeInsets.zero, itemCount: 100, itemBuilder: (context, index) => Container( width: double.infinity, color: index.isEven ? Colors.blue[100] : Colors.blue, height: 80, alignment: Alignment.center, child: Text('News 标签页第$index项'), ), ), ], ), ) ], ), ); }, ), ], ), );
如果需要实现「滚动过顶部内容区后TabBar才吸顶」的效果,只需要把Column结构替换为NestedScrollView,将顶部非吸顶内容放在
headerSliverBuilder中,把TabBar封装为pinned: true的SliverPersistentHeader,body中放置TabBarView即可,同样不会触发ParentData报错,也不需要写死固定高度。
内容的提问来源于stack exchange,提问作者jammaslamma
相关产品推荐
相关产品推荐

