From 7fe11b6e5e6bcef89f2c588b91874bba17a86502 Mon Sep 17 00:00:00 2001 From: changhwan77 Date: Fri, 23 Feb 2024 02:12:15 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=83=81=ED=92=88=20API=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/main_navigation_screen.dart | 13 +-- lib/common/service/goods_provider.dart | 17 ++++ lib/common/service/token_provider.dart | 7 +- .../auth/views/join_private_screen.dart | 6 ++ lib/features/auth/views/login_screen.dart | 5 +- .../profile/views/profile_screen.dart | 3 +- lib/features/tutis/models/goods.dart | 17 +++- .../tutis/views/personal_branding_screen.dart | 43 +++++----- .../widgets/tuti_widgets/goods_container.dart | 81 ++++++++++++------- .../tuti_widgets/tuti_card_mobile.dart | 25 ++++-- .../tuti_widgets/tuti_header_mobile.dart | 11 +-- 11 files changed, 141 insertions(+), 87 deletions(-) create mode 100644 lib/common/service/goods_provider.dart diff --git a/lib/common/main_navigation_screen.dart b/lib/common/main_navigation_screen.dart index 28ec212..898e2b4 100644 --- a/lib/common/main_navigation_screen.dart +++ b/lib/common/main_navigation_screen.dart @@ -31,13 +31,6 @@ class _MainNavigationScreenState extends ConsumerState Widget build(BuildContext context) { int selectedIndex = ref.watch(navigationSelectedIndexProvider); - void navigationToScreen(int index) { - setState(() { - // 유저가 클릭한 index를 navigationSelectedIndexProvider의 state에 할당 - ref.read(navigationSelectedIndexProvider.notifier).state = index; - }); - } - void onTap(int index) async { // 유저가 마이페이지로 이동 시 authToken이 있는지 검증 // 토큰이 null || 비어있으면 로그인 안내 다이얼로그 띄움. @@ -51,11 +44,11 @@ class _MainNavigationScreenState extends ConsumerState ); } } else { - navigationToScreen(index); + ref.read(navigationSelectedIndexProvider.notifier).state = index; } - // 마이페이지로 이동하는 것이 아닐 때는 바로 이동 } else { - navigationToScreen(index); + // 마이페이지로 이동하는 것이 아닐 때는 바로 이동 + ref.read(navigationSelectedIndexProvider.notifier).state = index; } } diff --git a/lib/common/service/goods_provider.dart b/lib/common/service/goods_provider.dart new file mode 100644 index 0000000..61657e0 --- /dev/null +++ b/lib/common/service/goods_provider.dart @@ -0,0 +1,17 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tuti/constants/string.dart'; +import 'package:tuti/features/tutis/models/goods.dart'; + +final goodsProvider = FutureProvider>((ref) async { + final dio = Dio(); + final response = await dio.get('${StringConstants.baseUrl}/products'); + + if (response.statusCode == 200) { + final List goods = response.data['data']; + final goodsList = goods.map((e) => Goods.fromJson(e)).toList(); + return goodsList; + } else { + throw Exception('Failed to load Goods'); + } +}); diff --git a/lib/common/service/token_provider.dart b/lib/common/service/token_provider.dart index 35ea0ef..d7044b0 100644 --- a/lib/common/service/token_provider.dart +++ b/lib/common/service/token_provider.dart @@ -1,8 +1,3 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -enum TokenState { - present, - absent, -} - -final tokenProvider = StateProvider((ref) => TokenState.absent); +final tokenProvider = StateProvider((ref) => ''); diff --git a/lib/features/auth/views/join_private_screen.dart b/lib/features/auth/views/join_private_screen.dart index f1f2901..fa831d2 100644 --- a/lib/features/auth/views/join_private_screen.dart +++ b/lib/features/auth/views/join_private_screen.dart @@ -3,6 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import 'package:tuti/common/constraints_scaffold.dart'; +import 'package:tuti/common/custom_token_manager.dart'; +import 'package:tuti/common/service/token_provider.dart'; import 'package:tuti/features/auth/models/user_profile_model.dart'; import 'package:tuti/features/auth/widgets/auth_form_field.dart'; @@ -176,6 +178,10 @@ class _JoinPrivateScreenState extends ConsumerState { ); final authService = ref.read(authServiceProvider); await authService.signUp(context, userProfileModel); + final token = await CustomTokenManager.getToken(); + print('로그인 시 저장되는 토큰: $token'); + + ref.read(tokenProvider.notifier).state = token; if (context.mounted) { context.goNamed(TuTiScreen.routeName); } diff --git a/lib/features/auth/views/login_screen.dart b/lib/features/auth/views/login_screen.dart index 58e1e3c..e105e30 100644 --- a/lib/features/auth/views/login_screen.dart +++ b/lib/features/auth/views/login_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:tuti/common/constraints_scaffold.dart'; +import 'package:tuti/common/custom_token_manager.dart'; import 'package:tuti/common/service/token_provider.dart'; import 'package:tuti/features/auth/services/auth_service.dart'; import 'package:tuti/features/auth/widgets/auth_form_field.dart'; @@ -33,7 +34,9 @@ class _LoginScreenState extends ConsumerState { final authService = ref.read(authServiceProvider); await authService.login( context, formData['email']!, formData['password']!); - ref.read(tokenProvider.notifier).state = TokenState.present; + final token = await CustomTokenManager.getToken(); + + ref.read(tokenProvider.notifier).state = token; } } } diff --git a/lib/features/profile/views/profile_screen.dart b/lib/features/profile/views/profile_screen.dart index 6ac767e..60cb0b1 100644 --- a/lib/features/profile/views/profile_screen.dart +++ b/lib/features/profile/views/profile_screen.dart @@ -119,8 +119,7 @@ class _ProfileScreenState extends ConsumerState { TextButton( onPressed: () async { await CustomTokenManager.removeToken(); - ref.read(tokenProvider.notifier).state = - TokenState.absent; + ref.read(tokenProvider.notifier).state = ''; ref .read(navigationSelectedIndexProvider.notifier) .state = 1; diff --git a/lib/features/tutis/models/goods.dart b/lib/features/tutis/models/goods.dart index 8d1c3b3..d5c69e3 100644 --- a/lib/features/tutis/models/goods.dart +++ b/lib/features/tutis/models/goods.dart @@ -1,12 +1,23 @@ class Goods { - final String title; + final String name; final double? regularPrice; final double? discountRate; final double discountedPrice; + final String? discountPolicy; const Goods( - {required this.title, + {required this.name, required this.discountedPrice, this.discountRate, - this.regularPrice}); + this.regularPrice, + this.discountPolicy}); + + factory Goods.fromJson(Map json) { + return Goods( + name: json['name'], + regularPrice: json['price'], + discountRate: json['discountValue'], + discountedPrice: json['discountedPrice'], + discountPolicy: json['discountPolicy']); + } } diff --git a/lib/features/tutis/views/personal_branding_screen.dart b/lib/features/tutis/views/personal_branding_screen.dart index 8aa7a4b..db7884d 100644 --- a/lib/features/tutis/views/personal_branding_screen.dart +++ b/lib/features/tutis/views/personal_branding_screen.dart @@ -1,8 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:tuti/common/constraints_scaffold.dart'; +import 'package:tuti/common/service/goods_provider.dart'; import 'package:tuti/common/tuti_text.dart'; import 'package:tuti/constants/color.dart'; import 'package:tuti/features/tutis/models/goods.dart'; @@ -11,14 +13,16 @@ import 'package:tuti/features/tutis/widgets/tuti_widgets/goods_container.dart'; import 'package:tuti/features/tutis/widgets/tuti_widgets/main_banner.dart'; import 'package:universal_html/js.dart'; -class PersonalBrandingScreen extends StatelessWidget { +class PersonalBrandingScreen extends ConsumerWidget { const PersonalBrandingScreen({super.key}); static const String routeName = "personalBranding"; static const String routePath = "/personalBranding"; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final AsyncValue> goodsList = ref.watch(goodsProvider); + return ConstraintsScaffold( child: Column( children: [ @@ -31,30 +35,23 @@ class PersonalBrandingScreen extends StatelessWidget { ), ), Expanded( - child: ListView.builder( - itemCount: goodsCards.length, - itemBuilder: (BuildContext context, index) { - return GoodsContainer( - title: goodsCards[index].title, - regularPrice: goodsCards[index].regularPrice, - discountRate: goodsCards[index].discountRate, - discountedPrice: goodsCards[index].discountedPrice); - }, - ), + child: switch (goodsList) { + AsyncData(:final value) => ListView.builder( + itemCount: value.length, + itemBuilder: (context, index) => GoodsContainer( + name: value[index].name, + discountRate: value[index].discountRate, + regularPrice: value[index].regularPrice, + discountedPrice: value[index].discountedPrice, + discountPolicy: value[index].discountPolicy, + ), + ), + AsyncError() => const Text('Oops, something unexpected happened'), + _ => const CircularProgressIndicator(), + }, ), ], ), ); } } - -List goodsCards = const [ - Goods( - title: '강점 발견 연구소 1기', - regularPrice: 600000, - discountRate: 50, - discountedPrice: 300000), - Goods(title: '발표잘하는 방법 특강 1회', discountedPrice: 250000), - Goods(title: '[건축학과] 공모전 컨설팅', discountedPrice: 250000), - Goods(title: '내 성격에 맞는\n외모 스타일링 컨설팅', discountedPrice: 250000), -]; diff --git a/lib/features/tutis/widgets/tuti_widgets/goods_container.dart b/lib/features/tutis/widgets/tuti_widgets/goods_container.dart index 953b698..d0070bd 100644 --- a/lib/features/tutis/widgets/tuti_widgets/goods_container.dart +++ b/lib/features/tutis/widgets/tuti_widgets/goods_container.dart @@ -8,15 +8,17 @@ import 'package:tuti/constants/color.dart'; class GoodsContainer extends StatelessWidget { const GoodsContainer( {super.key, - required this.title, + required this.name, required this.discountedPrice, this.discountRate, - this.regularPrice}); + this.regularPrice, + this.discountPolicy}); - final String title; + final String name; final double? regularPrice; final double? discountRate; final double discountedPrice; + final String? discountPolicy; @override Widget build(BuildContext context) { @@ -39,37 +41,62 @@ class GoodsContainer extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - title, + name, style: Theme.of(context) .textTheme .bodyLarge! .copyWith(color: Colors.white), ), - if (regularPrice != null) - Text( - numberFormat.format(regularPrice), - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - color: Colors.white, - fontSize: 22.sp, - decoration: TextDecoration.lineThrough, - decorationColor: - ColorConstants.personalBrandingDividerColor, - decorationThickness: 2.0), + if (discountPolicy == '정액') + Column( + children: [ + Text( + numberFormat.format(regularPrice), + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: Colors.white, + fontSize: 22.sp, + decoration: TextDecoration.lineThrough, + decorationColor: + ColorConstants.personalBrandingDividerColor, + decorationThickness: 2.0), + ), + Text( + '${numberFormat.format(discountRate)}원 할인', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + fontSize: 18.sp, + color: + ColorConstants.personalBrandingTextHighlightColor), + ), + ], ), - if (regularPrice == null) - RSizedBox( - height: 35.w, + if (discountPolicy == '정률') + Column( + children: [ + Text( + numberFormat.format(regularPrice), + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: Colors.white, + fontSize: 22.sp, + decoration: TextDecoration.lineThrough, + decorationColor: + ColorConstants.personalBrandingDividerColor, + decorationThickness: 2.0), + ), + Text( + '${numberFormat.format(discountRate)}% 할인', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + fontSize: 18.sp, + color: + ColorConstants.personalBrandingTextHighlightColor), + ), + ], ), - if (discountRate != null) - Text( - '${numberFormat.format(discountRate)}%할인', - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - fontSize: 18.sp, - color: ColorConstants.personalBrandingTextHighlightColor), - ), - if (discountRate == null) - RSizedBox( - height: 35.w, + if (discountPolicy == '없음') + Column( + children: [ + RSizedBox(height: 35.w), + RSizedBox(height: 35.w), + ], ), Text( numberFormat.format(discountedPrice), diff --git a/lib/features/tutis/widgets/tuti_widgets/tuti_card_mobile.dart b/lib/features/tutis/widgets/tuti_widgets/tuti_card_mobile.dart index e756835..bb219de 100644 --- a/lib/features/tutis/widgets/tuti_widgets/tuti_card_mobile.dart +++ b/lib/features/tutis/widgets/tuti_widgets/tuti_card_mobile.dart @@ -162,6 +162,10 @@ class _TuTiCardMobileState extends ConsumerState { String _maskName(String name) { if (name.length >= 3) { return name.replaceRange(1, 2, '*'); + } else if (name.length == 2) { + String firstName = name.substring(0, 1); + String maskName = firstName + "*"; + return maskName; } return name; } @@ -183,19 +187,24 @@ class _TuTiCardMobileState extends ConsumerState { _buildCircleAvatar(), Gaps.h8, FutureBuilder( - future: _getDisplayName(member), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const SizedBox(); - } else if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } else { + future: _getDisplayName(member), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const SizedBox(); + } + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData) { return TuTiText.small( context, snapshot.data ?? '', ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); } - }), + } + return const SizedBox(); + }, + ), Gaps.h12, TuTiText.small( context, diff --git a/lib/features/tutis/widgets/tuti_widgets/tuti_header_mobile.dart b/lib/features/tutis/widgets/tuti_widgets/tuti_header_mobile.dart index 8cd594e..dc2f4c4 100644 --- a/lib/features/tutis/widgets/tuti_widgets/tuti_header_mobile.dart +++ b/lib/features/tutis/widgets/tuti_widgets/tuti_header_mobile.dart @@ -31,6 +31,8 @@ class TuTiHeaderMobile extends ConsumerStatefulWidget { class _TuTiHeaderMobileState extends ConsumerState { @override Widget build(BuildContext context) { + // 토큰 유무 상태가 변경될 시 header 부분 rebuild + String? token = ref.watch(tokenProvider); return Padding( padding: EdgeInsets.symmetric(horizontal: 25.w, vertical: 20.h), child: Row( @@ -63,12 +65,10 @@ class _TuTiHeaderMobileState extends ConsumerState { //TODO: 에러 발생 시 어떻게 처리할 것인지 추가 필요 } if (snapshot.hasData) { - // 토큰 유무 상태가 변경될 시 header 부분 rebuild - TokenState tokenState = ref.watch(tokenProvider); - if (snapshot.data!.isEmpty || snapshot.data == null || - tokenState == TokenState.absent) { + token == null || + token.isEmpty) { return TuTiButton( padding: EdgeInsets.symmetric( vertical: 10.h, horizontal: 25.w), @@ -76,9 +76,6 @@ class _TuTiHeaderMobileState extends ConsumerState { title: '로그인', onPressed: () => _showLoginDialog(context), ); - } else { - // 토큰이 있을 시 tokenProvider의 값을 토큰 있음으로 변경 - ref.read(tokenProvider.notifier).state = TokenState.present; } } } else {