Forms 2: Sliders and Buttons

강아지의 능력을 평가 할 수 있는 매우 중요한 기능을 추가할때가 됐다.

1. Add the Form

dog_detail_page.dart에 폼 UI를 추가하는것부터 시작하자.

아래 그림은 페이지가 어떻게 보여줄지를 보여준다:
sliders

인터페이스는 두개의 중요 위젯으로 이루어 진다.

  1. 점수를 변경하는 Slider.
  2. slider의 값을 제출하는 RaisedButton.

_DogDetailPageState에 둘다 추가하자.

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

class _DogDetailPageState extends State<DogDetailPage> {
final double dogAvatarSize = 150.0;
// slider의 초기값
double _sliderValue = 10.0;

// ...

Widget get addYourRating {
return Column(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 16.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// row, column, listvie, 기타 등에서 Flexbile(유연한) 위젯은
// CSS의 flex-grow속성과 매우 유사하게 작동하는 래퍼(wrapper)다.
Flexible(
flex: 1,
// 많은 폼 요소와 마찬가지로 slider는 자체 값을 알고 그 값을 업데이트하는 방법을 알야야함.
//
// slider는 값이 변경될때 onChanged가 호출 되지만,
// 오직 setState를 사용하여 state의 value속성이 변경될때만 다시 그린다.
//
// The workflow is:
// 작동흐름은
// 1. 사용자가 slider를 드래그(drag)한다.
// 2. onChanged가 호출된다.
// 3. onChanged의 콜백이 sliderValue state를 설정한다.
// 4. 플러터는 SliderValue에 의존하는 모든것을 다시 그린다.
// 이 경우에는 slidera만 새로운 값으로 변경한다.
child: Slider(
activeColor: Colors.indigoAccent,
min: 0.0,
max: 15.0,
onChanged: (newRating) {
setState(() => _sliderValue = newRating);
},
value: _sliderValue,
),
),

// slider의 값을 표시하는 부분.
Container(
width: 50.0,
alignment: Alignment.center,
child: Text('${_sliderValue.toInt()}',
style: Theme.of(context).textTheme.display1),
),
],
),
),
submitRatingButton,
],
);
}

// 현재로선 아무것도 하는게 없는 간단한 Raised 버튼.
Widget get submitRatingButton {
return RaisedButton(
onPressed: () => print('pressed!'),
child: Text('Submit'),
color: Colors.indigoAccent,
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black87,
appBar: AppBar(
backgroundColor: Colors.black87,
title: Text('Meet ${widget.dog.name}'),
),
// Make the body a ListView that displays
// both the profile and the rating form.
// body를 프로필(profile)과 평가 폼 두개를 모두 표시하는 ListView로 만든다.
body: ListView(
children: <Widget>[dogProfile, addYourRating],
),
);
}
}

앱을 hot reload 했다면 작동되는 slider가 있을것이다.

2. Wire up the Submit button (submit버튼 연결)

submitRatingButtonSlider onChanged 콜백에서 기술적으로 처리 할 수 있는 일을 한다.

Dog클래스 자체 평가(rating)를 업데이트한다. 그렇게 되면 플러터는 Dog평가가 포하는 모든것을 다시 작성하기 때문에 앱 전체에 걸쳐 새로운 평가가 보여진다.

_DogDetailPageState 클래스에 이 함수를 추가하고 submitButton이 눌려졌을때 이를 호출 하도록 추가하자.

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

// 다음 섹션에서 여러분은 error 처리를 추가할 것이지만,
// 지금은 이것이 전부 이다.
void updateRating() {
setState(() => widget.dog.rating = _sliderValue.toInt());
}

그리고 submitRatingButton 위젯을 다음과 같이 변경하자.

1
2
3
4
5
6
7
8
9
// dog_detail_page.dart

Widget get submitRatingButton {
return RaisedButton(
onPressed: updateRating,
child: Text('Submit'),
color: Colors.indigoAccent,
);
}

추가 후에 slider를 이동하고 submit을 누르면 메인 페이지로 돌아 가서 변경된 강아지 평가를 볼 수 있다.

Comment and share

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

Forms 1: User Input

