首页 > 解决方案 > 从外部小部件触发小部件动画

问题描述

我有一个具有正常/动画状态的自定义小部件。有时我希望它是动画的,有时是静态的。

我做了一个简单的测试项目来演示我的问题:测试页面包含我的自定义小部件 ( ScoreBoard) 和 2 个按钮来开始/停止动画记分板。我的问题是,记分板没有动画,即使我开始动画。

这是我的代码:

TestPage

class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  bool _animate;

  @override
  void initState() {
    _animate = false;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          ScoreBoard(
            text: "Hello, world!",
            animate: _animate,
          ),
          FlatButton(
            child: Text("START animation"),
            onPressed: () {
              setState(() {
                _animate = true;
              });
            },
          ),
          FlatButton(
            child: Text("STOP animation"),
            onPressed: () {
              setState(() {
                _animate = false;
              });
            },
          ),
        ],
      ),
    );
  }
}

记分板小部件:

class ScoreBoard extends StatefulWidget {
  final String text;
  final bool animate;

  const ScoreBoard({Key key, this.text, this.animate}) : super(key: key);

  @override
  _ScoreBoardState createState() => _ScoreBoardState();
}

class _ScoreBoardState extends State<ScoreBoard>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = new AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    )..forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return widget.animate
        ? ScaleTransition(
            child:
                Text(widget.text, style: Theme.of(context).textTheme.display1),
            scale: new CurvedAnimation(
              parent: _controller,
              curve: Curves.easeIn,
            ),
          )
        : Container(
            child:
                Text(widget.text, style: Theme.of(context).textTheme.display1),
          );
  }
}

你会这么好心来帮助我吗?提前致谢!

标签: flutterwidgetstate

解决方案


回答

如果您初始化一个AnimationController小部件并且没有为lowerBoundand指定参数upperBound(这里就是这种情况),那么您的动画将默认以lowerBound0.0 开始。

AnimationController({double value, Duration duration, String debugLabel, double lowerBound: 0.0, double upperBound: 1.0, AnimationBehavior animationBehavior: AnimationBehavior.normal, @required TickerProvider vsync }) 创建一个动画控制器。[...]

https://docs.flutter.io/flutter/animation/AnimationController-class.html

如果您初始化小部件的状态ScoreBoard,则该方法forward在您的应用程序的整个生命周期中仅被调用一次。该方法forward使您的动画在 1 秒内从lowerBound(0.0) 增加到(1.0)。upperBound

开始向前运行此动画(接近尾声)。

https://docs.flutter.io/flutter/animation/AnimationController/forward.html

forward在我们的例子中,一旦方法被调用,就没有回头路了。我们只能停止动画。

按 Ctrl + F5 完全重启应用程序以查看动画。为了更清楚,将动画的持续时间更改为 10 秒。

顺便提一句。从 Dart 2 开始,您不需要使用new关键字。

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 10),
      vsync: this,
    )..forward();
  }

例子

要查看会发生什么,您可以将其添加到您的build方法中:

  @override
  Widget build(BuildContext context) {
    // Execute 'reverse' if the animation is completed
    if (_controller.isCompleted)
      _controller.reverse();
    else if (_controller.isDismissed)
      _controller.forward();
    // ...

...并且不要在方法forward中调用该initState方法:

  @override
  void initState() {
    super.initState();
    _controller = new AnimationController(
      duration: const Duration(seconds: 10),
      vsync: this,
    );
  }

推荐阅读