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