조용한 담장

Flutter : Animation 본문

Flutter

Flutter : Animation

iosroid 2020. 2. 7. 18:04

Animation library

Flutter 의 animation system 을 구현한 animation library 을 살펴보자.

import package:flutter/animation.dart

flutter 는 animation 을 값의 변화와 시간을 통해 표현한다.

Flutter represents an animation as a value that changes over a given duration, and that value may be of any type.

animation 의 시작의 값이 0 이라면 끝나는 값이 100 이라는 수의 값으로 표현한다.

이 변화되는 값은 다양한 속성에 적용되어 그 변화에 따른 animation 을 만들어 낸다.

예를들어 opacity(불투명도) 값을 사용한다면 1.0 에서 0.0 으로 값이 변할수록 투명해지는 효과가 생기게 된다.

그리고 지정된 시간의 값(duration) 위에서 animation 을 구현해 낸다.

Animation class

animation 의 현재 값을 Animation class 로 표현한다.

Animation<T>

flutter 의 animation system 은 Animation class 를 기반으로 구성되어 있다.

T 타입의 값(value)을 가지고 있다. animation 의 현재를 표현하는 값 이다.

animation 의 현재 상태를 나타내는 status 를 가지고 있다.

AnimationStatus enum 타입으로 현재 animation 이 진행되고 있는 방향(forward, reverse), 상태(completed, dismissed) 등을 표현한다.

addListener, addStatusListener 을 통해 value 와 status 의 값의 변화를 알려주는 listener 를 등록할 수 있다.

Simple animation example

간단한 코드 구현을 통해 값의 변화를 통한 animation 동작을 살펴보자.

AnimatedOpacity 는 0.0 ~ 1.0 의 값의 범위로 위젯의 불투명도를 변경하여 animation 을 만들어 낸다.

AnimatedOpacity

"Fade Out" 버튼을 누르면 opacity 값이 0.0, "Face In" 버튼을 누르면 1.0 으로 값을 변경한다.

AnimatedOpacity 는 현재의 opacity 값이 변경된 값과 다른것을 감지하면 변경된 값으로 duration(1초) 시간위에서 animation 을 만들어 낸다.

AnimatedOpacity(
    opacity: opacityValue,
    duration: Duration(seconds: 1),
    child: FlutterLogo(
        size: 100.0,
    ),
),

opacity 로 지정된 opacityValue 값이 변경되면 duration 시간 동안 child 위젯에 animation 효과를 만든다.

class _MyHomePageState extends State<MyHomePage> {
  var opacityValue = 1.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'AnimatedOpacity',
            ),
            AnimatedOpacity(
              opacity: opacityValue,
              duration: Duration(seconds: 1),
              child: FlutterLogo(
                size: 100.0,
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                RaisedButton(
                    child: Text("Fade In"),
                    onPressed: () => setState(() {
                          opacityValue = 1.0;
                        })),
                RaisedButton(
                    child: Text("Fade Out"),
                    onPressed: () => setState(() {
                          opacityValue = 0.0;
                        })),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

AnimatedOpacity 는 정해진 범위내에서 animation 을 만들어 주는 기능을 제공하지만 조금 더 복잡한 animation 을 만드려면 AnimationController 를 사용할 수 있다.

AnimationController

AnimationController

AnimationController({
    double value, Duration duration, Duration reverseDuration, String debugLabel, 
    double lowerBound: 0.0, double upperBound: 1.0, 
    AnimationBehavior animationBehavior: AnimationBehavior.normal, 
    @required TickerProvider vsync})

Animation 이 abstract class 이고 AnimationController 는 그 구현자(implementer) 중 하나 이다.

기본적으로 0.0 ~ 1.0 의 값의 범위를 가지고 아래와 같은 기능을 지원한다.

  • animation 을 앞(forward), 뒤로(reverse) 재생하거나 멈출(stop) 수 있다.
  • animation 에 특정 값을 지정할 수 있다.
  • animation 의 upperBound 와 lowerBound 값을 지정할 수 있다.
  • physics simulation 을 사용하여 fling animation effect 를 만들 수 있다.

Animation 의 implementer 인 CurvedAnimation 을 사용하여 AnimationController 를 사용해본 예제이다.

AnimationController

class _AnimationControllerPageState extends State<AnimationControllerPage> with SingleTickerProviderStateMixin {
  Animation<double> _animation;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeIn,
    );
    _animation.addListener(() {
      setState(() {
      });
    });
  }

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

initState 에서 AnimationController 를 생성하고 CurvedAnimationCurve 효과를 적용할 animation 대상을 parent 로 지정해 준다.

listener 를 추가하여 value 가 변경될 때 마다 UI 를 업데이트 하도록 한다.

사용이 완료되면 dispose 를 해줘야 한다.

Text(
  'AnimationController value:${_animation.value.toStringAsFixed(2)}',
),
Container(
  height: _animation.value*100,
  width: _animation.value*100,
  child: FlutterLogo(),
),
Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
    RaisedButton(
      child: Text("forward"),
      onPressed: () => _controller.forward(),
    ),
    RaisedButton(
      child: Text("stop"),
      onPressed: () => _controller.stop(),
    ),
    RaisedButton(
      child: Text("reverse"),
      onPressed: () => _controller.reverse(),
    ),
  ],
),

