https://notion6780.tistory.com/121
플러터) 레시피 앱 만들기 1. 프로젝트 생성
1️⃣ 프로젝트 생성 2️⃣ 메인과 홈 코드 작성 3️⃣ 컴퍼넌트 코드 작성프로젝트 생성1.안드로이드 스튜디오를 열어 기존프로젝트를 끄고 플러터 프로젝트를 만들어준다. 2.프로젝트 이름과
notion6780.tistory.com
앞서 프로젝트를 생성했으니 코드를 작성해볼 차례다.
메인
PatuaOne 폰트를 기본으로 사용하는 머티리얼 디자인 스타일의 Flutter 앱을 정의
앱이 시작되면 MyApp 위젯이 로드된다.
MyApp은 MaterialApp을 통해 앱의 기본 구조와 테마를 설정한 후
초기 화면으로 RecipePage를 보여주게 된다.
import 'package:flutter/material.dart';
import 'pages/recipe_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(fontFamily: "PatuaOne"),
home: RecipePage(),
);
}
}
RecipePage
메인에서 홈페이지로 불러온 RecipePage 위젯을 정의
RecipePage는 StatelessWidget으로
화면에 레시피와 관련된 여러 UI 요소를 표시한다.
흰색 배경을 중심으로
상단에는 검색 및 하트 아이콘이 있는 앱 바를 가지고
본문에는 좌우 여백이 있는 스크롤 가능한 목록을 표시한다.
이 목록에는 레시피 제목, 메뉴, 그리고 커피, 버거, 피자 레시피 항목들이 순서대로 표시된다.
_buildAppBar라는 프라이빗 메서드를 사용하여 앱 바 UI를 분리했다.
import 'package:class_recipes/components/recipe_list_item.dart';
import 'package:class_recipes/components/recipe_menu.dart';
import 'package:class_recipes/components/recipe_title.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RecipePage extends StatelessWidget{
const RecipePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: _buildAppBar(),
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"),
],
),
),
);
}//end of build
//클래스 안의 메서드를 생성
AppBar _buildAppBar() {
return AppBar(
backgroundColor: Colors.white,
elevation: 1.0,
actions: [
Icon(CupertinoIcons.search,
color: Colors.black),
const SizedBox(width: 15),
Icon(CupertinoIcons.heart,
color: Colors.redAccent),
const SizedBox(width: 15),
],
);
}
}//end of Page
컴퍼넌트
UI를 작은, 독립적인 위젯 클래스로 분리했다.
이를 통해 코드의 구조를 개선하고
개발 효율성을 높일 수 있다.
컴퍼넌트를 분리했을때의 장점
가독성 향상 (Readability)
재사용성 증가 (Reusability)
유지보수 용이성 (Maintainability)
성능 최적화 (Performance Optimization)
관심사 분리 (Separation of Concerns)
이를 위해 메인페이지에서 주요 영역을
RecipeTitle
RecipeMenu
RecipeListItem 위젯으로 분리한다.
RecipeTitle
화면 상단에 20픽셀의 여백을 두고, 폰트 크기 30인 "Recipe"라는 텍스트를 표시함
import 'package:flutter/cupertino.dart';
class RecipeTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20),
child: Text(
'Recipe',
style: TextStyle(fontSize: 30),
),
);
}
}
RecipeMenu
상단에 20픽셀의 여백을 두고
네 개의 메뉴 아이템("All", "Cofee", "Burger", "Pizza")을 가로로 나열하여 표시했다.
각 메뉴 아이템은 아이콘과 텍스트로 구성되며
둥근 모서리와 검은색 테두리가 있는 컨테이너 내에 중앙 정렬된다.
메뉴 아이템 사이에는 25픽셀의 간격이 있다.
_buildMenuItem이라는 내부 메서드를 사용하여 반복되는 메뉴 아이템 UI 생성을 효율화했다.
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: [
_buildMenuItem(Icons.food_bank, 'All'),
SizedBox(width: 25),
_buildMenuItem(Icons.coffee, 'Coffee'),
SizedBox(width: 25),
_buildMenuItem(Icons.fastfood, 'Burger'),
SizedBox(width: 25),
_buildMenuItem(Icons.local_pizza, 'Pizza'),
],
),
);
}
//함수메서드 생성
Container _buildMenuItem(IconData mIcon, String mText) {
return Container(
//컨테이너로 대부분의 커스텀위젯 처리
width: 60,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
border: Border.all(color: Colors.black),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(mIcon, color: Colors.redAccent, size: 30),
SizedBox(height: 5),
Text(mText),
],
),
);
}
}
RecipeListItem
하나의 레시피 항목을 표시한다.
상하로 20픽셀의 여백을 가지며 내부적으로는 세로로 정렬된 요소들로 구성된다.
가장 위에는 2:1 비율의 둥근 모서리를 가진 이미지가 표시되고
그 아래에는 레시피 제목과 고정된 형식의 상세 설명이 차례로 나타난다.
이미지 경로는 생성자를 통해 동적으로 설정된다.
import 'package:flutter/material.dart';
class RecipeListItem extends StatelessWidget {
final String imageName;
final String title;
//final String detail;
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 made your own ${title} Once you've tried a homemade ${title}, you'll never go back.",
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
);
}
}
최종 결과물
'Flutter' 카테고리의 다른 글
플러터에 MVVM 패턴을 적용해 관심사를 분리 (1) | 2025.08.18 |
---|---|
SharedPreferences (1) | 2025.08.13 |
구글 지도 api 써보기 (1) | 2025.08.13 |
플러터) 콜백메서드, 아이의 일을 부모가 알게 하자 (0) | 2025.07.28 |
플러터) 레시피 앱 만들기 1. 프로젝트 생성 (2) | 2025.07.25 |