✨ Today I Learned 2회차 - Flutter 위젯 기초 🎯

10 분 소요

🚀 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.dartmain() 함수가 앱의 진입점

📦 패키지 사용 방법

방법 명령어/설정 설명
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

위젯 트리 구조 설명:

  • MyAppMaterialAppMyHomePageScaffold
  • 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') 프로젝트 내 이미지 사용

🎯 로컬 이미지 설정 과정

  1. 📁 프로젝트 → assets 폴더 생성
  2. 📝 pubspec.yaml 수정 ```yaml assets:
    • assets/ ```
  3. 💡 장점: pubspec.yaml에 필요한 이미지만 포함 → 앱 용량 절약

💡 왜 named 생성자를 사용할까? 기본 생성자를 사용하면 이미지 속성에 객체 타입을 지정해야 하기 때문에 코드가 길어짐

🎯 Icon

Flutter에서 제공하는 기본 아이콘을 표시하는데 사용

📚 정의되어 있는 아이콘 목록들

📐 Column, Row

⬇️ Column

위젯들을 세로로 배치할 때 필요한 위젯

🔧 주요 속성

  • children: 배치할 위젯들의 리스트 정의
  • mainAxisAlignment: 수직 방향 정렬

🎨 mainAxisAlignment 옵션들

옵션 설명 시각적 배치
start 위쪽 정렬 ⬆️ 상단
end 아래쪽 정렬 ⬇️ 하단
center 수직 가운데 정렬 🎯 중앙
spaceBetween 위젯들 사이에 동일한 간격, 양 끝에는 간격 없음 📏 균등 분할
spaceAround 위젯들 사이에 동일한 간격, 첫/마지막에는 절반 간격 🎪 적당한 여백
spaceEvenly 모든 곳에 동일한 간격 📊 완전 균등
간격 정렬 예시 1 간격 정렬 예시 2
  • 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 클릭, 제스처 입력 등 사용자 상호작용 처리

댓글남기기