AddDogForm에 기능을 추가하는것은 쉬운 일이다.
기본적으로 폼 입력을 추적하고 라우터를 통해 메인 페이지로 데이터를 반환하는 함수를 플러터 클래스에 추가하면 된다.

1. TextEditingController class

입력 폼 요소를 추적하려면 Form위젯을 추가하거나, 텍스트 입력을 별도로 추적 하면 된다.

이 예제에선 후자(텍스트 입력을 별도로 추적)를 통해서 처리 하는 방법을 보여줄 것이다. TextEditingController 는 플러터에서 중요하고 기본적인 것이다.

TextEditingController는 기본적으로 할당된 TextField를 청취(listens)하는 클래스이고 TextField의 텍스트가 변경 될때마다 내부 상태를 업데이트 한다.

여러분의 _AddDogFormPageState 클래스에서 각 TextFieldcontrolleronChanged속성을 추가하자.

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

class _AddDogFormPageState extends State<AddDogFormPage> {
// One TextEditingController for each form input:
TextEditingController nameController = TextEditingController();
TextEditingController locationController = TextEditingController();
TextEditingController descriptionController = TextEditingController();

@override
Widget build(BuildContext context) {
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),
child: TextField(
// TextField에 controller를 지정.
controller: nameController,
// TextField의 텍스트가 변경될때마다 onChanged 콜백 함수가 호출되고 값이 전달된다.
//
// controller의 텍스트를 다음 value로 설정.
onChanged: (v) => nameController.text = v,
decoration: InputDecoration(
labelText: 'Name the Pup',
)),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: TextField(
controller: locationController,
onChanged: (v) => locationController.text = v,
decoration: InputDecoration(
labelText: "Pups location",
)),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: TextField(
controller: descriptionController,
onChanged: (v) => descriptionController.text = v,
decoration: InputDecoration(
labelText: 'All about the pup',
)),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Builder(
builder: (context) {
return RaisedButton(
onPressed: () => print('PRESSED'),
color: Colors.indigoAccent,
child: Text('Submit Pup'),
);
},
),
),
],
),
),
),
);
}
}

새로운 일이 일어나고 있는 것처럼 보이지는 않지만, TextEditingControllers는 여러분의 폼(form)을 추적하고 있다.

2. Submit The Form

new_dog_form.dart 파일에 dog_model.dart import처리.

1
2
3
4
5
// new_dog_form.dart

import 'package:flutter/material.dart';

import 'dog_model.dart';

동일한 _AddDogFormPageState 클래스에 Navigator를 통해 폼 정보를 다시 전달하는 submitPup함수를 추가한다.

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

// Navigator가 작동하려면 context가 필요하다.
void submitPup(BuildContext context) {
// 먼저 몇몇 폼 정보가 있는지 확인한다.
// 강아지는 name을 필요로 하지만 location은 불필요할 수도 있으므로, name이 없으면 저장을 포기해야 한다.
if (nameController.text.isEmpty) {
print('Dogs need names!');
} else {
// 폼 정보로 새로운 강아지를 생성한다.
var newDog = Dog(nameController.text, locationController.text,
descriptionController.text);
// route stack에서 page를 pop하고 페이지가 만들어진곳으로 새로운 강아지를 다시 전달한다.
Navigator.of(context).pop(newDog);
}
}

마지막으로, 'RaisedButton’의 onPressed 콜백에 submitPop을 추가한다.

1
2
3
4
5
6
7
8
9
// new_dog_form.dart

builder: (BuildContext context) {
return RaisedButton(
onPressed: () => submitPup(context),
color: Colors.indigoAccent,
child: Text('Submit Pup'),
);
},

그게 다다.(이게 끝이다) 이제 새로운 강아지를 제출해서 main페이지에서 확인해 보자.

Comment and share

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

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 대한민국 라이선스에 따라 이용할 수 있습니다.

Gradients Background

Gradient 배경을 추가하여 앱을 좀 더 예쁘게 만들 시간이다.

Gradient는 CSS와 마찬가지로 플러터에서도 쉽게 사용 할 수 있으며, 매우 핫(hot)하기 때문에 사용하기 좋다.

Gradient를 사용하려면 먼저 Container위젯이 필요하며, 그 안에 decoration 속성을 액세스 해야 한다.

