Flutter

[Flutter] recipe app 만들기

ekkkang 2025. 1. 7. 18:54

 

PatuaOne-Regular.ttf
0.03MB

 

 

디자인 시안

 

 

폴더 생성

 

폴더 전체로 사용하려면?
  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/images/

 

폰트 설정
  # example:
  fonts:
    - family: PatuaOne
      fonts:
        - asset: assets/fonts/PatuaOne-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf

 

 

앱바 만들어 보기
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_recipe_app/recipe_list_item.dart';
import 'package:flutter_recipe_app/recipe_menu.dart';
import 'package:flutter_recipe_app/recipe_title.dart';

// 플러터 코드의 시작점
void main() {
  // MyApp 위젯을 루트 위젯으로 만들어 주는 함수 이다.
  // 사전 기반 지식 ---. 위젯 트리를 떠올려 주세요
  runApp(MyApp());
}

// 위젯을 만들 때 크게 두가지로 구분한다.
// StatelessWidget 위젯은 상태 변경이 없는 위젯을 화면에 그릴 때 선택한다.

// 상속문법 사용
// 우리들만에 약속 - MyApp -> 머터리얼 앱 객체를 활용하자.
class MyApp extends StatelessWidget {
  // 생성자 - 선택적 명명 매개 변수( 값을 넣어도 안 넣어도 되고)
  MyApp({super.key});

  // 보통 루트 위젯을 시작할 때 MaterialApp을 많이 활용 한다.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(fontFamily: 'PatuaOne'),
      // 앱 실행 되었을 때 첫 페이지를 지정할 수 있다.
      home: RecipePage(),
    );
  }
} // end of class

// 우리들만에 규칙 2 - 페이지라고 이름 붙이는 클래스는 스캐아폴더를 활용하자.
class RecipePage extends StatelessWidget {
  const RecipePage({super.key});

  @override
  Widget build(BuildContext context) {
    // 시각적 레이아웃 구조를 잡아 주는 위젯
    // app 영역
    // body 영역
    // bottom 영역
    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'),
            // 레시피 아이템
            // 레시피 아이템
          ],
        ),
      ),
    );
  } // end of build method

  // 메서드를 만들어 보자.
  // private
  AppBar _buildRecipeAppBar() {
    return AppBar(
      backgroundColor: Colors.white,
      iconTheme: IconThemeData(color: Colors.black),
      //elevation: 1.0,
      actions: [
        Icon(CupertinoIcons.search, color: Colors.black),
        SizedBox(width: 15),
        Icon(CupertinoIcons.heart, color: Colors.red),
        SizedBox(width: 15)
      ],
    );
  }
}

 

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'),
        ],
      ),
    );
  }

  // 메서드 만들어 보기
  // Container 위젯을 꾸며 주고 싶다면 ...
  Widget _buildMenuIcon(IconData mIcon, String text) {
    // Container 위젯에 color 속성과 decoration 함께 쓰면
    // 무조건 오류
    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),
          )
        ],
      ),
    );
  }
}

 

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 erver ................ ')
          // 텍스트 1
          // 텍스트 2
        ],
      ),
    );
  }
}