
1. 기본 셋팅하기
- 파일 구조 잡기
재사용 가능한 models, 각 화면들(컴포넌트, 페이지), 테마

- 구글과 폰트어썸 이모티콘 사용을 위한 셋팅하기
cupertino_icons: ^1.0.2
google_fonts: ^6.1.0
font_awesome_flutter: ^10.5.0
intl: ^0.18.1

2. 홈 화면 뼈대 만들기
import 'package:flutter/cupertino.dart';
class MainScreens extends StatefulWidget {
@override
State<MainScreens> createState() => _MainScreensState();
}
class _MainScreensState extends State<MainScreens> {
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: const Text("MainScreens"),
),
);
}
}
import 'package:flutter/material.dart';
import 'screens/main_screens.dart';
import 'theme.dart';
void main() {
runApp(const CarrotMarket());
}
class CarrotMarket extends StatelessWidget {
const CarrotMarket({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'carrot_market',
home: MainScreens(),
theme: theme(),
);
}
}
- 기본 실행앱이 myApp에서 변경되어서 오류가 남
- 이름을 바꿔주면 사용 가능하나 테스트를 할 건 아니라 그냥 삭제하기

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MainScreens extends StatefulWidget {
@override
State<MainScreens> createState() => _MainScreensState();
}
class _MainScreensState extends State<MainScreens> {
int _seletedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _seletedIndex,
children: [
Container(
color: Colors.orange[100],
child: Center(
child: Text(
"IndexedStack 1",
style: TextStyle(fontSize: 20, color: Colors.black),
),
),
),
Container(
color: Colors.orange[100],
child: Center(
child: Text(
"IndexedStack 2",
style: TextStyle(fontSize: 20, color: Colors.black),
),
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
label: "홈",
icon: Icon(
CupertinoIcons.chat_bubble,
),
),
],
onTap: (index){
setState(() {
_seletedIndex = index;
},);
},
currentIndex: _seletedIndex,
),
);
}
}
import 'package:flutter/cupertino.dart';
class ChattingScreen extends StatelessWidget {
const ChattingScreen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Text("chattingScreen"),
);
}
}
import 'package:flutter/cupertino.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Text("homeScreen"),
);
}
}
import 'package:flutter/cupertino.dart';
class MyCarrotScreen extends StatelessWidget {
const MyCarrotScreen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Text("myCattorScreen"),
);
}
}
import 'package:flutter/cupertino.dart';
class NearMeScreen extends StatelessWidget {
const NearMeScreen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Text("nearMeScreen"),
);
}
}

import 'package:flutter/cupertino.dart';
class NeighborhoodLifeScreen extends StatelessWidget {
const NeighborhoodLifeScreen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Text("neighborhoodLifeScreen"),
);
}
}
import 'package:carrot_market_ui/screens/chatting/chatting_sceen.dart';
import 'package:carrot_market_ui/screens/home/home_screen.dart';
import 'package:carrot_market_ui/screens/my_carrot/my_carrot_screen.dart';
import 'package:carrot_market_ui/screens/near_me/near_me_screen.dart';
import 'package:carrot_market_ui/screens/neighborhood_life/neighborhood_life_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MainHolder extends StatefulWidget {
@override
_MainScreensState createState() => _MainScreensState();
}
class _MainScreensState extends State<MainHolder> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _selectedIndex,
children: [HomeScreen(), NeighborhoodLifeScreen(), NearMeScreen(), ChattingScreen(), MyCarrotScreen()],
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
currentIndex: _selectedIndex,
onTap: (index) {
setState(() {
_selectedIndex = index;
});
},
items: [
const BottomNavigationBarItem(label: '홈', icon: Icon(CupertinoIcons.home)),
const BottomNavigationBarItem(label: '동네생활', icon: Icon(CupertinoIcons.square_on_square)),
const BottomNavigationBarItem(label: '내 주변', icon: Icon(CupertinoIcons.placemark)),
const BottomNavigationBarItem(label: '채팅', icon: Icon(CupertinoIcons.chat_bubble_2)),
const BottomNavigationBarItem(label: '나의 당근', icon: Icon(CupertinoIcons.person)),
],
),
);
}
}

import 'package:carrot_market_ui/theme.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("HomeScreen appbar 영역(index:0)"),
),
body: Container(
color: Colors.orange[100],
child: Center(
child: Text(
"HomeScreen body 영역(index:0)",
style: textTheme().displayMedium,
),
),
),
);
}
}

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
children: [
const Text("좌동"),
const SizedBox(width: 4.0),
const Icon(CupertinoIcons.chevron_down, size: 15.0),
],
),
actions: [
IconButton(
icon: const Icon(CupertinoIcons.list_dash), onPressed: () {}),
IconButton(icon: const Icon(CupertinoIcons.bell), onPressed: () {}),
],
bottom: const PreferredSize(
preferredSize: Size.fromHeight(0.5),
child: Divider(thickness: 0.5, height: 0.5, color: Colors.grey),
),
),
body: Container(),
);
}
}

