【问题标题】:Multiple controllers with AnimatedBuilder in flutter颤动中带有 AnimatedBuilder 的多个控制器
【发布时间】:2021-09-14 08:00:15
【问题描述】:

我正在寻找一种在 Flutter 中结合重复动画和非重复动画的方法。例如,开始打开动画(不重复),然后显示一些重复的动画,比如弹跳动画。目前,我在一个小部件上使用了 2 个动画控制器和 2 个动画构建器。这是我的示例代码:

   @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: _outerAnimationController, //starting animation, not repetitive
        builder: (context, _) {
          return AnimatedBuilder( 
              animation: _innerCurvedAnimation, //repetitive animation, bouncing
              builder: (context, _) {
                return CustomPaint(
                  size: Size(MediaQuery.of(context).size.width,
                      MediaQuery.of(context).size.height),
                  painter: ShowCasePainter(
                    centerPosition: Offset(
                        MediaQuery.of(context).size.width / 2,
                        MediaQuery.of(context).size.height / 2),
                    innerCircleRadius: widget.innerCircleRadius +
                        (_innerCurvedAnimation.value * PaddingSmall), //repetitive animation value, bouncing
                    outerCircleRadius: _outerAnimationTween.value, //starting animation value, not repetitive
                  ),
                );
              });
        });
  }

以这种方式使用多个控制器是一种好习惯吗?如何从代表不同动画的两个控制器影响 animatedBuilder?

感谢您的帮助!

【问题讨论】:

  • 检查Listenable.mergector
  • 你能给我一些更详细的答案吗?
  • 请发表您的评论作为答案,我会检查它是否正确!
  • 如果Listenable.merge 为您工作,请随意写一个自我回答

标签: flutter flutter-animation


【解决方案1】:

在@pskink 的帮助下,我终于找到了解决方案。这里不需要使用多个 AnimatedBuilder,Listenable.merge 和多个 AnimationController 就足够了。可以合并是因为 AnimationControllers 从 Listenable 类扩展而来。这是正确的代码:

@override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: Listenable.merge(
            [_innerAnimationController, _outerAnimationController]),
        builder: (context, _) {
          return CustomPaint(
            size: Size(MediaQuery.of(context).size.width,
                MediaQuery.of(context).size.height),
            painter: ShowCasePainter(
              centerPosition: Offset(MediaQuery.of(context).size.width / 2,
                  MediaQuery.of(context).size.height / 2),
              innerCircleRadius: widget.innerCircleRadius +
                  (_innerCurvedAnimation.value * PaddingSmall),
              outerCircleRadius: _outerAnimationTween.value,
            ),
          );
        });
  }

此解决方案适用于任何小部件,但使用 CustomPainter 有更好的解决方案。在自定义画家的情况下,animationControllers 可以通过构造函数传递到 CustomPainter 中,并且应该合并此类内部。 父构建方法:

 @override
  Widget build(BuildContext context) {
    return CustomPaint(
      willChange: true,
      size: Size(MediaQuery.of(context).size.width,
          MediaQuery.of(context).size.height),
      painter: ShowCasePainter(
        centerPosition: Offset(MediaQuery.of(context).size.width / 2,
            MediaQuery.of(context).size.height / 2),
        innerCircleRadius: widget.innerCircleRadius,
        outerCircleRadius: widget.outerCircleRadius,
        innerAnimationController: _innerAnimationController,
        outerAnimationController: _outerAnimationController,
      ),
    );
  }

如您所见,我只传递了不可变的值,而不是使用来自控制器/动画的值。 这是 CustomPainter 构造函数:

class ShowCasePainter extends CustomPainter {
  final Offset centerPosition;
  final double innerCircleRadius;
  final double outerCircleRadius;
  final Color backgroundColor;
  final Color ringColor;
  final Animation<double> innerAnimationController;
  final Animation<double> outerAnimationController;
  Animation<double> _innerCurvedAnimation;
  Animation<double> _outerAnimationTween;
  Animation<Color> _backgroudColorTween;
  ShowCasePainter(
      {this.innerAnimationController,
      this.outerAnimationController,
      this.centerPosition,
      this.innerCircleRadius = 32.0,
      this.outerCircleRadius = 128.0,
      this.backgroundColor,
      this.ringColor})
      : super(
            repaint: Listenable.merge(
                [innerAnimationController, outerAnimationController])) {
    _innerCurvedAnimation =
        CurvedAnimation(parent: innerAnimationController, curve: Curves.easeIn);

    _outerAnimationTween =
        Tween(begin: innerCircleRadius, end: outerCircleRadius)
            .animate(outerAnimationController);

    _backgroudColorTween = ColorTween(
            begin: Colors.transparent,
            end: backgroundColor ?? Colors.black.withOpacity(0.2))
        .animate(outerAnimationController);
  }

现在动画可以正常工作了。

【讨论】:

  • 顺便说一句,您不需要 AnimatedBuilder 并为每个动画帧创建一个新的 ShowCasePainter - 而是检查 CustomPainter 构造函数及其 repaint 可选参数 - 这样您只创建一个 ShowCasePainterListenable 触发时,它会“重新绘制”自己
  • 我认为如果你想收听更新,必须有一些观察者之王。我明白你的意思。我需要检查一下。谢谢!))
  • 不幸的是,它不适用于 AnimationControllers。所以,我找到了您的回复:stackoverflow.com/a/58595116/10084055,这不是我要寻找的。此外,我找到了 setState 的解决方案,这是一个更脏的解决方案。例如,在自定义 Painter 类上方的合并可听对象上添加侦听器,并在每个控制器更新时调用 setstate。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-23
相关资源
最近更新 更多