Text 로 animation 의 value 를 출력한다.

value 값의 변화에 따라 Container 의 크기가 변하도록 값을 지정한다.

AnimationController 의 method 를 통해 animation 의 status 를 변경하는 버튼을 추가했다.

ImplicitlyAnimatedWidget and AnimatedWidget

다시 animation library 로 돌아가서 flutter 의 animation 위젯은 크게 ImplicitlyAnimatedWidgetAnimatedWidget 이 있다.

ImplicitlyAnimatedWidget 은 위의 예제에서 사용한 AnimatedOpacity  처럼 특정 속성값의 변경에 따라 animation 을 자동으로 만들어 주는 위젯이다.

widget tree 에 처음 추가되었을 때는 아무것도 안하고, 속성값이 변경되어 재빌드 되면 animation 을 만든다.

AnimationController 를 내부적으로 자동으로 만들어 animation 을 관리하므로 durationcurve 만 설정하여 간편하게 사용할 수 있다.

보통 위젯 "Foo" 의 implicitly animated widget 이라는 개념으로 "AnimatedFoo" 식으로 이름을 가진다.

AnimatedWidgetListenable 위젯을 통해 값의 변화를 감지하여 재빌드 한다.

Listenable 은 보통 Animation 위젯이며 ChangeNotifierValueNotifier 도 사용할 수 있다.

AnimatedWidget 위젯들은 위 예제에서 사용한 AnimationController 을 통해 AnimationListenable 위젯 으로써 argument 로 받고, 이를 통해 변화를 감지하여 animation 을 관리하게 되는 방식으로 많이 사용된다.

AnimationController  을 명시적으로 사용하므로 생성과 삭제또한 명시적으로 구현해 주어야 한다.

보통 "Foo" 위젯의 animated widget 이라는 개념으로 "FooTransition" 식으로 이름을 가진다.

ImplicitlyAnimatedWidget class

ImplicitlyAnimatedWidget

ImplicitlyAnimatedWidget({
    Key key, Curve curve: Curves.linear, 
    @required Duration duration, VoidCallback onEnd})

AnimatedAlign

AnimatedAlign

AnimatedAlign({
    Key key, @required AlignmentGeometry alignment, Widget child, 
    Curve curve: Curves.linear, @required Duration duration, VoidCallback onEnd})
AlignmentGeometry _alignment = Alignment.bottomLeft;
// ...
Container(
    width: 200.0,
    height: 200.0,
    color: Colors.green[100],
    padding: EdgeInsets.zero,
    child: AnimatedAlign(
        child: FlutterLogo(size: 100),
        alignment: _alignment,
        duration: Duration(seconds: 1),
        curve: Curves.easeIn,
    ),
),
RaisedButton(
    child: Text("AnimatedAlign Start"),
    onPressed: () => setState(() {
        _alignment = _alignment==Alignment.bottomLeft?Alignment.topRight:Alignment.bottomLeft;
    }),
),

AnimatedAlign

AnimatedContainer

AnimatedContainer

AnimatedContainer({
    Key key, AlignmentGeometry alignment, EdgeInsetsGeometry padding, Color color, 
    Decoration decoration, Decoration foregroundDecoration, 
    double width, double height, 
    BoxConstraints constraints, EdgeInsetsGeometry margin, Matrix4 transform, 
    Widget child, Curve curve: Curves.linear, 
    @required Duration duration, VoidCallback onEnd})
