[Flutter] 애니메이션

merci·2023년 4월 2일
1

Flutter

목록 보기
12/24
post-thumbnail
post-custom-banner

앱바의 Drawer 위젯을 이용하면 상단 메뉴바를 쉽게 만들 수 있다.

	size = MediaQuery.of(context).size; // 디스플레이 크기

	Scafford(
      endDrawer: Container( // 왼쪽에 만들고 싶으면 drawer속성
        width: size.width * 0.6,
        color: Colors.lightBlue,
      ),
    )

애니메이션을 연습하기 위해 이 위젯을 직접 구현한다면

AnimatedContainer


AnimationController와 다르게 Container위젯을 애니메이션화 하는데 사용된다.

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  late Size size;
  bool isOpen = false;

  
  void initState() {
    super.initState();
  }

  
  Widget build(BuildContext context) {
    size = MediaQuery.of(context).size;
    print("size width: ${size.width}");
    return Scaffold(
      appBar: AppBar(
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 12.0),
            child: InkWell(
              child: Icon(Icons.menu),
              onTap: () {
                setState(() {
                  isOpen = !isOpen; // 토글
                });
              },
            ),
          )
        ],
      ),
      body: Stack(
        children: [
          Center(
            child: Text("Animation Screen"),
          ),
          AnimatedContainer(
            curve: Curves.fastLinearToSlowEaseIn, // 효과
            duration: Duration(seconds: 1), // 애니메이션 시간
            height: double.infinity,
            width: size.width * 0.7,
            color: Colors.blue,
            transform: Matrix4.translationValues( // 움직이는 좌표설정
                isOpen ? size.width * 0.3 : size.width, 0, 0),
          )
        ],
      ),
    );
  }
}

fastLinearToSlowEaseIn 외에도 다양한 효과들이 있다.

AnimatedWidget

AnimatedWidget을 상속한 ScaleTransition 을 이용해서 애니메이션을 만들어보자.
AnimationController는 애니메이션을 제어하고 Animation클래스가 애니메이션을 처리한다.

AnimatedWidgetAnimation객체를 이용해서 애니메이션을 처리하기 때문에 반드시 필요로 한다.

   required Animation<double> scale,
class MyHome2 extends StatefulWidget {
  const MyHome2({super.key});

  
  State<MyHome2> createState() => _MyHome2State();
}

class _MyHome2State extends State<MyHome2> with SingleTickerProviderStateMixin {

  late AnimationController _animationController;  // 메소드에서 필요로 하기 때문에 전역변수

  
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this, // required
      duration: const Duration(seconds: 2),
    );
    _animationController.repeat(); // 반복되도록 설정
  }

AnimationControllerrequired TickerProvider vsync, 필요로 하기 때문에 TickerProvider 객체를 얻기 위해서 SingleTickerProviderStateMixin클래스를 Mixin한다.

ScaleTransition

  
  Widget build(BuildContext context) {
    Animation<double> _animation =
        Tween(begin: 0.0, end: 2.0).animate(_animationController);
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: ScaleTransition(
          scale: _animation,
          child: Container(
            width: 200,
            height: 200,
            color: Colors.blue,
          ),
        ),
      ),
    );
  }
}

animateTween클래스와 함께 사용되고 animate는 부모의 Animation<double>을 받아서 Animation<double>타입을 리턴한다.

AnimationController는 내부적으로 Animation<double>을 상속하고 있다.

`class AnimationController extends Animation<double>

FadeTransition

서서히 나타나거나 사라지게 할경우 사용한다 옵션은 위에서 몇개만 수정하면 된다.

// scale 수정
opacity: _animation,
// Tween의 범위는 0 ~ 1
Tween(begin: 0.0, end: 1.0).animate(_animationController);

PositionedTransition


마찬가지로 필드에 따라 코드를 수정한다.

	// 필요로 하는 필드는
	required Animation<RelativeRect> rect,

PositionedTransition는 부모 위젯으로 Stack을 필요로 한다.

  
  Widget build(BuildContext context) {
    // double screenHeight = MediaQuery.of(context).size.height;
    double screenWidth = MediaQuery.of(context).size.width;

    Animation<RelativeRect> _animation =RelativeRectTween(
      begin: RelativeRect.fromLTRB(screenWidth, 0, 0, 0),
      end: RelativeRect.fromLTRB(0, 0, 0, 0),
    ).animate(CurvedAnimation(
      parent: _animationController,
      curve: Curves.elasticInOut,
    ));

    return Scaffold(
      body: Stack(
          children: [ PositionedTransition(
            rect: _animation,
            child: Container(
              color: Colors.blue,
            ),
          )
          ],
      ),
    );
  }
}

공식문서에 따르면 다음과 같은 애니메이션도 가능하다

  
  void dispose() {
    _animationController.dispose(); // 리소스 해제 - 메모리 누수 방지
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    const double smallLogo = 100;
    const double bigLogo = 200;

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

RotationTransition


로딩에 사용하면 좋을것 같다.
위 코드에서 Build 만 수정한다.

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: RotationTransition(
          turns: CurvedAnimation(
              parent: _animationController,
              curve: Curves.ease),
          child: Container(
            color: Colors.blue,
            width: 100,
            height: 100,
          ),
        ),
      ),
    );
  }

AnimatedSwitcher


클래스에서 다른 클래스로 위젯을 변환한다.

  // state 클래스에 필드추가
  Widget mWidget = FirstWidget();  
  
  
  Widget build(BuildContext context) {
    return Scaffold(
      // backgroundColor: Colors.white,
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          AnimatedSwitcher(
            duration: Duration(seconds: 3),
            child: mWidget,
          ),
          ElevatedButton(
              onPressed: () {
                setState(() {
                  mWidget = SecondWidget();
                });
              },
              child: Text("버튼")),
        ],
      )),
    );
  }
  
  // 변하는 위젯 - 이런 모양으로 다르게 만들면 된다.
  class FirstWidget extends StatelessWidget {
  const FirstWidget({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue[300],
      width: 100,
      height: 100,
    );
  }
}

AnimatedOpacity

3초후 희미해지게 설정해봤다.

몇가지만 수정한다.

// 필드에 상태값 추가
double myOpacity = 1.0;

// initState에 추가 - 3초뒤 change() 호출
    Future.delayed(Duration(seconds: 3), () {change();},

    void change(){
      setState(() {
        myOpacity = 0.2; // 3초뒤
      });
    }
  
// 변환하고 싶은 위젯을 감싼다
  AnimatedOpacity(
    duration: Duration(seconds: 3),
    opacity: myOpacity,
    child: Container(
      color: Colors.blue,
      width: 200,
      height: 200,
    ),
  ),
profile
작은것부터
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 6월 12일

잘봤습니다!

답글 달기