Skip to content

Commit

Permalink
Merge pull request #48 from 2024-Saphy/feat/SAPHY-33-product-favlist
Browse files Browse the repository at this point in the history
[FEAT] : 구매 기능 추가, 상품 모델 수정
  • Loading branch information
cho4u4o authored Oct 4, 2024
2 parents d71202c + d34a270 commit ac77dca
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 131 deletions.
51 changes: 26 additions & 25 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,31 @@ class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Saphy',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: altBlack),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
initialRoute: WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => const WelcomeScreen(),
SignupScreen.id: (context) => const SignupScreen(
socialType: '',
userEmail: '',
userName: '',
userPhotoUrl: '',
),
OtpScreen.id: (context) => OtpScreen(
verificationId: '',
phoneNumber: '',
onVerificationSuccess: () {},
),
SplashSellingScreen.id: (context) => const SplashSellingScreen(),
ScreenController.id: (context) => const ScreenController(),
},
);
return const ScreenController();
// return MaterialApp(
// title: 'Saphy',
// theme: ThemeData(
// colorScheme: ColorScheme.fromSeed(seedColor: altBlack),
// useMaterial3: true,
// ),
// debugShowCheckedModeBanner: false,
// initialRoute: WelcomeScreen.id,
// routes: {
// WelcomeScreen.id: (context) => const WelcomeScreen(),
// SignupScreen.id: (context) => const SignupScreen(
// socialType: '',
// userEmail: '',
// userName: '',
// userPhotoUrl: '',
// ),
// OtpScreen.id: (context) => OtpScreen(
// verificationId: '',
// phoneNumber: '',
// onVerificationSuccess: () {},
// ),
// SplashSellingScreen.id: (context) => const SplashSellingScreen(),
// ScreenController.id: (context) => const ScreenController(),
// },
// );
}
}
38 changes: 27 additions & 11 deletions lib/models/product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ class Product {
String deviceType;
String name;
String description;
double price;
int price;
int stock;
Map<String, String> images; // 이미지 정보를 Map<String, String>으로 저장
List<ImageModel> images;
String brand;
String color;
String storage;
Expand All @@ -28,16 +28,18 @@ class Product {
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
deviceType: json['deviceType'],
name: json['name'],
description: json['description'],
deviceType: json['deviceType'] ?? "",
name: json['name'] ?? "",
description: json['description'] ?? "",
price: json['price'],
stock: json['stock'],
images: Map<String, String>.from(json['images']), // 직접 변환
brand: json['brand'],
color: json['color'],
storage: json['storage'],
grade: json['grade'],
stock: json['stock'] ?? "",
images: (json['images'] as List)
.map((item) => ImageModel.fromJson(item))
.toList(),
brand: json['brand'] ?? "",
color: json['color'] ?? "",
storage: json['storage'] ?? "",
grade: json['grade'] ?? "",
);
}

Expand All @@ -57,3 +59,17 @@ class Product {
};
}
}

class ImageModel {
final String name;
final String url;

ImageModel({required this.name, required this.url});

factory ImageModel.fromJson(Map<String, dynamic> json) {
return ImageModel(
name: json['name'] as String,
url: json['url'] as String,
);
}
}
98 changes: 56 additions & 42 deletions lib/screens/main/main_screen.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:dio/dio.dart';
import 'package:saphy/models/product.dart';
import 'package:saphy/utils/colors.dart';
Expand All @@ -17,27 +16,38 @@ class MainScreen extends StatefulWidget {
}

class _MainScreenState extends State<MainScreen> {
final NumberFormat numberFormat = NumberFormat('###,###,###,###');
final int productLength = 6;
// Future<List<Product>> getProduct = getProducts();
late Future<List<Product>> _products;

Product product = Product(
id: 1,
name: "iPhone 14",
brand: "Apple",
images: {
"name": "iPhone 14",
'url':
'https://i.pinimg.com/564x/f3/54/dc/f354dc1f040fc1fc4dfc4c436ad52159.jpg',
},
price: 130000.0,
description: "Latest model from Apple with advanced features.",
color: "Green",
storage: "128GB",
grade: "A",
deviceType: "phone",
stock: 10,
);
@override
void initState() {
super.initState();
_products = getProducts();
}

Future<List<Product>> getProducts() async {
final dio = Dio();
try {
final response =
await dio.get('https://saphy.site/api/items/all?type=ALL');
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['results'] != null) {
List<Product> products = (data['results'] as List)
.map((item) => Product.fromJson(item))
.toList();
return products;
} else {
throw Exception('No results found in the response');
}
} else {
throw Exception('Failed to load products');
}
} catch (e) {
print('Error: ${e.toString()}'); // 오류 메시지 확인
return [];
}
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -68,9 +78,32 @@ class _MainScreenState extends State<MainScreen> {
spacing: 15,
runSpacing: 15,
children: [
ProductCard(
product: product,
)
FutureBuilder(
future: _products,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error.toString()}');
} else if (!snapshot.hasData ||
snapshot.data!.isEmpty) {
return const Center(
child: Text('No products found')); // 데이터 없음 메시지
} else {
final products = snapshot.data!; // 데이터 가져오기
return Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
spacing: 15,
runSpacing: 15,
children: products
.map((product) => ProductCard(product: product))
.toList(),
);
}
})
],
),
),
Expand All @@ -79,23 +112,4 @@ class _MainScreenState extends State<MainScreen> {
),
);
}