bool _trigger = false;
// ...
Container(
    color: Colors.green[100],
    padding: EdgeInsets.zero,
    child: AnimatedContainer(
        width: _trigger ? 200.0 : 100.0,
        height: _trigger ? 100.0 : 200.0,
        child: FlutterLogo(size: 50),
        alignment: _trigger ? Alignment.topCenter : Alignment.bottomCenter,
        duration: Duration(seconds: 1),
        curve: Curves.easeIn,
    ),
),
RaisedButton(
    child: Text("AnimatedContainer Start"),
    onPressed: () => setState(() {
        _trigger = !_trigger;
    }),
),

AnimatedContainer

AnimatedDefaultTextStyle

AnimatedDefaultTextStyle

AnimatedDefaultTextStyle({
    Key key, @required Widget child, @required TextStyle style, TextAlign textAlign, 
    bool softWrap: true, TextOverflow overflow: TextOverflow.clip, int maxLines, 
    Curve curve: Curves.linear, @required Duration duration, VoidCallback onEnd})
bool _trigger = false;

TextStyle textStyle() {
    return _trigger
        ? TextStyle(
        	color: Colors.blue,
        	fontWeight: FontWeight.w900,
        	fontSize: 20)
        : TextStyle(
        	color: Colors.black,
        	fontWeight: FontWeight.w400,
        	fontSize: 30);
}
// ...
Container(
    padding: EdgeInsets.zero,
    child: AnimatedDefaultTextStyle(
        child: Text("Default Text Style"),
        style: textStyle(),
        duration: Duration(seconds: 1),
        curve: Curves.easeIn,
    ),
),
RaisedButton(
    child: Text("AnimatedDefaultTextStyle Start"),
    onPressed: () => setState(() {
        _trigger = !_trigger;
    }),
),

AnimatedDefaultTextStyle

AnimatedPadding

AnimatedPadding

AnimatedPadding({
    Key key, @required EdgeInsetsGeometry padding, Widget child, 
    Curve curve: Curves.linear, @required Duration duration, VoidCallback onEnd})
bool _trigger = false;
// ...

Container(
    width: 100.0,
    height: 100.0,
    color: Colors.red,
    child: AnimatedPadding(
        child: Container(
            color: Colors.black,
        ),
        padding: _trigger
        	? EdgeInsets.all(5.0)
        	: EdgeInsets.symmetric(horizontal: 30.0, vertical: 10.0),
        duration: Duration(seconds: 1),
        curve: Curves.easeIn,
    ),
),
RaisedButton(
    child: Text("AnimatedPadding Start"),
    onPressed: () => setState(() {
        _trigger = !_trigger;
    }),
),

AnimatedPadding

AnimatedPhysicalModel

AnimatedPhysicalModel

AnimatedPhysicalModel({
    Key key, @required Widget child, @required BoxShape shape, 
    Clip clipBehavior: Clip.none, BorderRadius borderRadius: BorderRadius.zero, 
    @required double elevation, @required Color color, bool animateColor: true, 
    @required Color shadowColor, bool animateShadowColor: true, 
    Curve curve: Curves.linear, @required Duration duration, VoidCallback onEnd})
bool _trigger = true;
//...
AnimatedPhysicalModel(
    child: Container(
        height: 100.0,
        width: 100.0,
    ),
    shape: BoxShape.rectangle,
    borderRadius: _trigger ? BorderRadius.circular(20.0) : BorderRadius.zero,
    elevation: _trigger ? 10.0 : 20.0,
    color: _trigger? Colors.black : Colors.yellow,
    shadowColor: _trigger? Colors.blue : Colors.red,
    duration: Duration(seconds: 1),
    curve: Curves.easeIn,
),
Container(height: 10.0,),
RaisedButton(
    child: Text("AnimatedPhysicalModel Start"),
    onPressed: () => setState(() {
        _trigger = !_trigger;
    }),
),

AnimatedPhysicalModel

AnimatedPositioned

AnimatedPositioned

AnimatedPositioned({
    Key key, @required Widget child, 
    double left, double top, double right, double bottom, 
    double width, double height, 
    Curve curve: Curves.linear, @required Duration duration, VoidCallback onEnd})
bool _trigger = true;
// ...
body: Stack(
    alignment: Alignment.center,
    children: <Widget>[
        Positioned(
            child: Text("Positioned"),
            top: 10.0,
        ),
        AnimatedPositioned(
            child: Container(
                color: Colors.blue,
            ),
            top: _trigger ? 10.0 : 30.0,
            height: _trigger ? 50.0 : 10.0,
            width: 70.0,
            duration: Duration(seconds: 1),
            curve: Curves.easeIn,
        ),
        ],),),

AnimatedPositioned

AnimatedPositionedDirectional

AnimatedPositionedDirectional

