From bfc213736332a4eb98e6a87f1f4bc9b6ba9c7de5 Mon Sep 17 00:00:00 2001 From: wonyong Date: Wed, 11 Feb 2026 11:32:08 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Drawer=20=ED=91=B8=ED=84=B0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20=EC=82=AC=EC=97=85=EC=9E=90=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drawer에 로그아웃, 이용약관, 개인정보취급방침, 사업자 정보 링크를 포함한 푸터를 구현했습니다. - 기존 설정 화면에 있던 푸터를 Drawer로 이동했습니다. - 사업자 정보 페이지를 신규로 추가하고, Drawer 푸터에서 해당 페이지로 이동할 수 있도록 라우팅을 설정했습니다. - 로그아웃 버튼에 아이콘을 추가했습니다. --- assets/icons/icon/out.svg | 4 + lib/app/config/app_router.dart | 12 +++ .../business_info/business_info_page.dart | 64 ++++++++++++++ lib/presentation/drawer/main_app_drawer.dart | 2 + lib/presentation/drawer/view/drawer_view.dart | 24 +++-- .../drawer/widget/drawer_footer.dart | 88 +++++++++++++++++++ .../setting/view/setting_body_view.dart | 2 - .../setting/widget/setting_footer.dart | 83 ----------------- 8 files changed, 188 insertions(+), 91 deletions(-) create mode 100644 assets/icons/icon/out.svg create mode 100644 lib/presentation/business_info/business_info_page.dart create mode 100644 lib/presentation/drawer/widget/drawer_footer.dart delete mode 100644 lib/presentation/setting/widget/setting_footer.dart diff --git a/assets/icons/icon/out.svg b/assets/icons/icon/out.svg new file mode 100644 index 00000000..79fb1c1d --- /dev/null +++ b/assets/icons/icon/out.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/app/config/app_router.dart b/lib/app/config/app_router.dart index 3c69752a..e2b4a2a4 100644 --- a/lib/app/config/app_router.dart +++ b/lib/app/config/app_router.dart @@ -14,6 +14,7 @@ import 'package:grimity/presentation/album_organize/album_organize_page.dart'; import 'package:grimity/presentation/block/blocked_users_page.dart'; import 'package:grimity/presentation/board/tabs/board_page.dart'; import 'package:grimity/presentation/board/search/board_search_page.dart'; +import 'package:grimity/presentation/business_info/business_info_page.dart'; import 'package:grimity/presentation/chat_message/chat_message_page.dart'; import 'package:grimity/presentation/chat_new/new_chat_page.dart'; import 'package:grimity/presentation/common/enum/upload_image_type.dart'; @@ -571,3 +572,14 @@ class BlockedUsersRoute extends GoRouteData { @override Widget build(BuildContext context, GoRouterState state) => BlockedUsersPage(); } + +@TypedGoRoute(path: BusinessInfoRoute.path, name: BusinessInfoRoute.name) +class BusinessInfoRoute extends GoRouteData { + const BusinessInfoRoute(); + + static const String path = '/business-info'; + static const String name = 'business-info'; + + @override + Widget build(BuildContext context, GoRouterState state) => const BusinessInfoPage(); +} diff --git a/lib/presentation/business_info/business_info_page.dart b/lib/presentation/business_info/business_info_page.dart new file mode 100644 index 00000000..045c8577 --- /dev/null +++ b/lib/presentation/business_info/business_info_page.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:grimity/app/config/app_color.dart'; +import 'package:grimity/app/config/app_theme.dart'; +import 'package:grimity/app/config/app_typeface.dart'; + +class BusinessInfoPage extends StatelessWidget { + const BusinessInfoPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + toolbarHeight: AppTheme.kToolbarHeight.height, + titleSpacing: 0, + bottom: const PreferredSize( + preferredSize: Size.fromHeight(1), + child: Divider(height: 1, color: AppColor.gray300), + ), + ), + body: SafeArea( + child: ListView( + padding: const EdgeInsets.all(16.0), + children: [ + Text( + '사업자 정보', + style: AppTypeface.title2, + ), + Gap(24), + _buildInfoItem('사업자 등록 번호', '408-27-02500'), + Gap(16), + _buildInfoItem('법인 여부', '개인'), + Gap(16), + _buildInfoItem('상호', '그리미티 (Grimity)'), + Gap(16), + _buildInfoItem('대표자명', '임종훈'), + Gap(16), + _buildInfoItem('전화번호', '070-8098-7916'), + Gap(16), + _buildInfoItem('개업일', '2025-10-11'), + Gap(16), + _buildInfoItem('전자우편', 'grimity.official@gmail.com'), + Gap(16), + _buildInfoItem('사업장소재지(도로명)', '부산광역시 사상구 가야대로255번길 5, 107동 104호'), + Gap(16), + _buildInfoItem('인터넷 도메인', 'https://www.grimity.com/'), + Gap(16), + ], + ), + ), + ); + } + + Widget _buildInfoItem(String title, String content) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: AppTypeface.subTitle3), + Gap(8), + Text(content, style: AppTypeface.label3), + ], + ); + } +} diff --git a/lib/presentation/drawer/main_app_drawer.dart b/lib/presentation/drawer/main_app_drawer.dart index 7a961717..20af8e55 100644 --- a/lib/presentation/drawer/main_app_drawer.dart +++ b/lib/presentation/drawer/main_app_drawer.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:grimity/presentation/drawer/view/drawer_view.dart'; import 'package:grimity/presentation/drawer/widget/drawer_close_button.dart'; +import 'package:grimity/presentation/drawer/widget/drawer_footer.dart'; import 'package:grimity/presentation/drawer/widget/drawer_menu.dart'; import 'package:grimity/presentation/drawer/widget/drawer_profile.dart'; import 'package:grimity/presentation/drawer/widget/drawer_upload_button.dart'; @@ -15,6 +16,7 @@ class MainAppDrawer extends StatelessWidget { profileWidget: DrawerProfile(), uploadButton: DrawerUploadButton(), menuListView: DrawerMenuListView(), + drawerFooter: DrawerFooter(), ); } } diff --git a/lib/presentation/drawer/view/drawer_view.dart b/lib/presentation/drawer/view/drawer_view.dart index c3cd7c8c..e03b3903 100644 --- a/lib/presentation/drawer/view/drawer_view.dart +++ b/lib/presentation/drawer/view/drawer_view.dart @@ -9,12 +9,14 @@ class DrawerView extends StatelessWidget { required this.profileWidget, required this.uploadButton, required this.menuListView, + required this.drawerFooter, }); final Widget closeButton; final Widget profileWidget; final Widget uploadButton; final Widget menuListView; + final Widget drawerFooter; @override Widget build(BuildContext context) { @@ -22,15 +24,25 @@ class DrawerView extends StatelessWidget { backgroundColor: AppColor.gray00, width: 260, child: SafeArea( - child: ListView( + child: Column( children: [ + Expanded( + child: ListView( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Column(children: [closeButton, profileWidget, Gap(24), uploadButton, Gap(24)]), + ), + Divider(height: 1, color: AppColor.gray300), + Gap(24), + menuListView, + ], + ), + ), Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: Column(children: [closeButton, profileWidget, Gap(24), uploadButton, Gap(24)]), + padding: EdgeInsets.all(16), + child: drawerFooter, ), - Divider(height: 1, color: AppColor.gray300), - Gap(24), - menuListView, ], ), ), diff --git a/lib/presentation/drawer/widget/drawer_footer.dart b/lib/presentation/drawer/widget/drawer_footer.dart new file mode 100644 index 00000000..96d99321 --- /dev/null +++ b/lib/presentation/drawer/widget/drawer_footer.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gap/gap.dart'; +import 'package:grimity/app/config/app_color.dart'; +import 'package:grimity/app/config/app_const.dart'; +import 'package:grimity/app/config/app_router.dart'; +import 'package:grimity/app/config/app_typeface.dart'; +import 'package:grimity/app/enum/login_provider.enum.dart'; +import 'package:grimity/gen/assets.gen.dart'; +import 'package:grimity/presentation/common/provider/user_auth_provider.dart'; +import 'package:grimity/presentation/common/widget/grimity_gesture.dart'; +import 'package:grimity/presentation/common/widget/grimity_gray_circle.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class DrawerFooter extends StatelessWidget { + const DrawerFooter({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _LogoutButton(), + Gap(12), + Row( + children: [ + GrimityGesture( + onTap: () async { + await launchUrl(Uri.parse(AppConst.serviceTermsUrl)); + }, + child: Text('이용약관', style: AppTypeface.caption2.copyWith(color: AppColor.gray500)), + ), + GrimityGrayCircle(), + GrimityGesture( + onTap: () async { + await launchUrl(Uri.parse(AppConst.privacyPolicyUrl)); + }, + child: Text('개인정보취급방침', style: AppTypeface.caption2.copyWith(color: AppColor.gray500)), + ), + ], + ), + Gap(4), + GrimityGesture( + onTap: () async { + const BusinessInfoRoute().push(context); + }, + child: Text('사업자 정보', style: AppTypeface.caption2.copyWith(color: AppColor.gray500)), + ), + Gap(4), + Text( + '© Grimity. All rights reserved.', + style: AppTypeface.caption2.copyWith(color: AppColor.gray500), + ), + ], + ); + } +} + +class _LogoutButton extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return GestureDetector( + onTap: () async { + final user = ref.read(userAuthProvider); + if (user == null) return; + final provider = LoginProvider.fromString(user.provider ?? ''); + + await ref.read(userAuthProvider.notifier).logout(provider); + if (context.mounted) { + SignInRoute().go(context); + } + }, + child: Row( + children: [ + Text( + '로그아웃', + style: AppTypeface.caption2.copyWith(color: AppColor.gray500), + ), + Gap(4), + Assets.icons.icon.out.svg( + width: 16, + colorFilter: ColorFilter.mode(AppColor.gray500, BlendMode.srcIn), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/setting/view/setting_body_view.dart b/lib/presentation/setting/view/setting_body_view.dart index 18ee5dc8..0e6ffe7d 100644 --- a/lib/presentation/setting/view/setting_body_view.dart +++ b/lib/presentation/setting/view/setting_body_view.dart @@ -6,7 +6,6 @@ import 'package:grimity/app/config/app_router.dart'; import 'package:grimity/presentation/setting/widget/setting_action_tile.dart'; import 'package:grimity/presentation/setting/widget/setting_contact_options_bottom_sheet.dart'; import 'package:grimity/presentation/setting/widget/setting_delete_account_dialog.dart'; -import 'package:grimity/presentation/setting/widget/setting_footer.dart'; import 'package:grimity/presentation/setting/widget/setting_notification_section.dart'; class SettingBodyView extends ConsumerWidget { @@ -24,7 +23,6 @@ class SettingBodyView extends ConsumerWidget { SettingActionTile(title: '문의하기', onTap: () => showContactOptionsBottomSheet(context)), divider, SettingActionTile(title: '회원 탈퇴', onTap: () => showDeleteAccountDialog(context, ref)), - SettingFooter(), ], ), ); diff --git a/lib/presentation/setting/widget/setting_footer.dart b/lib/presentation/setting/widget/setting_footer.dart deleted file mode 100644 index aa2d00c2..00000000 --- a/lib/presentation/setting/widget/setting_footer.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:gap/gap.dart'; -import 'package:grimity/app/config/app_color.dart'; -import 'package:grimity/app/config/app_const.dart'; -import 'package:grimity/app/config/app_router.dart'; -import 'package:grimity/app/config/app_typeface.dart'; -import 'package:grimity/app/enum/login_provider.enum.dart'; -import 'package:grimity/presentation/common/provider/user_auth_provider.dart'; -import 'package:grimity/presentation/common/widget/grimity_gesture.dart'; -import 'package:grimity/presentation/common/widget/grimity_gray_circle.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class SettingFooter extends StatelessWidget { - const SettingFooter({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding(padding: EdgeInsets.symmetric(vertical: 30), child: _LogoutTextButton()), - Divider(height: 1, color: AppColor.gray300), - Padding( - padding: EdgeInsets.symmetric(vertical: 30), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GrimityGesture( - onTap: () async { - await launchUrl(Uri.parse(AppConst.privacyPolicyUrl)); - }, - child: Text('개인정보취급방침', style: AppTypeface.caption2.copyWith(color: AppColor.gray500)), - ), - GrimityGrayCircle(), - GrimityGesture( - onTap: () async { - await launchUrl(Uri.parse(AppConst.serviceTermsUrl)); - }, - child: Text('이용약관', style: AppTypeface.caption2.copyWith(color: AppColor.gray500)), - ), - ], - ), - Gap(4), - Text('© Grimity. All rights reserved.', style: AppTypeface.caption2.copyWith(color: AppColor.gray500)), - ], - ), - ), - ], - ); - } -} - -class _LogoutTextButton extends ConsumerWidget { - const _LogoutTextButton(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return TextButton( - onPressed: () async { - final user = ref.read(userAuthProvider); - if (user == null) return; - final provider = LoginProvider.fromString(user.provider ?? ''); - - await ref.read(userAuthProvider.notifier).logout(provider); - if (context.mounted) { - SignInRoute().go(context); - } - }, - child: Text( - '로그아웃', - style: AppTypeface.label2.copyWith( - color: AppColor.gray600, - decoration: TextDecoration.underline, - decorationColor: AppColor.gray500, - decorationThickness: 1, - ), - ), - ); - } -}