在 Flutter 中,您如何制作动画以将容器从 0 高度扩展到其内容的高度?

Posted

技术标签:

【中文标题】在 Flutter 中,您如何制作动画以将容器从 0 高度扩展到其内容的高度?【英文标题】:How do you animate to expand a container from 0 height to the height of its contents in Flutter? 【发布时间】:2021-06-12 21:25:00 【问题描述】:

我有一个从零高度开始的容器,需要在用户交互后展开。

我尝试使用 AnimatedContainer / AnimatedSize 并将子小部件的高度从 0 更改为 null,但在这两种情况下,Flutter 都抱怨它无法从 0 插入到 null。 我还尝试使用 BoxConstraints(使用 maxHeight = double.infinity 扩展)而不是显式高度,在这种情况下,Flutter 抱怨它无法从有限值内插到不定值。 我也尝试将 mainAxisSize 设置为 min/max,在这种情况下 Flutter 抱怨 vsyncnull

如何以动画方式扩展小部件,使其动态增长到足以包裹其内容?如果这不能动态完成,那么有什么安全的方法来调整内容大小以使它们在不同的屏幕尺寸上都有意义?在 web 开发中,我知道像 em 这样的东西是相对大小的,但是在 Flutter 的上下文中,我不知道如何可靠地控制事物的大小。


更新:正如@pskink 所建议的,将孩子包装在 Align 小部件中并为 Align 的 heightFactor 参数设置动画即可完成折叠。但是,当崩溃的孩子本身有孩子时,我仍然无法崩溃工作。例如,Column 小部件根本不使用 ClipRect 进行剪辑(请参阅https://github.com/flutter/flutter/issues/29357),即使我使用 Wrap 而不是 Column,如果 Wrap 的子项是 Rows,那也不起作用。不知道如何让剪辑始终如一地工作。

【问题讨论】:

检查 expansion_tile.dart 源文件 - 它使用了 Align.heightFactor 来制造技巧 @pskink 它有点工作,但即使使用 ClipRect 包装它也不会剪裁。你是怎么剪辑的? @pskink ClipRect 不适用于列/换行/行 我不明白你的意思 github.com/flutter/flutter/issues/29357 我的意思是它不会剪切列/换行/行 【参考方案1】:

将@pskink 的 cmets 移至后代的答案:

主要概念是Align 小部件有一个名为heightFactor 的属性,它采用0 和1 之间的双精度来缩放其子级的高度(还有一个类似的widthFactor 属性用于宽度)。通过动画这个属性,我们可以折叠/展开孩子。例如:

ClipRect(
      child: Align(
        alignment: alignment,
        child: Align(
          alignment: innerAlignment,
          widthFactor: constantValue,
          heightFactor: animatedValue.value,
          child: builder(context, animation),
        ),
      )
)

其中animatedValueAnimation<double> 类型,ClipReact 用于剪辑/截断子小部件。请注意,ClipReact 需要包裹在 外部 Align 小部件;包装 Align 的子小部件时,它不能始终如一地工作。

编辑:动画的接收者也必须是 AnimatedWidget 才能使事情顺利进行。请参阅所选答案以了解为您处理此问题的方法。

【讨论】:

【参考方案2】:

也许您也可以通过SizeTransition 解决这个问题?

class VariableSizeContainerExample extends StatefulWidget 
  VariableSizeContainerExample();

  @override
  _VariableSizeContainerExampleState createState() => _VariableSizeContainerExampleState();


class _VariableSizeContainerExampleState extends State<VariableSizeContainerExample> with TickerProviderStateMixin 
  AnimationController _controller;
  Animation<double> _animation;

  @override
  void initState() 
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.fastLinearToSlowEaseIn,
    );
  

  _toggleContainer() 
    print(_animation.status);
    if (_animation.status != AnimationStatus.completed) 
      _controller.forward();
     else 
      _controller.animateBack(0, duration: Duration(seconds: 1));
    
  

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Column(
            children: [
              TextButton(
                onPressed: () => _toggleContainer(),
                child: Text("Toggle container visibility"),
              ),
              SizeTransition(
                sizeFactor: _animation,
                axis: Axis.vertical,
                child: Container(
                  child: Text(
                    "This can have variable size",
                    style: TextStyle(fontSize: 40),
                  ),
                ),
              ),
              Text("This is below the above container"),
            ],
          ),
        ),
      ),
    );
  

【讨论】:

这很棒! @pskink 的答案也有效,但需要更多接线。这个以一种直接的方式完全符合我的要求。我还想提一下,如果有人希望它从底部(而不是向中间)折叠,您可以将 axisAlignment: -1 添加到 SizeTransition 参数

以上是关于在 Flutter 中,您如何制作动画以将容器从 0 高度扩展到其内容的高度?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 中使用 Android 滚动行为为容器移动设置动画?

Flutter: Animation - 如何制作动画列表?

如何在 Flutter 中制作密码验证动画

如何在颤动中添加主题切换动画?

如何在 Flutter 中为容器的底部边框设置动画?

Flutter - 动画列表中容器的消失