[flutter/플러터] 애니메이션 공부

박민준·2022년 1월 27일
0

플러터 애니메이션 정리

구글링 및 공식문서를 통해 배운 걸 깔끔하게 정리해보고자 한다. 실사용 코드들은 많이 있지만 더 명확히 이해한 다음 사용하고 싶어서 이것저것 많이 뒤져봤다.

애니메이션 작동 구조

2가지 작업이 필요하다.

1) 애니메이션의 정보가 들어가 있을 애니메이션 객체들을 생성한다. 일반적으로 Animation 자체와 AnimationController 이렇게 2개.

2) 애니메이션 정보를 받아서 view로 나타내주도록 하는 세팅
ex) addListner와 setState 조합. / AnimatedWidget 상속 활용

1. 애니메이션 기본 객체들 생성

Animation<T>

애니메이션을 활용하기 위한 기본 추상 클래스다. 애니메이션에 활용되는 많은 객체들은 대부분 이 클래스의 하위 클래스이다.

AnimationController

애니메이션을 컨트롤하기 위한 객체이다.
Animation을 실행시키는 메소드를 가지고 있다. 말 그대로 컨트롤러 역할. 직접 애니메이션을 컨트롤하기 위해선 무조건 이 객체를 거쳐야 한다고 보면 된다.

실행 메소드!
ex) reverse() / repeat() / forward()

이 객체를 선언할 때 넣어줘야 하는 필수 파라미터가 2개 있다.

  • duration : 애니메이션이 작동하는 기간을 정해준다.
  • vsync : 정확한 개념은 모르겠으나 애니메이션이 깨지지 않고 잘 실행될 수 있도록 만들어주기 위해 넣는 것이라고 한다.

애니메이션이 보여질 화면 위젯에 wtih SingleTickerProvider 로 믹스인을 해 준다. 그리고 해당 위젯 자체를 넣어준다.
vsync: this 이런 형태로

예시는 아래 그림과 같다.

Animation<T>

위 애니메이션 컨트롤러에선 기간과 싱크만 맞춰줬다면, 이제 구체적인 애니메이션 값을 넣어줄 객체가 필요하겠지. 그게 이거다.

Animation 종류가 하나 더 있다고 알고 있는데 일단 주로 사용하는 건 Tween 객체를 활용해 만드는 Animation이다.

Tween<T>는 그 자체론 Animation 객체가 아니라 Animatable 객체다. 여기에 추가 메소드를 넣어줘야 한다.

이렇게 Tween 객체 뒤에 animate()를 붙여주면 Animation 객체를 반환해준다.
animate() 안에는 animation의 기준이 될 다른 Animation 객체를 넣어주면 된다고 하는데... 이게 무슨 소리냐!

AniamaionController도 Animation의 하위 클래스다. AnimationController의 추가적 역할은 일정 범위의(일반적으로 0~1) 숫자 값을 연속적으로 발생시켜주는 것.
그래서 Tween().animate(animationController) 이렇게 해주면 완성 형태의 Animation 객체가 생성되는 것이다.

Animation 객체 얘기해서 헷갈렸을테니 다시 정리하면...

불완전한 Animation 객체가 있고 완전한 Animation이 있다.
불완전한 애들은 특정 기능 위주로만 만들어진 거고, 완전한 애들은 이제 controller 통해서 컨트롤까지 가능해진 애니메이션이다.

AnimationController는 불완전한 애니메이션이고 완전한 애니메이션을 만들기 위한 재료로 쓰인다.

완전한 Animation = Tween(어쩌구 저쩌구).animate(AnimationController);이렇게 만들어진다는 것!

완전한 애니메이션 
= _controller.drive(
  Tween<Offset>(
    begin: const Offset(100.0, 50.0),
    end: const Offset(200.0, 300.0),
  ),
);
  이런식으로 만드는 것도 가능하다고 함.

cf) CurvedAnimation -> controller 진화시키기!

final curve = CurvedAnimation(parent: controller, curve: Curves.bounceOut);

이렇게 정의해주면 animationController에 curve값을 추가한게 된다. curve는 값 생성 패턴을 정의해주는 거임. 자세한 설명은 아래 참고
https://api.flutter.dev/flutter/animation/Curves-class.html
위에처럼 정의해주고

.animate(AnimationController 대신 curve); 이렇게 넣어주면 됨.

그럼 지금까지 애니메이션을 위한 객체 생성은 마무리됐다.

그럼 다음 코스

2. View에서 애니메이션을 받아들일 수 있도록 만들기

