본문 바로가기

Flutter

당근 마켓 만들어 보기

https://github.com/insertvalue2/class_carrot_market

 

GitHub - insertvalue2/class_carrot_market

Contribute to insertvalue2/class_carrot_market development by creating an account on GitHub.

github.com

폴더 및 파일 만들기
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

1단계
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 context) {
    return MaterialApp(
      title: 'carrotMarket',
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.orangeAccent)
      ),
      home: MainScreens(),
    );
  }
}
import 'package:flutter/material.dart';


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

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

class _MainScreensState extends State<MainScreens> {
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

 

2단계
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 Scaffold(
      body: IndexedStack(
        index: _selectedIndex,
        children: [
          // 0번 인덱스
          Container(
            child: Text(
              'indexedStack0',
              style: TextStyle(fontSize: 20, color: Colors.black),
            ),
          ),
          // 1번 인덱스
          Container(
            color: Colors.redAccent[100],
            child: Text(
              'indexedStack1',
              style: TextStyle(fontSize: 20, color: Colors.black),
            ),
          ),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: '홈'
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.chat_bubble),
              label: '채팅'
          ),
        ],
        onTap: (index) {
          setState(() {
            _selectedIndex = index;
          });
        },
        currentIndex: _selectedIndex,
      ),
    );
  }
}

 

 

3단계
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Text('chattingScreen');
  }
}
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Text('HomeScreen');
  }
}

 

 

 

홈 화면 만들기

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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 ),
        ),
      ),

    );
  }
}

 

 

 

샘플 데이터 확인
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,
  ),
];

 

 

 

productItem
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,
            ),
          )
        ],
      ),
    );
  }
}

 

 

 

임시 코드
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],),
    );
  }
}

 

 

 

product_detail.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../../models/product.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));
  }
}

 

 

 

 

샘플 데이터
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')
 ];

 

 

ChattingScreen
import 'package:carrot_market_ui/models/chatMessage.dart';
import 'package:carrot_market_ui/screens/chatting/components/chat_container.dart';
import 'package:flutter/material.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]),
        ),
      ),
    );
  }
}

 

 

chat_container
import 'package:carrot_market_ui/components/image_container.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 ?? '',
              ),
            )
          ],
        ),
      ),
    );
  }
}

 

 

 

import 'package:flutter/material.dart';

class ImageContainer extends StatelessWidget {
  final double borderRadius;
  final String imageUrl;
  final double width;
  final double height;

  const ImageContainer(
      {required this.borderRadius,
      required this.imageUrl,
      required this.width,
      required this.height});

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(borderRadius),
      child: Image.network(
        imageUrl,
        width: width,
        height: height,
        fit: BoxFit.cover,
      ),
    );
  }
}
728x90