개발노트

71. [Flutter] flutter_animate 패키지로 손쉽게 애니메이션 효과 주기 본문

앱 개발/Flutter

71. [Flutter] flutter_animate 패키지로 손쉽게 애니메이션 효과 주기

mroh1226 2024. 2. 26. 15:39
반응형

flutter_animate 

flutter_animate 패키지는 Flutter 애플리케이션에서 애니메이션을 쉽게 적용할 수 있도록 도와주는 패키지입니다. 이 패키지는 다양한 애니메이션 효과를 제공하며, 간편한 구문을 사용하여 애니메이션을 구현할 수 있습니다.

flutter_animate 패키지의 주요 특징은 다음과 같습니다:

  1. 다양한 애니메이션 효과: 패키지는 여러 가지 애니메이션 효과를 제공합니다. 이 효과들은 Fade, Scale, Rotate, Flip 등의 기본적인 애니메이션부터 Pulse, Shake, Wiggle과 같은 고급 애니메이션까지 다양합니다.
  2. 간편한 구문: 애니메이션을 구현할 때 간편한 구문을 제공하여 개발자가 복잡한 애니메이션 코드를 작성하지 않아도 되도록 합니다. 이로써 애니메이션 적용이 더욱 쉬워집니다.
  3. 메서드 체이닝 지원: 패키지는 메서드 체이닝을 지원하여 여러 애니메이션을 연속적으로 적용할 수 있도록 합니다. 이로써 여러 애니메이션을 간단하게 조합할 수 있습니다.
  4. 시간 지연 및 커브 제어: 애니메이션의 시작 시간을 지연시키고, 커브를 조정하여 애니메이션의 움직임을 더욱 자연스럽게 만들 수 있습니다.

flutter_animate 패키지를 사용하면 Flutter 애플리케이션에서 다양한 애니메이션을 쉽게 추가할 수 있으며, 사용자 경험을 향상시키는 데 도움이 됩니다.

 

 

- flutter_animate 설치링크: https://pub.dev/packages/flutter_animate

 

flutter_animate | Flutter package

Add beautiful animated effects & builders in Flutter, via an easy, customizable, unified API.

pub.dev

 

아래 부터 flutter_animate를 사용하는 3가지 방식을 나눠서 설명드리겠습니다.


1. AnimateList [] 를 사용하는 방식

 

*AnimateList는 List형태로, onInit, onPlay, onComplete와 같은 콜백 함수를 통해 Controller를 다루고 effects: 속성에 있는 애니메이션을 호출합니다.

  • onInit: 애니메이션 리스트가 초기화될 때 호출되는 콜백 함수입니다. 여기서는 컨트롤러를 초기화하거나 다른 초기 설정을 수행할 수 있습니다. 여기서는 아무 동작도 하지 않습니다.
  • onPlay: 애니메이션 재생이 시작될 때 호출되는 콜백 함수입니다. 애니메이션이 시작되면 이 콜백에서 원하는 작업을 수행할 수 있습니다. 여기서는 아무 동작도 하지 않습니다.
  • onComplete: 애니메이션이 완료될 때 호출되는 콜백 함수입니다. 여기서는 애니메이션을 역방향으로 재생하도록 컨트롤러에 지시합니다.
  • effects: 적용할 애니메이션 효과의 리스트입니다. 여기서는 **FlipEffect**와 ScaleEffect 두 가지 효과를 사용하고 있습니다. **FlipEffect**는 카드를 수직으로 뒤집는 효과를 주고, **ScaleEffect**는 크기를 조정하는 효과를 주는 것으로 보입니다.
  • interval: 각 애니메이션 간의 지연 시간입니다. 여기서는 500밀리초로 설정되어 있습니다.
  • children: 각 항목에 대한 위젯 리스트입니다. 여기서는 **colorList**의 각 색상에 대해 **Container**를 만들어 표시하고 있습니다. 각 **Container**는 정해진 색상과 모양을 가지고 있습니다.

*AnimateList가 List형이기 때문에 Column []의 children: 에 대괄호 없이 바로 AnimateList를 연결해주면됩니다.

 

AinmateList [] 사용 예시 소스.
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';

