플러터에서 화면을 이동하는 방법은 여러 가지가 있습니다.
그중에서 PageRouteBuilder를 사용한 방법을 알아보겠습니다.
그리고 MaterialPageRoute를 사용한 방법도 존재하는데
둘의 차이점도 무엇인지 알아보겠습니다.
PageRouteBuilder를 사용한 화면 이동
먼저 PageRouteBuilder를 사용하기 위해선 StatelessWidget이나 StateFullWidget 같은 위젯이 필요합니다.
예를 들어 다음과 같은 StatelessWidget이 있다고 가정합니다.
import 'package:flutter/material.dart';
class DetailScreen extends StatelessWidget {
final String title, thumb, id;
const DetailScreen({
super.key,
required this.title,
required this.thumb,
required this.id,
});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Center(
child: Text(
title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w500,
),
),
),
foregroundColor: Colors.green,
backgroundColor: Colors.white,
elevation: 1,
),
);
}
}
간단하게 Appbar 하나만을 갖고 있는 StatelessWidget입니다.
(여기서 포인트는 AppBar에는 title만 정의하고 있다는 것입니다.)
이를 특정 뷰를 클릭했을 때 이동하도록 만들어보겠습니다.
다음과 같이 GestureDetector를 사용하면 특정 위젯을 클릭했을 때 이벤트를 설정할 수 있습니다.
여기서 Navigator.push를 사용하여 다른 화면으로 이동할 수 있다.
GestureDetector(
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
transitionsBuilder:
// secondaryAnimation: 화면 전화시 사용되는 보조 애니메이션효과
// child: 화면이 전환되는 동안 표시할 위젯을 의미(즉, 전환 이후 표시될 위젯 정보를 의미)
(context, animation, secondaryAnimation, child) {
// Offset에서 x값 1은 오른쪽 끝 y값 1은 아래쪽 끝을 의미한다.
// 애니메이션이 시작할 포인트 위치를 의미한다.
var begin = const Offset(1.0, 0);
var end = Offset.zero;
// Curves.ease: 애니메이션이 부드럽게 동작하도록 명령
var curve = Curves.ease;
// 애니메이션의 시작과 끝을 담당한다.
var tween = Tween(
begin: begin,
end: end,
).chain(
CurveTween(
curve: curve,
),
);
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
// 함수를 통해 Widget을 pageBuilder에 맞는 형태로 반환하게 해야한다.
pageBuilder: (context, animation, secondaryAnimation) =>
// (DetailScreen은 Stateless나 Stateful 위젯으로된 화면임)
DetailScreen(title: title, thumb: thumb, id: id),
// 이것을 true로 하면 dialog로 취급한다.
// 기본값은 false
fullscreenDialog: false,
),
);
},
child: Column(
children: [
// CardView 처럼 보이게 하기 위해 Container를 사용
Container(
width: 200,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
blurRadius: 8,
offset: const Offset(8, 8),
color: Colors.black.withOpacity(0.5),
)
]),
child: Image.network(
thumb,
),
),
const SizedBox(
height: 8,
),
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
],
),
);
PageRouteBuilder는 transitionBuilder를 필요로 하는데 해당 Builder에서 어떠한 이동을 통해 화면 전환을 할지 정해야 한다.
위 코드와 같이 작성했을 때 화면 전환은 다음과 같이 진행된다.
Offset 조정을 통해 애니메이션의 시작을 어디서 할지 지정할 수 있다.
또한 동작을 보면 AppBar에 만들지도 않은 <-(뒤로 가기)가 표시된 것을 확인할 수 있다.
플러터는 화면 이동을 통해 다른 Widget으로 이동하고 해당 Widget에 Appbar가 있다면 해당 AppBar에 자동으로 뒤로가기 또는 닫기 버튼을 만들어준다.
Appbar에 뒤로가기 버튼을 만들고 싶지 않다면?
Navigator.push 대신 Navigator.pushReplacement를 사용한다.
MaterialPageRoute와는 어떻게 다를까?
PageRouteBuilder뿐 아니라 MaterialPageRoute를 사용해서도 화면 전환을 할 수 있다.
이는 PageRouteBuilder와 어떻게 다른지 알아본다.
MaterialPageRoute를 사용해서 만든 코드는 다음과 같다.
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(title: title, thumb: thumb, id: id),
),
);
},
child: Column(
children: [
// CardView 처럼 보이게 하기 위해 Container를 사용
Container(
width: 200,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
blurRadius: 8,
offset: const Offset(8, 8),
color: Colors.black.withOpacity(0.5),
)
]),
child: Image.network(
thumb,
),
),
const SizedBox(
height: 8,
),
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
],
),
);
MaterailPageRoute의 경우 builder만 정의하면 되기 때문에 코드가 매우 간결해진 모습을 볼 수 있다.
이를 사용한 페이지 전환 모습은 다음과 같다.
안드로이드의 startActivity와 유사한 방법으로 navigate 하는 모습을 볼 수 있다.
그 이유는 MaterialPageRoute가 각 OS의 기본적인 navigation 애니메이션을 통해 화면을 바꾸기 때문이다.
이것을 보면 MaterialPageRoute의 동작은 startActivity와 유사한 방식으로 다른 화면으로 navigate 한다는 것을 알 수 있다.
Hero 위젯을 사용하여 애니메이션 추가하기
Hero 위젯을 사용하면 화면 전환할 때 넘긴 데이터를 사용하여 좀 더 자연스러운 애니메이션을 추가할 수 있다.
예시에서는 카드뷰 안에 있는 Image 위젯의 그림을 클릭 시 자연스럽게 다음 화면으로 전환함과 동시에 이동시키는 효과를 추가한다.
먼저 기존 데이터를 갖고 있는 위젯을 Hero 위젯으로 감싼다.
Hero(
tag: id,
child: Container(
width: 200,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
blurRadius: 8,
offset: const Offset(8, 8),
color: Colors.black.withOpacity(0.5),
)
]),
child: Image.network(
thumb,
),
),
),
위와 같이 그림 데이터가 있는 Container 위젯을 Hero 위젯으로 감싼다.
그리고 tag에는 해당 그림에 데이터가 갖고 있는 고유 id를 넣어준다.
(tag에 넣어주는 값은 해당 데이터가 갖고 있느 고유 값이라면 아무거나 상관없다)
화면은 전환하는 위젯에서도 똑같이 Hero 위젯으로 이미지가 있는 위젯을 감싼다.
Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Center(
child: Text(
title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w500,
),
),
),
foregroundColor: Colors.green,
backgroundColor: Colors.white,
elevation: 1,
),
body: Column(
children: [
const SizedBox(
height: 48,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Hero(
tag: id,
child: Container(
width: 200,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
blurRadius: 8,
offset: const Offset(8, 8),
color: Colors.black.withOpacity(0.5),
)
]),
child: Image.network(
thumb,
),
),
),
],
)
],
),
);
이렇게 화면 전환 전후 동일한 데이터를 갖고 있는 동일한 위젯을 Hero 위젯으로 감싸면
다음과 같은 애니메이션이 추가된다.
'플러터(flutter)' 카테고리의 다른 글
플러터(flutter) - 온보딩 화면 만들기 (0) | 2023.06.03 |
---|---|
Flutter 기초 - SharedPreference 사용 방법 (0) | 2023.05.28 |
플러터 기초 - HttpOverrides으로 userAgent 설정하기 (0) | 2023.05.13 |
플러터 기초 - ListView 위젯 사용하기 (0) | 2023.05.13 |
플러터 기초 - FutureBuilder를 사용한 비동기 작업 수행 (0) | 2023.05.12 |
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
댓글