[Flutter] Hero 애니메이션 사용법
Hero 애니메이션은 안드로이드에서 공유 요소 전환과 같은 효과를 내는 애니메이션이다.
Hero 애니메이션의 공식문서부터 먼저 확인한다.
https://docs.flutter.dev/ui/animations/hero-animations
한 화면에서 다른 화면으로 이미지를 이동하는 걸 Hero 애니메이션이라 하며 공유 요소 전환이라고도 한다. Hero 위젯을 써서 쉽게 구현할 수 있고 MaterialPageRoute를 써서 새 경로를 지정하면 머티리얼 디자인 모션 사양에 설명된 대로 이미지가 곡선 경로를 따라 날아간다
아래는 플러터 문서에서 링크된 깃허브로 이동하면 볼 수 있는 코드다.
class PhotoHero extends StatelessWidget {
const PhotoHero({
super.key,
required this.photo,
this.onTap,
required this.width,
});
final String photo;
final VoidCallback? onTap;
final double width;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: Hero(
tag: photo,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: Image.asset(
photo,
fit: BoxFit.contain,
),
),
),
),
);
}
}
이 코드의 핵심 정보는 아래와 같다.
- 시작 경로는 Hero 애니메이션이 앱의 home 프로퍼티로 제공될 때 MaterialApp에 의해 암시적으로 push된다
- InkWell이 이미지를 감싸서 onTap 제스처를 쉽게 추가할 수 있다
- 머티리얼 위젯을 투명한 색으로 정의하면 이미지가 목적지로 날아갈 때 배경에서 튀어나오게 할 수 있다
- SizedBox는 애니메이션 시작, 끝에서 Hero 위젯 크기를 지정한다
- 이미지의 맞춤 속성을 BoxFit.contain으로 설정하면 화면 비율을 바꾸지 않고 전환하는 동안 이미지가 최대한 커진다
아래는 HeroAnimation 클래스의 전체 코드다.
class HeroAnimation extends StatelessWidget {
const HeroAnimation({super.key});
Widget build(BuildContext context) {
timeDilation = 5.0; // 1.0 means normal animation speed.
return Scaffold(
appBar: AppBar(
title: const Text('Basic Hero Animation'),
),
body: Center(
child: PhotoHero(
photo: 'images/flippers-alpha.png',
width: 300.0,
onTap: () {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flippers Page'),
),
body: Container(
color: Colors.lightBlueAccent,
padding: const EdgeInsets.all(16),
alignment: Alignment.topLeft,
child: PhotoHero(
photo: 'images/flippers-alpha.png',
width: 100.0,
onTap: () {
Navigator.of(context).pop();
},
),
),
);
}
));
},
),
),
);
}
}
이 코드의 핵심은 아래와 같다.
- 유저가 Hero가 포함된 InkWell을 탭하면 MaterialPageRoute를 써서 목적지 경로를 생성한다. 이 경로를 네비게이터의 스택으로 push하면 Hero 애니메이션이 트리거된다
- Container는 대상 경로의 왼쪽 상단 모서리, AppBar 아래에 PhotoHero를 배치한다
- 목적지 PhotoHero의 onTap은 네비게이터 스택을 팝업해서 Hero를 원래 경로로 되돌리는 애니메이션을 트리거한다
- 디버깅하는 동안 전환 속도를 늦추려면 timeDilation 프로퍼티를 사용한다
위 코드를 실행하면 아래처럼 작동한다.
PhotoHero 클래스는 Hero의 크기, 이미지, onTap에 정의한 동작을 유지하는데 아래와 같은 위젯 트리를 빌드한다.
정리하면 Hero 애니메이션은 한 화면에서 다른 화면으로 이동할 때 같이 날아가는 것처럼 보이게 만들어주는 애니메이션이다. 구현하려면 Hero 위젯을 사용하며 애니메이션을 트리거하기 위해 InkWell을 사용할 수 있다. InkWell이 아니라도 GestureDetector를 써서 onTap 매개변수를 구현하면 동일한 효과를 낼 수 있다.
그리고 Hero 애니메이션을 구현할 때 아래를 신경써야 한다.
- 두 화면에서 각각 Hero 위젯을 사용해야 한다
- Hero 위젯을 사용하면 tag는 필수 구현대상이다
- Navigator 기반 화면 전환(push, pop 등)
PhotoHero의 build()에서 Hero 위젯의 첫 번째 매개변수로 tag를 적고 photo를 넣은 걸 볼 수 있다.
그리고 HeroAnimation 클래스에서 Center, Container 안에서 각각 PhotoHero 위젯을 구현하고 push, pop을 통해 앞으로 / 뒤로 화면을 이동할 때마다 Hero 애니메이션을 보여준다.
적절하게 넣으면 단조로운 화면 이동 과정을 색다르게 보여줄 수 있을 것이다.