main.dart파일에 _MyHomePageState클래스의 build메서드안 Container위젯의 decoration을 빌드 하는것으로 시작해 보자.

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

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Colors.black87,
),
body: Container(
// Box decoration 추가
decoration: BoxDecoration(
// Box decoration은 gradient를 가진다.
gradient: LinearGradient(
// linear gradient가 시작되고 끝나는 위치
begin: Alignment.topRight,
end: Alignment.bottomLeft,
// 각 색상에 대해 하나의 종료점을 추가. 종료점은 0에서 1까지 증가 해야함.
// Add one stop for each color. Stops should increase from 0 to 1
stops: [0.1, 0.5, 0.7, 0.9],
colors: [
// 색상처리는 플러터의 Colors클래스 덕분에 겁나 쉬움.
Colors.indigo[800],
Colors.indigo[700],
Colors.indigo[600],
Colors.indigo[400],
],
),
),
child: Center(
child: DogList(initialDoggos),
),
),
);
}

gradients가 적용된 모습:
apply gradients

Comment and share

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

ListView and Builder Methods

지금 여러분은 여러분의 강아지를 위한 카드(역자주: 이전에 만들었던 DogCard)를 가지고 있고, 강아지들의 데이터를 목록으로 렌더링 하는 것이 더 유용 할 것이다.
플러터UI의 가장 중요한 개념중 하나는 빌더 메서드에서 자주 수행되는 UI목록 렌더링 이다.(rendering)
빌더 메서드는 기본적으로 Dart List 데이터의 각 데이터 조각에 대해 위젯을 생성한다.

첫번째로 dog_list.dart라는 신규 파일을 만들자.

1. DogList Class

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

import 'package:flutter/material.dart';

import 'dog_card.dart';
import 'dog_model.dart';

class DogList extends StatelessWidget {
// 빌더 메서드는 목록(list)같은 데이터 집합에 의존한다.
final List<Dog> doggos;
DogList(this.doggos);

// 우선, 빌드 메서드를 평범하게 만든다.
// 위젯을 리턴하는 대신에 위젯을 리턴하는 메서드를 리턴한다.
// context를 전달하는걸 잊지 말자.
@override
Widget build(BuildContext context) {
return _buildList(context);
}

// 빌더 메서드는 거의 항상 ListView를 리턴한다.
// ListView는 Colum 또는 Row와 유사한 위젯이다.
// ListView는 스크롤 가능 여부를 알 수 있다.
// ListView는 builder라는 생성자를 가지고 있는데 이것은 목록으로 작동 할것 이라는 것을 알고 있다.

ListView _buildList(context) {
return ListView.builder(
// item수와 같은 item 카운트를 반드시 가져야 한다!
itemCount: doggos.length,
// 위젯을 리턴하는 콜백이다.
itemBuilder: (context, int) {
// 우리의 경우 각각의 강아지에 대한 DogCard이다.
return DogCard(doggos[int]);
},
);
}
}

그리고 _MyHomePageState의 build메서드를 수정하자.

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

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Colors.black87,
),
body: Container(
// DogCard위제를 제거한다.
// 대신 여러분은 새로운 DogList 클래스를 사용해라.
//
// 위의 강아지 목록 데이터를 전달하라.
child: Center( // Changed code
child: DogList(initialDoggos), // Changed code
),
),
);
}

random한 강아지 사진을 보여주는 현 시점까지의 앱:
random dog image

Comment and share

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

본 문서는 Fluter Example의 내용을 원저작자의 동의하에 번역한것 입니다.
원 저작자 Eric Windmill에게 감사를 전합니다.
이해하는데 불필요한 문장은 과감하게 버렸습니다. 오 번역에 대해서 의견 주시면 적극 반영 하겠습니다.

Reusable Custom Card Widget

1. Dog Card 위젯 만들기

우리는 우리 강아지들을 display할 나이스한 위젯이 필요하다.

첫번째로 다음과 같은 card를 만들 것이다.
card widget

'dog_card.dart’파일을 신규로 생성한다.
파일안에서 빈 상태의 StatefulWidget을 생성한다. 생성자에서 강아지를 가져와야 한다.
당분간 이 card는 강아지의 이름만 표시 할것 이다.

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