class TestScreen extends StatefulWidget {
  static String routeName = "testscreen";
  static String routeURL = "testscreen";
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  final List<MaterialColor> colorList = [
    Colors.red,
    Colors.orange,
    Colors.yellow,
    Colors.lightGreen,
    Colors.lightBlue,
    Colors.indigo,
    Colors.deepPurple,
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Column(
                children: AnimateList(
                    onInit: (controller) {},
                    onPlay: (controller) {},
                    onComplete: (controller) {
                      controller.reverse();
                    },
                    effects: [
                      const FlipEffect(
                          begin: 0,
                          end: 1,
                          direction: Axis.vertical,
                          duration: Duration(milliseconds: 500)),
                      const ScaleEffect(
                          begin: Offset(1, 1), end: Offset(1.5, 1.5))
                    ],
                    interval: 500.ms,
                    children: [
                      for (int i = 0; i < colorList.length; i++)
                        Padding(
                          padding: const EdgeInsets.all(10),
                          child: Container(
                            height: 70,
                            width: 350,
                            decoration: BoxDecoration(
                                color: colorList[i],
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(25))),
                          ),
                        ),
                    ]),
              )
            ],
          ),
        ),
      ),
    );
  }
}

2. Animate() 를 사용하는 방식

Animate() 사용 예시

 

*Animate() 은 target: 의 상태를 감시하며, 이에 따라 effects: [] 속성에 List에 있는 애니메이션 효과가 진행됩니다.

  • target: 애니메이션의 대상값을 지정합니다. 여기서는 **_toggle**의 상태에 따라 0 또는 1의 값을 가집니다. 이 값이 변할 때마다 애니메이션도 갱신됩니다.
  • effects: 적용할 애니메이션 효과의 리스트입니다. 여기서는 각 항목에 대해 **FlipEffect**와 ScaleEffect 두 가지 효과를 적용하고 있습니다. **FlipEffect**는 해당 위젯을 뒤집는 효과를 주고, **ScaleEffect**는 해당 위젯의 크기를 조정하는 효과를 줍니다. 각 효과에는 delay 속성이 있어서 각 항목에 애니메이션을 적용하는 시간을 조절할 수 있습니다.
  • child: 각 애니메이션 효과가 적용될 위젯입니다. 여기서는 **Padding**을 통해 각 컨테이너를 감싸고 있습니다. 이렇게 함으로써 컨테이너 사이의 여백을 추가할 수 있습니다. **Container**는 리스트의 각 항목을 나타내며, **colorList**의 각 색상을 가지고 있습니다. 모든 컨테이너는 동일한 크기와 모양을 가지며, 각각의 색상을 가집니다.
Animate() 사용 예시 소스.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';