애니메이션이라는건 자연스럽게 위젯이 변해야 한다는것! -> 위젯이 변한다는 건 소위 상태관리라는 것이 있어야 한다는 것 -> 상태관리는 대표적으로 setState가 있다는 것.

거기에.. 자연스러우려면 setState가 프레임 단위로 매우 연속적으로 일어나야 한다는 것!!
그래서 자연스럽게 연상할 수 있는 첫번째 방법 - 완전 근본임. 플러터 애니메이션의 근본 원리라고 볼 수 있음.

addListner와 setState 사용

animation = Tween<double>(begin: 0, end: 300).animate(controller)
  ..addListener(() {
      setState(() {
         // The state that has changed here is the animation object’s value.
      });
    });

addListner를 추가한다는 것은 animation 레퍼런스의 값이 바뀔 때마다 addListner 안의 콜백을 호출한다는 것이다. 안에 setState가 있으니 계속 리빌드를 해준다는 의미겠지!

cf) 결국 Animation 객체라는 건 값을 발생시켜주는 generator임. 컨트롤러가 정해준 시간 동안, Animation 객체 안에 정의된 값들을 순서대로 와다다 발생시켜주는 generator! 예를 들어 begin:0 , end:300 이다? duration은 2초다.
2초 동안 0부터300까지의 값을 와다다 나타내주는 거임. 애니메이션으로 보일 정도로 자연스럽게!

위에 저렇게 해주고

이래 써주면 animation.value에서 0-> 0.1될 때 addListner때문에 setState한번 하고 이게 말도안되게 많이 반복되겠지. 애니메이션처럼 보이게. 이러한 원리임!

지금까지 정리한걸 한눈에 보이게 보여주면 아래와 같음. 위에서 언급한 객체들을 어디 선언하고 어디서 초기화시켜줘야 하고 추가로 뭘 써줘야 하는지 아래 코드를 찬찬히 읽어보면 이제 하나로 정리되며 이해가 될 거임.

코드 읽기 전에 추가 설명해주자면 initState에서 주요 객체들을 다 초기화시켜준다.

import 'package:flutter/material.dart';

class NewPage extends StatefulWidget {
  const NewPage({Key? key}) : super(key: key);

  
  _NewPageState createState() => _NewPageState();
}

class _NewPageState extends State<NewPage> with SingleTickerProviderStateMixin {

  late Animation<double> animation;
  late AnimationController controller;


  
  void initState() {
    // TODO: implement initState
    super.initState();
    controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000));
    final curve = CurvedAnimation(parent: controller, curve: Curves.bounceOut);
    animation = Tween<double>(begin: 0,end: 300).animate(curve)
    ..addListener(() {
      setState(() {

      });
    });
// 나중에 탭했을 때 실행되도록 하고싶으면 아래 코드를 다른 곳에 넣어주면 되겠지?
    controller.forward();
  }

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: Colors.green,
        height: animation.value,
        width: animation.value,
      ),
    );
  }
}

이 코드 그대로 실행시켜보면 커지는 정사각형을 볼 수 있을거임.
이런 방식의 단점은 setState를 사용한다는 것. 전체 위젯을 재실행하게 되므로 빌드 메모리 효율성이 떨어진다.

AnimatedWidget 사용

이친구를 사용해주면 더 쉽다.

AnimatedWidget은 statfulWidget을 상속한 클래스다.

이런 식으로 AnimatedWidget을 상속해서 사용해주면 된다.
addListner를 따로 추가해주고 이런게 아니라 그냥 listen할 animation을 객체 생성 때부터 받아버린다. 이렇게 해주면 setState를 굳이 사용하지 않고도 알아서 적용시킬 수 있다.

AnimatedWidget을 상속한 내장 위젯들도 있다.
~Trainsition 이렇게 끝나는 위젯들
아래 참고해보자

이렇게 해주면, 그냥 AnimationController(불완전한 Animation)만 넣어주고도 애니메이션이 잘 굴러간다. controller에서 값 받은 걸 위젯 목적에 맞게 알아서 변환해준다.

  • RotatioinTransition은 한바뀌 도는거

  • FadeTransition은 불투명도 조절

    AnimatedBuilder 사용

    이런 방식도 있다.
    https://papabee.tistory.com/141
    자세한건 사이트 참조. 위 방식이랑 비슷한데 굳이 class 정의를 안 해주고도 사용할 수 있다는 것.

    3) 그냥 Implicit 애니메이션 사용.

    제일 간단하다. 그냥 값을 상태관리 활용해서 바꿔주면 알아서 자연스러운 애니메이션을 구현해준다. 아래는 implicit 애니메이션 위젯 목록

profile
코린이

0개의 댓글