From 25724a96050e53df45424aaec607ae969992d89f Mon Sep 17 00:00:00 2001 From: cho4u4o Date: Thu, 17 Oct 2024 17:59:54 +0900 Subject: [PATCH] feat : add product detail image --- lib/screens/products/product_detail_page.dart | 358 ++++++++---------- pubspec.lock | 36 +- 2 files changed, 171 insertions(+), 223 deletions(-) diff --git a/lib/screens/products/product_detail_page.dart b/lib/screens/products/product_detail_page.dart index 95b1463..5fc1379 100644 --- a/lib/screens/products/product_detail_page.dart +++ b/lib/screens/products/product_detail_page.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/widgets.dart'; import 'package:saphy/screens/purchase/purchase_page.dart'; +import 'package:saphy/service/api_service.dart'; import 'package:saphy/service/authentication/secure_storage.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'; -import 'package:dio/dio.dart'; class ProductDetail extends StatefulWidget { final Product product; @@ -18,7 +19,7 @@ class ProductDetail extends StatefulWidget { class _ProductDetailState extends State { Product? productDetail; - bool isWished = false; // 아이템이 찜되었는지 상태를 관리 + bool isWished = false; @override void initState() { @@ -34,10 +35,17 @@ class _ProductDetailState extends State { } Future getProduct() async { - final dio = Dio(); + String token = await readJwt(); + token = token.toString().split(" ")[2]; + try { - final response = - await dio.get('https://saphy.site/api/items/${widget.product.id}'); + final response = await APIService.instance.request( + 'https://saphy.site/api/items/${widget.product.id}', + DioMethod.get, + contentType: 'application/json', + token: "Bearer $token", + ); + if (response.statusCode == 200) { final data = response.data as Map; List results = data['results']; @@ -51,7 +59,6 @@ class _ProductDetailState extends State { throw Exception('Failed to load products'); } } catch (e) { - print('Error: ${e.toString()}'); // 오류 메시지 확인 return Product( id: 0, deviceType: "deviceType", @@ -70,41 +77,35 @@ class _ProductDetailState extends State { } Future toggleWish() async { - final dio = Dio(); try { String token = await readJwt(); token = token.toString().split(" ")[2]; if (!isWished) { // 아이템을 찜하는 POST 요청 - final response = await dio.post( + + final response = await APIService.instance.request( 'https://saphy.site/item-wishes?itemId=${widget.product.id}', - options: Options( - headers: { - 'Authorization': token, - }, - ), + DioMethod.post, + contentType: 'application/json', + token: "Bearer $token", ); - if (response.statusCode == 201) { + if (response.statusCode == 200) { // 요청이 성공적으로 처리된 경우 setState(() { isWished = true; // 아이템이 찜 상태로 변경 }); } } else { - // 아이템 찜 해제하는 POST 요청 - final response = await dio.delete( - 'https://saphy.site/item-wishes/${widget.product.id}', // 삭제 요청을 보낼 URL - options: Options( - headers: { - 'Authorization': 'Bearer $token', - }, - ), + final response = await APIService.instance.request( + 'https://saphy.site/item-wishes/${widget.product.id}', + DioMethod.delete, + contentType: 'application/json', + token: "Bearer $token", ); if (response.statusCode == 200) { - // 요청이 성공적으로 처리된 경우 setState(() { isWished = false; // 아이템이 찜 해제 상태로 변경 }); @@ -145,22 +146,7 @@ class _ProductDetailState extends State { SingleChildScrollView( child: Column( children: [ - SizedBox( - height: 400, - child: Center( - child: Container( - height: 400, - width: double.infinity, - decoration: BoxDecoration( - image: DecorationImage( - image: CachedNetworkImageProvider( - widget.product.images[0].url), - fit: BoxFit.cover, - ), - ), - ), - ), - ), + productImage(widget.product.images[0].url), Padding( padding: const EdgeInsets.all(20.0), child: Column( @@ -246,180 +232,71 @@ class _ProductDetailState extends State { ], ), const SizedBox(height: 10), - Container( - // 등급 안내 상자 - width: double.infinity, - height: 100, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: const Color.fromRGBO(222, 222, 222, 1), - gradient: const LinearGradient( - begin: Alignment(-0.92, -0.39), - end: Alignment(0.92, 0.39), - colors: [ - Color(0xffa1a1a1), - Color(0xffdedede), - Color(0xffdedede), - Color(0xffdedede), - Color(0xffdedede), - white, - white, - Color(0xffffffff), - Color(0xffdedede), - Color(0xffdedede), - Color(0xffdedede), - Color(0xffa1a1a1), - ], - stops: [ - 0, - 0.16, - 0.21, - 0.24, - 0.27, - 0.36, - 0.45, - 0.6, - 0.72, - 0.8, - 0.84, - 1 - ], - ), - ), - child: Padding( - padding: const EdgeInsets.all(15.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '해당 상품은 등급 ${productDetail?.grade} 제품입니다.', - style: bodyText(), - ), - Text( - "Grade ${productDetail?.grade}", - style: titleText30(), - ) - ], - ), - Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Flexible( - flex: 1, - child: InkWell( - child: Container( - width: screenWidth * 0.32, - decoration: BoxDecoration( - color: const Color(0xff404756), - borderRadius: - BorderRadius.circular(10)), - child: const Center( - child: Text( - "실제 사진 보기", - style: TextStyle( - fontFamily: "Pretendard", - color: white), - ), - ), - ), - ), - ), - const SizedBox( - height: 5, - ), - Flexible( - flex: 1, - child: InkWell( - child: Container( - width: screenWidth * 0.32, - decoration: BoxDecoration( - color: const Color(0xff404756), - borderRadius: - BorderRadius.circular(10)), - child: Center( - child: Text( - "${productDetail?.grade}등급 리뷰 보기", - style: const TextStyle( - fontFamily: "Pretendard", - color: white), - ), - ), - ), - ), - ), - ], - ), - ], - ), - ), - ), + gradeInformation(screenWidth), const SizedBox(height: 20), - spaceDivider("다른 색상 메뉴"), - const SizedBox(height: 20), - spaceDivider("쿠폰 발급 메뉴"), - const SizedBox(height: 20), - Row( - // 버튼 - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Flexible(flex: 1, child: button("구매하기")), - const SizedBox(width: 10), - Flexible(flex: 1, child: button("판매하기")), - ], - ), - const SizedBox(height: 20), - spaceDivider("상품 정보 사진 칸"), - const SizedBox(height: 20), - spaceDivider("댓글 칸"), - const SizedBox(height: 160), + spaceDivider(""), + const SizedBox(height: 80), ], ), ), ], ), ), - Positioned( - bottom: 0, - child: Container( - width: screenWidth, - height: 100, - color: altWhite, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - IconButton( - onPressed: toggleWish, - icon: Icon( - isWished - ? Icons.favorite - : Icons.favorite_outline, // 찜 상태에 따라 아이콘 변경 - color: isWished - ? Colors.red - : Colors.black, // 찜 상태에 따라 색상 변경 - ), - ), - const SizedBox(width: 10), - Flexible(flex: 1, child: button("구매하기")), - const SizedBox(width: 10), - Flexible(flex: 1, child: button("판매하기")), - ], + buttonBar(screenWidth) + ], + ), + ); + } + + SizedBox productImage(String url) { + return SizedBox( + height: 400, + child: Center( + child: Container( + height: 400, + width: double.infinity, + decoration: BoxDecoration( + image: DecorationImage( + image: CachedNetworkImageProvider(widget.product.images[0].url), + fit: BoxFit.cover, + ), + ), + ), + ), + ); + } + + Positioned buttonBar(double screenWidth) { + return Positioned( + bottom: 0, + child: Container( + width: screenWidth, + height: 100, + color: altWhite, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + onPressed: toggleWish, + icon: Icon( + isWished + ? Icons.favorite + : Icons.favorite_outline, // 찜 상태에 따라 아이콘 변경 + color: isWished + ? const Color(0xff9abcff) + : Colors.black, // 찜 상태에 따라 색상 변경 ), ), - ), - ) - ], + const SizedBox(width: 10), + Flexible(flex: 1, child: button("구매하기")), + const SizedBox(width: 10), + Flexible(flex: 1, child: button("판매하기")), + ], + ), + ), ), ); } @@ -456,11 +333,82 @@ class _ProductDetailState extends State { ); } - Container spaceDivider(String label) { + Container gradeInformation(double screenWidth) { return Container( + // 등급 안내 상자 width: double.infinity, height: 100, decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: const Color.fromRGBO(222, 222, 222, 1), + ), + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '해당 상품은 등급 ${productDetail?.grade} 제품입니다.', + style: bodyText(), + ), + Text( + "Grade ${productDetail?.grade}", + style: titleText30(), + ) + ], + ), + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + gradeButton("실제 사진 보기", screenWidth), + const SizedBox( + height: 5, + ), + gradeButton("${productDetail?.grade}등급 리뷰 보기", screenWidth), + ], + ), + ], + ), + ), + ); + } + + Flexible gradeButton(String label, double screenWidth) { + return Flexible( + flex: 1, + child: InkWell( + child: Container( + width: screenWidth * 0.32, + decoration: BoxDecoration( + color: const Color(0xff404756), + borderRadius: BorderRadius.circular(10)), + child: Center( + child: Text( + label, + style: const TextStyle(fontFamily: "Pretendard", color: white), + ), + ), + ), + ), + ); + } + + Container spaceDivider(String label) { + return Container( + width: double.infinity, + height: 2000, + decoration: BoxDecoration( + image: DecorationImage( + image: CachedNetworkImageProvider( + productDetail?.descriptionImage.url ?? ""), + fit: BoxFit.cover), color: white, borderRadius: BorderRadius.circular(20), border: Border.all(color: gray400, width: 0.5), diff --git a/pubspec.lock b/pubspec.lock index f215cea..a0f31bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -426,10 +426,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398" + sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda" url: "https://pub.dev" source: hosted - version: "2.0.23" + version: "2.0.22" flutter_secure_storage: dependency: "direct main" description: @@ -628,10 +628,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: d34e0d9e024e81321b2aeed7b202ec6181cc282e6a1c0c0b4e6ad07ef1065d82 + sha256: "8c5abf0dcc24fe6e8e0b4a5c0b51a5cf30cefdf6407a3213dae61edc75a70f56" url: "https://pub.dev" source: hosted - version: "0.8.12+16" + version: "0.8.12+12" image_picker_for_web: dependency: transitive description: @@ -756,18 +756,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: @@ -812,18 +812,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.12.0" mime: dependency: "direct main" description: @@ -1169,10 +1169,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.0" toggle_list: dependency: "direct main" description: @@ -1305,10 +1305,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.2.1" watcher: dependency: transitive description: @@ -1390,5 +1390,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0"