1. 기본 세팅

assets 폴더를 만든 후 사진을 넣는다.

pubspec.yaml 설정 후 pub get을 누른다.
2. 기본 테마 설정하기
constants.dart
import 'package:flutter/material.dart';
const kAccentColor = Color(0xFFFF385C);
size.dart
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_heigh = 620 ;
double getBodyWidth(BuildContext context){
return MediaQuery.of(context).size.width*0.7;
}
style.dart
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 , fontWeight : FontWeight.bold,color :mColor);
}
3. HomePage 만들기
main.dart
import 'package:airbnb_app/size.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView(
children: [
SizedBox(
width: double.infinity,
height: header_heigh,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background.jpeg"),
fit: BoxFit.cover,
),
),
),
),
],
),
),
);
}
}

가독성을 위해 컴포넌트를 분리한다.
pages/home_page.dart
import 'package:flutter/material.dart';
import '../components/home_header.dart';
class HomePage extends StatelessWidget {
const HomePage({
super.key,
});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
HomeHeader(),
],
),
);
}
}
components/home_header.dart
import 'package:flutter/material.dart';
import '../size.dart';
class HomeHeader extends StatelessWidget {
const HomeHeader({
super.key,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: header_heigh,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background.jpeg"),
fit: BoxFit.cover,
),
),
),
);
}
}
components/form_title.dart
import 'package:flutter/material.dart';
import '../size.dart';
import '../style.dart';
class FormTitle extends StatelessWidget {
const FormTitle({
super.key,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"모두의 숙소에서 숙소를 검색하세요.",
style: h4(),
),
SizedBox(height: gap_xs),
Text(
"혼자하는 여행에 적합한 개인실부터 여럿이 함께 하는 여행에 좋은 '공간전체' 숙소까지, 모두의 숙소에 다 있습니다.",
style: body1(),
),
SizedBox(height: gap_m),
],
);
}
}
home_header.dart
import 'package:flutter/material.dart';
import '../size.dart';
import '../style.dart';
import 'form_title.dart';
class HomeHeader extends StatelessWidget {
const HomeHeader({
super.key,
});
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return SizedBox(
width: double.infinity,
height: header_heigh,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background.jpeg"),
fit: BoxFit.cover,
),
),
child: Column(
children: [
Align(
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: Column(
children: [
FormTitle(),
],
),
),
),
),
],
),
),
);
}
}

4. CommonFormField 만들기
common/common_form_field.dart
import 'package:flutter/material.dart';
import '../style.dart';
class CommonFormField extends StatelessWidget {
final prefixText;
final hintText;
CommonFormField({required this.prefixText, required this.hintText});
@override
Widget build(BuildContext context) {
return Stack(
children: [
TextFormField(
textAlignVertical: TextAlignVertical.bottom,
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(),
),
)
],
);
}
}
components/form_field_detail.dart
import 'package:flutter/material.dart';
import '../common/common_form_field.dart';
import '../size.dart';
import 'form_title.dart';
class FormFieldDetial extends StatelessWidget {
@override
Widget build(BuildContext context) {
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",
)),
]),
],
);
}
}
components/form_submit.dart
import 'package:flutter/material.dart';
import '../constants.dart';
import '../style.dart';
class FormSubmit extends StatelessWidget {
const FormSubmit({
super.key,
});
@override
Widget build(BuildContext context) {
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),
),
),
);
}
}
home_header_form.dart
import 'package:airbnb_app/size.dart';
import 'package:flutter/material.dart';
import 'form_field_detail.dart';
import 'form_submit.dart';
import 'form_title.dart';
class HomeHeaderForm extends StatelessWidget {
const HomeHeaderForm({
super.key,
required this.screenWidth,
});
final double screenWidth;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: gap_m),
child: Align(
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: Column(
children: [
FormTitle(),
FormFieldDetial(),
FormSubmit(),
],
),
),
),
),
);
}
}
components/home_header.form
import 'package:flutter/material.dart';
import '../size.dart';
import 'home_header_form.dart';
class HomeHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return SizedBox(
width: double.infinity,
height: header_heigh,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background.jpeg"),
fit: BoxFit.cover,
),
),
child: Column(
children: [
HomeHeaderForm(screenWidth: screenWidth),
],
),
),
);
}
}