import 'package:flutter/material.dart';

import 'dog_model.dart';

class DogCard extends StatefulWidget {
final Dog dog;

DogCard(this.dog);

@override
_DogCardState createState() => _DogCardState(dog);
}

class _DogCardState extends State<DogCard> {
Dog dog;

_DogCardState(this.dog);

@override
Widget build(BuildContext context) {
return Text(widget.dog.name); // (역자주 : widget은 this와 동일 즉 this.dog.name으로 무관하다.)
}
}

DogCard를 나타나게 main.dart파일의 _MyHomePageState클래스의 build메서드를 수정해 보자.

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

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Colors.black87,
),
body: Container(
child: DogCard(initialDoggos[1]), // New code
),
);
}

당연히 dog_cart.dart를 import해줘야 함.

1
2
3
4
5
6
// main.dart

import 'package:flutter/material.dart';

import 'dog_card.dart';
import 'dog_model.dart';

앱을 새로고침(refresh) 하면 바로 연결되어 있는것을(강아지의 이름) 확인 할 수 있을것이다. (역자주: 오른쪽 상단에 'Rex’라는 텍스트가 출력 된다)
자 이제 Card를 만들 시간이다.

2. Dog Card UI

이 카드는 두가지 주요 부분이 있다. 이미지와 그 밑에 있는 실제카드.

첫번째로 이미지를 만들어 보자.

아래 getter를 _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
// dog_card.dart

// URI를 나타내는 클래스 속성.
// Dog클래스에서 렌더링 된다.
String renderUrl;

Widget get dogImage {
return Container(
// 명시적으로 Container의 width, height를 설정할 수 있다.
// 그렇지 않으면 children의 공간만큼 차지한다.
width: 100.0,
height: 100.0,
// Decoration 속성은 컨테이너를 설정할 수 있게 해준다
// 이것은 BoxDecoration을 기대한다.
decoration: BoxDecoration(
// BoxDecorations have many possible properties.
// BoxDecorations은 여러가지 가능한 속성을 가지고 있다.
// BoxShape를 배경 이미지와 함께 사용하면 아바타 스타일로 잘라진 원circle)을 쉽게 만들 수 있다.
shape: BoxShape.circle,
image: DecorationImage(
// CSS의 imagesize와 동일한 속성.
fit: BoxFit.cover,
// NetworkImage위젯은 URL로 이미지를 가져 오는 위젯이다.
// ImageProviders (예) NetworkImage)는 이미지를 로드하거나 변경할 필요가 있을때 이상적이다.
// 오류를 방지하려면 Null체크를 사용하라.
image: NetworkImage(renderUrl ?? ''),
),
),
);
}

이미지를 보기 위해선 Dog클래스에 인터넷을 통해 이미지를 가져오게 해야 한다.
dog_cart.dart안의 _DogCardState클래스에 다음 코드를 추가하자.

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


// State 클래스들은 state가 생성될때 이 메서드를 실행한다.
// initState에선 비동기 작업을 해선 안되기 때문에 다른 메서드에서 처리되도록 연기 시킬 것이다.
void initState() {
super.initState();
renderDogPic();
}

// 우린 Dog클래스 자체적으로 이미지를 가져오길 원하지만
// 이것은 플러터의 기본을 설명 할 수 있는 손쉬운 방법이다. (역자주: Dog클래스가 이미지를 가져오는게 맞겠지만 플러터의 기본을 설명하는데엔 아래 방식이 더 쉽다는것으로 이해됨.)
void renderDogPic() async {
// 서비스 호출
await dog.getImageUrl();
// setState는 플러터에게 변경된 모든것을 다시 렌더링 하도록 지시한다.
// setState는 비동기가 될 수 없으므로 덮어 쓸 수 있는 변수를 사용한다.
setState(() {
renderUrl = dog.imageUrl;
});
}

이제 우리는 렌더링 할 URL을 제대로 가져오는 아바타를 가지고 있다.
카드의 겹침(overlap) 모양을 얻으러면 내장된 위젯 Stack을 사용해야 한다.
Stack 위젯은 가장 자리를 기준으로 자식들을 배치한다.

즉 CSS positiontop, bottom, left, right 속성과 동일하다.
Stack 내에서 Position위젯으로 자식들을 감쌀 수 있지만 꼭 할필요는 없다.

  • Position으로 감싸진 위젯은 웹 개발용어를 사용하자면 'document flow’의 외부에 있다.기본적으로 Stack위젯의 상단 모서리인 [0,0]위치에 있다.
  • 감싸지지 않은 위젯은 배치되지 않는다.(positioned). 기본적으로 위젯의 열(column)으로 배치된 정상적인 'document flow’을 유지한다.

다음은 stack을 시작하는 방법이다.

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

@override
Widget build(BuildContext context) {
// Start with a container so we can add layout and style props:
return Container(
// Arbitrary number that I decided looked good:
height: 115.0,
// A stack takes children, with a list of widgets.
child: Stack(
children: <Widget>[
// position our dog image, so we can explicitly place it.
// We'll place it after we've made the card.
Positioned(
child: dogImage,
),
],
),
);
}

앱을 새로고침하면 상단의 모서리에 강아지 그림이 보인다.

_DocCardState의 레이아웃을 만들어 보자.

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

Widget get dogCard {
// 새로운 컨테이너
// height와 width는 스타일링을 위한 임의의 숫자이다.
return Container(
width: 290.0,
height: 115.0,
child: Card(
color: Colors.black87,
// padding처리를 위해 자식을 Padding위젯으로 감싼다.
child: Padding(
// padding을 제어하는 클래스를 EdgeInsets라고 한다.
// EdgeInsets.only 생성자는 자식의 각 side에 명시적으로 padding을 설정하는데 사용.
padding: const EdgeInsets.only(
top: 8.0,
bottom: 8.0,
left: 64.0,
),
// Columm은 또다른 레이아웃 위젯 - stack과 비슷 -
// 자식들로 위젯의 목록을 취하고 위젯을 위에서 아래로 배치한다.
child: Column(
// 이들 정렬 속성은 CSS의 flexbox 속성과 정확하게 똑같이 작동한다.
// column의 주축은 세로축이고 `MainAxisAlignment.spaceAround` 는
// CSS의 세로로 배치된 flexbox의 'justify-content: space-around'와 동일하다.
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(widget.dog.name,
// 테마는 앱의 root에서 설정한 MaterialApp widget 이다.
// 우리가 우리 소유의 것을 정하지 않았기 때문에 기본값을 가지고 있다.
// 쉽게 변경 가능한 앱 차원의 일관된 스타일을 유지 하는데 탁월하다.
style: Theme.of(context).textTheme.headline),
Text(widget.dog.location,
style: Theme.of(context).textTheme.subhead),
Row(
children: <Widget>[
Icon(
Icons.star,
),
Text(': ${widget.dog.rating} / 10')
],
)
],
),
),
),
);
}