AnimatedPositionedDirectional({
    Key key, @required Widget child, 
    double start, double top, double end, double bottom, double width, double height, 
    Curve curve: Curves.linear, @required Duration duration, VoidCallback onEnd})
bool _trigger = true;
// ...
Stack(
    alignment: Alignment.center,
    children: <Widget>[
        AnimatedPositionedDirectional(
            child: Container(
                child: Text("AnimatedPositionedDirectionalAnimatedPositionedDirectional"),
                color: Colors.blue,
            ),
            top: 50.0,
            start: _trigger? 100.0 : 150.0,
            end: _trigger ? 100.0 : 200.0,
            height: _trigger ? 100.0 : 200.0,
            duration: Duration(seconds: 1),
            curve: Curves.easeIn,
        ),
      ],),

AnimatedPositionedDirectional

AnimatedTheme

AnimatedTheme

AnimatedTheme({
    Key key, @required ThemeData data, bool isMaterialAppTheme: false, 
    Curve curve: Curves.linear, Duration duration: kThemeAnimationDuration, 
    VoidCallback onEnd, @required Widget child})
bool _trigger = true;
// ...
AnimatedTheme(
    data: _trigger ? ThemeData.light() : ThemeData.dark(),
    child: Builder(
        builder: (BuildContext context) {
            return Container(
                width: 100,
                height: 100,
                color: Theme.of(context).primaryColor,
            );
        },
    ),
),

AnimatedTheme

AnimatedCrossFade

AnimatedCrossFade

AnimatedCrossFade({
    Key key, @required Widget firstChild, @required Widget secondChild, 
    Curve firstCurve: Curves.linear, Curve secondCurve: Curves.linear, 
    Curve sizeCurve: Curves.linear, AlignmentGeometry alignment: Alignment.topCenter, 
    @required CrossFadeState crossFadeState, @required Duration duration, Duration 
        reverseDuration, AnimatedCrossFadeBuilder layoutBuilder: defaultLayoutBuilder})
bool _trigger = true;
// ...
AnimatedCrossFade(
    firstChild: FlutterLogo(style: FlutterLogoStyle.horizontal, size: 100.0),
    secondChild: FlutterLogo(style: FlutterLogoStyle.stacked, size: 100.0),
    crossFadeState: _trigger ? CrossFadeState.showFirst : CrossFadeState.showSecond,
    duration: Duration(seconds: 1),
),

AnimatedCrossFade

AnimatedWidget class

AnimatedWidget

AnimatedWidget({Key key, @required Listenable listenable})

RotationTransition

RotationTransition

RotationTransition({
    Key key, @required Animation<double> turns, 
    Alignment alignment: Alignment.center, Widget child})
class _RotationTransitionPageState extends State<RotationTransitionPage>
    with SingleTickerProviderStateMixin {
  Animation<double> _animation;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    )..repeat(reverse: true);
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.elasticInOut,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('RotationTransition'),
      ),
      body: Center(
        child: Container(
          child: RotationTransition(
            turns: _animation,
            child: FlutterLogo(size: 100,),
          )
        ),
      ),
    );
  }
}

RotationTransition

ScaleTransition

ScaleTransition

ScaleTransition({
    Key key, @required Animation<double> scale, 
    Alignment alignment: Alignment.center, Widget child})
@override
void initState() {
  super.initState();
  _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 2),
  )..repeat(reverse: true);
  _animation = CurvedAnimation(
    parent: _controller,
    curve: Curves.easeIn,
  );
}
//...
ScaleTransition(
  scale: _animation,
  child: FlutterLogo(size: 100,),
)

ScaleTransition

SizeTransition

SizeTransition

SizeTransition({
    Key key, Axis axis: Axis.vertical, @required Animation<double> sizeFactor, 
    double axisAlignment: 0.0, Widget child})
SizeTransition(
    sizeFactor: _animation,
    axis: Axis.horizontal,
    child: FlutterLogo(size: 100,),
)

SizeTransition

FadeTransition

FadeTransition

FadeTransition({
    Key key, @required Animation<double> opacity, 
    bool alwaysIncludeSemantics: false, Widget child})
FadeTransition(
    opacity: _animation,
    child: FlutterLogo(size: 100,),
)

FadeTransition

Animatable

AnimatedWidget 의 예제들은 Animation<double> 을 argument 로 사용한다.

Animatable 을 사용하여 Animation<T> 로 타입을 T 로 변경할 수 있다.

AnimationControllerdouble 타입의 animation value 값을 다른 타입의 값의 범위로 변경시켜주는 동작을 한다.

