
1. 기본 세팅하기
- 파일 구조 잡기

- 이미지 경로 잡기

2. 기본 셋팅하기
- 색깔 정의
import 'package:flutter/material.dart';
// 색상을 정의합니다.
const kAccentColor = Color(0xFFFF385C);
- 여백 정의
import 'package:flutter/cupertino.dart';
// 간격
const double gap_xl = 40;
const double gap_l = 30;
const double gap_m = 20;
const double gap_s = 10;
const double gap_xs = 5;
// 헤더 높이
const double header_height = 620;
// MediaQuery 클래스로 화면 사이즈를 받을 수 있다.
double getBodyWidth(BuildContext context) {
return MediaQuery.of(context).size.width * 0.7;
}
- 스타일 정의
import 'package:flutter/material.dart';
TextStyle h4({Color mColor = Colors.black}) {
return TextStyle(fontSize: 34, fontWeight: FontWeight.bold, color: mColor);
}
TextStyle h5({Color mColor = Colors.black}) {
return TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: mColor);
}
TextStyle subtitle1({Color mColor = Colors.black}) {
return TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: mColor);
}
TextStyle subtitle2({Color mColor = Colors.black}) {
return TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: mColor);
}
TextStyle overLine({Color mColor = Colors.black}) {
return TextStyle(fontSize: 10, fontWeight: FontWeight.bold, color: mColor);
}
TextStyle body1({Color mColor = Colors.black}) {
return TextStyle(fontSize: 16, color: mColor);
}
import 'package:flutter/cupertino.dart';
class CommonFormField extends StatelessWidget {
const CommonFormField({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}
import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
class HomeHeaderAppBar extends StatelessWidget {
const HomeHeaderAppBar({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(gap_m),
child: Row(
children: [
_buildAppBarLogo(),
Spacer(),
_build_AppBarMenu(),
],
),
);
}
Widget _buildAppBarLogo() {
return SizedBox();
}
Widget _build_AppBarMenu() {
return SizedBox();
}
}
import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomeHeaderForm extends StatelessWidget {
const HomeHeaderForm({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: gap_m),
child: Align(
alignment: Alignment(-0.6, 0),
child: Container(
width: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Form(
child: Column(
children: [
_buildFormTitle(),
_buildFormField(),
_buildFormSubmit(),
],
),
),
),
),
);
}
Widget _buildFormTitle() {
return SizedBox();
}
Widget _buildFormField() {
return SizedBox();
}
Widget _buildFormSubmit() {
return SizedBox();
}
}
import 'package:airbnb_app/components/home/home_header_appbar.dart';
import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
import 'home_header_form.dart';
class HomeHeader extends StatelessWidget {
const HomeHeader({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: header_height,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background.jpeg"),
fit: BoxFit.cover,
),
),
child: Column(
children: [
HomeHeaderAppBar(),
HomeHeaderForm(),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import '../../size.dart';
class HomeBodyBanner extends StatelessWidget {
const HomeBodyBanner({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
// 1. 상단에 마진을 준다.
padding: const EdgeInsets.only(top: gap_m),
// 2 이미지와 글자를 겹치게 하기 위해서 Stack 위젯을 사용한다.
child: Stack(
children: [
_buildBannerImage(),
_buildBannerCaption(),
],
),
);
}
Widget _buildBannerImage() {
return SizedBox();
}
Widget _buildBannerCaption() {
return SizedBox();
}
}
import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
class HomeBodyPopularItem extends StatelessWidget {
final id;
final popularList = [
"p1.jpeg",
"p2.jpeg",
"p3.jpeg",
];
HomeBodyPopularItem({required this.id});
@override
Widget build(BuildContext context) {
double popularItemWidth = getBodyWidth(context) / 3 - 5;
return Padding(
padding: EdgeInsets.only(bottom: gap_xl),
child: Container(
constraints: BoxConstraints(
minWidth: 320,
),
child: SizedBox(
width: popularItemWidth,
child: Column(
children: [
_buildPopularvItemImage(),
_buildPopularvItemStar(),
_buildPopularvItemComment(),
_buildPopularvItemUserInfo(),
],
),
),
),
);
}
Widget _buildPopularvItemImage(){
return SizedBox();
}
Widget _buildPopularvItemStar(){
return SizedBox();
}
Widget _buildPopularvItemComment(){
return SizedBox();
}
Widget _buildPopularvItemUserInfo(){
return SizedBox();
}
}
import 'package:airbnb_app/components/home/home_body_popular_item.dart';
import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
class HomeBodyPopular extends StatelessWidget {
const HomeBodyPopular({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: gap_m),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildPopularTitle(),
_buildPopularList(),
],
),);
}
Widget _buildPopularTitle() {
return SizedBox();
}
Widget _buildPopularList() {
return Wrap(
children: [
HomeBodyPopularItem(id: 0),
SizedBox(width: 7.5),
HomeBodyPopularItem(id: 1),
SizedBox(width: 7.5),
HomeBodyPopularItem(id: 2),
],
);
}
}
import 'package:airbnb_app/components/home/home_body_popular.dart';
import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
import 'home_body_banner.dart';
class HomeBody extends StatelessWidget {
const HomeBody({super.key});
@override
Widget build(BuildContext context) {
double bodyWidth = getBodyWidth(context);
return Align(
child: SizedBox(
width: bodyWidth,
child: Column(
children: [
HomeBodyBanner(),
HomeBodyPopular(),
],
),
),
);
}
}
import 'package:airbnb_app/components/home/home_body.dart';
import 'package:airbnb_app/components/home/home_header.dart';
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
HomeHeader(),
HomeBody(),
],
),
);
}
import 'package:flutter/material.dart';
import 'package:flutter/pages/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
import 'package:airbnb_app/constants.dart';
import 'package:airbnb_app/size.dart';
import 'package:airbnb_app/style.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomeHeaderAppBar extends StatelessWidget {
const HomeHeaderAppBar({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(gap_m),
child: Row(
children: [
_buildAppBarLogo(),
Spacer(),
_build_AppBarMenu(),
],
),
);
}
Widget _buildAppBarLogo() {
return Row(
children: [
Image.asset("assets/logo.png", width: 30,height: 30, color: kAccentColor),
SizedBox(width: gap_s),
Text("RoomOfAll", style: h5(mColor: Colors.white)),
],
);
}
Widget _build_AppBarMenu() {
return Row(
children: [
Text("회원가입", style: subtitle1(mColor: Colors.white)),
SizedBox(width: gap_m),
Text("로그인", style: subtitle1(mColor: Colors.white)),
],
);
}
}
import 'package:flutter/material.dart';
import '../../style.dart';
class CommonFormField extends StatelessWidget {
final prefixText;
final hintText;
const CommonFormField({Key? key, required this.prefixText, required this.hintText}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
TextFormField(
textAlignVertical: TextAlignVertical.bottom, // 2. TextFormField 내부 세로 정렬
decoration: InputDecoration(
contentPadding: EdgeInsets.only(top: 30, left: 20, bottom: 10),
hintText: hintText,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(
color: Colors.black,
width: 2,
),
),
),
),
Positioned(
top: 8,
left: 20,
child: Text(
prefixText,
style: overLine(),
),
),
],
);
}
}
import 'package:flutter/material.dart';
import '../../constants.dart';
import '../../size.dart';
import '../../style.dart';
import '../common/common_form_field.dart';
class HomeHeaderForm extends StatelessWidget {
const HomeHeaderForm({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery
.of(context)
.size
.width; // 추가
return Padding(
padding: const EdgeInsets.only(top: gap_m), // 1. AppBar와 거리주기
// 2. 정렬 위젯
child: Align(
// 3. -1.0 부터 1.0 까지 가로 범위에서 0.1의 값은 5%이다.
alignment:
screenWidth < 520 ? Alignment(0, 0) : Alignment(-0.6, 0), // 변경
child: Container(
width: 420,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Form(
child: Padding(
padding: const EdgeInsets.all(gap_l), // 4. Form 내부 여백
child: Column(
children: [
_buildFormTitle(), // 5. Form 위젯 제목 영역
_buildFormField(), // 6. Form 위젯 Text 입력 양식 영역
_buildFormSubmit(), // 7. Form 위젯 전송 버튼 영역
],
),
),
),
),
),
);
}
Widget _buildFormTitle() {
return Column(
children: [
Text(
"모두의 숙소에서 숙소를 검색하세요.",
style: h4(),
// overflow: TextOverflow.ellipsis, 앱에선 터지고 크롬에선 안터짐
),
SizedBox(height: gap_xs),
Text(
"혼자하는 여행에 적합한 개인실부터 여럿이 함께하는 여행에 좋은 '공간전체' 숙소까지, 모두의숙소에 다 있습니다.",
style: body1(),
),
SizedBox(height: gap_m),
],
);
}
Widget _buildFormField() {
return Column(
children: [
CommonFormField(
prefixText: "위치",
hintText: "근처 추천 장소",
),
SizedBox(height: gap_s),
Row(
children: [
Expanded(
child: CommonFormField(
prefixText: "체크인",
hintText: "날짜 입력",
)),
Expanded(
child: CommonFormField(
prefixText: "체크 아웃",
hintText: "날짜 입력",
)),
],
),
SizedBox(height: gap_s),
Row(
children: [
Expanded(
child: CommonFormField(
prefixText: "성인",
hintText: "2",
)),
Expanded(
child: CommonFormField(
prefixText: "어린이",
hintText: "0",
)),
],
),
SizedBox(height: gap_m),
],
);
}
Widget _buildFormSubmit() {
return SizedBox(
width: double.infinity,
height: 50,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: kAccentColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
)),
onPressed: () {
print("서브밋 클릭됨");
},
child: Text(
"검색",
style: subtitle1(mColor: Colors.white),
),
),
);
}
}
import 'package:flutter/material.dart';
import '../../size.dart';
import '../../style.dart';
class HomeBodyBanner extends StatelessWidget {
const HomeBodyBanner({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
// 1. 상단에 마진을 준다.
padding: const EdgeInsets.only(top: gap_m),
// 2 이미지와 글자를 겹치게 하기 위해서 Stack 위젯을 사용한다.
child: Stack(
children: [
_buildBannerImage(),
_buildBannerCaption(),
],
),
);
}
Widget _buildBannerImage() {
return ClipRRect(
// 3. 이미지 모서리 둥글게
borderRadius: BorderRadius.circular(20),
child: Image.asset(
"assets/banner.jpg",
fit: BoxFit.cover,
width: double.infinity,
height: 320,
),
);
}
Widget _buildBannerCaption() {
return Positioned(
// 4. Stack 위젯 내부에 위치 설정을 위해
top: 40,
left: 40,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(
// 5. 글자 범위 최대 제약 조건 주기
maxWidth: 250,
),
child: Text(
"이제, 여행은 가까운 곳에서",
style: h4(mColor: Colors.white),
),
),
SizedBox(height: gap_m),
Container(
constraints: BoxConstraints(
maxWidth: 250,
),
child: Text(
"새로운 공간에 머물러 보세요. 살아보기, 출장, 여행 등 다양한 목적에 맞는 숙소를 찾아보세요.",
style: subtitle1(mColor: Colors.white),
),
),
SizedBox(height: gap_m),
SizedBox(
height: 35,
width: 170,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
onPressed: () {},
child: Text(
"가까운 여행지 둘러보기",
style: subtitle2(),
),
),
),
],
),
);
}
}

