概述


前几篇文章我们都是使用Flutter中的AnimationController、Animation以及Tween来实现我们的动画效果,如果我们只想实现一些简单动画,该怎么办呢?今天我们就一起了解Flutter中内置的动画组件.接下来,我们就来分类来看一下Futter内置的动画组件.



  • 需要外部Animation支持
Animation组件功能
DecoratedBoxTransitionDecoratedBox的动画版本,可以给它的Decoration不同属性使用动画
FadeTransition对透明度使用动画的widget
PositionedTransitionPositioned的动画版本,它需要一个特定的动画来将孩子的位置从动画的生命周期的起始位置移到结束位置。
RotationTransition对widget使用旋转动画
ScaleTransition对widget使用缩放动画
SizeTransition对widget使用尺寸相关动画
SlideTransition对相对于其正常位置的某个位置之间使用动画


  • 不需要外部Animation支持
Animation组件功能
AnimatedPadding在padding发生变化时会执行过渡动画到新状态
AnimatedPositioned配合Stack一起使用,当定位状态发生变化时会执行过渡动画到新的状态。
AnimatedOpacityOpacity的动画版本,在给定的透明度变化时,自动地在给定的一段时间内改变child的Opacity
AnimatedAlign当alignment发生变化时会执行过渡动画到新的状态。
AnimatedContainer当Container属性发生变化时会执行过渡动画到新的状态。
AnimatedDefaultTextStyle当字体样式发生变化时,子组件中继承了该样式的文本组件会动态过渡到新样式。

本篇文章大部分示例直接摘抄官方,具体可去 动画&Motion Widget 查询.(PS:写不动了,着实写不动了. 😂 😂 😂 )


需要外部Animation支持的组件示例



DecoratedBoxTransition示例

final DecorationTween decorationTween = DecorationTween(
  begin: BoxDecoration(
    color: const Color(0xFFFFFFFF),
    border: Border.all(style: BorderStyle.none),
    borderRadius: BorderRadius.circular(60.0),
    shape: BoxShape.rectangle,
    boxShadow: const <BoxShadow>[
      BoxShadow(
        color: Color(0x66666666),
        blurRadius: 10.0,
        spreadRadius: 3.0,
        offset: Offset(0, 6.0),
      )
    ],
  ),
  end: BoxDecoration(
    color: const Color(0xFFFFFFFF),
    border: Border.all(
      style: BorderStyle.none,
    ),
    borderRadius: BorderRadius.zero,
    // No shadow.
  ),
);

late AnimationController _controller = AnimationController(
  vsync: this,
  duration: const Duration(seconds: 3),
)..repeat(reverse: true);

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

 @override
 Widget build(BuildContext context) {
   return Container(
     color: Colors.white,
     child: Center(
       child: DecoratedBoxTransition(
         position: DecorationPosition.background,
         decoration: decorationTween.animate(_controller),
         child: Container(
           width: 200,
           height: 200,
           padding: const EdgeInsets.all(10),
           child: const FlutterLogo(),
         ),
       ),
     ),
   );
 }

FadeTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.easeIn,
);

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

@override
Widget build(BuildContext context) {
  return Container(
    color: Colors.white,
    child: FadeTransition(
      opacity: _animation,
      child: const Padding(
        padding: EdgeInsets.all(8),
        child: FlutterLogo()
      ),
    ),
  );
}

PositionedTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);

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

@override
Widget build(BuildContext context) {
  final double smallLogo = 100;
  final double bigLogo = 200;

  return LayoutBuilder(
    builder: (context, constraints) {
      final Size biggest = constraints.biggest;
      return Stack(
        children: [
          PositionedTransition(
            rect: RelativeRectTween(
              begin: RelativeRect.fromSize(Rect.fromLTWH(0, 0, smallLogo, smallLogo), biggest),
              end: RelativeRect.fromSize(Rect.fromLTWH(biggest.width - bigLogo, biggest.height - bigLogo, bigLogo, bigLogo), biggest),
            ).animate(CurvedAnimation(
              parent: _controller,
              curve: Curves.elasticInOut,
            )),
            child: Padding(
              padding: const EdgeInsets.all(8),
              child: FlutterLogo()
            ),
          ),
        ],
      );
    },
  );
}

RotationTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.elasticOut,
);

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

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: RotationTransition(
        turns: _animation,
        child: const Padding(
          padding: EdgeInsets.all(8.0),
          child: FlutterLogo(size: 150.0),
        ),
      ),
    ),
  );
}

ScaleTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.fastOutSlowIn,
);

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

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: ScaleTransition(
        scale: _animation,
        child: const Padding(
          padding: EdgeInsets.all(8.0),
          child: FlutterLogo(size: 150.0),
        ),
      ),
    ),
  );
}

SizeTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 3),
  vsync: this,
)..repeat();
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.fastOutSlowIn,
);

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

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: SizeTransition(
      sizeFactor: _animation,
      axis: Axis.horizontal,
      axisAlignment: -1,
      child: Center(
          child: FlutterLogo(size: 200.0),
      ),
    ),
  );
}

SlideTransition示例

class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin {
  late AnimationController _controller = AnimationController(
    duration: const Duration(seconds: 2),
    vsync: this,
  )..repeat(reverse: true);
  late Animation<Offset> _offsetAnimation = Tween<Offset>(
    begin: Offset.zero,
    end: const Offset(1.5, 0.0),
  ).animate(CurvedAnimation(
    parent: _controller,
    curve: Curves.elasticIn,
  ));

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

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _offsetAnimation,
      child: const Padding(
        padding: EdgeInsets.all(8.0),
        child: FlutterLogo(size: 150.0),
      ),
    );
  }
}

不需要外部Animation支持组件示例



AnimatedPadding示例

double padValue = 0.0;
_updatePadding(double value) {
  setState(() {
    padValue = value;
  });
}

@override
Widget build(BuildContext context) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      AnimatedPadding(
        padding: EdgeInsets.all(padValue),
        duration: const Duration(seconds: 2),
        curve: Curves.easeInOut,
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height / 5,
          color: Colors.blue,
        ),
      ),
      Text('Padding: $padValue'),
      ElevatedButton(
        child: Text('Change padding'),
        onPressed: () {
          _updatePadding(padValue == 0.0 ? 100.0 : 0.0);
        }
      ),
    ],
  );
}

AnimatedPositioned示例

bool selected = false;

@override
Widget build(BuildContext context) {
  return Container(
    width: 200,
    height: 350,
    child: Stack(
      children: [
        AnimatedPositioned(
          width: selected ? 200.0 : 50.0,
          height: selected ? 50.0 : 200.0,
          top: selected ? 50.0 : 150.0,
          duration: Duration(seconds: 2),
          curve: Curves.fastOutSlowIn,
          child: GestureDetector(
            onTap: () {
              setState(() {
                selected = !selected;
              });
            },
            child: Container(
              color: Colors.blue,
              child: Center(child: Text('Tap me')),
            ),
          ),
        ),
      ],
    ),
  );
}

AnimatedOpacity示例

class LogoFade extends StatefulWidget {
  @override
  createState() => LogoFadeState();
}

class LogoFadeState extends State<LogoFade> {
  double opacityLevel = 1.0;

  void _changeOpacity() {
    setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedOpacity(
          opacity: opacityLevel,
          duration: Duration(seconds: 3),
          child: FlutterLogo(),
        ),
        ElevatedButton(
          child: Text('Fade Logo'),
          onPressed: _changeOpacity,
        ),
      ],
    );
  }
}

AnimatedAlign示例

bool selected = false;

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () {
      setState(() {
        selected = !selected;
      });
    },
    child: Center(
      child: Container(
        width: 250.0,
        height: 250.0,
        color: Colors.red,
        child: AnimatedAlign(
          alignment: selected ? Alignment.topRight : Alignment.bottomLeft,
          duration: const Duration(seconds: 1),
          curve: Curves.fastOutSlowIn,
          child: const FlutterLogo(size: 50.0),
        ),
      ),
    ),
  );
}

AnimatedContainer示例

bool selected = false;

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () {
      setState(() {
        selected = !selected;
      });
    },
    child: Center(
      child: AnimatedContainer(
        width: selected ? 200.0 : 100.0,
        height: selected ? 100.0 : 200.0,
        color: selected ? Colors.red : Colors.blue,
        alignment: selected ? Alignment.center : AlignmentDirectional.topCenter,
        duration: Duration(seconds: 2),
        curve: Curves.fastOutSlowIn,
        child: FlutterLogo(size: 75),
      ),
    ),
  );
}

AnimatedDefaultTextStyle示例

  var duration = Duration(seconds: 5);

  TextStyle _style = TextStyle(color: Colors.black);

  @override
  Widget build(BuildContext context) {
    return AnimatedDefaultTextStyle(
      child: GestureDetector(
        child: Text("hello world"),
        onTap: () {
          setState(() {
            _style = TextStyle(
              color: Colors.blue,
              decorationStyle: TextDecorationStyle.solid,
              decorationColor: Colors.blue,
            );
          });
        },
      ),
      style: _style,
      duration: duration,
    );
  }       

结语


整体上来说,如果是实现比较简单的动画可以直接使用Flutter内置的动画组件,可以大大减少代码量.本篇文章算是一个记录吧,如果深入,可自行查看下面的参考内容.

欢迎持续关注骚栋,有任何问题欢迎联系骚栋.

动画&Motion Widget
动画过渡组件


IT界无底坑洞栋主 欢迎加Q骚扰:676758285