거의다 왔다!!, DogCard UI를 완성하기 위해 한 가지 더 할일이 있다.
build 메서드의 기본 위젯에 조금 더 스타일을 추가해 보자.

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

@override
Widget build(BuildContext context) {
return 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),
],
),
),
);
}

Comment and share

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

본 문서는 Fluter Example의 내용을 원저작자의 동의하에 번역한것 입니다.
원 저작자 Eric Windmill에게 감사를 전합니다.
이해하는데 불필요한 문장은 과감하게 버렸습니다. 오 번역에 대해서 의견 주시면 적극 반영 하겠습니다.

#Data Model & HTTP

1. Get to a Clean Slate

모든 플러터앱은 main.dart로 시작한다. Counter앱 관련한 부분을 제거하면 다음과 같다:

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
// main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// MaterialApp is the base Widget for your Flutter Application
/// Gives us access to routing, context, and meta info functionality.
return MaterialApp(
title: 'We Rate Dogs',
// Make all our text default to white
// and backgrounds default to dark
theme: ThemeData(brightness: Brightness.dark),
home: MyHomePage(title: 'We Rate Dogs'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

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

class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
/// Scaffold is the base for a page.
/// It gives an AppBar for the top,
/// Space for the main body, bottom navigation, and more.
return Scaffold(
/// App bar has a ton of functionality, but for now lets
/// just give it a color and a title.
appBar: AppBar(
/// Access this widgets properties with 'widget'
title: Text(widget.title),
backgroundColor: Colors.black87,
),
/// Container is a convenience widget that lets us style it's
/// children. It doesn't take up any space itself, so it
/// can be used as a placeholder in your code.
body: Container(),
);
}
}

