일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- FastAPI
- 파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습
- Computer Architecture
- BFS
- bloc
- study book
- ARM
- Algorithm
- MDP
- 백준
- fastapi를 사용한 파이썬 웹 개발
- ML
- Widget
- PCA
- Stream
- 영상처리
- Kaggle
- llm을 활용 단어장 앱 개발일지
- pytorch
- Flutter
- Dreamhack
- DART
- rao
- BOF
- MATLAB
- system hacking
- C++
- Image Processing
- Got
- BAEKJOON
- Today
- Total
Bull
[Flutter::Animation] Hero 기법 본문
Flutter 애니메이션으로 자주 사용되는 기법은 Fade와 Hero가 있습니다. 이번엔 Hero가 뭔지 언제 주로 사용하는 지에 대한 기본적인 내용만 다뤄 보겠습니다.
Hero 기본 개념
Flutter의 Hero 위젯은 두 개의 화면 사이에서 애니메이션 효과를 통해 부드럽게 전환되는 위젯을 제공합니다. 주로 이미지나 특정 위젯이 한 화면에서 다른 화면으로 이동할 때 자연스러운 전환 효과를 구현하는 데 사용됩니다.
Hero 애니메이션은 소스 화면과 목적지 화면에서 동일한 Hero 태그를 가진 위젯을 식별하여 작동합니다. 이렇게 하면 Flutter는 두 화면 사이에서 위젯의 위치와 크기를 애니메이션으로 연결할 수 있습니다.
Hero의 이름이 Hero인 이유?
Hero class 공식 문서에서는 Hero 위젯을 날아간다라고 표현하였습니다. 공식적으로 이 주제에 대한 설명은 못찾았지만 우리가 아는 "영웅"의 역할이 슈퍼맨과 같이 날아가거나 주인공이 눈에 띄게 튀는 효과를 내서 Hero라는 이름이 붙었다고 확신해도 될 거 같습니다. GPT와 제 추측이지만 만약 이게 사실이 아니더라도 Hero가 어떤 위젯인지 표현할 때 알기 쉬운 비유법이라고 생각되니 저는 이렇게 생각해도 좋을 것 같습니다.
CODE
본격적으로 Hero의 CODE를 살펴보겠습니다.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hero Animation Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Page')),
body: GridView.count(
crossAxisCount: 3,
children: <Widget>[
GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailPage(
tag: 'hero-image-1',
imageUrl: 'https://picsum.photos/250?image=9',
description: 'Image 1 Description',
);
}));
},
child: Hero(
tag: 'hero-image-1',
child: Image.network('https://picsum.photos/250?image=9'),
),
),
GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailPage(
tag: 'hero-image-2',
imageUrl: 'https://picsum.photos/250?image=10',
description: 'Image 2 Description',
);
}));
},
child: Hero(
tag: 'hero-image-2',
child: Image.network('https://picsum.photos/250?image=10'),
),
),
GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailPage(
tag: 'hero-image-3',
imageUrl: 'https://picsum.photos/250?image=11',
description: 'Image 3 Description',
);
}));
},
child: Hero(
tag: 'hero-image-3',
child: Image.network('https://picsum.photos/250?image=11'),
),
),
],
),
);
}
}
class DetailPage extends StatelessWidget {
final String tag;
final String imageUrl;
final String description;
DetailPage({required this.tag, required this.imageUrl, required this.description});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detail Page')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(height: 50),
Hero(
tag: tag,
child: Container(
child: Image.network(imageUrl, fit: BoxFit.cover),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
description,
style: TextStyle(fontSize: 18),
),
),
],
),
),
);
}
}
이미지 소스는 url을 통해 가져오니 DartPad를 통하여 확인 가능합니다. 결과 내용을 설명해보겠습니다.
사진을 누르면 페이지가 설명페이지로 전환되며 기존에 첫 페이지에 있던 사진이 없어지지 않고 부드럽게 전환되며 정해진 위치로 오게됩니다. 저는 해당 이미지에 대해 강조해주는 느낌을 받았습니다. Hero 위젯의 역할이 이런 게 아닐까요?
Hero의 동작을 한 문장으로 표현해서 글만 보면 힘들 수 있는데 결과를 보면 바로 이해할 수 있을 것 같습니다.
CODE가 어떻게 흘러가는지, 어떤 약속을 지켜야 하는지도 중요합니다. 이번엔 그것에 대해서 말해보겠습니다.
본론만 먼저 말하자면 두 페이지 모두 Hero 위젯을 사용합니다. 여기서 중요한 건, tag
프로퍼티를 사용했다는 점 인데요. 감이 오지 않나요? 바로 두 페이지간의 Hero 위젯에 tag
프로퍼티를 일치시켜 주어야합니다. 사실상 본론만으로 충분해요. 다른 개념은 라우트에 대한 기반지식이나 이미지 가져오고 파라미터 전달해주는 정도는 Hero 위젯과는 약간 동떨어진 내용일 수 있으니깐요. 기본으로 알아 두시는 게 좋습니다.
생소할 거 같은 부분만 살펴 보겠습니다.
Image.network
: 이미지를 url을 통해 가져올 수 있습니다. 첫 번째 인자로 url을 받고 fit
프로퍼티는 사진을 어떻게 컨테이너에 표현할 것인지를 나타냅니다. fit
프로퍼티에 대한 내용이 궁금하다면 Boxfit 공식 문서를 확인해주세요.
Route
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailPage(
tag: 'hero-image-2',
imageUrl: 'https://picsum.photos/250?image=10',
description: 'Image 2 Description',
);
}));
Flutter에서 기본으로 제공하는 라우트를 이동하는 방법입니다. Navigator.push
를 통해 위젯을 스택에 쌓아서 화면으로 보여줍니다. 반환값은 우리가 정의했던 DetailPage
로 넘어갈 수 있게 반환하고 인자로 tag, url, description을 전달해 줍니다. 원하는 사진을 눌렀을 때 GestureDetector
class의 onTap
으로 함수를 실행할 수 있는데 이 때, 각각의 이미지 tag
에 대한 값을 전달해줄 수 있습니다.
이를 통해 DetailPage
에서는 다시 Hero 위젯을 사용하여 전달받은 파라미터를 사용하여 눌렀던 이미지 내용과 동일한 tag
를 사용해서 표현할 수 있습니다.
Behind the scenes
Hero 위젯은 어떻게 동작하는 걸까요? 한 번 Hero 공식 문서의 내용을 그대로 설명해보겠습니다.
Hero를 실행하기 전입니다. 소스 지점은 위젯이 표시되어 있고 목적 지점은 아직 존재하지 않습니다. 여기 Overlay
라는 위젯이 표시되는데요.
Overlay
는 Flutter에서 위젯 트리를 통해 여러 위젯을 쌓아 올리는 데 사용되는 위젯입니다. 이는 일반적으로 앱의 위젯 계층 구조에서 가장 상위에 위치하며, 위젯을 임시로 표시하거나 특정 애니메이션 효과를 적용하는 데 사용합니다. Hero 애니메이션에서는 오버레이를 사용하여 애니메이션 중에 히어로 위젯을 화면 위에 떠있는 것처럼 보이게 합니다. 설명이 어렵다면 어떤 위젯을 화면에 표시하기 전에 아무것도 없는 장판같은 프레임이라고 봐도 무방합니다.
Navigator
를 통해 라우트를 눌렀을 때 입니다. 여기서 t는 누르는 시점에서 초기부분이라는 것을 0에서 1로 표시합니다.
Dest Hero는 우리의 코드로 치면 DetailPage
에 나와야 할 Hero 위젯을 우선 Overlay
위에 놓습니다. Dest Route 부분은 아직 랜딩이 안됐네요.
이제 Hero 위젯은 Dest Route에 설정했던 Hero 위젯의 위치로 날아갑니다. Hero의 직사각형 경계는 Hero's createRectTween 속성에 지정된 Tween<Rect>를 사용하여 애니메이션화됩니다. 기본적으로 Flutter는 직사각형의 반대쪽 모서리를 곡선 경로를 따라 애니메이션화하는 MaterialRectArcTween 인스턴스를 사용합니다.
비행이 완료되면 Flutter는 Hero 위젯을 Overlay
에서 목적지 경로로 이동합니다. 이제 Overlay
는 비어 있습니다.
Dest Route의 마지막 위치에 Dest Hero가 나타납니다.
'Software Framework > Flutter' 카테고리의 다른 글
[Flutter::Widget] Bottom Menu Dropdown 만들어보자. (0) | 2024.07.27 |
---|---|
[Flutter::Widget] 채팅 UI를 만들어 봅시다! (0) | 2024.07.25 |
[Flutter::Animation] Fade 기법 (1) | 2024.07.25 |
[Flutter::Animation] implicits animations (ImplicitlyAnimatedWidget) (2) | 2024.07.24 |
[Flutter::Animation] Ticker 이해 (0) | 2024.07.23 |