Routing 2: Add New Dog Page (강아지 등록 페이지 추가)

우리가 만들 페이지는 강아지를 추가하는 페이지이다.
다음 섹션에서 사용자 입력을 처리하는 방법을 보여주겠지만, 지금 해당 경로를 추가하는 것이 좋다.

1. Add NewDogPage

lib폴더에 new_dog_form.dart라는 파일을 신규로 생성한다.

이 페이지의 UI는 간단하다:
newdogpage

다음은 기능이 없는 코드이다. (다음 섹션에서 사용자 입력 기능을 추가할 예정이다)

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
// new_dog_form.dart

import 'package:flutter/material.dart';

class AddDogFormPage extends StatefulWidget {
@override
_AddDogFormPageState createState() => _AddDogFormPageState();
}

class _AddDogFormPageState extends State<AddDogFormPage> {
@override
Widget build(BuildContext context) {
// 새로운 페이지는 scaffolding이 필요하다.
return Scaffold(
appBar: AppBar(
title: Text('Add a new Dog'),
backgroundColor: Colors.black87,
),
body: Container(
color: Colors.black54,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 32.0,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
// Text Field는 플러터의 기본 입력 위젯이다.
// 이것은 아래에서 볼 수 있는 labelText와 같은 훌륭한UI 및 기능이 내장되어 있다.
child: TextField(
decoration: InputDecoration(
labelText: 'Name the Pup',
)),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: TextField(
decoration: InputDecoration(
labelText: "Pup's location",
)),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: TextField(
decoration: InputDecoration(
labelText: 'All about the pup',
),
),
),
// A Strange situation.
// 이상한 현상.
// 여러분이 다음 섹션에서 추가해야할 앱의 일부는 context를 알아야 하며,
// context를 전달하는 가장 쉬운 방법은 builder 메서드를 사용하는 것이다.
// 그래서 나는 이 button을 Builder에 일종의 'hack'으로 감쌌다.
Padding(
padding: const EdgeInsets.all(16.0),
child: Builder(
builder: (context) {
// 기본 Material 디자인 액션 버튼
return RaisedButton(
// 만약 onPressed가 null이면 button 은 비활성화(disabled)된다.
// 이건 필자가 만든 임시 callback 이다.
onPressed: () => print('PRESSED'),
color: Colors.indigoAccent,
child: Text('Submit Pup'),
);
},
),
),
],
),
),
),
);
}
}

2. Add the Routing

마지막 섹션과 마찬가지로 등록 페이지는 아직 액세스 할 수 없다. 버튼과 라우팅 정보를 _MyHomePageState 클래스에 추가하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// main.dart

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Colors.black87,
// Material appBar의 오른쪽 상단에 새로운 버튼을 추가하는 방법이다.
// 여러분이 원하는 만큼 추가 할 수 있다.
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: _showNewDogForm,
),
],
),
...

그러면 앱의 오른쪽 상단 모서리에 플러스 기호 버튼이 추가되고 새로운 경로(route) 를 빌드 하는 메서드를 추가 할 수 있다.

main.dartnew_dog_form.dart import.

1
2
3
4
5
6
7
// main.dart

import 'package:flutter/material.dart';

import 'dog_list.dart';
import 'dog_model.dart';
import 'new_dog_form.dart';

아래 코드를 _MyHomePageState 클래스 아무곳에나 추가하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 새로운 경로를 pushing하고 그 경로가 여러분에게 되돌아 오기를 기대할때마다 여러분은 비동기 함수(async funtion)를 사용해야 한다.
// 이 경우 함수는 사용자가 작성하고 제출 할 수 있는 양식 페이지를 만든다.
// 제출시(submission) 해당 페이지의 정보가 이 함수로 다시 전달 된다.
Future _showNewDogForm() async {
// push a new route like you did in the last section
Dog newDog = await Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return AddDogFormPage();
},
),
);

// 사용자가 양식을 입력했는지를 확인하기 위한 null 검사.
if (newDog != null) {
// 새로운 강아지 정보를 강아지 목록에 추가.
initialDoggos.add(newDog);
}
}

Comment and share

크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

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 위젯에서 재정의 할 수 있다.

Comment and share

크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.
  • page 1 of 1
Author's picture

Jace Shim


Seoul, Korea