✨ Today I Learned 2회차 - Flutter 위젯 기초 🎯
🚀 Flutter 위젯 기초
📂 Flutter 프로젝트 생성 및 구조 설명
🎯 주요 폴더 및 파일 구조
폴더/파일 | 역할 | 설명 |
---|---|---|
📁 lib/ |
🎨 메인 코드 폴더 | • 앱 개발을 위한 Dart 코드 작성 • 기본적으로 main.dart 가 진입점 |
📄 pubspec.yaml |
⚙️ 프로젝트 설정 | • 프로젝트 정보 기록 • pub.dev 패키지 추가• 이미지, 폰트 파일 위치 선언 |
📄 pubspec.lock |
🔒 버전 고정 | • 설치된 패키지의 정확한 버전 고정 • 프로젝트 재설치 시 동일한 환경 보장 • 패키지 추가 시 자동 생성 |
📁 플랫폼 폴더들 | 🏛️ 플랫폼별 설정 | • android , ios , linux , macos , web , windows • 디바이스 권한 설정 (GPS, 카메라 등) • 플랫폼별 이름 및 아이콘 수정 |
📁 test/ |
🧪 테스트 코드 | • lib 폴더의 Dart 코드 테스트• 단위 테스트 및 통합 테스트 작성 |
📄 .gitignore |
🚫 Git 제외 설정 | • Git에 포함하지 않을 파일/폴더 명시 • 예: .dart_tool/ , build/ 등 |
📄 analysis_options.yaml |
📋 코드 분석 설정 | • Dart 정적 분석 도구 옵션 • 코드 스타일 및 린트 규칙 정의 |
📁 .dart_tool/ |
🔧 내부 캐시 | • Dart 도구들의 내부 캐시 디렉토리 • 개발자가 직접 수정하지 않음 |
📄 .metadata |
📊 메타 정보 | • Flutter 프로젝트 메타 정보 • 자동 생성됨 |
📁 .idea , .iml |
💻 IDE 설정 | • 코드 에디터 관련 설정 파일 |
💡 가장 많이 사용하는 것들:
- 📁
lib/
폴더: Dart 파일 저장 - 📄
pubspec.yaml
파일: 패키지 추가, 폰트 수정, 앱 내 이미지 경로 추가
🎯 시작 점
📝 main.dart
→ main()
함수가 앱의 진입점
📦 패키지 사용 방법
방법 | 명령어/설정 | 설명 |
---|---|---|
YAML 파일 수정 | pubspec.yaml 편집 |
수동으로 패키지 정보 추가 후 flutter pub get |
터미널 명령어 | flutter pub add http |
자동으로 패키지 추가 및 설치 |
🔢 버전 표기법
^1.4.0
→>= 1.4.0 < 2.0.0
- 🛡️ 안정성과 호환성을 유지하면서 최신 패치를 자동 적용
🎨 Widget 이란?
Widget은 Flutter에서 UI를 구성하는 모든 것의 기본 단위
👁️ 눈에 보이는 모든 것이 위젯
✨ Widget의 특징
- 🔄 재사용 가능하고 조합 가능한 구조를 가짐
- 🧩 각각의 위젯을 하나로 묶어서 사용 가능
- 🌳 Flutter는 위젯 트리를 기반으로 화면을 구성
📊 샘플 프로젝트의 위젯 트리 구조
📱 MyApp
└── 🎨 MaterialApp
└── 🏠 MyHomePage
└── 🏗️ Scaffold
├── 📋 AppBar
│ └── 📝 Text
├── 🎯 Center
│ └── 📐 Column
│ ├── 📝 Text
│ └── 📝 Text
└── 🚀 FloatingActionButton
└── 🎯 Icon
위젯 트리 구조 설명:
- MyApp → MaterialApp → MyHomePage → Scaffold
- Scaffold 안에 3개의 주요 영역:
- 📋 AppBar: 상단 바 (Text 포함)
- 🎯 Center: 중앙 영역 (Column → Text들)
- 🚀 FloatingActionButton: 플로팅 버튼 (Icon 포함)
🔄 StatelessWidget vs StatefulWidget
타입 | 특징 | 사용 시기 |
---|---|---|
🔒 StatelessWidget | 상태 없음 | 한 번 생성되면 UI가 바뀌지 않는 위젯 |
🔄 StatefulWidget | 상태 있음 | 사용자의 동작에 따라 UI가 바뀌는 위젯 |
🏗️ MaterialApp, Scaffold, AppBar
📱 MaterialApp
Flutter 앱의 루트(최상위) 위젯
✨ 주요 특징
- 🎨 앱 전체 테마 설정
- 🧭 라우팅(페이지 이동) 관리
- 📝 타이틀 및 폰트 설정
- 🏠 앱 당 1개만 존재
🔧 주요 속성
title
: 앱 제목home
: 앱 실행 시 첫 화면 위젯theme
: 전체 테마 설정routes
: 라우트 정의
🏗️ Scaffold
앱의 기본적인 화면 구조를 잡는 뼈대 위젯
✨ 특징
- 🏠 상단 바, 본문, 하단 영역으로 나눠서 골격을 잡아줌
- 📱 Flutter 앱의 화면 1개를 구성할 때 대부분 Scaffold 사용
🔧 주요 속성
appBar
: 상단바 설정body
: 화면의 주요 내용floatingActionButton
: 동작 버튼 정의drawer
: 사이드 메뉴
📋 AppBar
화면 상단에 나타나는 헤더 영역
타이틀, 아이콘, 액션 버튼 등을 배치할 수 있으며, 상단 영역의 골격을 잡아주는 역할
🗂️ 구성 요소
leading
: 왼쪽 영역에 위치title
: 가운데 타이틀actions
: 오른쪽 액션 리스트bottom
: 하단 (선 그을 때 사용)
📝 Text, Image, Icon
✏️ Text
화면에 글자를 표시할 때 사용하는 기본 위젯
const Text(
String this.data, {
super.key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
@Deprecated(
'Use textScaler instead. '
'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
'This feature was deprecated after v3.12.0-2.0.pre.',
)
this.textScaleFactor,
this.textScaler,
this.maxLines,
this.semanticsLabel,
this.textWidthBasis,
this.textHeightBehavior,
this.selectionColor,
})
🔧 주요 속성
style
: 텍스트 스타일 (크기, 색상, 굵기 등)textAlign
: 텍스트 정렬maxLines
: 최대 줄 수overflow
: 텍스트 오버플로우 처리
💡 사용 예시
Text(
"안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w900,
color: Colors.green,
),
maxLines: 1,
overflow: TextOverflow.ellipsis, // 뒤에 텍스트 ... 처리
textAlign: TextAlign.center,
),
🖼️ Image
🌐 이미지 사용 방법
방법 | 사용 예시 | 설명 |
---|---|---|
인터넷 이미지 | Image.network('URL') |
온라인 이미지 출력 |
로컬 이미지 | Image.asset('assets/image.png') |
프로젝트 내 이미지 사용 |
🎯 로컬 이미지 설정 과정
- 📁 프로젝트 →
assets
폴더 생성 - 📝
pubspec.yaml
수정 ```yaml assets:- assets/ ```
- 💡 장점:
pubspec.yaml
에 필요한 이미지만 포함 → 앱 용량 절약
💡 왜 named 생성자를 사용할까? 기본 생성자를 사용하면 이미지 속성에 객체 타입을 지정해야 하기 때문에 코드가 길어짐
🎯 Icon
Flutter에서 제공하는 기본 아이콘을 표시하는데 사용
📚 정의되어 있는 아이콘 목록들
- 🎨 Material Icons: Google의 Material Design 아이콘 스타일
- 🍎 Cupertino Icons: iOS 스타일 아이콘
📐 Column, Row
⬇️ Column
위젯들을 세로로 배치할 때 필요한 위젯
🔧 주요 속성
children
: 배치할 위젯들의 리스트 정의mainAxisAlignment
: 수직 방향 정렬
🎨 mainAxisAlignment 옵션들
옵션 | 설명 | 시각적 배치 |
---|---|---|
start | 위쪽 정렬 | ⬆️ 상단 |
end | 아래쪽 정렬 | ⬇️ 하단 |
center | 수직 가운데 정렬 | 🎯 중앙 |
spaceBetween | 위젯들 사이에 동일한 간격, 양 끝에는 간격 없음 | 📏 균등 분할 |
spaceAround | 위젯들 사이에 동일한 간격, 첫/마지막에는 절반 간격 | 🎪 적당한 여백 |
spaceEvenly | 모든 곳에 동일한 간격 | 📊 완전 균등 |
.png)
.png)
crossAxisAlignment
: 수평 정렬
🎨 crossAxisAlignment 옵션들
옵션 | 설명 |
---|---|
start | 왼쪽 정렬 |
end | 오른쪽 정렬 |
center | 수평 가운데 정렬 |
stretch | 위젯들이 교차 축 방향으로 가능한 공간을 모두 채움 |
💡 Column 크기 특성
- Column 위젯에는 기본적으로 크기가 없음
- 자녀 위젯의 크기에서 가장 큰 위젯의 가로 크기를 따라감
- 부모 위젯에 크기가 있다면, 부모 위젯의 가로 크기를 따라감
🎯 크기 확장 팁
body: SizedBox(
width: double.infinity,
child: Column(children: [Text("data"), Text("data"), Text("data")]),
),
➡️ Row
자식 위젯들을 가로(수평) 방향으로 나열하는 위젯
📋 상세 속성 값은 Column과 동일
📜 SingleChildScrollView
Column 또는 Row를 스크롤 가능한 위젯으로 변경해 줌
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("data"),
Text("data"),
Text("data"),
Text("data"),
Text("data"),
Text("data"),
Text("data"),
]
)
)
📦 SizedBox, Container
📏 SizedBox
고정된 너비와 높이를 갖는 박스를 생성하는 위젯
🎯 사용 목적
- 자녀 위젯의 크기를 지정할 때 사용
- 간격(공백)을 만들 때 사용
🔧 주요 속성
width
: 너비height
: 높이child
: 자식 위젯
💡 사용 예시
// 간격 만들기
SizedBox(height: 20)
// 고정 크기 안에 위젯 배치
SizedBox(
width: 200,
height: 100,
child: ElevatedButton(onPressed: (){}, child: Text('Hi')),
)
// 무한 확장
SizedBox(
width: double.infinity, // 무한히 확대하겠다는 의미!
height: 100,
child: ElevatedButton(onPressed: (){}, child: Text('Hi')),
)
💡 기본적으로 크기가 없는 위젯들은 자녀 또는 부모의 사이즈를 따라감 (ex. ElevatedButton)
🎨 Container
SizedBox + 레이아웃, 스타일, 정렬 등 다양한 기능을 가진 다목적 위젯
✨ 특징
padding
,margin
,color
,alignment
,decoration
등 다양한 속성 제공- 크기가 지정되지 않으면 부모 위젯의 크기를 물려받음
🔧 주요 속성
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.green, width: 10),
borderRadius: BorderRadius.circular(20),
),
child: Container(color: Colors.black),
)
📋 속성 상세 설명
속성 | 설명 |
---|---|
width , height |
컨테이너의 너비와 높이 지정 |
color |
배경색 설정 (⭐️ decoration 과 함께 사용 불가) |
padding |
자식 위젯 내부 여백 (내용과 테두리 사이 공간) |
margin |
외부 여백 (Container 자신과 바깥 위젯 사이 공간) |
alignment |
자식 위젯의 정렬 위치 지정 |
decoration |
배경, 테두리, 그림자, 둥근 모서리 등 스타일 설정 |
child |
Container 내부에 위치할 단일 자식 위젯 |
📐 EdgeInsets 클래스
왼쪽, 위, 오른쪽, 아래 방향 각각의 오프셋(거리) 정보를 가지는 클래스
🎯 주요 사용법
메서드 | 설명 | 예시 |
---|---|---|
EdgeInsets.all(value) |
네 방향 모두 같은 여백 | EdgeInsets.all(16) |
EdgeInsets.symmetric(horizontal: x, vertical: y) |
좌우/상하 각각 설정 | EdgeInsets.symmetric(horizontal: 20, vertical: 10) |
EdgeInsets.only(left: x, top: y, ...) |
방향별 개별 설정 | EdgeInsets.only(left: 10, right: 5, top: 4, bottom: 20) |
🎨 플러터 색상 클래스
🎯 색상 사용 방법
방법 | 예시 | 설명 |
---|---|---|
HEX (16진수) | Color(0xFF42A5F5) |
0xFF는 alpha(불투명도), 뒤는 RGB |
RGBO | Color.fromRGBO(66, 165, 245, 1.0) |
RGB 값과 Opacity(0~1) 지정 |
ARGB | Color.fromARGB(255, 66, 165, 245) |
Alpha(0~255) 값과 RGB 값 지정 |
Material 색상 | Colors.blue , Colors.redAccent |
정의된 Material 색상 사용 |
🤔 SizedBox vs Container 언제 써야할까요?
📏 SizedBox를 사용하는 경우
- 단순히 공간을 만들거나 위젯의 크기만 지정할 때
- 스타일, 패딩, 정렬이 필요 없고 간단한 간격만 줄 때
- 🎯 예: 위젯 사이에
height: 16
간격 만들기
🎨 Container를 사용하는 경우
- 크기뿐 아니라 스타일링이 필요한 경우
- 배경색(
color
), 모서리 둥글게(borderRadius
), 그림자(boxShadow
) 등
- 배경색(
- padding, margin, 정렬, decoration 등 다양한 속성을 써야 할 때
- 복합적인 UI를 꾸밀 때 필수
- 💡 padding만 사용 시
Padding
위젯도 사용 가능
⚡ Container는 여러 기능을 제공하기 때문에 SizedBox에 비해서 성능적으로는 좋지 않음!
🔧 Expanded, Spacer
📏 Expanded
Row, Column 내에서 남은 공간을 차지하도록 자식 위젯을 확장해주는 위젯
✨ 특징
- 남은 공간을 “비율대로” 나눠서 채움
- Row와 Column의 남은 부분을 채워주는 기능 담당
- ⚠️ 반드시 Row와 Column과 함께 사용해야 함
🌌 Spacer
Expanded와 동일하나 자녀 위젯은 가지지 못함
✨ 특징
- 빈 공간만 차지 (중간 간격용)
- 내부에
child
없음 (자식 불가)
🌈 과제
아래 화면과 같이 무지개를 만들어 보자!
🎯 내 풀이
body: Column(
children: [
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.red)),
),
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.orange)),
),
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.yellow)),
),
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.green)),
),
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.blue)),
),
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.indigo)),
),
Expanded(
child: Container(decoration: BoxDecoration(color: Colors.purple)),
),
],
),
✅ 정답 코드
Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("무지개"),
),
body: Column(
children: [
Expanded(child: Container(color: Colors.red)),
Expanded(child: Container(color: Colors.orange)),
Expanded(child: Container(color: Colors.yellow)),
Expanded(child: Container(color: Colors.green)),
Expanded(child: Container(color: Colors.blue)),
Expanded(child: Container(color: Colors.indigo)),
Expanded(child: Container(color: Colors.purple)),
],
),
)
🎉 정답 그대로 했다!
📚 Stack, Positioned, Align, Center
🏗️ Stack
여러 위젯을 겹쳐서 배치할 수 있는 위젯
✨ 특징
- 자식들을
children
속성에 배치하여 겹치기 가능 - 기본 정렬은 좌측 상단
Positioned
와 함께 자식의 위치를 지정할 수 있음Align
과도 함께 사용 가능Center
로 간단한 중앙 정렬- Stack은 크기가 없고 자식 또는 부모 위젯의 크기를 따라감
🔧 주요 속성
children
: 겹쳐서 배치할 위젯 리스트alignment
: 기본 정렬 기준 (Alignment.topLeft
기본값)clipBehavior
: 넘치는 부분 자를지 설정
🎯 clipBehavior 옵션들
옵션 | 설명 |
---|---|
Clip.none |
자식이 부모 영역을 벗어나도 잘리지 않음 (기본값) |
Clip.hardEdge |
벗어난 영역을 딱 잘라냄 |
Clip.antiAlias |
가장자리 부드럽게 잘라냄 (곡선에 부드러움), 잘 안 씀 |
Clip.antiAliasWithSaveLayer |
antiAlias + 퍼포먼스 조금 더 낮음, 잘 안 씀 |
💡 사용 예시
Stack(
children: [
Container(width: 200, height: 200, color: Colors.green),
Container(width: 150, height: 150, color: Colors.blue),
Container(width: 100, height: 100, color: Colors.yellow),
Positioned(
right: 10,
bottom: 10,
child: Container(width: 50, height: 50, color: Colors.blueGrey),
),
],
)
💡 Positioned의 경우 Stack의 자식의 크기를 기준으로 배치가 정해짐 (만약 부모 위젯이 있는 경우 해당 부모를 따라감)
🎯 GestureDetector, ElevatedButton, 기초 위젯 정리
🚀 ElevatedButton
눌렀을 때 약간 튀어나온 듯한 입체 효과(Elevation)가 적용되는 버튼
🔧 주요 속성
onPressed
: 버튼 클릭 시 실행할 함수child
: 버튼 안에 표시할 위젯 (텍스트, 아이콘 등)style
: 색상, 테두리, 폰트 크기, 모양 등 스타일 지정
💡 사용 예시
void onButtonClick(){
print("버튼 터치됨");
}
...
ElevatedButton(
// 버튼의 스타일은 ElevatedButton.styleFrom 메서드로 정의 가능
// 중요 파라미터
// - backgroundColor : 배경색
// - foregroundColor : 전경색 (글자, Icon)
// - shape : 모양. 주로 RoundedRectangleBorder 클래스를 통해 모서리 둥글기만 수정
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
onPressed: onButtonClick,
child: Text('data'),
)
✨ 특징
- 부모 사이즈의 크기를 따라감
🎨 복합 버튼 예시
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Column(
children: [
SizedBox(
width: 200,
height: 80,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber, // 배경 색
foregroundColor: Colors.green, // 자식 색
textStyle: TextStyle(fontSize: 20),
iconSize: 40,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
onPressed: onButtonPressed,
child: Row(
children: [Icon(Icons.star), Text("좋아요"), Text("123")],
),
),
),
],
),
);
}
🔧 익명 함수
✨ 특징
- 이름이 없는 함수
- 딱 한 번만 함수를 작성할 때 유용
- callback 동작을 정의할 때 유용
💡 사용 예시
ElevatedButton(
onPressed: () {
print("버튼 터치됨")
},
child: Text('data'),
)
👆 GestureDetector
사용자의 터치, 제스처(탭, 더블탭, 스와이프 등)를 감지하여 반응하는 위젯
🎯 모든 시각적 요소를 클릭 가능하게 만들고 싶을 때 사용
🔧 주요 속성
onTap
: 탭(클릭) 이벤트onDoubleTap
: 더블탭 이벤트onLongPress
: 길게 누르기- 그 외
onPanUpdate
(드래그 중 움직임 감지),onVerticalDragStart
,onHorizontalDragUpdate
(방향별 스와이프 제스처) 등 대부분의 사용자 제스처를 처리할 수 있는 속성들이 존재 child
: 이벤트 대상이 될 위젯
💡 사용 예시
GestureDetector(
onTap: () {
print("이미지 터치됨");
},
onDoubleTap: () {
print("이미지 두번 터치됨");
},
onLongPress: () {
print("이미지 길게 터치됨");
},
child: Image.network('https://picsum.photos/200/300'),
)
📊 기초 위젯 복습
기능/역할 | 대표 위젯들 | 설명 |
---|---|---|
🏗️ 앱 뼈대 구성 | MaterialApp , Scaffold , AppBar |
앱의 기본 구조와 상단 앱바 설정 |
📝 기본 출력 요소 | Text , Image , Icon |
텍스트, 이미지, 아이콘 출력 |
📐 1열/1행 배치 | Column , Row |
자식 위젯들을 수직/수평으로 나열 |
📦 간격/크기 조절 | SizedBox , Container , Padding |
크기 지정, 여백 추가, 배경 및 정렬 |
🔧 공간 분할 | Expanded , Spacer |
남은 공간을 유연하게 분할하거나 비워둠(Column, Row 내에서만) |
🏗️ 겹치기/정렬 | Stack , Positioned , Align , Center |
위젯을 겹치거나 원하는 위치로 배치 |
👆 입력/반응 처리 | GestureDetector , ElevatedButton |
클릭, 제스처 입력 등 사용자 상호작용 처리 |
댓글남기기