import 'package:airbnb_app/size.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../constants.dart';
import '../../style.dart';
class HomeBodyPopularItem extends StatelessWidget {
final id;
final popularList = [
"p1.jpeg",
"p2.jpeg",
"p3.jpeg",
];
HomeBodyPopularItem({Key? key, required this.id}) : super(key: key);
@override
Widget build(BuildContext context) {
// 1. 인기아이템은 전체화면의 70%의 1/3만큼의 사이즈의 -5 의 크기를 가진다.
double popularItemWidth = getBodyWidth(context) / 3 - 5;
return Padding(
padding: const EdgeInsets.only(bottom: gap_xl),
child: Container(
// 2. 화면이 줄어들 때 너무 작게 줄어드는 것을 방지하기 위해 최소 제약조건을 잡아준다.
constraints: BoxConstraints(
minWidth: 320,
),
child: SizedBox(
width: popularItemWidth,
child: Column(
children: [
_buildPopularItemImage(),
_buildPopularItemStar(),
_buildPopularItemComment(),
_buildPopularItemUserInfo(),
],
),
),
),
);
}
Widget _buildPopularItemImage() {
return Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset("assets/${popularList[id]}", fit: BoxFit.cover),
),
SizedBox(height: gap_s),
],
);
}
Widget _buildPopularItemStar() {
return Column(
children: [
Row(
children: [
Icon(Icons.star, color: kAccentColor),
Icon(Icons.star, color: kAccentColor),
Icon(Icons.star, color: kAccentColor),
Icon(Icons.star, color: kAccentColor),
Icon(Icons.star, color: kAccentColor),
],
),
SizedBox(height: gap_s),
],
);
}
Widget _buildPopularItemComment() {
return Column(
children: [
Text(
"깔끔하고 다 갖춰져있어서 좋았어요:) 위치도 완전 좋아용 다들 여기 살고싶다구ㅋㅋㅋㅋㅋ 화장실도 3개예요!!! 5명이서 씻는것도 전혀 불편함 없이 좋았어요^^ 이불도 포근하고 좋습니당ㅎㅎ",
style: body1(),
maxLines: 3,
overflow: TextOverflow.ellipsis, // 3. 글자가 3 라인을 벗어나면 ... 처리된다.
),
SizedBox(height: gap_s),
],
);
}
Widget _buildPopularItemUserInfo() {
return Row(
children: [
CircleAvatar(
backgroundImage: AssetImage("assets/p1.jpeg"),
),
SizedBox(width: gap_s),
Column(
children: [
Text(
"데어",
style: subtitle1(),
),
Text("한국"),
],
)
],
);
}
}



Share article