개발/Flutter

[Flutter] Animation :: 3. 애니메이션 만들기 (2)

Blaan 2023. 4. 21. 17:21
반응형

이번 글의 목표는 아이콘이 천천히 나타난 뒤에 오른쪽으로 이동해주는 연속적인 애니메이션 구현하기 입니다.

 


1. 오른쪽으로 이동하는 AnimationController와 Animation을 추가로 선언해줍시다.

import 'package:flutter/material.dart'; 

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

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget>
	with TickerProviderStateMixin {
	late AnimationController _iconVisibleController;
	late Animation<double> _iconVisibleAnimation;

	//오른쪽으로 이동하는 AnimationController와 Animation 선언
	late AnimationController _iconMoveRightController;
	late Animation<double> _iconMoveRightAnimation;

	@override
	void initState() {
		super.initState();

	_iconVisibleController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 1),
	);

	_iconVisibleAnimation = Tween<double>(
		begin: 0,
		end: 1, 
	).animate(CurvedAnimation(
		parent: _iconVisibleController,
		curve: const Interval(0.0, 1.0, curve: Curves.easeIn),
	));
	_iconVisibleController.forward(); 
}

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

	@override
	  Widget build(BuildContext context) {
	    return Scaffold(
	      backgroundColor: Colors.white,
	      body: Center(
	        child: AnimatedBuilder(
	          animation: _iconVisibleAnimation, 
	          builder: (context, child) => Opacity(
	            opacity: _iconVisibleAnimation.value,
	             child: Icon(
	               Icons.home,
	               size: 60,
	               color: Colors.black,
	             ),
	          ),
	        ),
	      ),
	    );
	  }
	}
	

 

2. 선언했으니 선언한게 뭔지 정의해줘야겠죠?
2초동안 오른쪽으로 움직이게 합시다.

import 'package:flutter/material.dart'; 

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

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget>
	with TickerProviderStateMixin {
	late AnimationController _iconVisibleController;
	late Animation<double> _iconVisibleAnimation;

	late AnimationController _iconMoveRightController;
	late Animation<double> _iconMoveRightAnimation;

	@override
	void initState() {
		super.initState();

	_iconVisibleController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 1),
	);

	_iconVisibleAnimation = Tween<double>(
		begin: 0,
		end: 1, 
	).animate(CurvedAnimation(
		parent: _iconVisibleController,
		curve: const Interval(0.0, 1.0, curve: Curves.easeIn),
	));

	//선언했으니 정의합시다.
	_iconMoveRightController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 2),
	);

	_iconMoveRightAnimation = Tween<double>(
		//현재 위치에서 시작해서 오른쪽으로 100px만큼 움직입니다.
		begin: 0,
		end: 100,
	).animate(CurvedAnimation(
		parent: _iconMoveRightController,
		//시작은 어느정도 속도가 있는 상태로 시작
		curve: const Interval(0.2, 1.0, curve: Curves.fastOutSlowIn),
	));

	_iconVisibleController.forward(); 
}

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

	@override
	  Widget build(BuildContext context) {
	    return Scaffold(
	      backgroundColor: Colors.white,
	      body: Center(
	        child: AnimatedBuilder(
	          animation: _iconVisibleAnimation, 
	          builder: (context, child) => Opacity(
	            opacity: _iconVisibleAnimation.value,
	             child: Icon(
	               Icons.home,
	               size: 60,
	               color: Colors.black,
	             ),
	          ),
	        ),
	      ),
	    );
	  }
	}
	

 

3. 이제 새로운 것을 추가해야 합니다. 여러 애니메이션을 순서대로 추가하려면 순서를 직접 지정해줘야 합니다.

그 순서는 addStatusListener로 지정해 줄 수 있어요.

처음 시작을 _iconVisibleController.forward()로 잡아줬죠?

그 밑에 만약 _iconVisibleController가 완료가 된 상태(Status)라면, 어떻게 할건지 지정해줍시다.