class Product {
String title;
String author;
String address;
String urlToImage;
String publishedAt;
String price;
int heartCount;
int commentsCount;
Product({
required this.title,
required this.author,
required this.address,
required this.urlToImage,
required this.publishedAt,
required this.price,
required this.heartCount,
required this.commentsCount,
});
}
// 샘플 데이터
List<Product> productList = [
Product(
title: '니트 조끼',
author: 'author_1',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_7.jpg?raw=true',
publishedAt: '2시간 전',
heartCount: 8,
price: '35000',
address: '좌동',
commentsCount: 3),
Product(
title: '먼나라 이웃나라 12',
author: 'author_2',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_6.jpg?raw=true',
publishedAt: '3시간 전',
heartCount: 3,
address: '중동',
price: '18000',
commentsCount: 1),
Product(
title: '캐나다구스 패딩조',
author: 'author_3',
address: '우동',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_5.jpg?raw=true',
publishedAt: '1일 전',
heartCount: 0,
price: '15000',
commentsCount: 12,
),
Product(
title: '유럽 여행',
author: 'author_4',
address: '우동',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_4.jpg?raw=true',
publishedAt: '3일 전',
heartCount: 4,
price: '15000',
commentsCount: 11,
),
Product(
title: '가죽 파우치 ',
author: 'author_5',
address: '우동',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_3.jpg?raw=true',
publishedAt: '1주일 전',
heartCount: 7,
price: '95000',
commentsCount: 4,
),
Product(
title: '노트북',
author: 'author_6',
address: '좌동',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_2.jpg?raw=true',
publishedAt: '5일 전',
heartCount: 4,
price: '115000',
commentsCount: 0,
),
Product(
title: '미개봉 아이패드',
author: 'author_7',
address: '좌동',
urlToImage: 'https://github.com/flutter-coder/ui_images/blob/master/carrot_product_1.jpg?raw=true',
publishedAt: '5일 전',
heartCount: 8,
price: '85000',
commentsCount: 3,
),
];
import 'package:flutter/cupertino.dart';
import '../../models/product.dart';
import 'product_detail.dart';
class ProductItem extends StatelessWidget {
final Product product;
ProductItem({required this.product});
@override
Widget build(BuildContext context) {
return Container(
height: 135.0,
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image.network(
product.urlToImage,
width: 115,
height: 115,
fit: BoxFit.cover,
),
),
const SizedBox(width: 16.0),
ProductDetail(product: product)
],
),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:intl/intl.dart'; // Import the intl package for NumberFormat
import '../../models/product.dart';
import '../../theme.dart';
class ProductDetail extends StatelessWidget {
final Product product;
const ProductDetail({required this.product});
@override
Widget build(BuildContext context) {
return Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(product.title, style: textTheme().bodyLarge),
const SizedBox(height: 4.0),
Text('${product.address} • ${product.publishedAt}'),
const SizedBox(height: 4.0),
Text(
'${numberFormat(product.price)}원',
style: textTheme().displayMedium,
),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Visibility(
visible: product.commentsCount > 0,
child: _buildIcons(
product.commentsCount,
CupertinoIcons.chat_bubble_2,
),
),
const SizedBox(width: 8.0),
Visibility(
visible: product.heartCount > 0,
child: _buildIcons(
product.heartCount,
CupertinoIcons.heart,
),
),
],
)
],
),
);
}
String numberFormat(String price) {
final formatter = NumberFormat('#,###');
return formatter.format(int.parse(price));
}
Widget _buildIcons(int count, IconData iconData) {
return Row(
children: [
Icon(iconData, size: 14.0),
const SizedBox(width: 4.0),
Text('$count'),
],
);
}
}

import 'package:carrot_market_ui/models/product.dart';
import 'package:carrot_market_ui/screens/components/product_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
children: [
const Text('좌동'),
const SizedBox(width: 4.0),
const Icon(
CupertinoIcons.chevron_down,
size: 15.0,
),
],
),
actions: [
IconButton(icon: const Icon(CupertinoIcons.search), onPressed: () {}),
IconButton(
icon: const Icon(CupertinoIcons.list_dash), onPressed: () {}),
IconButton(icon: const Icon(CupertinoIcons.bell), onPressed: () {})
],
bottom: const PreferredSize(
preferredSize: Size.fromHeight(0.5),
child: Divider(thickness: 0.5, height: 0.5, color: Colors.grey),
),
),
body: ListView.separated(
separatorBuilder: (context, index) => const Divider(
height: 0,
indent: 16,
endIndent: 16,
color: Colors.grey,
),
itemBuilder: (context, index) {
return ProductItem(
product: productList[index],
);
},
itemCount: productList.length,
),
);
}
}

Share article