Flutter

[Flutter] 당근 마켓 만들어 보기

미로910 2024. 11. 15. 10:15
폴더 및 파일 만들기
pubspec.ymal 파일 설정
기본 코드 입력
앱 테마 설정
main.dart 파일 완성

 

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.6
  google_fonts: ^6.2.1
  intl: ^0.20.0
  font_awesome_flutter: ^10.8.0

홈 화면

ImageContainer
import 'package:carrot_market_ui/screens/main_screens.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(CarrotMarketApp());
}

class CarrotMarketApp extends StatelessWidget {
   CarrotMarketApp({super.key});

  @override
  Widget build(BuildContext conte000xt) {
    return MaterialApp(
      title: 'carrotMarket',
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange)
      ),
      home: MainScreens(),
    );
  }
}
chatMessage
class ChatMessage {
  final String sender;
  final String profileImage;
  final String location;
  final String sendDate;
  final String message;
  final String? imageUri;
  ChatMessage({
    required this.sender,
    required this.profileImage,
    required this.location,
    required this.sendDate,
    required this.message,
    this.imageUri,
  });
}
// 샘플 데이터
List<ChatMessage> chatMessageList = [
  ChatMessage(
    sender: '당근이, ',
    profileImage: 'https://picsum.photos/id/870/200/100?grayscale',
    location: '대부동',
    sendDate: '1일전',
    message: 'developer 님,근처에 다양한 물품들이 아주 많이있습니다.',
  ),
  ChatMessage(
      sender: 'Flutter ',
      profileImage: 'https://picsum.photos/id/880/200/100?grayscale',
      location: '중동',
      sendDate: '2일전',
      message: '안녕하세요 지금 다 예약 상태 인가요?',
      imageUri: 'https://picsum.photos/id/890/200/100?grayscale')
];
product
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,
  ),
];
chat_container
import 'package:carrot_market_ui/components/ImageContainer.dart';
import 'package:flutter/material.dart';
import '../../../../models/chatMessage.dart';

class ChatContainer extends StatelessWidget {
  final ChatMessage chatMessage;

  const ChatContainer({required this.chatMessage, super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
          border: Border(bottom: BorderSide(color: Colors.grey, width: 0.5))),
      height: 100,
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Row(
          children: [
            ImageContainer(
              borderRadius: 25,
              imageUrl: chatMessage.profileImage,
              width: 50,
              height: 50,
            ),
            const SizedBox(width: 16.0),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Spacer(),
                  Text.rich(
                    TextSpan(
                      children: [
                        TextSpan(
                          text: chatMessage.sender,
                          style: TextStyle(fontSize: 14),
                        ),
                        TextSpan(
                          text: chatMessage.location,
                          style: TextStyle(fontSize: 12, color: Colors.grey),
                        ),
                        TextSpan(
                          text: ' • ${chatMessage.sendDate}',
                          style: TextStyle(fontSize: 12, color: Colors.grey),
                        ),
                      ],
                    ),
                  ),
                  Text(
                    chatMessage.message,
                    overflow: TextOverflow.ellipsis,
                  ),
                  const Spacer()
                ],
              ),
            ),

            /// 이미지 추가
            Visibility(
              visible: chatMessage.imageUri != null,
              child: ImageContainer(
                width: 50,
                height: 50,
                borderRadius: 8,
                imageUrl: chatMessage.imageUri ?? '',
              ),
            )
          ],
        ),
      ),
    );
  }
}
chatting_screen
import 'package:carrot_market_ui/screens/chatting/components/chat_container.dart';
import 'package:flutter/material.dart';

import '../../models/chatMessage.dart';

class ChattingScreen extends StatelessWidget {
  const ChattingScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('채팅'),
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(0.5),
          child: Divider(thickness: 0.5, height: 0.5, color: Colors.grey),
        ),
      ),
      body: ListView(
        children: List.generate(
          chatMessageList.length,
          (index) => ChatContainer(chatMessage: chatMessageList[index]),
        ),
      ),
    );
  }
}
product_detail
import 'package:carrot_market_ui/models/product.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

class ProductDetail extends StatelessWidget {
  final Product product;

  const ProductDetail({required this.product, super.key});

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            product.title,
            style: TextStyle(fontSize: 16),
          ),
          const SizedBox(height: 4.0),
          Text(
            '${product.address} ‧ ${product.publishedAt} ',
            style: TextStyle(fontSize: 13),
          ),
          const SizedBox(height: 4.0),
          Text(
            '${numberFormat(product.price)} 원',
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          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,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

Widget _buildIcons(int count, IconData iconData) {
  return Row(
    children: [
      Icon(iconData, size: 14.0),
      const SizedBox(
        width: 4.0,
      ),
      Text('${count}')
    ],
  );
}

String numberFormat(String price) {
  final formatter = NumberFormat('#,###');
  return formatter.format(int.parse(price));
}
product_item
import 'package:carrot_market_ui/models/product.dart';
import 'package:flutter/material.dart';

class ProductItem extends StatelessWidget {
  final Product product;
  const ProductItem({required this.product, super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 135.0,
      child: Row(
        children: [
          ClipRRect(
            borderRadius: BorderRadius.circular(10.0),
            child: Image.network(
              product.urlToImage,
              width: 115,
              height: 115,
              fit: BoxFit.cover,
            ),
          ),
        ],
      ),
    );
  }
}
home
import 'package:carrot_market_ui/screens/home/components/product_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import '../../models/product.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Row(
          children: [
            const Text('좌동'),
            const SizedBox(width: 4.0),
            const Icon(CupertinoIcons.chevron_down),
          ],
        ),
        actions: [
          IconButton(
            onPressed: () {},
            icon: Icon(CupertinoIcons.search),
          ),
          IconButton(
            onPressed: () {},
            icon: Icon(CupertinoIcons.list_dash),
          ),
          IconButton(
            onPressed: () {},
            icon: Icon(CupertinoIcons.bell),
          )
        ],
        // 구분선
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(0.5),
          child: Divider(thickness: 0.5, height: 0.5, color: Colors.grey,),
        ),
      ),
      body: ProductItem(product: productList[0],),
    );
  }
}
main_screens
import 'package:carrot_market_ui/screens/chatting/chatting_screen.dart';
import 'package:carrot_market_ui/screens/home/home.dart';
import 'package:flutter/material.dart';

class MainScreens extends StatefulWidget {
  const MainScreens({super.key});

  @override
  State<MainScreens> createState() => _MainScreensState();
}

class _MainScreensState extends State<MainScreens> {
  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        // 최대 5개까지 관리 함
        body: IndexedStack(
          index: _selectedIndex,
          children: [
            HomeScreen(),
            // 1번 인덱스
            ChattingScreen(),
          ],
        ),
        bottomNavigationBar: BottomNavigationBar(
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: '홈'
            ),
            BottomNavigationBarItem(
                icon: Icon(Icons.chat_bubble),
                label: '채팅'
            ),
          ],
          onTap: (index){
            setState(() {
              _selectedIndex = index;
            });
          },
          currentIndex: _selectedIndex,
        ),
      ),
    );
  }
}
main
import 'package:carrot_market_ui/screens/main_screens.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(CarrotMarketApp());
}

class CarrotMarketApp extends StatelessWidget {
   CarrotMarketApp({super.key});

  @override
  Widget build(BuildContext conte000xt) {
    return MaterialApp(
      title: 'carrotMarket',
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange)
      ),
      home: MainScreens(),
    );
  }
}