class TestScreen extends StatefulWidget {
  static String routeName = "testscreen";
  static String routeURL = "testscreen";
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  bool _toggle = false;
  final List<MaterialColor> colorList = [
    Colors.red,
    Colors.orange,
    Colors.yellow,
    Colors.lightGreen,
    Colors.lightBlue,
    Colors.indigo,
    Colors.deepPurple,
  ];
  void _onPressed() {
    setState(() {
      _toggle = !_toggle;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              CupertinoButton(
                  onPressed: _onPressed, child: const Text("Card Flip")),
              for (int i = 0; i < colorList.length; i++)
                Animate(
                  target: _toggle ? 0 : 1,
                  effects: [
                    FlipEffect(
                        begin: 0,
                        end: 1,
                        delay: Duration(milliseconds: 200 * i),
                        curve: Curves.fastOutSlowIn),
                    ScaleEffect(
                        begin: const Offset(1, 1),
                        end: const Offset(2, 1.5),
                        delay: Duration(milliseconds: 200 * i),
                        curve: Curves.bounceOut),
                  ],
                  child: Padding(
                    padding: const EdgeInsets.all(10),
                    child: Container(
                      height: 70,
                      width: 350,
                      decoration: BoxDecoration(
                          color: colorList[i],
                          borderRadius:
                              const BorderRadius.all(Radius.circular(25))),
                    ),
                  ),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

 

Animate()의 effects: 속성 리스트 예시

Animate()의 effects 속성 애니메이션 효과 (일부분)


3. Widget 뒤에 .animate() 를 붙이는 방식 (*메서드 체이닝)

 

*[메서드 체이닝] 에 대해서는 아래에서 설명드리겠습니다.

 

위의 코드는 flutter_animate 패키지에서 제공하는 애니메이션 메서드 체이닝 기능을 사용하여 컨테이너에 여러 애니메이션 효과를 적용하는 부분입니다. 아래는 각 메서드와 속성에 대한 설명입니다.

  • animate: 애니메이션을 적용할 위젯을 선택합니다. 여기서는 Container 위젯을 선택합니다.
  • flipV: 수직 방향으로 뒤집는 애니메이션 효과를 추가합니다. **begin**과 **end**는 애니메이션의 시작과 끝 값을 나타냅니다. **curve**는 애니메이션의 변화를 제어하는 곡선을 나타냅니다. **delay**는 애니메이션 시작을 지연시키는 데 사용됩니다. 여기서는 FastOutSlowIn 곡선과 200 * i 밀리초의 지연을 사용합니다.
  • then: 이전 애니메이션 후에 다음 애니메이션을 적용하기 위한 지연 시간을 추가합니다. 여기서는 1000밀리초의 지연을 추가했습니다.
  • scale: 크기 조정 애니메이션 효과를 추가합니다. **begin**과 **end**는 애니메이션의 시작과 끝 값을 나타냅니다. **curve**는 애니메이션의 변화를 제어하는 곡선을 나타냅니다. **delay**는 애니메이션 시작을 지연시키는 데 사용됩니다. 여기서는 BounceOut 곡선과 200 * i 밀리초의 지연을 사용합니다.
Widget.animate() 사용 예시
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';

class TestScreen extends StatefulWidget {
  static String routeName = "testscreen";
  static String routeURL = "testscreen";
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  bool _toggle = false;
  final List<MaterialColor> colorList = [
    Colors.red,
    Colors.orange,
    Colors.yellow,
    Colors.lightGreen,
    Colors.lightBlue,
    Colors.indigo,
    Colors.deepPurple,
  ];
  void _onPressed() {
    setState(() {
      _toggle = !_toggle;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              CupertinoButton(
                  onPressed: _onPressed, child: const Text("Card Flip")),
              for (int i = 0; i < colorList.length; i++)
                Padding(
                  padding: const EdgeInsets.all(10),
                  child: Container(
                    height: 70,
                    width: 350,
                    decoration: BoxDecoration(
                        color: colorList[i],
                        borderRadius:
                            const BorderRadius.all(Radius.circular(25))),
                  )
                      .animate(target: _toggle ? 0 : 1)
                      .flipV(
                          begin: 0,
                          end: 1,
                          curve: Curves.fastOutSlowIn,
                          delay: Duration(milliseconds: 200 * i))
                      .then(delay: 1000.ms)
                      .scale(
                          curve: Curves.bounceOut,
                          begin: const Offset(1, 1),
                          end: const Offset(1.5, 1.5),
                          delay: Duration(milliseconds: 200 * i)),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

 

Widget.animate() 메서드 체이닝으로 사용 가능한 애니메이션효과 리스트 예시

사용가능한 애니메이션 효과(일부분)



응용해서 만든 카드 애니메이션.

- 출처: 노마드코더


메서드 체이닝이란?.

메서드 체이닝(Method Chaining)은 객체 지향 프로그래밍에서 자주 사용되는 기술 중 하나입니다. 이 기술은 한 줄에 여러 메서드를 연속적으로 호출하여 하나의 객체에서 다양한 작업을 수행할 수 있게 해줍니다.

일반적으로 메서드 체이닝은 빌더 패턴(Builder Pattern)의 한 형태로 사용됩니다. 이 패턴은 객체의 생성 및 구성을 단순화하고 유연성을 높여줍니다.

메서드 체이닝의 작동 방식은 다음과 같습니다:

  1. 객체의 메서드가 호출되면, 해당 메서드는 보통 자기 자신(this 또는 self라 불리는)을 반환합니다.
  2. 이렇게 반환된 객체는 다시 다른 메서드 호출에 사용됩니다.
  3. 이런 식으로 연쇄적으로 메서드를 호출하며 작업이 계속됩니다.

이렇게 함으로써 여러 메서드 호출을 한 줄에 연속적으로 작성할 수 있으며, 코드의 가독성을 높이고 간결하게 만들 수 있습니다.

예를 들어, 다음은 Dart에서의 메서드 체이닝을 사용한 간단한 예시입니다.

class Calculator {
  double _result = 0;

  Calculator add(double value) {
    _result += value;
    return this;
  }

  Calculator subtract(double value) {
    _result -= value;
    return this;
  }

  Calculator multiply(double value) {
    _result *= value;
    return this;
  }

  Calculator divide(double value) {
    if (value != 0) {
      _result /= value;
    }
    return this;
  }

  double getResult() {
    return _result;
  }
}

void main() {
  var calc = Calculator();
  var result = calc.add(5).multiply(2).subtract(3).divide(2).getResult();
  print(result); // 출력: 4.0
}

 

반응형
Comments