주요 위젯
Container
- 정의: 레이아웃을 구성하는 가장 기본적인 위젯.
- 특징: 크기, 색상, 테두리, 모서리 둥글기 등 다양한 스타일을 설정할 수 있습니다.
Container(
width: 100, // 가로 길이
height: 100, // 세로 길이
decoration: BoxDecoration(
color: Colors.blue, // 배경 색상
borderRadius: BorderRadius.circular(10), // 모서리를 둥글게
border: Border.all(color: Colors.black, width: 2), // 테두리 설정
),
);
AppBar
- 정의: 화면 상단에 고정되는 네비게이션 바입니다.
- 특징: 제목, 아이콘, 버튼 등을 추가할 수 있습니다.
AppBar(
title: Text("My App"), // 앱바 제목
backgroundColor: Colors.blue, // 앱바 배경색
actions: [ // 앱바 오른쪽에 추가할 아이콘들
Icon(Icons.search), // 검색 아이콘
Icon(Icons.more_vert), // 더보기 아이콘
],
);
Icon
- 정의: 작은 크기의 아이콘을 표시하는 데 사용합니다.
- 특징: 다양한 기본 제공 아이콘을 활용할 수 있습니다.
Icon(
Icons.home, // 아이콘 종류
color: Colors.red, // 아이콘 색상
size: 30, // 아이콘 크기
);
ListView
- 정의: 스크롤 가능한 리스트를 표시합니다.
- 특징: 많은 데이터가 있을 때 유용하며, 스크롤이 기본으로 지원됩니다.
ListView(
children: [ // 리스트 아이템
ListTile(title: Text("Item 1")), // 리스트 첫 번째 항목
ListTile(title: Text("Item 2")), // 리스트 두 번째 항목
ListTile(title: Text("Item 3")), // 리스트 세 번째 항목
],
);
레시피 앱
프로젝트 폴더 구조
- assets/images/: 앱에서 사용할 이미지 저장.
- assets/fonts/: 앱에서 사용할 폰트 저장.
pubspec.yaml 파일에서 이미지와 폰트를 프로젝트에 등록합니다.
pubspec.yaml 설정
flutter:
assets:
- assets/images/
fonts:
- family: PatuaOne
fonts:
- asset: assets/fonts/PatuaOne-Regular.ttf
앱 시작점
import 'package:flutter/material.dart';
// 앱의 시작점
void main() {
runApp(MyApp()); // MyApp을 실행
}
// MyApp 클래스: 앱 전체의 루트 위젯
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // 디버그 배너 제거
theme: ThemeData(fontFamily: 'PatuaOne'), // 기본 폰트 설정
home: RecipePage(), // 앱의 첫 번째 화면 설정
);
}
}
RecipePage: 앱의 첫 화면
class RecipePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold( // 화면의 기본 구조를 잡아주는 위젯
appBar: _buildRecipeAppBar(), // AppBar 추가
body: ListView( // 스크롤 가능한 리스트
padding: EdgeInsets.all(20), // 리스트의 여백
children: [
Text("Recipe App", style: TextStyle(fontSize: 24)), // 제목
SizedBox(height: 20), // 위젯 간 간격
RecipeMenu(), // 메뉴 위젯 호출
RecipeListItem('coffee', 'Made Coffee'), // 리스트 아이템
RecipeListItem('burger', 'Made Burger'), // 리스트 아이템
],
),
);
}
// AppBar를 빌드하는 메서드
AppBar _buildRecipeAppBar() {
return AppBar(
title: Text("Recipes"), // 앱바 제목
backgroundColor: Colors.white, // 앱바 배경 색
iconTheme: IconThemeData(color: Colors.black), // 아이콘 색상
actions: [
Icon(Icons.search, color: Colors.black), // 검색 아이콘
SizedBox(width: 10), // 간격
Icon(Icons.favorite, color: Colors.red), // 즐겨찾기 아이콘
],
);
}
}
RecipeMenu: 메뉴 구성
class RecipeMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row( // 메뉴를 가로로 정렬
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 균등 간격
children: [
_buildMenuIcon(Icons.food_bank, "All"), // 메뉴 아이콘 1
_buildMenuIcon(Icons.coffee, "Coffee"), // 메뉴 아이콘 2
_buildMenuIcon(Icons.fastfood, "Burger"), // 메뉴 아이콘 3
],
);
}
// 개별 메뉴 아이콘을 빌드하는 메서드
Widget _buildMenuIcon(IconData icon, String text) {
return Column(
children: [
Icon(icon, color: Colors.red, size: 40), // 아이콘
SizedBox(height: 5), // 간격
Text(text, style: TextStyle(color: Colors.black)), // 텍스트
],
);
}
}
RecipeListItem: 레시피 아이템
class RecipeListItem extends StatelessWidget {
final String imageName; // 이미지 파일 이름
final String title; // 레시피 제목
RecipeListItem(this.imageName, this.title); // 생성자
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 10), // 위아래 여백
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // 왼쪽 정렬
children: [
AspectRatio( // 이미지 비율 조정
aspectRatio: 2 / 1,
child: ClipRRect( // 모서리를 둥글게 처리
borderRadius: BorderRadius.circular(15),
child: Image.asset(
'assets/images/$imageName.jpg', // 이미지 경로
fit: BoxFit.cover, // 이미지를 채우기
),
),
),
SizedBox(height: 5), // 간격
Text(title, style: TextStyle(fontSize: 20)), // 레시피 제목
],
),
);
}
}
정리
- 기본 위젯
- Container: 레이아웃과 스타일 설정.
- AppBar: 상단 네비게이션 바.
- Icon: 아이콘 표시.
- ListView: 스크롤 가능한 리스트.
- Padding: 여백 설정.
- 프로젝트 구조
- main.dart: 앱의 시작점.
- RecipePage: 레이아웃과 앱바 구성.
- RecipeMenu: 메뉴 아이콘 표시.
- RecipeListItem: 리스트 아이템 구성.
- 플러터 앱의 핵심
- StatelessWidget과 StatefulWidget을 이해하고, 위젯 트리를 통해 화면을 구성합니다.
전체 코드
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'recipe_list_item.dart';
import 'recipe_menu.dart';
import 'recipe_title.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(fontFamily: 'PatuaOne'),
home: RecipePage(),
);
}
}
class RecipePage extends StatelessWidget {
const RecipePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildRecipeAppBar(),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: ListView(
children: [
RecipeTitle(),
RecipeMenu(),
RecipeListItem('coffee', 'Made Coffee'),
RecipeListItem('burger', 'Made Burger'),
RecipeListItem('pizza', 'Made Pizza'),
],
),
),
);
}
AppBar _buildRecipeAppBar() {
return AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
actions: [
Icon(CupertinoIcons.search, color: Colors.black),
SizedBox(width: 15),
Icon(CupertinoIcons.heart, color: Colors.red),
SizedBox(width: 15),
],
);
}
}
recipe_menu.dart
import 'package:flutter/material.dart';
class RecipeMenu extends StatelessWidget {
const RecipeMenu({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20),
child: Row(
children: [
_buildMenuIcon(Icons.food_bank, 'ALL'),
SizedBox(width: 25),
_buildMenuIcon(Icons.emoji_food_beverage, 'Coffee'),
SizedBox(width: 25),
_buildMenuIcon(Icons.fastfood, 'Burger'),
SizedBox(width: 25),
_buildMenuIcon(Icons.local_pizza, 'Pizza'),
],
),
);
}
Widget _buildMenuIcon(IconData mIcon, String text) {
return Container(
width: 60,
height: 80,
decoration: BoxDecoration(
border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.circular(25),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(mIcon, color: Colors.red),
SizedBox(height: 5),
Text(
text,
style: TextStyle(color: Colors.black54),
),
],
),
);
}
}
recipe_list_item.dart
import 'package:flutter/material.dart';
class RecipeListItem extends StatelessWidget {
final String imageName;
final String title;
const RecipeListItem(this.imageName, this.title, {super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 2 / 1,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
'assets/images/${imageName}.jpeg',
fit: BoxFit.cover,
),
),
),
SizedBox(height: 10),
Text(title, style: TextStyle(fontSize: 20)),
Text('Have you ever...'),
],
),
);
}
}
recipe_title.dart
import 'package:flutter/material.dart';
class RecipeTitle extends StatelessWidget {
const RecipeTitle({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20),
child: Text(
'Recipes',
style: TextStyle(fontSize: 30),
),
);
}
}