You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter使用SliverAppBar时如何去除ExpansionTile多余间距

问题场景

在Flutter开发中使用SliverAppBar,将ExpansionTile作为页面body内容时,展开ExpansionTile后,其subtitle与下方通过ListView.builder构建的子列表之间会出现多余空白间距;相同逻辑替换为Scaffold的普通AppBar时该问题消失,需要在保留SliverAppBar的前提下消除该多余间距。

复现代码

class Test {
  final String name;

  Test(this.name);
}

class SliverAppbarTest extends StatefulWidget {
  const SliverAppbarTest({Key? key}) : super(key: key);

  @override
  State<SliverAppbarTest> createState() => _SliverAppbarTestState();
}

class _SliverAppbarTestState extends State<SliverAppbarTest> {
  List<Test> test = <Test>[
    Test('test 1'),
    Test('test 2'),
    Test('test 3'),
  ];

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.height;
    return Scaffold(
        backgroundColor: backGround,
        body: CustomScrollView(slivers: [
          SliverAppBar(
            expandedHeight: screenWidth / 4.69,
            backgroundColor: appbarColor,
            floating: true,
            pinned: true,
            shape: const ContinuousRectangleBorder(
                borderRadius: BorderRadius.only(
                    bottomLeft: Radius.circular(40),
                    bottomRight: Radius.circular(40))),
            flexibleSpace: FlexibleSpaceBar(
                background: Container(
                    margin: const EdgeInsets.all(25.0),
                    child: Image.asset('assets/images/sliverappbartest.png'))),
          ),
          SliverToBoxAdapter(
            child: Column(children: [
              const SizedBox(
                height: 20,
              ),
              ExpansionTile(
                collapsedTextColor: titleColor,
                collapsedIconColor: titleColor,
                title: const Text('Test'),
                subtitle: const Text(
                  'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren',
                  style: TextStyle(color: Colors.white),
                ),
                children: [TestList(test: test)],
              )
            ]),
          )
        ]));
  }
}

class TestList extends StatefulWidget {
  final List<Test> test;

  const TestList({Key? key, required this.test}) : super(key: key);

  @override
  State<TestList> createState() => _TestListState();
}

class _TestListState extends State<TestList> {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      shrinkWrap: true,
      itemCount: widget.test.length,
      itemBuilder: (context, index) {
        var test = widget.test[index];
        var name = test.name;
        return ListTile(
          title: Text(
            name,
            style: const TextStyle(color: titleColor),
          ),
        );
      },
    );
  }
}

效果对比

  • SliverAppBar场景下的异常效果(存在多余间距):
    SliverAppBar场景下存在多余间距
  • 普通AppBar场景下的正常效果(无多余间距):
    普通AppBar场景下无多余间距
问题原因

多余间距来自两处默认配置的叠加:

  1. ListView组件默认会自动添加匹配当前MediaQuery的内边距,普通AppBar场景下框架会自动消费列表顶部的预留padding,但是在CustomScrollView的Sliver体系中,放在SliverToBoxAdapter内的ListView不会被自动移除该顶部padding,直接显示为空白。
  2. ExpansionTile默认的childrenPadding属性预设了子区域的顶部内边距,和ListView自带的padding叠加后进一步放大了空白间距。
解决方案

不需要替换为普通AppBar,通过以下任意一种修改即可消除多余间距:

  • 方案1:手动移除两个组件的默认内边距
    1. 给ExpansionTile添加childrenPadding: EdgeInsets.zero配置,清除组件自身对子区域的默认内边距
    2. 给内部的ListView.builder添加padding: EdgeInsets.zero配置,清除列表默认携带的MediaQuery内边距,建议同时添加physics: const NeverScrollableScrollPhysics()避免嵌套滚动冲突
      关键修改代码示例:
    // ExpansionTile修改
    ExpansionTile(
      collapsedTextColor: titleColor,
      collapsedIconColor: titleColor,
      childrenPadding: EdgeInsets.zero, // 新增
      title: const Text('Test'),
      subtitle: const Text(
        'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren',
        style: TextStyle(color: Colors.white),
      ),
      children: [TestList(test: test)],
    )
    
    // ListView修改
    ListView.builder(
      shrinkWrap: true,
      padding: EdgeInsets.zero, // 新增
      physics: const NeverScrollableScrollPhysics(), // 新增,解决嵌套滚动冲突
      itemCount: widget.test.length,
      itemBuilder: (context, index) {
        var test = widget.test[index];
        var name = test.name;
        return ListTile(
          title: Text(
            name,
            style: const TextStyle(color: titleColor),
          ),
        );
      },
    )
    
  • 方案2:通过MediaQuery移除列表继承的顶部padding
    如果不想修改ListView自身配置,可以用MediaQuery.removePadding包裹列表组件,移除上下文中继承的顶部padding,同时给ExpansionTile设置childrenPadding: EdgeInsets.zero即可:
    ExpansionTile(
      // 其他配置不变
      childrenPadding: EdgeInsets.zero,
      children: [
        MediaQuery.removePadding(
          context: context,
          removeTop: true,
          child: TestList(test: test),
        )
      ],
    )
    

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:54:18