Routing 1: Pages on the Fly (페이지 이동)

플러터 앱에 페이지를 추가하는 방법에는 몇가지 있다.

여러분이 알고 있는 모든 페이지에 대해, 여러분은 선언된 경로를 사용 할 수 있다.

하지만 이 앱은 정확히 강아지 한마리당 하나씩이 페이지를 만들것이고 이는 경로 작성자에게 좋은 예(case) 이다.

1. Create a Dog Detail Page: (강아지 상세페이지 만들기)

dog_detail_page.dart라는 신규 파일을 생성하자.

이것은 StatefulWidget이 될것이고 앱 사용자는 나중에 강아지를 평가 하게 할거지만 현재로선 관리할 상태(state)가 없다.

아래 그림은 당분간 여러분이 만들려고 하는 것이다.
dog detail page

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// dog_detail_page.dart

import 'package:flutter/material.dart';

import 'dog_model.dart';

class DogDetailPage extends StatefulWidget {
final Dog dog;

DogDetailPage(this.dog);

@override
_DogDetailPageState createState() => _DogDetailPageState();
}

class _DogDetailPageState extends State<DogDetailPage> {
// 스타일에 대한 임의의 사이즈 선택
final double dogAvatarSize = 150.0;

Widget get dogImage {
// Container는 자식들의 사이즈를 정의
return Container(
height: dogAvatarSize,
width: dogAvatarSize,
// Box Decoration을 사용하여 이미지를 원형으로 만들고, 스타일을 위해 임의의 그림자(shadow)를 추가한다.
decoration: BoxDecoration(
shape: BoxShape.circle,
// CSS에서 처럼 여러분은 종종 오른쪽 모양을 위해 다수의 BoxShadows를 추가하길 원한다.
// 그래서 boxShadow 속성은 BoxShadows의 목록을 가진다.
boxShadow: [
const BoxShadow(
// CSS 처럼:
// 이것은 동일한 4개의 속성을 가진다.
offset: const Offset(1.0, 2.0),
blurRadius: 2.0,
spreadRadius: -1.0,
color: const Color(0x33000000)),
const BoxShadow(
offset: const Offset(2.0, 1.0),
blurRadius: 3.0,
spreadRadius: 0.0,
color: const Color(0x24000000)),
const BoxShadow(
offset: const Offset(3.0, 1.0),
blurRadius: 4.0,
spreadRadius: 2.0,
color: const Color(0x1F000000)),
],
// 컨테이너 배경에 이미지를 추가.
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(widget.dog.imageUrl),
),
),
);
}

// ★ 10/10으로 표시되는 등급 부분
Widget get rating {
// 위젯을 수평으로 배치하기 위해 row를 사용
return Row(
// 위젯을 행(row)의 가로축 중심에 배치
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.star,
size: 40.0,
),
Text(' ${widget.dog.rating} / 10',
style: Theme.of(context).textTheme.display2),
],
);
}

// 이미지, 평가점수, 강아지 정보를 표시하는 위젯
Widget get dogProfile {
return Container(
padding: EdgeInsets.symmetric(vertical: 32.0),
decoration: BoxDecoration(
// 이것은 앱 전체에 공유 할 수 있는 커스텀 LinearGradient 위젯을 생성 할 수 있는 좋은 기회다
// 하지만 난 이걸 당신에게 맡길것이다. (재사용 가능한 컴포넌트 관점임)
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
stops: [0.1, 0.5, 0.7, 0.9],
colors: [
Colors.indigo[800],
Colors.indigo[700],
Colors.indigo[600],
Colors.indigo[400],
],
),
),
// 강아지 프로필 정보
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
dogImage,
Text(
'${widget.dog.name} 🎾',
style: TextStyle(fontSize: 32.0),
),
Text(
widget.dog.location,
style: TextStyle(fontSize: 20.0),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 32.0, vertical: 16.0),
child: Text(widget.dog.description),
),
rating
],
),
);
}

// 마지막으로 빌드 메서드:
// 하나의 거대한 빌드 메서드를 작성하는것 보다 이 파일의 구현처럼 위젯을 분해해서 UI를 만드는것이 훨씬 쉽다.
@override
Widget build(BuildContext context) {
// This is a new page, so you need a new Scaffold!
return Scaffold(
backgroundColor: Colors.black87,
appBar: AppBar(
backgroundColor: Colors.black87,
title: Text('Meet ${widget.dog.name}'),
),
body: dogProfile,
);
}
}

2. Add the Routing mechanism: (라우팅 메커니즘 추가)

여러분은 DogDetailPage를 추가했지만 해당 페이지로 이동할 수 없다. 자~ 라우팅을 추가해 보자.

모든 강아지 목록을 나열하는 메인 페이지상에서 모든 card는 tap하면 강아지의 상세 페이지로 이동하는 버튼이 될 것이다.

dog_card.dart안에 DogDetailPage을 import하자.

1
2
3
4
5
6
// dog_card.dart

import 'package:flutter/material.dart';

import 'dog_detail_page.dart';
import 'dog_model.dart';

_DogCardState클래스에 모든것을 버튼(역자주: 실제 버튼이 아니라 버튼처럼 작동하는) 하나로 감싸야 한다.

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
32
33
34
35
36
37
38
39
40
41
42
43
// dog_card.dart

@override
Widget build(BuildContext context) {
// InkWell은 자식들을 태핑(tappable) 할 수 있게 해주는 특별한 Material위젯으로 , 탭되면 Material design 잉크(ink) 리플(ripple)을 추가함.
return InkWell(
// onTap은 탭 되면 트리거되는 callback이다.
onTap: showDogDetailPage,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
height: 115.0,
child: Stack(
children: <Widget>[
Positioned(
left: 50.0,
child: dogCard,
),
Positioned(top: 7.5, child: dogImage),
],
),
),
),
);
}

// 새로운 페이지를 만드는 builder메서드
showDogDetailPage() {
// Navigator.of(context) 는 현재 앱의 navigator에 접근한다.
// Navigators는 새로운 경로를 stack안에 'push'할 뿐만 아니라 pop 경로를 스택에서 없어지게 한다.
//
// This is the easiest way to build a new page on the fly
// and pass that page some state from the current page.
// 이것은 새로운 페이지를 만들고 해당 페이지를 현재 페이지의 일부 상태를 전달하는 가장 쉬운 방법이다.
Navigator.of(context).push(
MaterialPageRoute(
// builder methods always take context!
builder: (context) {
return DogDetailPage(dog);
},
),
);
}

이제 앱은 각 강아지 마다 페이지가 존재한다.
강아지 상세 페이지에 ‘뒤로가기’ 버튼이 있다는 것을 눈치 챘을 거다, 하지만 그에 대한 코드는 없다.
플러터는 자동으로 AppBar에 경로를 되돌리는 버튼을 추가한다. 필요한 경우 AppBar 위젯에서 재정의 할 수 있다.