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

Flutter中是否有类似AnimatedSize的Sliver尺寸动画组件?

Flutter中实现Sliver尺寸变化动画的方案

Flutter官方并没有提供与AnimatedSize直接对应的Sliver组件,但可以通过以下两种方式实现Sliver的尺寸变化动画:

一、错误原因分析

你之前的代码报错是因为:

  • AnimatedSize仅支持RenderBox类型的普通组件,而Sliver属于RenderSliver类型,直接嵌套会导致类型不匹配;
  • 错误地将SliverList作为AnimatedSize的子组件,Sliver组件只能放在CustomScrollView的slivers列表中,不能作为普通Widget的子节点。

二、方案1:用SliverToBoxAdapter包裹AnimatedSize(适合静态内容)

将需要动画的内容用普通Widget实现,通过AnimatedSize做尺寸动画,再用SliverToBoxAdapter转换为Sliver组件,适用于非滚动的动态高度内容(比如展开/折叠的详情块):

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

  @override
  State<Testingyeye> createState() => _TestingyeyeState();
}

class _TestingyeyeState extends State<Testingyeye> {
  bool _expanded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Testing"),
      ),
      body: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: GestureDetector(
              // 点击切换展开/折叠状态
              onTap: () => setState(() => _expanded = !_expanded),
              child: AnimatedSize(
                duration: const Duration(milliseconds: 500),
                curve: Curves.fastOutSlowIn,
                child: Container(
                  color: Colors.blue[100],
                  // 根据状态切换显示内容
                  child: _expanded
                      ? Column(
                          children: List.generate(10, (index) => Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: Text("hiii $index"),
                          )),
                        )
                      : const SizedBox.shrink(),
                ),
              ),
            ),
          ),
          // 其他Sliver组件
          const SliverFillRemaining(
            child: Center(child: Text("下方内容")),
          ),
        ],
      ),
    );
  }
}

三、方案2:自定义SliverAnimatedSize组件(适合滚动内容)

如果需要给滚动型Sliver(比如列表)做尺寸动画,可以自定义一个基于动画控制器的Sliver组件,动态控制其高度:

自定义组件代码

class SliverAnimatedSize extends StatefulWidget {
  final Widget child;
  final Duration duration;
  final Curve curve;
  final bool isExpanded;

  const SliverAnimatedSize({
    super.key,
    required this.child,
    required this.duration,
    this.curve = Curves.linear,
    required this.isExpanded,
  });

  @override
  State<SliverAnimatedSize> createState() => _SliverAnimatedSizeState();
}

class _SliverAnimatedSizeState extends State<SliverAnimatedSize> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _heightAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration);
    // 动画值从0到无限大(实际会根据子组件内容自适应)
    _heightAnimation = Tween<double>(begin: 0, end: double.infinity).animate(
      CurvedAnimation(parent: _controller, curve: widget.curve)
    );
    // 初始状态如果是展开,直接播放正向动画
    if (widget.isExpanded) _controller.forward();
  }

  @override
  void didUpdateWidget(covariant SliverAnimatedSize oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 状态变化时切换动画方向
    if (widget.isExpanded != oldWidget.isExpanded) {
      widget.isExpanded ? _controller.forward() : _controller.reverse();
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _heightAnimation,
      builder: (context, child) {
        return SliverToBoxAdapter(
          child: SizedBox(
            height: _heightAnimation.value,
            // 子组件需设置shrinkWrap和禁止内部滚动(外层已有CustomScrollView)
            child: child,
          ),
        );
      },
      child: widget.child,
    );
  }
}

使用示例

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

  @override
  State<Testingyeye> createState() => _TestingyeyeState();
}

class _TestingyeyeState extends State<Testingyeye> {
  bool _expanded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Testing"),
      ),
      body: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: TextButton(
              onPressed: () => setState(() => _expanded = !_expanded),
              child: Text(_expanded ? "收起列表" : "展开列表"),
            ),
          ),
          // 使用自定义的SliverAnimatedSize
          SliverAnimatedSize(
            duration: const Duration(milliseconds: 500),
            curve: Curves.fastOutSlowIn,
            isExpanded: _expanded,
            child: ListView.builder(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              itemCount: 15,
              itemBuilder: (context, index) => ListTile(
                title: Text("列表项 $index"),
              ),
            ),
          ),
          const SliverFillRemaining(
            child: Center(child: Text("下方内容")),
          ),
        ],
      ),
    );
  }
}

总结

  • 静态内容优先用方案1,实现简单;
  • 滚动内容用自定义SliverAnimatedSize,灵活控制动画过程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:21:30