본문 바로가기
개발/Flutter

[Flutter] 레시피 UI 만들기

by 가시죠 2021. 12. 12.
반응형

순서

1. 화면구조 구성하기

2. 폰트, 이미지등 assets 구성하기

3. 각 화면 구성

4. 실행

 

1. 화면구조 구성하기

상단 AppBar

본문 Listview

  본문안의 내용 Row, ClipRRect

 

2. 폰트, 이미지등 assets 구성하기

pubspec.yaml - 변경후에는 Pub get 버튼을 클릭하여 적용해야 한다.

name: flutter_recipe
description: Flutter Recipe App UI

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true
  assets: # assets 폴더 지정
    - assets/images/
  fonts: # 폰트 지정
    - family: BMDOHYEON
      fonts:
      - asset: assets/fonts/BMDOHYEON_ttf.ttf

 

3. 각 화면 구성

프로젝트 파일 구조

프로젝트 파일 구조

사용된 클래스

AppBar, Icon, Text, Container, ClipRRect, Listview

 

AppBar : https://api.flutter.dev/flutter/material/AppBar-class.html

Icon : https://api.flutter.dev/flutter/widgets/Icon-class.html

Text : https://api.flutter.dev/flutter/dart-html/Text-class.html

Container : https://api.flutter.dev/flutter/widgets/Container-class.html

ClipRRect : https://api.flutter.dev/flutter/widgets/ClipRRect-class.html

Listview : https://api.flutter.dev/flutter/widgets/ListView-class.html

 

 

파일별 소스

 

components/recipe_list_item.dart

import 'dart:ui';

import 'package:flutter/material.dart';

class RecipeListItem extends StatelessWidget {
  final String imageName;
  final String title;
  final String desc;

  const RecipeListItem(this.imageName, this.title, this.desc);

  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: const EdgeInsets.symmetric(vertical: 10), // 세로 단격 10으로
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          AspectRatio(
            aspectRatio: 3/1, // 이미지사이즈 크기를 조정 3/1 -> 3배 줄임
            child: ClipRRect(
              borderRadius: BorderRadius.circular(30), // 모서리 라운딩 처리
              child: Image.asset( // 이미지 지정, 인자값으로 받은 이미지명을 assets에서 찾아 가져옴
                "assets/images/$imageName.jpeg",
                fit: BoxFit.cover, // 이미지 맞춤을 커버 형식으로
              ),
            ),
          ),

          SizedBox(height: 5,), // 이미지와 아래 text 간격
          Text(
            title,
            style: TextStyle(fontSize: 15),
          ),
          Text(
            desc,
            style: TextStyle(color: Colors.grey, fontSize: 12),
          )
        ],
      ),
    );
  }
}

 

components/recipe_menu.dart

import 'package:flutter/material.dart';

class RecipeMenu extends StatelessWidget {
  const RecipeMenu({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: const EdgeInsets.only(top: 20),
      child: Row( // 메뉴 아이템들의 방향이 수평 방향이라서 row를 사용
        children: [
          _buildMenuItem(Icons.food_bank, "Menu1"),
          SizedBox(width: 20),
          _buildMenuItem(Icons.emoji_food_beverage, "Menu2"),
          SizedBox(width: 20),
          _buildMenuItem(Icons.fastfood, "Menu3"),
          SizedBox(width: 20),
          _buildMenuItem(Icons.local_pizza, "Menu4"),
        ],
      ),
    );
  }
}

/**
 * 메뉴 아이콘을 만들기 위한 함수
 */
Widget _buildMenuItem(IconData mIcon, String iconText) {
  return Container( // 데코레이션을 위해 사용
    width: 50,
    height: 50,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(20),
      border: Border.all(color: Colors.black12),
    ),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(mIcon, color: Colors.redAccent, size: 25,),
        SizedBox(height: 5,),
        Text(
          iconText,
          style: TextStyle(color: Colors.black87, fontSize: 10),
        )
      ],
    ),
  );
}

 

components/recipe_title.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class RecipeTitle extends StatelessWidget {
  const RecipeTitle({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: const EdgeInsets.only(top:20),
      child: Text(
        "타이틀",
        style: TextStyle(fontSize: 20),
      ),
    );
  }
}

 

pages/recipe_page.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_recipe/components/recipe_list_item.dart';
import 'package:flutter_recipe/components/recipe_menu.dart';
import 'package:flutter_recipe/components/recipe_title.dart';

/**
 * 하단의 리스트 페이지
 */
class RecipePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white, // 배경색 지정
      appBar: _buildRecipeAppBar(), // AppBar 연결
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20), // 수평으로 여백 주기
        child: ListView(
          // Column 위에서 아래로 내려가는 구조
          children: [
            RecipeTitle(),
            RecipeMenu(),
            RecipeListItem("pizza", "피자 만들기", "도우를 만드는게 중요합니다. 준비물:밀가루, 피망, 치즈, "),
            RecipeListItem("burger", "버거 만들기", "버거에는 감자튀김이 제일 중요하죠. 준비물:감자, 햄, "),
            RecipeListItem("coffee", "커피 만들기","좋은 원두를 준비합니다. 커피머신에 원두를 넣고"),
          ],
        ),
      ),
    );
  }
}

/**
 * 상단의 앱 바
 */
AppBar _buildRecipeAppBar() {
  return AppBar(
    title: Text("앱바 타이틀", style: TextStyle(color: Colors.black, fontSize: 15),),
    backgroundColor: Colors.white, //배경색
    elevation: 3.0, // 그림자 효과 조정
    leading: IconButton(icon: Icon(Icons.menu), onPressed: null,),
    actions: [
      Icon(
        CupertinoIcons.search, // 쿠퍼티노 아이콘 사용
        color: Colors.black, // 아이콘 색상
      ),
      SizedBox(width: 15,), // 공백 박스
      Icon(
        CupertinoIcons.heart,
        color: Colors.redAccent,
      ),
      SizedBox(width: 15,)
    ],
  );
}

 

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_recipe/pages/recipe_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(fontFamily: "BMDOHYEON"), // pubspec.yaml에 등록한 폰트지정
      home: RecipePage(), // 홈 화면으로 RecipePage
    );
  }
}

 

4. 실행

 


참고 : https://github.com/flutter-coder/flutter-ui-book1

반응형

댓글