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

Flutter双ExpansionTile布局问题:展开时另一个需固定在容器底部

解决方案

要实现一个ExpansionTile展开时另一个固定在SizedBox底部,收起时回到正常排列的效果,你需要通过状态跟踪动态调整布局结构。以下是两种可行的实现方案:

方案一:基于Column的简洁实现(推荐)

这种方案通过StatefulWidget跟踪两个ExpansionTile的展开状态,动态切换是否使用Expanded包裹展开的面板,让未展开的面板自动固定在底部:

class DualExpansionTiles extends StatefulWidget {
  final SuccessState successState;

  const DualExpansionTiles({super.key, required this.successState});

  @override
  State<DualExpansionTiles> createState() => _DualExpansionTilesState();
}

class _DualExpansionTilesState extends State<DualExpansionTiles> {
  bool _devExpanded = false;
  bool _testerExpanded = false;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 500,
      child: Column(
        children: [
          // 开发者面板:展开时占满剩余空间,收起时仅占自身高度
          _devExpanded
              ? Expanded(
                  child: SingleChildScrollView(
                    child: ExpansionTile(
                      title: const Text('developer'),
                      initiallyExpanded: true,
                      onExpansionChanged: (value) {
                        setState(() {
                          _devExpanded = value;
                          // 可选:展开一个时自动收起另一个
                          if (value) _testerExpanded = false;
                        });
                      },
                      children: List.generate(
                        widget.successState.developerList.length,
                        (index) => EmployeeTileWidget(
                            employee: widget.successState.developerList[index]),
                      ),
                    ),
                  ),
                )
              : ExpansionTile(
                  title: const Text('developer'),
                  onExpansionChanged: (value) {
                    setState(() {
                      _devExpanded = value;
                      if (value) _testerExpanded = false;
                    });
                  },
                  children: List.generate(
                    widget.successState.developerList.length,
                    (index) => EmployeeTileWidget(
                        employee: widget.successState.developerList[index]),
                  ),
                ),

          // 两个面板都收起时的间距,还原正常布局
          if (!_devExpanded && !_testerExpanded)
            const SizedBox(height: 16),

          // 测试者面板:展开时占满剩余空间,收起时固定在底部
          _testerExpanded
              ? Expanded(
                  child: SingleChildScrollView(
                    child: ExpansionTile(
                      title: const Text('testerList'),
                      initiallyExpanded: true,
                      onExpansionChanged: (value) {
                        setState(() {
                          _testerExpanded = value;
                          if (value) _devExpanded = false;
                        });
                      },
                      children: List.generate(
                        widget.successState.testerList.length,
                        (index) => EmployeeTileWidget(
                            employee: widget.successState.testerList[index]),
                      ),
                    ),
                  ),
                )
              : ExpansionTile(
                  title: const Text('testerList'),
                  onExpansionChanged: (value) {
                    setState(() {
                      _testerExpanded = value;
                      if (value) _devExpanded = false;
                    });
                  },
                  children: List.generate(
                    widget.successState.testerList.length,
                    (index) => EmployeeTileWidget(
                        employee: widget.successState.testerList[index]),
                  ),
                ),
        ],
      ),
    );
  }
}

方案思路

  1. 用_devExpanded和_testerExpanded两个布尔值跟踪面板的展开状态
  2. 当某个面板展开时,用Expanded包裹它,使其占据SizedBox内的剩余空间,内部通过SingleChildScrollView处理过长内容的滚动
  3. 未展开的面板保持自身高度,在Column布局中会被Expanded组件推到底部,实现固定效果
  4. 两个面板都收起时,添加间距还原正常的上下排列布局

方案二:基于Stack的精准控制

如果需要更精确地控制面板位置(比如展开时未激活的面板始终固定在边缘),可以使用Stack布局:

class DualExpansionTiles extends StatefulWidget {
  final SuccessState successState;

  const DualExpansionTiles({super.key, required this.successState});

  @override
  State<DualExpansionTiles> createState() => _DualExpansionTilesState();
}

class _DualExpansionTilesState extends State<DualExpansionTiles> {
  bool _devExpanded = false;
  bool _testerExpanded = false;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 500,
      child: Stack(
        children: [
          // 展开的面板:占满整个SizedBox空间,内部滚动
          if (_devExpanded)
            Positioned.fill(
              child: SingleChildScrollView(
                child: ExpansionTile(
                  title: const Text('developer'),
                  initiallyExpanded: true,
                  onExpansionChanged: (value) => setState(() => _devExpanded = value),
                  children: List.generate(
                    widget.successState.developerList.length,
                    (index) => EmployeeTileWidget(
                        employee: widget.successState.developerList[index]),
                  ),
                ),
              ),
            ),
          if (_testerExpanded)
            Positioned.fill(
              child: SingleChildScrollView(
                child: ExpansionTile(
                  title: const Text('testerList'),
                  initiallyExpanded: true,
                  onExpansionChanged: (value) => setState(() => _testerExpanded = value),
                  children: List.generate(
                    widget.successState.testerList.length,
                    (index) => EmployeeTileWidget(
                        employee: widget.successState.testerList[index]),
                  ),
                ),
              ),
            ),

          // 未展开状态:正常上下排列
          if (!_devExpanded && !_testerExpanded)
            Column(
              children: [
                ExpansionTile(
                  title: const Text('developer'),
                  onExpansionChanged: (value) {
                    setState(() {
                      _devExpanded = value;
                      _testerExpanded = false;
                    });
                  },
                  children: List.generate(
                    widget.successState.developerList.length,
                    (index) => EmployeeTileWidget(
                        employee: widget.successState.developerList[index]),
                  ),
                ),
                const SizedBox(height: 16),
                ExpansionTile(
                  title: const Text('testerList'),
                  onExpansionChanged: (value) {
                    setState(() {
                      _testerExpanded = value;
                      _devExpanded = false;
                    });
                  },
                  children: List.generate(
                    widget.successState.testerList.length,
                    (index) => EmployeeTileWidget(
                        employee: widget.successState.testerList[index]),
                  ),
                ),
              ],
            ),

          // 开发者面板展开时,测试面板固定在底部
          if (_devExpanded)
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: ExpansionTile(
                title: const Text('testerList'),
                onExpansionChanged: (value) {
                  setState(() {
                    _testerExpanded = value;
                    _devExpanded = false;
                  });
                },
                children: List.generate(
                  widget.successState.testerList.length,
                  (index) => EmployeeTileWidget(
                      employee: widget.successState.testerList[index]),
                ),
              ),
            ),

          // 测试面板展开时,开发者面板固定在顶部
          if (_testerExpanded)
            Positioned(
              top: 0,
              left: 0,
              right: 0,
              child: ExpansionTile(
                title: const Text('developer'),
                onExpansionChanged: (value) {
                  setState(() {
                    _devExpanded = value;
                    _testerExpanded = false;
                  });
                },
                children: List.generate(
                  widget.successState.developerList.length,
                  (index) => EmployeeTileWidget(
                      employee: widget.successState.developerList[index]),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

方案思路

  1. 用Stack分层管理面板:展开的面板在底层占满空间,未展开的面板在顶层固定位置
  2. 通过Positioned精确控制未展开面板的位置(顶部或底部)
  3. 未展开状态下回退到Column布局,保持正常排列

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 10:15:00