Building Real App
플러터를 활용해서 실제와 비슷한 어플리케이션을 구현해보자
What's in this section?
•
Widgets
•
Theming & Material Design
•
Working with Images & Fonts
•
Adding Business Logic to an App
Most Important Widgets
이번 장에서는 위젯에 대한 대부분의 내용을 배워 볼 것이다.
1. App & Page Setup
MaterialApp & Cupertinopp
Scaffold & CupertinoPageScaffold
2. Layout
Container
Row
Column
Children
•
Flexible
•
Expanded
3. Content Containers
Stack
Card
4. Repeat ElemACents
ListView
GridView
ListTile
5. Content Types
Text
Image
Icon
6. User Input
TextField
RaisedButton & FlatButton
GestureDetector
InkWell
Transaction.dart
Transaction(트랜잭션)이란?
컴퓨터 과학분야에 트랜잭션은 "쪼개질 수 없는 업무처리의 단위"를 의미한다. DB에서 자주 사용하는 용어이고 해당 파트에서는 이러한 단어의 의미를 가진 클래스를 생성한다.
class Transaction {
final String id;
final String title; //class
final double amount;
final DateTime date; //class
Transaction({
this.id,
this.title,
this.amount,
this.date,
});
}
Dart
복사
@required 속성 사용하기
@required 어노테이션이 붙은 아규먼트는 꼭 채워져야 한다는 의미를 가진다. 하지만 @required 어노테이션이 설정된 가변인자도 개발자가 파라미터를 입력하지 않으면 null로 채워지고 프로그램은 정상동작 된다. 즉 어노테이션만으로는 강제할 수 없다는 것이다.
강제로 이 인자들을 입력받게 하기 위해서는 assert설정을 해주어야 한다. 입력된 인자가 없어서 null인 경우를 체크해서 에러를 출력해준다.
import 'package:flutter/foundation.dart';
class TestRequired {
final String name;
final int age;
TestRequired({
this.name,
this.age,
}) : assert(age != null);
}
Dart
복사
클래스를 요소로 가지는 리스트 생성하기
final List<Transaction> transaction = [
Transaction(요소 1),
Transaction(요소 2),
Transaction(요소 3)...
];
Dart
복사
iterable.map() 그리고 toList()
Dart에는 Iterable이라는 클래스가 있다.(물론 C++, Java 등 대부분의 언어에서도 들어봤을 것이다.) 이것은 반복이 가능한 집단 이란 뜻이다.
Dar API Document예제를 보면 Map이 가지고 있는 Key로도 iterate가 가능하다고 나와있다. 왜냐하면 Map의 key들도 반복이 가능한 집단이기 때문이다.
Map kidsBooks = {'Matilda': 'Roald Dahl',
'Green Eggs and Ham': 'Dr Seuss',
'Where the Wild Things Are': 'Maurice Sendak'};
for (var book in kidsBooks.keys) {
print('$book was written by ${kidsBooks[book]}');
}
Dart
복사
추가적으로 List, Set, Array, Linked Map 등과 dart:collection library에 있는 대부분의 클래스들이 Iterable class이다.
이 Iterable클래스는 map이라는 메소드를 가지고 있다. API Document에서는 아래와 같이 설명이 되어 있다.
map<T> method
Returns a new lazy Iterable with elements that are created by calling f on each element of this Iterable in iteration order.
'map을 호출한 클래스가 가진 요소들에 대해 함수 f를 호출하여 만들어진 새로운 lazy iterable을 리턴한다'라고 되어있다. 이전에 공부했던 lstmap, strmapi와 같은 의미라고 생각할 수 있겠다.
간단하게 말해서 map메소드는 iterable collection들을 대상으로 map함수를 돌려주는 것이다. 안에 넣어진 함수는 return으로 '하나'의 Widget을 출력해야만 한다. (하나의 요소를 받아서 하나의 결과물, 1 : 1 매칭과 같다) 따라서 map메소드가 실행되면 iterable의 요소 수만큼 함수가 호출 된다.
Column(
children: transactions.map((tx) {
return Card(
child: Text(tx.title),
);
})
),
Dart
복사
그렇다면 우리는 이 반복가능한 집단, 즉 iterable collection들을 가지고 어떤 일을 할 수 있을까?
모든 요소들에 대해서 f함수를 호출한 뒤 반복이 끝나게 되면 결과로 생성된 Widget들이 모인 Iterable 객체가 map의 return 값으로 나오게 될 것이다. 우리는 이때, toList() 메서드를 통해 리스트로 만들 것이다.
toList() method
Creates a List containing the elements of this Iterable.
The elements are in iteration order. The list is fixed-length if growable is false.
Column(
children: transactions.map((tx) {
return Card(
child: Text(tx.title),
);
}).toList(),
),
Dart
복사
이러한 방식을 사용해서 우리의 List의 크기가 변할때(유동적인 데이터를 받는 경우에), 원하는 만큼의 Widget을 생성해서 화면에 보여줄 수 있다. 여기서는 Column과 Card를 사용했지만, Row 혹은 직접 만들어둔 함수를 사용해도 가능하고 다양한 방식을 적용해 볼 수 있다.
Flutter Widget 구조잡기
html과 CSS를 이전에 자주 했었기 때문에 Widget의 구조를 잡는 것은 생각보다 재밌었고 대부분의 규칙이 비슷했기 때문에 어려움은 없었다. 명령어가 달라서 다시 외워야하는 점이 있었지만...!!!
이번 Section에서는 이러한 형태의 Widget Tree를 만들어 볼 예정이다. 웹에서 자주 쓰던 레이아웃 구조이고 이 구조를 Flutter의 문법으로 제작을 해 볼 예정이다.
직관적으로 봤을 때에는 대충 아래와 같은 구조가 될 것 같다.
•
Card Widget [ Row ["dollar", Column [ "title", "date" ] ] ]
이제 코드로 한번 작성을 해 볼 것이다.
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
tx.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
tx.date.toString(),
style: TextStyle(
color: Colors.grey,
),
),
],
),
Dart
복사
Styling Contents
to be continue
Container vs Column, Row
기본 보기
Search
Container 는 하나의 공간을 꾸미고 사이즈를 조절하기 완벽하다.
Column, Row 는 여러개의 요소들을 나열하고 정렬하기에 완벽하다.
따라서 Container과 Column, Row를 적절히 혼합하여 사용하면 가장 좋다!!
String Interpolation
프로그래밍 언어에서는 문자열을 처리하는 다양한 방법이 있다. 예를 들어 JavaScript라고 생각해보자. 아래의 문장이 있다.
age = 3;
console.log("I'm " + age + " years old!");
JavaScript
복사
이렇게 문자열을 나눠서 처리한다면 문장 하나에 변수가 많이 들어간다면 모두 따옴표와 + 연산을 활용해서 나눠주어야 한다. 하지만 이제 다수의 언어들은 String Interpolation을 지원한다. JS에서는 $ 와 {}를 활용해서 문자열 하나로 표현하는 방식을 지원한다. 아래와 같은 형태이다.
console.log("I'm ${age} years old!");
JavaScript
복사
Dart도 JS와 비슷하게 String Interpolation을 지원해준다. 아래의 예제를 보자.
Text('I\'m ${age} years old', style: ...),
Dart
복사
예약된 문자 $ 와 {} 사인을 활용해서 이렇게 특수한 처리를 할 수 있다. 만약 예약된 문자를 화면에 처리하고 싶다면 \ 를 활용해서 출력을 유도할 수 있다.
Installing External Packages & Formatting Dates
pub.dev사이트를 활용해서 개발에 필요한 여러 패키지들을 받을 수 있다. 예를들어 Google Map API, Date Formatter와 같은 패키지들이 있다.
intl패키지를 활용해서 Date Format을 가독성 좋게 바꿔보는 코드를 작성해보자.
우선 pub.dev사이트로 가서 해당 패키지의 정보를 복사한다.
이후 pubspec.yaml파일로 이동해서 dependencies 하위에 해당 코드를 넣는다.
이때 탭 라인이 중요하다. pubspec.yaml파일에서는 파이썬과 마찬가지로 탭을 기준으로 의존성을 판단하기 때문에 dependencies보다 탭 하나만큼만 들여쓰기한 후 작성해 주어야 한다.
이후 파일을 저장하면 Flutter에서 알아서 flutter pub get명령을 실행해서 해당 패키지를 받아준다. 이제 우리는 intl패키지를 활용할 수 있다. 이렇게 다운받은 패키지에 대한 설명은 각 패키지의 해당 페이지 오른쪽의 API reference 페이지에서 확인할 수 있다.
intl패키지 사용해보기
이제 우리는 intl패키지를 사용해 볼 것이다.
우선 우리가 사용할 intl을 import를 해야한다.
import 'package:intl/intl.dart';
Dart
복사
이제 DateFormat()함수를 활용해서 원하는 format을 만들어보자
Text(
DateFormat().format(tx.date),
style: TextStyle(
color: Colors.grey,
),
),
Dart
복사
이제 날짜가 훨씬 깔끔한 형태로 표시되는 것을 알 수 있다.
DateFormat함수의 괄호 안에 다양한 패턴을 넣어서 형식을 지정할 수도 있고 클래스 내부 생성자를 활용해서 기존에 만들어져있는 형식중 원하는 것을 골라서 사용할 수도 있다.
DateFormat('yyyy/MM/dd').format(tx.date)
DateFormat().add_yMMMd().format(tx.date)
Dart
복사
저장되어있는 생성자들
Text Input Widget
어플리케이션에서 입력을 받을 수 있는 기능은 상당히 중요하다. 어플리케이션에서 사용자의 입력을 받을 수 있는 Text Input Widgets을 사용해보자.
TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
),
)
Dart
복사
onChanged()
TextField내의 onChanged기능을 활용해서 해당 TextField에 입력된 Text가 바뀔때마다 작동되는 기능을 넣을 수 있다.
TextField(
onCanged: (val) => text = val;
)
Dart
복사
Textfield의 추가적인 정보는 아래의 공식 문서에서 확인할 수 있다.
FlatButton Widget
어플리케이션에서 특정 동작을 유도하려면 버튼, 액션 등 다양한 방법을 사용한다. 이번에는 사용자의 행동을 유도하기 위해 FlatButton Widget을 사용해보자.
FlatButton(
onPressed: () {
/* 원하는 기능 */
},
child: Text(
"Flat Button",
),
)
Dart
복사
onPressed()
FlatButton내에 onPressed를 활용해서 해당 버튼을 클릭했을 때의 동작을 지정할 수 있다.
FlatButton(
onPressed: () {
print(1);
print(2);
},
)
Dart
복사
FlatButton의 추가적인 정보는 아래의 공식 문서에서 확인할 수 있다.