components/common/commom_form_field.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/styles.dart';
class CommonFormField extends StatelessWidget {
final prefixText;
final hintText;
const CommonFormField({required this.prefixText, 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/home/home_body.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/components/home/home_body_banner.dart';
import 'package:flutter_airbnb/components/home/home_body_popular.dart';
import 'package:flutter_airbnb/size.dart';
class HomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
double bodyWidth = getBodyWidth(context);
return Align(
child: SizedBox(
width: bodyWidth,
child: Column(
children: [
HomeBodyBanner(),
HomeBodyPopular(),
],
),
),
);
}
}
components/home/home_body_banner.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/size.dart';
import 'package:flutter_airbnb/styles.dart';
class HomeBodyBanner extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: gap_m),
child: Stack(
children: [
_buildBannerImage(),
_buildBannerCaption(),
],
),
);
}
Widget _buildBannerImage() {
return ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
"assets/banner.jpg",
fit: BoxFit.cover,
width: double.infinity,
height: 320,
),
);
}
Widget _buildBannerCaption() {
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(),
),
),
),
],
));
}
}
components/home/home_body_popular.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/components/home/home_body_popular_item.dart';
import 'package:flutter_airbnb/size.dart';
import 'package:flutter_airbnb/styles.dart';
class HomeBodyPopular extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: gap_m),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildPopularTitle(),
_buildPopularList(),
],
),
);
}
Widget _buildPopularTitle() {
return Column(
children: [
Text(
"한국 숙소에 직접 다녀간 게스트의 후기",
style: h5(),
),
Text(
"게스트 후기 2,500,000개 이상, 평균 평점 4.7점(5점 만점)",
style: body1(),
),
SizedBox(height: gap_m),
],
);
}
Widget _buildPopularList() {
return Wrap(
children: [
HomeBodyPopularItem(id: 0),
SizedBox(width: 7.5),
HomeBodyPopularItem(id: 1),
SizedBox(width: 7.5),
HomeBodyPopularItem(id: 2),
SizedBox(width: 7.5),
],
);
}
}
components/home/home_body_popular_item.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/constants.dart';
import 'package:flutter_airbnb/size.dart';
import 'package:flutter_airbnb/styles.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: const EdgeInsets.only(bottom: gap_x1),
child: Container(
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,
),
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("한국"),
],
),
],
);
}
}
components/home/home_header.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/components/home/home_header_appbar.dart';
import 'package:flutter_airbnb/components/home/home_header_form.dart';
import 'package:flutter_airbnb/size.dart';
class HomeHeader extends StatelessWidget {
@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(),
],
),
),
);
}
}
components/home/home_header_appbar.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/constants.dart';
import 'package:flutter_airbnb/size.dart';
import 'package:flutter_airbnb/styles.dart';
class HomeHeaderAppbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(gap_m),
child: Row(
children: [
_buildAppBarLogo(),
Spacer(),
_buildAppBarMenu(),
],
),
);
}
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 _buildAppBarMenu() {
return Row(
children: [
Text("회원가입", style: subtitle1(mColor: Colors.white)),
SizedBox(width: gap_m),
Text("로그인", style: subtitle1(mColor: Colors.white)),
],
);
}
}
components/home/home_header_form.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/components/common/common_form_field.dart';
import 'package:flutter_airbnb/constants.dart';
import 'package:flutter_airbnb/size.dart';
import 'package:flutter_airbnb/styles.dart';
class HomeHeaderForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
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: Padding(
padding: const EdgeInsets.all(gap_1),
child: Column(
children: [
_buildFormTitle(),
_buildFormField(),
_buildFormSubmit(),
],
),
)),
),
),
);
}
Widget _buildFormTitle() {
return Column(
children: [
Text(
"모두의숙소에서 숙소를 검색하세요.",
style: h4(),
),
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),
),
),
);
}
}
components/pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/components/home/home_body.dart';
import 'package:flutter_airbnb/components/home/home_header.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
HomeHeader(),
HomeBody(),
],
),
);
}
}
constants.dart
import 'package:flutter/material.dart';
const kAccentColor = Color(0xFFFF385C);
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_airbnb/pages/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
size.dart
import 'package:flutter/material.dart';
// 간격
const double gap_x1 = 40;
const double gap_1 = 30;
const double gap_m = 20;
const double gap_s = 10;
const double gap_xs = 5;
// 헤더 높이
const double header_height = 645;
// MediaQuery 클래스로 화면 사이즈를 받을 수 있다.
double getBodyWidth(BuildContext context) {
return MediaQuery.of(context).size.width * 0.7;
}
styles.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, color: mColor);
}