2. Dog Model 클래스 생성.

data model을 위해 Dog이라는 일반 Dart클래스를 만든다.

첫번째로 dog_model.dart라는 파일을 lib 폴더안에 생성한다.

1
2
3
- lib
-dog_model.dart
-main.dart

저 파일에서 몇 가지 속성을 가진 수퍼 기본 클래스 (super basic class)를 만들 것이다.

1
2
3
4
5
6
7
8
9
10
11
class Dog {
final String name;
final String location;
final String description;
String imageUrl;

// 모든 강아지는 10점부터 출발한다. 왜냐면 다 좋은 강아지니깐~
int rating = 10;

Dog(this.name, this.location, this.description);
}

3. 강아지 사진 얻기

우리는 강아지 이미지를 생성하기 위해 API키 또는 다른 어떠한것도 필요없는 매우 간단한 API를 사용할 것이다.
random하게 강아지 이미지를 찾아주는 dog.ceo를 이용할 것이다.

Dog클래스에 다음 메서드를 추가하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// dog_model.dart

Future getImageUrl() async {
// Null check so our app isn't doing extra work.
// If there's already an image, we don't need to get one.
if (imageUrl != null) {
return;
}

// This is how http calls are done in flutter:
HttpClient http = HttpClient();
try {
// Use darts Uri builder
var uri = Uri.http('dog.ceo', '/api/breeds/image/random');
var request = await http.getUrl(uri);
var response = await request.close();
var responseBody = await response.transform(utf8.decoder).join();
// The dog.ceo API returns a JSON object with a property
// called 'message', which actually is the URL.
imageUrl = json.decode(responseBody)['message'];
} catch (exception) {
print(exception);
}
}

주의: 위 코드를 추가 하려면 두개의 Dart패키지를 import해야 함.

1
2
import 'dart:convert'; // json string 변환을 위함.
import 'dart:io'; // HttpClient를 위함.

4. new Dog class로 약간의 sample 데이터 생성.

main.dart에서 강아지 몇마리를 생성해 보자.

첫번째로 dog_model.dart를 import하자.

1
2
3
4
5
// main.dart

import 'package:flutter/material.dart';

import 'dog_model.dart';

그리고 약간의 강아지를 추가.

1
2
3
4
5
6
7
8
9
10
11
12
// main.dart in the State class

class _MyHomePageState extends State<MyHomePage> {
List<Dog> initialDoggos = []
..add(Dog('Ruby', 'Portland, OR, USA',
'Ruby is a very good girl. Yes: Fetch, loungin\'. No: Dogs who get on furniture.'))
..add(Dog('Rex', 'Seattle, WA, USA', 'Best in Show 1999'))
..add(Dog('Rod Stewart', 'Prague, CZ',
'Star good boy on international snooze team.'))
..add(Dog('Herbert', 'Dallas, TX, USA', 'A Very Good Boy'))
..add(Dog('Buddy', 'North Pole, Earth', 'Self proclaimed human lover.'));
}

Comment and share

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

본 문서는 Fluter Example의 내용을 원저작자의 동의하에 번역한것 입니다.
원 저작자 Eric Windmill에게 감사를 전합니다.
이해하는데 불필요한 문장은 과감하게 버렸습니다. 오 번역에 대해서 의견 주시면 적극 반영 하겠습니다.

Basic Dogs App Setup ( 기본 강아지 앱 설정)

이것은 굉장한 프레임워크다.

이 기본 앱 튜토리얼에서는 2012년 We Rate Dogs vs Brant 대결을 통해 역대 최고의 Twitter대화에서 영감을 받아 매우 간단하고 순수한 플러터 앱을 만들 것이다. (역자주: 2010년 트위터의 WeRateDogs라는 계정이 애완견 사진을 올리고 사용자들끼리 평가와 대화를 하는 트윗에 Brant라는 계정이 딴지를 걸기 시작한 사건으로 제가 확인은 안해봤지만 단일 대화 주제로는 트위터 설립이래 최고의 대화가 오갔다고 함.)