import 'package:flutter/material.dart'; 

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

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget>
	with TickerProviderStateMixin {
	late AnimationController _iconVisibleController;
	late Animation<double> _iconVisibleAnimation;

	late AnimationController _iconMoveRightController;
	late Animation<double> _iconMoveRightAnimation;

	@override
	void initState() {
		super.initState();

	_iconVisibleController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 1),
	);

	_iconVisibleAnimation = Tween<double>(
		begin: 0,
		end: 1, 
	).animate(CurvedAnimation(
		parent: _iconVisibleController,
		curve: const Interval(0.0, 1.0, curve: Curves.easeIn),
	));

	_iconMoveRightController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 2),
	);

	_iconMoveRightAnimation = Tween<double>(
		begin: 0,
		end: 100,
	).animate(CurvedAnimation(
		parent: _iconMoveRightController,
		curve: const Interval(0.2, 1.0, curve: Curves.fastOutSlowIn),
	));

	_iconVisibleController.forward(); 

	//addStatusListener로 순서를 잡아줍시다.
	//iconVisibleController로 첫 시작했으니 이녀석의 상태가 중요하겠죠?
	_iconVisibleController.addStatusListener((status) {
	//만약, _iconVisibleController의 상태가 completed라면
		if (status == AnimationStatus.completed) {
			// 저는 개인적으로 두번째 애니메이션이 시작하기 전 delay를 넣어보겠습니다.
			Future.delayed(
				const Duration(
					seconds: 2,
				),
			);
			// _iconMoveRightController를 실행합니다.
			_iconMoveRightController.forward();
			},
		}
	);
}

	@override
	void dispose() {
		_iconVisibleController.dispose();
		//잊지맙시다. dispose
		_iconMoveRightController.dispose();
		super.dispose();
	}

	@override
	  Widget build(BuildContext context) {
	    return Scaffold(
	      backgroundColor: Colors.white,
	      body: Center(
	        child: AnimatedBuilder(
	          animation: _iconVisibleAnimation, 
	          builder: (context, child) => Opacity(
	            opacity: _iconVisibleAnimation.value,
	             child: Icon(
	               Icons.home,
	               size: 60,
	               color: Colors.black,
	             ),
	          ),
	        ),
	      ),
	    );
	  }
	}
	

 

4. Controller, Animation 선언과 정의도 다했고, 순서도 지정해줬습니다.

그러면 어떤 대상이 어떤 애니메이션으로 실행될 지 지정해줍시다.

import 'package:flutter/material.dart'; 

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

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget>
	with TickerProviderStateMixin {
	late AnimationController _iconVisibleController;
	late Animation<double> _iconVisibleAnimation;

	late AnimationController _iconMoveRightController;
	late Animation<double> _iconMoveRightAnimation;

	@override
	void initState() {
		super.initState();

	_iconVisibleController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 1),
	);

	_iconVisibleAnimation = Tween<double>(
		begin: 0,
		end: 1, 
	).animate(CurvedAnimation(
		parent: _iconVisibleController,
		curve: const Interval(0.0, 1.0, curve: Curves.easeIn),
	));

	_iconMoveRightController = AnimationController(
		vsync: this,
		duration: const Duration(seconds: 2),
	);

	_iconMoveRightAnimation = Tween<double>(
		begin: 0,
		end: 100,
	).animate(CurvedAnimation(
		parent: _iconMoveRightController,
		curve: const Interval(0.2, 1.0, curve: Curves.fastOutSlowIn),
	));

	_iconVisibleController.forward(); 

	_iconVisibleController.addStatusListener((status) {
		if (status == AnimationStatus.completed) {
			Future.delayed(
				const Duration(
					seconds: 2,
				),
			);
			_iconMoveRightController.forward();
			}
		}
	);
}

	@override
	void dispose() {
		_iconVisibleController.dispose();
		_iconMoveRightController.dispose();
		super.dispose();
	}

	@override
	  Widget build(BuildContext context) {
		return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: AnimatedBuilder(
          animation: _iconVisibleAnimation,
          builder: (context, child) => Opacity(
            opacity: _iconVisibleAnimation.value,
						// 두번째 애니메이션이 실행될 대상입니다.
            child: AnimatedBuilder(
							// 애니메이션은 iconMoveRightAnimation
              animation: _iconMoveRightAnimation,
							// 아까는 Opacity를 이용했지만 이번엔 위치를 옮기는 것이기 때문에 Transform.translate
              builder: (context, child) => Transform.translate(
								// Offset의 파라미터는 2개 필요. x값, y값
								// 오른쪽으로만 움직일거니까 x에 값을 적어줍니다.
                offset: Offset(_iconMoveRightAnimation.value, 0),
								child: const Icon(
	                Icons.home,
	                size: 60,
	                color: Colors.black,
	              ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
	

짠!

결과물입니다.

 

이제 다음 페이지에서는 아이콘이 천천히 나타난 뒤에 오른쪽으로 이동했으니,

이제 왼쪽으로 움직이는 애니메이션을 추가해보도록 하겠습니다.

 

원글 : https://blaan.cc/Flutter-Animation-3-2-831209c1e62348a3a07bfeb201ec7eda

반응형