seong
Flutter - Pro Animate 3D 입체감 살리기 본문
마지막으로 내가 이 클론코딩을 선택한 이유중 한개인 SideMenu클릭시 3D 입체감 표현이다.
시작 전 기본적으로 화면은 Stack으로 구성이 되어 있고,
AnimatedPosition, Transform이 주로 사용된다.
1. SideMenu 먼저 호출
Positioned(
width: 288,
height: MediaQuery.of(context).size.height,
child: const SideMenu(),
),
2. HomeScreen 에 translate 적용
Transform.translate는 자식 위젯을 이동시켜주는 역할을 한다.
Transform.translate(
offset: Offset(isSideMenuClosed ? 0 : 288,0), // x : 288, y : 0
child: HomeScreen()
)
3. HomeScreen 사이즈 조정
scale는 자식 위젯의 사이즈를 조정해준다.
Transform.translate(
offset: Offset(isSideMenuClosed ? 0 : 288,0),
child: Transform.scale(
scale:isSideMenuClosed ? 1 : 0.8,
child:const ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(24),
),
child: HomeScreen()),
),
),
4. SideMenu에 Animate추가
duration : 0.2초동안 Animate실행
curve : Animate의 속도를 제어
left: isSideMenuClosed ? -288 : 0 // isSideMenuClosed = true라면 SideMenu를 화면상 -288로 이동시킨다.
288과 -288은 언뜻 보기엔 다른점이 별로 없었다, 하지만 자세히보면 SideMenu가 생겼다가 다시 들어갈때 -288은 왼쪽으로 사라지고 288은 오른쪽으로 사라진다.
쉽게 말해, SideMenu가 288일 경우 HomeScreen 아래로 사라지는것 처럼 보이게된다.
AnimatedPositioned(
duration: const Duration(milliseconds: 200),
//curve는 애니메이션의 속도를 제어한다, fastOutSlowIn는 시작할 때 빠르게 가속하고, 중간에 천천히 감속
curve: Curves.fastOutSlowIn,
width: 288,
//isSideMenuClosed가 true이면 그대로, 만약 클릭되었다면 -288.
left: isSideMenuClosed ? -288 : 0,
height: MediaQuery.of(context).size.height,
child: const SideMenu(),
),
4번까지 SideMenu클릭 시 HomeScreen의 위치 조정 및 크기는 어느정도 정해졌다.
이제 HomeScreen이 이동할때 부드럽게이동 하는것과, 3D Animate를 입혀주면 된다.
5. SingleTickerProviderStateMixin 추가, AnimationController, Animation 선언 및 초기화
SingleTickerProviderStateMixin을 상속해 vsync를 매개변수로 받아서 AnimationController를 만들 수 있다.
만약 다중 AnimationController를 사용할 시 TickerProviderStateMixin 를 사용해주는게 좋다.
SingleTickerProviderStateMixin은 애니메이션 컨트롤러를 제어하는 데 사용되고, TickerProvider 구현부 중 일부이다.
TickerProvider는 애니메이션 프레임을 제공해준다.
controller 초기화
vsync는 필수 속성이며, 애니메이션을 언제 재생할지와, 위젯의 생명주기와 동기화 시켜준다.
animation을 초기화 하고, 리스너를 등록해주게 되면, animation이 동작하고 난 후 리스너에 등록된 setState가 동작해 rebuild가 된다.
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
)..addListener(() {
setState(() {});
});
animation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(parent: _animationController, curve: Curves.fastOutSlowIn),
);
scaleAnimation = Tween<double>(begin: 1, end: 0.8).animate(
CurvedAnimation(parent: _animationController, curve: Curves.fastOutSlowIn),
);
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
6. Transform 에 animation값 적용
Transform.translate(
//가로로 265만큼 이동
offset: Offset(animation.value * 265, 0),
//scale는 자식 위젯의 크기를 조정한다. 주로 동적으로 변하는 화면에 사용할 수 있다.
child: Transform.scale(
//1 -> 0.8만큼
scale: scaleAnimation.value,
child: const ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(24),
),
child: HomeScreen()),
),
),
7. Animate 시작
SideMenu버튼 클릭시 화면이 움직이기 때문에 버튼의 onPress부분에 .forward()메서드를 넣어주었다.
.foward() : animate 시작
.reverse() : animate 반대로
AnimatedPositioned(
// SideMenu가 열려있을 경우 x버튼을 오른쪽으로 이동
duration: const Duration(milliseconds: 200),
curve: Curves.fastOutSlowIn,
left: isSideMenuClosed ? 0 : 220,
top: 16,
child: MenuBtn(
riveOnInit: (artboard) {
StateMachineController controller = RiveUtils.getRiveController(artboard, stateMachineName: "State Machine");
isSideBarClosed = controller.findSMI("isOpen") as SMIBool;
isSideBarClosed.value = true;
},
press: () {
isSideBarClosed.value = !isSideBarClosed.value;
//SideBar가 닫혀 있으면(true) animate 시작
if (isSideMenuClosed) {
_animationController.forward();
} else {
//animate reverse(역방향)
_animationController.reverse();
}
setState(() {
isSideMenuClosed = isSideBarClosed.value;
});
},
),
),
8. 이제 마지막 단계인 3D를 적용 시켜준다.
setEntry() : 원근감,
rotateY() : Y축(세로)을 기준으로 회전 , 만약 X축(가로)을 원한다면 rotateX()를 사용하면 된다.
이것에 대한 자세한 설명은 https://lucky516.tistory.com/123 이 글이 설명이 아주 잘 되어있다.
Transform(
//3D화면
alignment: Alignment.center,
transform: Matrix4.identity()
//4 x 4 에서 x축으로 3, y축으로 2 에 0.001을 설정.
..setEntry(3, 2, 0.001)
// 1-(30 * 1 * pi / 180)
..rotateY(animation.value - 30 * animation.value * pi / 180),
child: Transform.translate(
offset: Offset(animation.value * 265, 0),
//scale는 자식 위젯의 크기를 조정한다. 주로 동적으로 변하는 화면에 사용할 수 있다.
child: Transform.scale(
scale: scaleAnimation.value,
child: const ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(24),
),
child: HomeScreen()),
),
),
),
'Flutter > Flutter' 카테고리의 다른 글
Flutter - Google font 적용 (1) | 2023.10.16 |
---|---|
Weather앱 만들기 - 날씨 API연동 (GetX,MVVM) (0) | 2023.10.13 |
Flutter - SideMenu (0) | 2023.09.12 |
Flutter -RiveAnimation Icon (2) (0) | 2023.09.12 |
Flutter - showDialog를 화면 위에서 내려오게 하기 (0) | 2023.09.12 |