使用Flutter开发APP时,一般都会遇到的一个问题:是嵌套滚动视图(nested scrollable views)。比如有两个可滚动区域:外部的 CustomScrollView 和内部 TabBarView 中的 SingleChildScrollView。这导致了滚动手势被内部的滚动区域"吞噬",无法传递到外部。
下面是 Claude3.7 给出的几种解决方案可以考虑(有些不靠谱,慎用!):
在内部滚动视图上使用 NeverScrollableScrollPhysics(),让内容展开到其完整高度:
javaWidget _buildDescriptionTab() {
  return Card(
    color: Colors.white,
    margin: const EdgeInsets.all(8),
    elevation: 1,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    child: Container(
      child: SingleChildScrollView(
        physics: NeverScrollableScrollPhysics(), // 禁用内部滚动
        padding: const EdgeInsets.all(8),
        child: HtmlWidget(
          _work.content ?? "",
          textStyle: const TextStyle(fontSize: 16),
        ),
      ),
    ),
  );
}
然后移除 SizedBox 的固定高度,让内容决定其高度:
javaSliverToBoxAdapter _buildTabContent() {
  return SliverToBoxAdapter(
    child: TabBarView(
      controller: _tabController,
      children: [
        _buildDescriptionTab(),
        _buildPaidContentTab(),
        if (_work.fileList != null && _work.fileList!.isNotEmpty)
          _buildAttachmentsTab(),
      ],
    ),
  );
}
更好的解决方案是重构代码,使用专为处理嵌套滚动而设计的 NestedScrollView:
javaWidget build(BuildContext context) {
  return Scaffold(
    body: NestedScrollView(
      headerSliverBuilder: (context, innerBoxIsScrolled) {
        return [
          _buildAppBar(),
          _buildWorkInfo(),
          _buildTabBar(),
        ];
      },
      body: TabBarView(
        controller: _tabController,
        children: [
          _buildScrollableDescriptionTab(),
          _buildScrollablePaidContentTab(),
          if (_work.fileList != null && _work.fileList!.isNotEmpty)
            _buildScrollableAttachmentsTab(),
        ],
      ),
    ),
  );
}
Widget _buildScrollableDescriptionTab() {
  return ListView(
    padding: const EdgeInsets.all(8),
    children: [
      Card(
        color: Colors.white,
        elevation: 1,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        child: Padding(
          padding: const EdgeInsets.all(8),
          child: HtmlWidget(
            _work.content ?? "",
            textStyle: const TextStyle(fontSize: 16),
          ),
        ),
      ),
      // 这里可以添加集合列表和相关作品
      if (_work.collectionList != null && _work.collectionList!.isNotEmpty)
        _buildCollectionListContent(),
      if (_relatedWorks.isNotEmpty) 
        _buildRelatedWorksContent(),
    ],
  );
}
javaWidget build(BuildContext context) {
  return Scaffold(
    body: CustomScrollView(
      slivers: [
        _buildAppBar(),
        _buildWorkInfo(),
        _buildTabBar(),
        SliverFillRemaining(
          child: TabBarView(
            controller: _tabController,
            children: [
              _buildScrollableDescriptionTab(),
              _buildScrollablePaidContentTab(),
              if (_work.fileList != null && _work.fileList!.isNotEmpty)
                _buildScrollableAttachmentsTab(),
            ],
          ),
        ),
      ],
    ),
  );
}
考虑到你的需求,我建议使用解决方案 2(NestedScrollView),因为:
这将需要重构部分代码,但能提供最佳的用户体验,避免滚动中断的问题。如果重构成本太高,可以先尝试解决方案 1 作为临时解决方法。
本文作者:DingDangDog
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!