예를 들어 0.0 ~ 1.0 범위의 값이 Color, Size 등의 다른 타입이 가지는 범위의 값으로 바꿔주는 것이다.

Animation<Offset> _offsetAnimation;

_offsetAnimation = Tween<Offset>(
  begin: Offset.zero,
  end: const Offset(1.5, 0.0),
).animate(CurvedAnimation(
  parent: _controller,
  curve: Curves.elasticIn,
));

위의 예제는 Tween<Offset>animate() 메소드를 통해 CurvedAnimation() 이 생성한 Animation<double> 의 타입을 변경해 Animation<Offset> 를 만들어 낸다.

TweenAnimatable 의 implementer 중 하나 이다.

Tweens

Tween<T extends dynamic>

Tween({T begin, T end})

Tweendouble 값 0.0 ~ 1.0 범위를 다른 타입의 시작(begin) 과 끝(end) 의 값으로 맵핑해주는 역할을 한다.

변경될 범위의 begin 과 end 를 지정해 줘야 한다.

위의 Animatable 예제 코드를 보면 0.0 ~ 1.0 이 Offset.zero ~ Offset(1.5, 0.0) 으로 맵핑된다.

Animation 이나 AnimationController 는 여러개의 Tween 을 가질 수 있다.

예를 들어 두개의 Tween 을 사용하여 크기와 색의 값이 동시에 변경되는 animation 를 생성할 수 있다.

TweenAnimationBuilder

TweenAnimationBuilder

TweenAnimationBuilder({
    Key key, @required Tween<T> tween, @required Duration duration, 
    Curve curve: Curves.linear, @required ValueWidgetBuilder<T> builder, 
    VoidCallback onEnd, Widget child})
double targetValue = 24.0;

@override
Widget build(BuildContext context) {
  return TweenAnimationBuilder(
    tween: Tween<double>(begin: 0, end: targetValue),
    duration: Duration(seconds: 1),
    builder: (BuildContext context, double size, Widget child) {
      return IconButton(
        iconSize: size,
        color: Colors.blue,
        icon: child,
        onPressed: () {
          setState(() {
            targetValue = targetValue == 24.0 ? 48.0 : 24.0;
          });
        },
      );
    },
    child: Icon(Icons.aspect_ratio),
  );
}

code from https://api.flutter.dev/flutter/widgets/TweenAnimationBuilder-class.html

TweenAnimationBuilder

TweenSequences

TweenSequence<T>

TweenSequence(List<TweenSequenceItem<T>> items)

TweenSequence 는 여러개의 Tween 을 연속적으로 수행되게 해준다.

TweenSequenceItem 이 한개의 TweenSequence 을 담는다.

class _TweenSequencePageState extends State<TweenSequencePage>
    with SingleTickerProviderStateMixin {
  Animation<double> _animation;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    );
    _animation = TweenSequence(
      <TweenSequenceItem<double>>[
        TweenSequenceItem<double>(
          tween: Tween<double>(begin: 5.0, end: 10.0)
              .chain(CurveTween(curve: Curves.ease)),
          weight: 40.0,
        ),
        TweenSequenceItem<double>(
          tween: ConstantTween<double>(10.0),
          weight: 20.0,
        ),
        TweenSequenceItem<double>(
          tween: Tween<double>(begin: 10.0, end: 5.0)
              .chain(CurveTween(curve: Curves.ease)),
          weight: 40.0,
        ),
      ],
    ).animate(_controller);
    _animation.addListener(() => setState(() {}));
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TweenSequence'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ScaleTransition(
              scale: _animation,
              child: FlutterLogo(size: 10,),
            ),
            SizedBox(height: 50.0,),
            Text("animation value : ${_animation.value.toStringAsFixed(2)}"),
            RaisedButton(
              child: Text("TweenSequence Start"),
              onPressed: () => _controller.forward(),
            )
          ],
        )
      ),
    );
  }
}

TweenSequence

AnimatedWidget examples

AlignTransition

AlignTransition

AlignTransition({
    Key key, @required Animation<AlignmentGeometry> alignment, 
    @required Widget child, double widthFactor, double heightFactor})
AlignmentGeometryTween _animation;
//...
@override
void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this,
        duration: const Duration(seconds: 2),
    )..repeat(reverse: true);
    _animation = AlignmentGeometryTween(
        begin: Alignment.bottomLeft,
        end: Alignment.topRight,
    );
}
//...
AlignTransition(
    alignment: _animation.animate(_controller),
    child: FlutterLogo(size: 100,),
),