Future<List<Product>> getProducts() async {
final dio = Dio();
try {
final response = await dio.get('https://saphy.site/item-wishes');
if (response.statusCode == 200) {
List<Product> products = (response.data as List)
.map((item) => Product.fromJson(item))
.toList();
return products;
} else {
throw Exception('Failed to load products');
}
} catch (e) {
// ignore: avoid_print
print('Error: $e');
return []; // Return an empty list in case of error
}
}
}
15 changes: 7 additions & 8 deletions lib/screens/products/product_detail_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:intl/intl.dart';
import 'package:saphy/screens/purchase/purchase_page.dart';
import 'package:saphy/utils/colors.dart';
import 'package:saphy/utils/number_format.dart';
import 'package:saphy/utils/textstyles.dart';
import 'package:saphy/models/product.dart';

Expand All @@ -13,7 +14,6 @@ class ProductDetail extends StatelessWidget {

@override
Widget build(BuildContext context) {
final NumberFormat numberFormat = NumberFormat('###,###,###,###');
var screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
Expand Down Expand Up @@ -47,13 +47,12 @@ class ProductDetail extends StatelessWidget {
child: Container(
height: 400,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: CachedNetworkImageProvider(
product.images["url"] ?? ""),
fit: BoxFit.cover,
),
),
decoration: const BoxDecoration(
// image: DecorationImage(
// image: CachedNetworkImageProvider(product.images[0]),
// fit: BoxFit.cover,
// ),
),
),
),
),
Expand Down
93 changes: 59 additions & 34 deletions lib/screens/purchase/payment_page.dart
Original file line number Diff line number Diff line change
@@ -1,56 +1,81 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
/* 아임포트 결제 모듈을 불러옵니다. */
import 'package:iamport_flutter/iamport_payment.dart';
/* 아임포트 결제 데이터 모델을 불러옵니다. */
import 'package:iamport_flutter/model/payment_data.dart';
import 'package:saphy/models/product.dart';

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

@override
Widget build(BuildContext context) {
return IamportPayment(
appBar: AppBar(
title: const Text('아임포트 결제'),
),
/* 웹뷰 로딩 컴포넌트 */
initialChild: Container(
child: Center(
color: Colors.white,
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/iamport-logo.png'),
const Padding(padding: EdgeInsets.symmetric(vertical: 15)),
const Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20)),
Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20)),
],
),
),
),
/* [필수입력] 가맹점 식별코드 */
userCode: 'iamport',
/* [필수입력] 결제 데이터 */
userCode: 'imp16147707',
data: PaymentData(
pg: 'html5_inicis', // PG사
payMethod: 'card', // 결제수단
name: '아임포트 결제데이터 분석', // 주문명
merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}', // 주문번호
amount: 39000, // 결제금액
buyerName: '홍길동', // 구매자 이름
buyerTel: '01012345678', // 구매자 연락처
buyerEmail: '[email protected]', // 구매자 이메일
buyerAddr: '서울시 강남구 신사동 661-16', // 구매자 주소
buyerPostcode: '06018', // 구매자 우편번호
appScheme: 'example', // 앱 URL scheme
cardQuota: [2, 3] //결제창 UI 내 할부개월수 제한
),
/* [필수입력] 콜백 함수 */
callback: (Map<String, String> result) {
Navigator.pushReplacementNamed(
context,
'/result',
arguments: result,
);
pg: 'tosspayments',
payMethod: 'card',
name: product.name,
merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}',
amount: product.price,
buyerName: '홍길동',
buyerTel: '01012345678',
buyerEmail: '[email protected]',
buyerAddr: '서울시 강남구 신사동 661-16',
buyerPostcode: '06018',
appScheme: 'example',
cardQuota: [2, 3]),
callback: (Map<String, String> result) async {
if (result['success'] == 'true') {
String? impUid = result['imp_uid'];
String? merchantUid = result['merchant_uid'];
await verifyIamport(impUid, merchantUid, context);
} else {
String? errorMsg = result['error_msg'];
print('결제 실패: $errorMsg');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('결제 실패: $errorMsg')),
);
}
},
);
}

Future<void> verifyIamport(
String? impUid, String? itemId, BuildContext context) async {
final dio = Dio();

if (impUid == null || itemId == null) return;

final data = {
'itemId': itemId,
'impUid': impUid,
};

try {
final response = await dio.post(
'https://saphy.site/payments',
data: data, // 수정된 부분
);

if (response.statusCode == 200) {
} else {}
} catch (e) {
print('Error: $e');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('서버와 연결할 수 없습니다.')),
);
}
}
}
Loading

0 comments on commit ac77dca

Please sign in to comment.