여기서 우리는 추가 package를 가져오지(사용하지) 않을 것이며 아키텍쳐나 state관리에 대해 생각하지 않을 것이다.

이 작업이 끝날 때쯤이면, 플러터가 우리에게 얼마나 많은 것을 주었는지에 대해 상당히 놀랄것이다. 그리고 우리는 거의 모든 것을 커버하지 않을 것이다.(역자주: 플러터가 제공하는 수 많은 기능에 대해 다루진 않겠다는 의미 인듯.)

우리가 무엇을 만들지 보자.
dogs1 dogs2

완성된 코드 링크

우리는 새로운 앱에서 시작할 것이다. 일단 플러터 설치 및 설정을 하고 난 후 (설치 및 설정 되어 있다면 pass) 새로운 앱을 만든다.

1
2
3
flutter create basic_flutter_app
cd basic_flutter_app
flutter run

또는 Github에서 이미 생성된 app을 clone받아도 된다.

1
2
3
4
git clone https://github.com/ericwindmill/flutter_by_example_apps.git
cd flutter_by_example_apps/blank_flutter_app
flutter packages get
flutter run

이것은 새롭고, 빈(?) 플러터 앱을 제공한다.

Comment and share

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

본 문서는 Fluter Example의 내용을 원저작자의 동의하에 번역한것 입니다.
원 저작자 Eric Windmill에게 감사를 전합니다.
이해하는데 불필요한 문장은 과감하게 버렸습니다. 오 번역에 대해서 의견 주시면 적극 반영 하겠습니다.

Stateful Widget Lifecycle(생명주기)

플러터가 StatefulWidget을 만들 때, State객체를 만든다. 이 개체는 해당 위젯의 모든 가변 상태가 유지 되는 곳이다.

state의 개념은 두 가지로 정의 된다:

  1. 위젯이 사용하는 데이터는 변경 될 수 있다.
  2. 위젯이 빌드 될때 데이터를 동시에(synchronously) 읽을 수 없다. (모든 state는 build 메서드가 호출 될때 까지 설정 되어야 한다.)

라이프 싸이클은 다음과 같은 간단한 단계를 포함하고 있다.

StatefulWidget 과 State 클래스는 별도로 분리되었는가?

한마디로 말하면 성능이다.

State객체는 오래 유지되지만 StatefulWidgets(및 모든 Widget의 서브클래스)는 구성이 변경 될때마다 폐기하고 다시 작성된다. 플러터가 변경 가능한(mutable) 위젯을 다시 작성하는것은 매우 저렴하다.

State는 재구축(rebuild)할때 마다 폐기되지 않으므로 비용 큰 계산을 피할 수 있으며 프레임별로(frame)로 프레임이 재구성 될때 마다 state속성, getter, setter등에서 가져온다.

중요한것은 이것이 플러터 애니메이션을 존재하도록 한다는 것이다. State는 폐기되지 않으므로 데이터 변경에 대한 응답으로 필요할 때 언제든지 위젯을 재 구성 할 수 있다.

1. createState()

플러터가 StatefulWidget을 빌드하도록 지시하면 즉시 [createState()]가 호출된다. 이 메서드는 반드시 존재해야 한다.(역자주: createState()를 말함) StatefulWidget은 이것보다 더 복잡할 필요가 거의 없다.

1
2
3
4
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}

2. mounted is true

createState가 state클래스를 생성하면 buildContext는 state에 할당 된다.
BuildContext는 위젯이 배치된 위젯 트리의 위치를 단순화 한 것이다.

모든 위젯은 bool형식의 this.mounted 속성을 가지고 있다. buildContext가 할당되면 true를 리턴한다. 위젯이 unmounted상태일때 setState를 호출하면 error가 발생한다.

