3일차 과정에서는 본격적으로 위젯을 사용하여 레이아웃 구성을 시작했다. 가장 중요한 Column과 Row 위젯을 기본으로 사용하여 여러 UI를 만들 수 있다.
학습한 내용
- Column과 Row 위젯
- ListTile과 Padding 위젯
- 위젯의 속성 값
- SizedBox와 Container 위젯
- Flutter 폴더 구조(pubspec.yaml 등)
- Image.network와 Image.asset 등 이미지 추가
pubspec.yaml
은 플러터 프로젝트에서 사용하는 모든 메타 데이터(패키지, 프로젝트 버전, 자원 등)의 종속성을 명시하고, 관리하는 파일이다.
YAML
은 직렬화 언어로 Key:Value
의 구조로 명시하며, 들여쓰기 및 Array(-) 표기법 등의 작성법이 존재한다.
pubspec.yaml 필드 종류
- name : 패키지의 이름, 모든 패키지에 필요
- description : 패키지에 대한 설명을 적는 곳, 개인 패키지일 경우 옵션이지만 패키지를 게시하려면 필수
- publish_to : 배포할 곳을 지정, 기본 값은 https://pub.dev/ 이고 none은 패키지로서 배포하지 않겠다는 의미
- version : 패키지의 버전을 의미, 기본값(1.0.0+1), 지정 안 할 경우(0.0.0)
- environment : Dart SDK 환경을 설정하는 속성
- dependencies : 패키지의 의존성을 작성, 주로 외부 패키지를 가져다 쓰기 위해 사용
- dev_dependencies : 패키지가 사용하는 개발용 의존성을 작성
- author / authors : 해당 패키지를 만든 사람을 표기
- homepage : 해당 패키지의 홈페이지가 있는 경우 표기
- documentation : 해당 패키지의 문서가 있는 경우 표기
- dependency_overrides : 임시로 의존성을 오버라이드 해야하는 경우 작성
- executables : 실행가능한 패키지인 경우 스크립트를 작성
pub get //pubspec.yaml 에 있는 내용을 다운로드
pub upgrade //패키지의 버전을 최신 버전으로 업데이트
pub outdated //오래된 패키지 종속성을 식별
flutter doctor //실행환경을 체크
- 절대 경로와 상대 경로
- Column과 Row 위젯의 mainAxisAlignment, crossAxisAlignment, mainAxisSize 속성
- 학습한 위젯을 사용한 UI 개발 연습
프로그래밍 언어, 운영체제 등에서 컴퓨터의 파일을 찾아가는 방법을 경로(path)라고 한다. 간단히 해당 파일의 위치를 경로라고 할 수 있다. 이러한 경로는 절대 경로와 상대 경로로 구분된다.
절대 경로란 최초의 시작점부터 경유한 경로를 모두 기입하는 방식이다. 모든 운영체제에서 최상위 루트부터 경유한 경로를 모두 기입한 절대 경로로만 파일을 찾을 수 있다. 예를 들어 윈도우 환경에서 Desktop에 위치한 example.txt 파일은 아래와 같은 절대 경로를 가진다.
C:\Users\UserID\Desktop\example.txt
상대 경로는 현재 자신이 위치한 경로를 기준으로 작성된다. 예를 들어 아래와 같은 상대 경로가 있다고 해보자
Desktop\example.txt
이러한 상태로 운영체제에게 그대로 전달되면 운영체제는 파일의 위치를 찾지 못한다. 따라서 상대 경로를 사용해야 하는데 기준 경로가 C:\Users\UserID
라면 위의 상대 경로는 아래와 같은 절대 경로로 해석된다.
C:\Users\UserID\Desktop\example.txt
하지만 만약 기준 경로가 C:\Users\
라면 아래와 같은 절대 경로로 해석될 것이다.
C:\Users\Desktop\example.txt
이 경우 제대로 파일을 찾지 못할 것이다. 이처럼 상대 경로는 기준이 되는 경로를 가지고 절대 경로가 구성되며, 상대 경로로 파일을 찾을 수 있는 이유는 기준 경로가 절대 경로로 변환되어 운영체제에게 전달하기 때문이다.
만약 Desktop에 플러터 프로젝트 PRJ 있고 img.png 이미지 파일이 프로젝트 안의 assets/image
에 있다고 가정하고 Image.asset()
을 사용하면 아래와 같이 두 가지 방법으로 사용할 수 있을 것이다.
Image.asset('C:/Users/UserID/Desktop/PRJ/assets/image/img.png') // 절대 경로
Image.asset('assets/image/img.png') // 상대 경로
플러터에서는 이미지를 pubspec.yaml
파일에 등록하고 사용하기 때문에 위와 같은 상대 경로로 사용할 수 있다.
주 축 정렬을 수정할 때 사용하는 속성이다.
- start : 시작 정렬
- end : 끝 정렬
- center : 중앙 정렬
- spaceAround : 중간 여백은 동일하나, 첫 번째와 마직막 여백은 중간 여백의 절반
- spaceBetween : 양 끝은 붙이고, 중간 여백만 동일하게
- spaceEvenly : 첫 번째, 중간, 마지막 여백이 모두 동일하게
교차 축 정렬을 수정할 때 사용하는 속성이다.
- start : 시작 정렬
- end : 끝 정렬
- center : 중앙 정렬
- stretch : 가능한 크기만큼 자식 요소의 크기를 키움
주 축의 크기를 수정할 때 사용하는 속성이다.
- max : 주축의 크기를 최대로 늘임 (남은 영역을 모두 사용)
- min : 주 축의 크기를 최소로 줄임
Column
과 Row
의 Main Axis
와 Cross Axis
는 차이가 있는데 아래 그림을 통해 알 수 있다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: 250,
height: 250,
decoration: BoxDecoration(
gradient: LinearGradient( // 그라데이션
begin: Alignment.topLeft, //시작점
end: Alignment.bottomRight, //끝점
colors: [
Colors.red,
Colors.orange,
Colors.blue,
Colors.indigo,
Colors.purple,
Colors.pink,
]
),
),
),
),
),
);
}
}
Center
위젯 아래에 Container
를 넣어 컨테이너가 가운데에 위치하게 했으며 width
와 height
속성을 사용해 크기를 조절했다. decoration
속성에서 LinearGradient
를 사용해 그라데이션을 적용했는데 6가지 색상을 사용해 보았다. begin
과 end
속성은 각각 그라데이션의 시작과 끝의 위치를 의미한다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: 250,
height: 250,
padding: EdgeInsets.all(90), // 안쪽 여백
decoration: BoxDecoration(
//왼쪽 위와 오른쪽 아래만 테두리 곡선 적용
borderRadius: BorderRadius.only(
topLeft: Radius.circular(120),
bottomRight: Radius.circular(120),
),
gradient: LinearGradient( // 그라데이션
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.yellow,
Colors.purple,
]
),
),
child: Container( // 안쪽 원
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(100),
),
),
),
),
),
);
}
}
3-1번과 같은 기본 컨테이너의 그라데이션을 두 가지 색상으로 변경하였다. 그리고 borderRadius
속성을 사용하여 왼쪽 위와 오른쪽 아래에만 곡선을 적용하였다. 또한 안에 그려지는 컨테이너가 가운데에 작은 크기로 위치하기 위해 paddind
속성을 EdgeInsets.all(90)
으로 설정했다. 안에 그려지는 컨테이너는 역시 decoration
속성에서 BoxDecoration()
을 사용해 색상과 원형이 되도록 곡선을 적용했다. 이러한 경우 color
속성은 항상 BoxDecoration()
의 속성으로 작성해야한다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container( //겉 배경
width: 120,
height: 120,
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(bottom: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(200),
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Colors.pink,
Colors.orange,
]
),
),
child: Container( //안쪽 배경
padding:EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(200),
),
// 이미지 불러오기
child: Image.network('https://sniperfactory.com/img/flutter/lee.png'),
),
),
Text( //텍스트
style:TextStyle(fontSize: 16),
"sniperfactory_official"
),
],
),
),
),
);
}
}
이미지가 들어가는 컨테이너와 텍스트를 출력할 위젯이 필요하기 때문에 Column
위젯을 사용했다. mainAxisAlignment
속성을 MainAxisAlignment.center
로 설정하면 주 축의 요소들을 가운데에 정렬할 수 있다. 첫 번째 요소는 두 개의 컨테이너를 패딩을 설정하여 배경으로 설정하고 이미지는 Image.network()
를 사용하여 네트워크에서 불러왔다. 테두리 곡선과 그라데이션은 앞에서 사용한 방식과 같다. 그리고 Column
의 요소로 Text
위젯을 추가하여 화면을 완성했다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child:Container( // 배경 컨테이너
height: 90,
padding: EdgeInsets.all(16),
color: Colors.blue,
child: Row( // 전체 요소 Row
//양쪽 끝에 요소가 위치하도록 정렬
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row( //이미지와 텍스트 Row
children: [
Image.asset('assets/images/example.png'),
Padding(
padding: EdgeInsets.only(left:16.0),
child: Column( // 텍스트 Column
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
style:TextStyle(
color:Colors.white,
fontSize: 20
),
'라이언'
),
Text(
style:TextStyle(
color:Colors.white,
fontSize: 11
),
'게임개발\nC#, Unity'
),
],
),
),
],
),
// + 아이콘
Icon(color:Colors.white, Icons.add),
],
),
),
),
),
);
}
}
ListTile
의 형태와 같은 배경은 Container
를 사용해 구성하였고 내용 요소들은 하나의 Row
위젯으로 묶어 주었다. 이 때 mainAxisAlignment
속성을 MainAxisAlignment.spaceBetween
으로 설정해 양쪽 끝에 요소들이 위치할 수 있도록 했다. 이미지와 텍스트는 또 하나의 Row
위젯으로 묶어 주었고 Text
위젯들은 Column
위젯을 사용해 세로로 나열되게 했다. 이번에는 이미지를 로컬에 저장해 Image.asset()
으로 불러왔다.
위와 같은 레이아웃을 아래와 같은 코드를 사용하면 ListTile로 간단하게 구현할 수 있다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child:ListTile( //리스트타일
tileColor: Colors.blue, //배경 색
textColor: Colors.white, //글씨 색
iconColor: Colors.white, //아이콘 색
contentPadding: EdgeInsets.all(16), //내부요소 padding
title: Text('라이언'),
subtitle: Text(
style: TextStyle(
fontSize: 11,
),
'게임개발\nC#, Unity'),
leading: Image.asset('assets/images/example.png'),
trailing: Icon(Icons.add),
),
),
),
);
}
}
만들어진 결과를 보면 ListTile
을 사용한 것과 사용하지 않은 것이 같은 것을 알 수 있다. 하지만 ListTile
을 사용했을 때 코드가 훨씬 쉽고, 간결하여 알아보기 쉽다. 이처럼 각 상황에 맞는 위젯을 적절하게 사용해야 한다.
3일차에 중요한 Column과 Row 등의 위젯을 배우고 사용했다. 2일차까지 했던 것 보다 코딩에 좀 더 집중된 내용이었다. 지난 시간에 플러터의 렌더링 과정을 공부하는 것은 조금 어려웠는데 직접 위젯 사용해서 레이아웃 만드는 것은 해 본 경험이 있어서 그렇게 어렵지 않게 끝낼 수 있었다.ㅎㅎ 시간 날 때 좀 더 연습해봐야지 ㅠㅠ