AlignTransition

DecoratedBoxTransition

DecoratedBoxTransition

DecoratedBoxTransition({
    Key key, @required Animation<Decoration> decoration, 
    DecorationPosition position: DecorationPosition.background, 
    @required Widget child})
DecorationTween _animation;
_animation = DecorationTween(
    begin: BoxDecoration(
        color: Colors.white,
        boxShadow: <BoxShadow>[
            BoxShadow(
                color: Colors.grey,
                blurRadius: 10.0,
                spreadRadius: 4.0,
            )
        ],
    ),
    end: BoxDecoration(
        color: Colors.white,
        boxShadow: <BoxShadow>[
            BoxShadow(
                color: Colors.grey,
                blurRadius: 2.0,
                spreadRadius: 2.0,
            )
        ],
        borderRadius: BorderRadius.circular(12),
    ),
);
//...
DecoratedBoxTransition(
    decoration: _animation.animate(_controller),
    child: Container(
        child: FlutterLogo(
            size: 100.0,
        ),
    ),
),

DecoratedBoxTransition

DefaultTextStyleTransition

DefaultTextStyleTransition

DefaultTextStyleTransition({
    Key key, @required Animation<TextStyle> style, @required Widget child, 
    TextAlign textAlign, bool softWrap: true, 
    TextOverflow overflow: TextOverflow.clip, int maxLines})
TextStyleTween _animation;
_animation = TextStyleTween(
    begin: TextStyle(
        color: Colors.blue,
        fontSize: 10.0,
        fontStyle: FontStyle.italic,
    ),
    end: TextStyle(
        color: Colors.blueGrey,
        fontSize: 20.0,
        fontWeight: FontWeight.bold,
    )
);
//...
DefaultTextStyleTransition(
    style: _animation.animate(_controller),
    child: Text("DefaultTextStyleTransition"),
),

DefaultTextStyleTransition

PositionedTransition

PositionedTransition

PositionedTransition({
    Key key, @required Animation<RelativeRect> rect, @required Widget child})
RelativeRectTween _animation;
_animation = RelativeRectTween(
    begin: RelativeRect.fromLTRB(0.0, 0.0, 50.0, 50.0),
    end: RelativeRect.fromLTRB(50.0, 50.0, 0.0, 0.0),
);
//...
Stack(
    children: <Widget>[
        PositionedTransition(
            rect: _animation.animate(_controller),
            child: FlutterLogo(size: 50.0,),
        ),
    ],
),

PositiontedTransition

RelativePositionedTransition

RelativePositionedTransition

RelativePositionedTransition({
    Key key, @required Animation<Rect> rect, @required Size size, 
    @required Widget child})
RectTween _animation;
_animation = RectTween(
    begin: Rect.fromLTRB(0.0, 0.0, 0.0, 0.0),
    end: Rect.fromLTRB(50.0, 50.0, 50.0, 50.0),
);
//...
Stack(
    children: <Widget>[
        RelativePositionedTransition(
            rect: _animation.animate(_controller),
            size: Size(50.0, 50.0),
            child: FlutterLogo(),
        ),
    ],
),

RelativePositinonedTransition

AnimatedBuilder

AnimatedBuilder

AnimatedBuilder({
    Key key, @required Listenable animation, 
    @required TransitionBuilder builder, Widget child})
Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: _controller,
        child: Container(
            width: 200.0,
            height: 200.0,
            color: Colors.green,
            child: const Center(
                child: Text('Wee'),
            ),
        ),
        builder: (BuildContext context, Widget child) {
            return Transform.rotate(
                angle: _controller.value * 2.0 * math.pi,
                child: child,
            );
        },
    );
}

code from https://api.flutter.dev/flutter/widgets/AnimatedBuilder-class.html

 

Animation Architecture

https://flutter.dev/docs/development/ui/animations/overview#architecture

 

Animation - AnimationController - SignleTickerProviderStateMixin - Ticker - SchedulerBinding - scheduleFrameCallback()

 

Reference

Introduction to animations - flutter.dev

Implicit animations - flutter.dev

'Flutter' 카테고리의 다른 글

Flutter: Staggered Animations  (0) 2020.02.19
Flutter: Hero  (0) 2020.02.12
플러터 프로젝트 코멘트 지우기 (remove comments)  (0) 2020.01.28
[플러터] Flutter 1.12 release  (0) 2019.12.12
Flutter : Custom font  (0) 2019.11.20
Comments