1
tip: 이 속성은 state에 `setState()`를 호출할때 유용하지만 해당 메서드가 언제, 얼마나 자주 호출 되는지는 명확하지 않다. 아마도 stream이 업데이트에 대한 응답으로 호출되는것 같다. `if (mounted) {...` 를 사용하여 `setState()`호출전에 State가 존재하는지 확인 할 수 있디.

3. initState()

위젯이 생성될때 처음으로 호출되는 메서드 이다.(물론 class생성자 다음 입죠~)
initState는 오직 한번 만 호출 된다. 또한 반드시 super.initState()를 호출해야 한다.

initState에서 실행되면 좋은 것들

  1. 생성된 위젯 인스턴스의 BuildContext에 의존적인 것들의 데이터 초기화
  2. 동일 위젯트리내에 부모위젯에 의존하는 속성 초기화
  3. Stream 구독, 알림변경, 또는 위젯의 데이터를 변경할 수 있는 다른 객체 핸들링.
1
2
3
4
5
6
7
8
9
@override
initState() {
// 부모의 initState호출
super.initState();
// 이 클래스애 리스너 추가
cartItemStream.listen((data) {
_updateWidget(data);
});
}

4. didChangeDependencies()

didChangeDependencies 메서드는 위젯이 최초 생성될때 initState 다음에 바로 호출 된다.
또한 위젯이 의존하는 데이터의 객체가 호출될때마다 호출된다. 예를 들면 업데이트되는 위젯을 상속한 경우.
공식문서 또한 상속한 위젯이 업데이트 될때 네트워크 호출(또는 다른 비용이 큰 액션)(역자주: API호출)이 필요한 경우 유용하다고 함.

5. build()

이 메서드는 자주 호출된다(fps + render로 생각하세요.). 필수이며 재정의 대상(@override)이고 반드시 Widget을 리턴해야 한다.

플러터의 모든 gui는 Padding, Center 조차도 child 또는 children을 가진 위젯 이라는것을 기억하라.

6. didUpdateWidget(Widget oldWidget)

didUpdateWidget()는 부모 위젯이 변경되어 이 위젯을 재 구성해야 하는 경우(다은 데이터를 제공 해야하기 때문)

이것은 플러터가 오래동안 유지 되는 state를 다시 사용하기 때문이다. 이 경우 initState()에서 처럼 읿부 데이터를 다시 초기화 해야 한다.

build() 메서드가 Stream이나 변경 가능한 데이터에 의존적인경우 이전 객체에서 구독을 취소하고 didUpdateWidget()에서 새로운 인스턴스에 다시 구독 해야함.

tip: 이 메서드는 기본적으로 위젯의 상태와 관련된 위젯을 재 구성해야 하는 경우 initState()을 대치한다.

플러터는 항상 이 메서드 수행 후에 build()메서드 호출 하므로, setState() 이후 모든 추가 호출은 불필요 하다.

1
2
3
4
5
6
@override
void didUpdateWidget(Widget oldWidget) {
if (oldWidget.importantProperty != widget.importantProperty) {
_init();
}
}

7. setState()

setState() 메서드는 플러터 프레임워크 자체적, 또는 개발자로 부터 자주 호출된다.
'데이터가 변경되었음’을 프레임워크에 알리는데 사용되며 build context의 위젯을 다시 빌드하게 한다.
setState()는 비동기적이 않은 callback을 사용한다.(역자주: callback으로 비동기를 사용할 수 없다는 말임).

8. deactivate()

이 메서드는 거의 사용되지 않는다.
deactivate()는 tree에서 State가 제거 될때 호출 된다. 그러나 현재 프레임 변경이 완료되기 전에 다시 삽입 될 수 있다. 이 메서드는 State객체가 tree의 한 지점에서 다른 지점으로 이동 할 수 있기 때문에 기본적으로 존재한다.
필요에 따라 자주 호출 할 수 있는 이유는 다시 그리(repainting)는데 소용되는 비용이 저렴하기 때문이다. :-)

1
2
3
void updateProfile(String name) {
setState(() => this.name = name);
}

9. dispose()

dispost()State객체가 영구히 제거 된다.

10. mounted is false

이 상태에서 state 객체는 결코 다시 mount되지 않으며, setState()가 호출되면 에러가 발생한다.

Comment and share

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

Jace Shim


Seoul, Korea