Container Widget에 Animation 추가하기- AnimatedSwitcher

이번에 시작한 프로젝트에서 버튼이나 텍스트 등 위젯에 사용자의 동작에 따른 애니메이션을 추가할 일이 생겼다.

관련 자료를 찾아보던 중 Flutter Docs에서 AnimatedSwitcher를 발견하였다.

한국어로 잘 설명되어 있는 자료가 없어서 남긴다.

 

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> test = [];
  int _index = 0;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    setTestWidget();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
                child:
                  AnimatedSwitcher(
                    duration: const Duration(milliseconds: 1000),
                    transitionBuilder: (Widget child, Animation<double> animation) {
                      return FadeTransition(
                        opacity: animation,
                        child: child,
                      );
                    },
                    child: test[_index],
                  ),
              ));
  }

  setTestWidget(){
    test.add(Container(
      width: 150,
      key: ValueKey<int>(0),
      color: Colors.red,
      child: TextButton(
        child: Text('빨강', style: TextStyle(color: Colors.white),),
        onPressed: (){
          setState(() {
            _index = 1;
          });
        },
      )
    ));
    test.add(Container(
      width: 200,
      key: ValueKey<int>(1),
      color:Colors.blue,
      child: TextButton(
          child: Text('파랑', style: TextStyle(color: Colors.white)),
        onPressed: (){
            setState(() {
              _index = 0;
            });
        },
      )
    ));
  }
}

간략하게 사용자가 Container에 감싸져 있는 TextButton을 터치했을 때, 위젯이 스위치되도록 예시를 만들어 보았다.

 

AnimatedSwitcher : 

AnimatedSwitcher(
                    duration: const Duration(milliseconds: 1000),
                    transitionBuilder: (Widget child, Animation<double> animation) {
                      return FadeTransition(
                        opacity: animation,
                        child: child,
                      );
                    },
                    child: test[_index],
                  ),

duration : 위젯이 변환하는 애니메이션의 시간이다.

transitionBuilder : 이 매개변수를 안 쓰고 Curve 계열의 매개변수(switchInCurve, switchOutCurve)로 애니메이션을 주는 방법도 있지만, 그래도 transitionBuilder를 사용하는게 더 다양한 애니메이션을 줄 수 있으니 이는 검색해보면 나올 것이다.

내가 사용한 FadeTranstion 이 외 다른 transiton으로는 SlideTranstion도 있는데 각 transtionBuilder 마다 필요로 하는 매개변수가 다르니 잘 찾아보고 사용하자.

child: 다들 알다시피 AnimatedSwitcher를 적용하고자 하는 자식 위젯이라고 보면 되겠다.

 

그럼 자식 위젯을 어떻게 구성할 것인가?를 보기로하자.

Container(
      width: 200,
      key: ValueKey<int>(1),
      color:Colors.blue,
      child: TextButton(
          child: Text('파랑', style: TextStyle(color: Colors.white)),
        onPressed: (){
            setState(() {
              _index = 0;
            });
        },
      )
    )

나는 가장 위 전체 코드에서 보았을 때, setTestWidget에서 위 Container 두개를 test 리스트에 삽입하였다.

그리고 리스트의 인덱스를 이용하여 위젯을 바꾸었는데, setState((){})를 이용해 index값을 바꾸고, 그걸로 위젯을 바꾸는 거는 이해되는데, 왜 애니메이션이 적용이 될 때가 있고 안 될 때가 있을까?

문제는 key값이다.

AnimatedSwitcher widget을 사용하는데 child에 key값이 존재하지 않는다면, 애니메이션은 동작하지 않는다.

 

그럼 key 값은 무엇일까? 간단하게 설명하자면, 각 위젯에 부여하는 id값이라고도 볼 수 있다. 즉, 동일한 Type의 Widget들 간의 구분자인 것이다.

 

기본적으로 flutter의 위젯은 Widget Tree와 ElementTree 그리고 RenderObjectTree 로 구성되는데, 각 트리는 서로 연동되어 있다.

Widget Tree는 우리가 추가한 Widget이, Element Tree에는 그 Widget에 대한 Type(ex. Container, Text ...)와 자식 Element에 대한 정보가 들어있는데 key 값을 추가하지 않고, Stateful Widget을 변경할 때(Stateless Widget의 경우에는 Element가 참조하는 Widget의 Type만 확인한다.), Widget의 Type이 같다면 ElementTree에는 변화가 없고 출력되는 화면은 그대로가 된다.

여기서 ElementTree에 변화가 없다는 것은, 해당 Element가 참조하는 Widget만 바뀌므로 결국 화면에 출력되는 Object는 그대로임을 뜻한다.

 

하지만 key값을 추가할 경우, element는 type과 key값을 모두 비교하여, key 값이 변하였으므로 이전 widget과 새로운 widget이 서로 다른 widget임을 인식한다. 결과적으로 switching이 정상적으로 동작하게 되는 것이다.

 

위 세 단락에 대한 설명이 글로는 와닿기 어려울 수 있으니, 원문을 한글로 번역해주신 분의 링크를 아래에 추가하겠다.

https://nsinc.tistory.com/214

 

[Flutter] Key란 무엇인가?

기본적으로 플러터의 위젯은 생성자에서 Key매개변수를 받을 수 있습니다. 하지만 그렇게 많이 사용되지는 않습니다. 위젯이 위젯트리에서 위치를 변경하더라도 Key는 상태정보를 유지합니다.

nsinc.tistory.com

여기서 key값이란 Container 안에 parameter로 들어있는 key: ValueKey<int>(1)과 같은 식인데, 구분하고 싶은 위젯 사이에 값을 임의로 잘 구분해주면 사용하는데 불편함은 없을 것이라 생각한다.

 

아래에는 위 코드로 작성된 프로그램의 영상을 첨부해놓겠다.

 

부족한 글 읽어주셔서 감사합니다.

'Programming > Flutter' 카테고리의 다른 글

flutter - firestore transaction 처리하기  (0) 2022.01.07

댓글