5. AppBar 만들기
components/home_header_appbar.dart
import 'package:airbnb_app/size.dart';
import 'package:airbnb_app/style.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: [
AppBarLogo(),
Spacer(),
AppBarMenu(),
],
),
);
}
Widget AppBarLogo() {
return Row(
children: [
Image.asset(
"assets/logo.png",
width: 30,
height: 30,
color: Colors.redAccent,
),
SizedBox(width: gap_s),
Text(
"RoomOfAll",
style: h5(mColor: Colors.white),
)
],
);
}
Widget AppBarMenu() {
return Row(
children: [
Text("회원가입", style: subTitle1(mColor: Colors.white)),
SizedBox(width: gap_m),
Text("로그인", style: subTitle1(mColor: Colors.white)),
],
);
}
}
components/home_header.dart
import 'package:airbnb_app/components/home_header_appbar.dart';
import 'package:flutter/material.dart';
import '../size.dart';
import 'home_header_form.dart';
class HomeHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return SizedBox(
width: double.infinity,
height: header_heigh,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background.jpeg"),
fit: BoxFit.cover,
),
),
child: Column(
children: [
HomeHeaderAppBar(),
HomeHeaderForm(screenWidth: screenWidth),
],
),
),
);
}
}
6. home_body.dart 만들기
6.1. home_body_banner.dart 만들기
components/home_body.dart
import 'package:airbnb_app/components/home_body_banner.dart';
import 'package:airbnb_app/size.dart';
import 'package:flutter/material.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(),
],
),
),
);
}
}
home_body_banner.dart
import 'package:airbnb_app/style.dart';
import 'package:flutter/material.dart';
import '../size.dart';
class HomeBodyBanner extends StatelessWidget {
const HomeBodyBanner({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
"assets/banner.jpg",
fit: BoxFit.cover,
width: double.infinity,
height: 320,
),
),
Positioned(
top: 40,
left: 40,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(
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()),
),
)
],
),
),
],
);
}
}
컴포넌트를 분리한다.
banner_image.dart
import 'package:flutter/material.dart';
class BannerImage extends StatelessWidget {
const BannerImage({
super.key,
});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
"assets/banner.jpg",
fit: BoxFit.cover,
width: double.infinity,
height: 320,
),
);
}
}
banner_caption.dart
import 'package:flutter/material.dart';
import '../size.dart';
import '../style.dart';
class BannerCaption extends StatelessWidget {
const BannerCaption({
super.key,
});
@override
Widget build(BuildContext context) {
return Positioned(
top: 40,
left: 40,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(
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()),
),
)
],
),
);
}
}

home_body_banner.dart
import 'package:airbnb_app/style.dart';
import 'package:flutter/material.dart';
import '../size.dart';
import 'banner_caption.dart';
import 'banner_image.dart';
class HomeBodyBanner extends StatelessWidget {
const HomeBodyBanner({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
BannerImage(),
BannerCaption(),
],
);
}
}
6.2. home_body_popular.dart
components/home_body_popular.dart
import 'package:flutter/material.dart';
import 'home_body_popular_item.dart';
class HomeBodyPopular extends StatelessWidget {
const HomeBodyPopular({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(),
Wrap(
children: [
HomeBodyPopularItem(id:0),
SizedBox(width: 7.5),
HomeBodyPopularItem(id:1),
SizedBox(width: 7.5),
HomeBodyPopularItem(id:2),
],
),
],
);
}
}
components/home_body_popular_item.dart
import 'package:airbnb_app/size.dart';
import 'package:flutter/material.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 Container(
constraints: BoxConstraints(
minWidth: 320,
),
child: SizedBox(
width: popularItemWidth,
child: Column(
children: [
PopularItemImage(id: id,popularList: popularList),
PopularItemStar(),
PopularItemComment(),
PopularItemUserInfo(),
],
),
),
);
}
}
components/popular_item_comment.dart
import 'package:flutter/material.dart';
import '../size.dart';
import '../style.dart';
class PopularItemComment extends StatelessWidget {
const PopularItemComment({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"깔끔하고 다 갖춰져있어서 좋았어요:) 위치도 완전 좋아용 다들 여기 살고싶다구ㅋㅋㅋㅋㅋ 화장실도 3개예요!!! 5명이서 씻는것도 전혀 불편함 없이 좋았어요^^ 이불도 포근하고 좋습니당ㅎㅎ",
style: body1(),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: gap_s),
],
);
}
}
components/popular_item_user_info.dart
import 'package:airbnb_app/style.dart';
import 'package:flutter/material.dart';
import '../size.dart';
class PopularItemUserInfo extends StatelessWidget {
const PopularItemUserInfo({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
CircleAvatar(
backgroundImage: AssetImage("assets/p1.jpeg"),
),
SizedBox(width: gap_s),
Column(
children: [
Text(
"데어",
style: subTitle1(),
),
Text("한국"),
],
),
],
);
}
}
popular_item_image.dart
import 'package:airbnb_app/constants.dart';
import 'package:flutter/material.dart';
import '../size.dart';
class PopularItemStar extends StatelessWidget {
const PopularItemStar({super.key});
@override
Widget build(BuildContext context) {
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,)
],
);
}
}



화면의 크기를 줄이면 반응형으로 적용된다.
Share article