Flutter开发·使用AnimatedWidget系列组件让你的APP动起来

1,878 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

上一篇介绍了Flutter中动画的简单实现,主要是通过监听Animation的大小、颜色等数值变化从而改变组件的变化而不是直接参与组件的渲染。Flutter中也提供了可以直接控制组件变化的带有动画的Widget,这样就避免了再手动调用setState以及添加Listener的繁琐,它就是AnimatedWidget。 下面看几个Flutter官方提供的几个AnimatedWidget。

AnimatedBuilder

通过设置传入的animation参数,可以实现自定义的动画效果,无需再调用setState方法。builder方法用来提供所要构建的组件,builder方法中的child参数可以复用。

@override
void initState() {
  // TODO: implement initState
  super.initState();
  _animationController = AnimationController(
      lowerBound: 0,
      upperBound: 1,
      duration: Duration(milliseconds: 800),
      vsync: this);
  _curvedAnimation =
      CurvedAnimation(parent: _animationController, curve: Curves.bounceIn);
  _animationController.forward();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text("动画"),
    ),
    body: Center(
      child: AnimatedBuilder(
        animation: _curvedAnimation,
        builder: (ctx, child) {
          return Image.asset(
            "assets/image/heart.png",
            width: _curvedAnimation.value * 300,
            height: _curvedAnimation.value * 300,
          );
        },
      ),
    ),
图片

AnimatedContainer

AnimatedContainer包括了几乎Container的所有属性,当属性发生变化时,更新组件状态,AnimatedContainer就会呈现出该属性动画过渡的效果。duration属性用来指定过渡动画的持续时间。

  • 如下为宽度改变的动画:
AnimatedContainer(
  color: Colors.yellow,
  duration: Duration(milliseconds: 300),
  width: width,
  height: width,
),
FlatButton(
    onPressed: () {
      setState(() {
        width =  (width == 300 ? 100 : 300);
      });
    },
    child: Text("变化"))
  • 颜色变化也是同理:
AnimatedContainer(
  color: color,
  duration: Duration(milliseconds: 300),
  width: width,
  height: width,
)
setState(() {
  color =  (color == Colors.yellow ? Colors.red : Colors.yellow);
});
图片
  • 子控件位置变化:Alignment属性
setState(() {
  alignment = alignment == Alignment.bottomRight ? 
  Alignment.topLeft : Alignment.bottomRight;
});
AnimatedContainer(
  alignment: alignment,
  color: color,
  duration: Duration(milliseconds: 300),
  width: width,
  height: width,
  child: Image.asset(
      "assets/image/heart.png",
      width: 50,
      height: 50,
    ),
),
图片
  • 当然也可以将所有属性都写进去,产生组合效果
setState(() {
  alignment = alignment == Alignment.bottomRight ? 
      Alignment.topLeft : Alignment.bottomRight;
  color =  (color == Colors.yellow ? Colors.red : Colors.yellow);
  width =  (width == 300 ? 100 : 300);
});
图片

AnimatedOpacity

Opacity组件的动画过渡,写法和AnimatedContainer一样,指定duration参数和Opacity中的透明度参数即可,这种效果经常可以使用在删除动画中。

AnimatedOpacity(
  duration: Duration(milliseconds: 300),
  opacity: opacity,
  child: Image.asset(
      "assets/image/heart.png",
      width: 50,
      height: 50,
    ),
),
FlatButton(
    onPressed: () {
      setState(() {
        opacity = (opacity == 1 ? 0 : 1);
      });
    },
    child: Text("变化"))
图片

AnimatedCrossFade

AnimatedCrossFade用来实现动画前后组件替换的过渡效果,firstChildsecondChild分别是前后变化的组件,crossFadeState参数用来指定动画结束后显示哪一个组件。

bool first = true;

AnimatedCrossFade(
  duration: Duration(milliseconds: 300),
  secondChild: Container(width:100,height:100,color: Colors.yellow,),
  firstChild: Image.asset(
      "assets/image/heart.png",
      width: 100,
      height: 100,
    ), crossFadeState: first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
),
FlatButton(
    onPressed: () {
      setState(() {
        first = !first;
      });
    },
    child: Text("变化"))
图片

AnimatedDefaultTextStyle

用来实现当textStyle发生变化时的过渡动画。

TextStyle textStyle1 = TextStyle(fontSize: 30,color: Colors.red,fontWeight: FontWeight.bold,);
TextStyle textStyle2 = TextStyle(fontSize: 40,color: Colors.blue,fontWeight: FontWeight.normal);
  
AnimatedDefaultTextStyle(
  duration: Duration(milliseconds: 300),
  style: first ? textStyle1 : textStyle2,
  child: Text("Flutter Test"),
),
FlatButton(
    onPressed: () {
      setState(() {
        first = !first;
      });
    },
    child: Text("变化"))
图片

除了以上举出的几个例子,Flutter中还有很多提供好的继承自AnimatedWidget的动画组件:AnimatedAlign,AnimatedSize,AnimatedList等等。这些组件用起来及其简单,但是所实现的动画效果却可以让你的APP上升一个level,可以平滑的过渡很多生硬的转场效果。