Flutter

[Flutter] 연습하기2 - flutter recipe app

연화 2025. 1. 7. 19:29

만들면서 배우는 플러터 앱 프로그래밍을 바탕으로 연습했습니다.
코드를 짜기 전, 최상단 폴더에 assets 폴더를 만들고 이미지와 폰트를 넣어줍니다.

이미지 폴더 전체 사용 설정 (pubspec.yaml)
  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/images/

images/ 해당 경로 하위의 모든 파일을 가져올 수 있게 됩니다.

폰트 사용 설정 (pubspec.yaml)
 # example:
  fonts:
    - family: PatuaOne
      fonts:
        - asset: assets/fonts/PatuaOne-Regular.ttf

해당 폰트를 불러올 때 입력할 폰트의 이름을 지정해줍니다.

코드
  • 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() {
  // MyApp 위젯을 루트 위젯으로 만들어 주는 함수
  // 사전 기반 지식 ---. 위젯 트리를 떠올려 주세요
  runApp(const MyApp());
}

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

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

  // 보통 루트 위젯을 시작할 때 머터리얼 앱을 많이 활용한다
  @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(
      backgroundColor: Colors.white,
      appBar: _buildRecipeAppBar(),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: ListView(
            children: [
              RecipeTitle(),
              RecipeMenu(),
              RecipeListItem('coffee', 'Made Coffee'),
              RecipeListItem('burger', 'Made Burger'),
              RecipeListItem('pizza', 'Made Pizza'),
            ],
          ),
        ),
      ),
    );
  }

  // 메서드를 만들어 보자
  // private
  AppBar _buildRecipeAppBar() {
    return AppBar(
      backgroundColor: Colors.white,
      bottom: PreferredSize(
          preferredSize: Size.fromHeight(3.0),
          child: Container(
            color: Colors.black,
            height: 1.0,
          )),
      shape: Border(
        bottom: BorderSide(color: Colors.black12, width: 1),
      ),
      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)
      ],
    );
  }
}
  • 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)),
    );
  }
}
  • 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(
        mainAxisAlignment: MainAxisAlignment.start,
        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(30),
      ),
      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),
          // 텍스트1
          Text(title, style: TextStyle(fontSize: 20)),
          // 텍스트2
          Text(
              'Have you ever..To add assets to your application, add an assets section, '
              'like this To add assets to your application, add an assets section, like this',
              style: TextStyle(color: Colors.black38, fontSize: 12)),
        ],
      ),
    );
  }
}

 

 

아래의 문헌을 참고하여 작성된 포스팅입니다.
최주호, 김근호, 이지원(공저) 『만들면서 배우는 플러터 앱 프로그래밍』, 앤써북, 2023.