From 41530d2ca41e84a4605513ea107d2e571fb332fe Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 13:30:51 +0900 Subject: [PATCH 01/10] =?UTF-8?q?style:=20=ED=99=88=ED=99=94=EB=A9=B4=20Ap?= =?UTF-8?q?pBar=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 55 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 4d6b499..4b4c4a7 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -12,6 +12,7 @@ import 'package:bobmoo/models/meal_widget_data.dart'; import 'package:bobmoo/screens/settings_screen.dart'; import 'package:bobmoo/services/permission_service.dart'; import 'package:bobmoo/services/widget_service.dart'; +import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:bobmoo/utils/meal_utils.dart'; import 'package:bobmoo/widgets/time_grouped_card.dart'; import 'package:bobmoo/utils/hours_parser.dart'; @@ -539,11 +540,11 @@ class _HomeScreenState extends State with WidgetsBindingObserver { } PreferredSizeWidget _buildAppBar() { - final String univName = context.watch().univName; + final UnivProvider univProvider = context.watch(); return AppBar( - toolbarHeight: 103.h, - backgroundColor: Theme.of(context).colorScheme.primary, + toolbarHeight: 140.h, + backgroundColor: univProvider.univColor, shadowColor: Colors.black, elevation: 4.0, surfaceTintColor: Colors.transparent, @@ -552,56 +553,44 @@ class _HomeScreenState extends State with WidgetsBindingObserver { centerTitle: false, // Appbar의 기본 여백 제거 titleSpacing: 0, - actionsPadding: EdgeInsets.only(right: 26.w), + actionsPadding: EdgeInsets.only(right: 24.w), title: Padding( - padding: EdgeInsets.only(left: 26.w), + padding: EdgeInsets.only(left: 20.w), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 20.h), + SizedBox(height: 45.h), // 앱의 왼쪽 위 Text( - univName, - style: TextStyle( - color: Colors.white, - // 자간 5% (픽셀 계산) - letterSpacing: 30.sp * 0.05, - // 행간 170% - height: 1.7, - fontWeight: FontWeight.w700, - fontSize: 30.sp, + univProvider.univName, + style: AppTypography.head.b30.copyWith( + color: AppColors.colorWhite, ), ), + SizedBox(height: 9.h), TextButton( style: TextButton.styleFrom( - backgroundColor: Colors.white.withValues(alpha: 0.1), + backgroundColor: AppColors.colorWhite10, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(59.r), ), padding: EdgeInsets.symmetric( - horizontal: 7.w, - vertical: 2.h, + horizontal: 12.w, + vertical: 1.h, ), minimumSize: Size.zero, // 최소 사이즈 제거 tapTargetSize: MaterialTapTargetSize.shrinkWrap, // 탭 영역을 최소화 ), onPressed: () => _selectDate(context), // 탭하면 _selectDate 함수 호출 - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - DateFormat( - 'yyyy년 MM월 dd일 (E)', - 'ko_KR', - ).format(_selectedDate), // 날짜 포맷 - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), - ], + child: Text( + DateFormat( + 'yyyy년 MM월 dd일 (E)', + 'ko_KR', + ).format(_selectedDate), // 날짜 포맷 + style: AppTypography.caption.sb11.copyWith( + color: AppColors.colorWhite, + ), ), ), SizedBox( From 29eee67bf0933635e6e0184467de8a88ce8965b2 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 15:49:21 +0900 Subject: [PATCH 02/10] =?UTF-8?q?chore:=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=8B=9D=EB=8B=A8=20=EC=B9=B4=EB=93=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B2=BD=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 2 +- lib/{widgets => ui/components/cards}/time_grouped_card.dart | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/{widgets => ui/components/cards}/time_grouped_card.dart (100%) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 4b4c4a7..832d4c7 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -14,7 +14,7 @@ import 'package:bobmoo/services/permission_service.dart'; import 'package:bobmoo/services/widget_service.dart'; import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:bobmoo/utils/meal_utils.dart'; -import 'package:bobmoo/widgets/time_grouped_card.dart'; +import 'package:bobmoo/ui/components/cards/time_grouped_card.dart'; import 'package:bobmoo/utils/hours_parser.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/time_grouped_card.dart b/lib/ui/components/cards/time_grouped_card.dart similarity index 100% rename from lib/widgets/time_grouped_card.dart rename to lib/ui/components/cards/time_grouped_card.dart From ef1aeb489d19a97e04f1d5d971e42ffe5dbd5c09 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 15:59:23 +0900 Subject: [PATCH 03/10] =?UTF-8?q?chore:=20=EB=82=98=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=8B=9D=EB=8B=A8=20=EA=B5=AC=EC=84=B1=EC=9A=94=EC=86=8C=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ui/components/cards/time_grouped_card.dart | 2 +- .../components/meal}/cafeteria_menu_column.dart | 4 ++-- lib/{widgets => ui/components/meal}/meal_item_row.dart | 0 lib/{widgets => ui/components/meal}/open_status_badge.dart | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename lib/{widgets => ui/components/meal}/cafeteria_menu_column.dart (94%) rename lib/{widgets => ui/components/meal}/meal_item_row.dart (100%) rename lib/{widgets => ui/components/meal}/open_status_badge.dart (100%) diff --git a/lib/ui/components/cards/time_grouped_card.dart b/lib/ui/components/cards/time_grouped_card.dart index 46a4514..2ef0fc4 100644 --- a/lib/ui/components/cards/time_grouped_card.dart +++ b/lib/ui/components/cards/time_grouped_card.dart @@ -1,6 +1,6 @@ import 'package:bobmoo/ui/theme/app_colors.dart'; import 'package:bobmoo/models/meal_by_cafeteria.dart'; -import 'package:bobmoo/widgets/cafeteria_menu_column.dart'; +import 'package:bobmoo/ui/components/meal/cafeteria_menu_column.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/svg.dart'; diff --git a/lib/widgets/cafeteria_menu_column.dart b/lib/ui/components/meal/cafeteria_menu_column.dart similarity index 94% rename from lib/widgets/cafeteria_menu_column.dart rename to lib/ui/components/meal/cafeteria_menu_column.dart index d24da0f..6ab6e4a 100644 --- a/lib/widgets/cafeteria_menu_column.dart +++ b/lib/ui/components/meal/cafeteria_menu_column.dart @@ -1,7 +1,7 @@ import 'package:bobmoo/models/meal_by_cafeteria.dart'; import 'package:bobmoo/models/menu_model.dart'; -import 'package:bobmoo/widgets/meal_item_row.dart'; -import 'package:bobmoo/widgets/open_status_badge.dart'; +import 'package:bobmoo/ui/components/meal/meal_item_row.dart'; +import 'package:bobmoo/ui/components/meal/open_status_badge.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; diff --git a/lib/widgets/meal_item_row.dart b/lib/ui/components/meal/meal_item_row.dart similarity index 100% rename from lib/widgets/meal_item_row.dart rename to lib/ui/components/meal/meal_item_row.dart diff --git a/lib/widgets/open_status_badge.dart b/lib/ui/components/meal/open_status_badge.dart similarity index 100% rename from lib/widgets/open_status_badge.dart rename to lib/ui/components/meal/open_status_badge.dart From 7b7b2af585dbb00ffe3491a1aae88e45909c7e0a Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 16:44:43 +0900 Subject: [PATCH 04/10] =?UTF-8?q?style:=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=8B=9D=EB=8B=A8=20=EC=B9=B4=EB=93=9C=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cards/time_grouped_card.dart | 39 ++++++------------- .../meal/cafeteria_menu_column.dart | 36 ++++++++++------- lib/ui/components/meal/meal_item_row.dart | 24 ++++-------- lib/ui/components/meal/open_status_badge.dart | 18 ++++----- 4 files changed, 47 insertions(+), 70 deletions(-) diff --git a/lib/ui/components/cards/time_grouped_card.dart b/lib/ui/components/cards/time_grouped_card.dart index 2ef0fc4..24b657f 100644 --- a/lib/ui/components/cards/time_grouped_card.dart +++ b/lib/ui/components/cards/time_grouped_card.dart @@ -1,5 +1,6 @@ import 'package:bobmoo/ui/theme/app_colors.dart'; import 'package:bobmoo/models/meal_by_cafeteria.dart'; +import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:bobmoo/ui/components/meal/cafeteria_menu_column.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -23,29 +24,14 @@ class TimeGroupedCard extends StatelessWidget { shadowColor: Colors.black.withValues(alpha: 0.5), elevation: 4, child: Padding( - padding: EdgeInsets.all(12.w), + padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 섹션 제목 (예: "점심") - Row( - children: [ - _getIconForMeal(title), // 위에서 만든 함수로 아이콘 가져오기 - SizedBox( - width: 5.w, - ), - Text( - title, - style: TextStyle( - fontSize: 21.sp, - fontWeight: FontWeight.w700, - // 자간 5% - letterSpacing: 21.sp * 0.05, - // 행간 170% - height: 1.7, - ), - ), - ], + Text( + title, + style: AppTypography.head.b21, ), // 각 식당별 메뉴 목록 ListView.separated( @@ -58,12 +44,7 @@ class TimeGroupedCard extends StatelessWidget { itemBuilder: (BuildContext context, int index) { // 각 인덱스에 해당하는 식당 메뉴 위젯을 반환 return Padding( - padding: EdgeInsets.only( - left: 13.w, - right: 5.w, - top: 4.h, - bottom: 12.h, - ), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 3.w), child: CafeteriaMenuColumn( data: mealData[index], mealType: title, @@ -73,10 +54,11 @@ class TimeGroupedCard extends StatelessWidget { }, separatorBuilder: (BuildContext context, int index) { // 각 아이템 사이에 들어갈 구분선 위젯을 반환 + // thickness를 정수로 고정해야 소수 픽셀으로 인한 렌더링 불일치 방지 return Divider( - height: 10.h, - thickness: 1.5, - color: AppColors.grayDividerColor, + height: 1, + thickness: 2, + color: AppColors.colorGray5, ); }, ), @@ -88,6 +70,7 @@ class TimeGroupedCard extends StatelessWidget { } // 아이콘을 결정하는 함수 +// ignore: unused_element Widget _getIconForMeal(String title) { // => 를 사용해 바로 위젯을 반환합니다. final icon = switch (title) { diff --git a/lib/ui/components/meal/cafeteria_menu_column.dart b/lib/ui/components/meal/cafeteria_menu_column.dart index 6ab6e4a..7d28090 100644 --- a/lib/ui/components/meal/cafeteria_menu_column.dart +++ b/lib/ui/components/meal/cafeteria_menu_column.dart @@ -2,6 +2,7 @@ import 'package:bobmoo/models/meal_by_cafeteria.dart'; import 'package:bobmoo/models/menu_model.dart'; import 'package:bobmoo/ui/components/meal/meal_item_row.dart'; import 'package:bobmoo/ui/components/meal/open_status_badge.dart'; +import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -29,24 +30,13 @@ class CafeteriaMenuColumn extends StatelessWidget { children: [ Text( data.cafeteriaName, - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w600, - // 자간 5% - letterSpacing: 18.sp * 0.05, - // 행간 170% - height: 1.7, - ), + style: AppTypography.head.sb18, ), - SizedBox(width: 3.w), + SizedBox(width: 5.w), Expanded( child: Text( _hoursTextForMealType(data.hours, mealType), - style: TextStyle( - fontSize: 9.sp, - color: Colors.black54, - fontWeight: FontWeight.w600, - ), + style: AppTypography.caption.sb9, overflow: TextOverflow.ellipsis, ), ), @@ -59,7 +49,23 @@ class CafeteriaMenuColumn extends StatelessWidget { ), SizedBox(height: 3.h), // 식당의 메뉴들 - ...data.meals.map((meal) => MealItemRow(meal: meal)), + ListView.builder( + padding: EdgeInsets.zero, + // 내용만큼 크기를 줄이도록 설정 + shrinkWrap: true, + // 스크롤 방지 + physics: const NeverScrollableScrollPhysics(), + itemCount: data.meals.length, + itemBuilder: (BuildContext context, int index) { + // 각 인덱스에 해당하는 식당 메뉴 위젯을 반환 + return Padding( + padding: EdgeInsets.all(3.w), + child: MealItemRow(meal: data.meals[index]), + ); + }, + ), + + // ...data.meals.map((meal) => MealItemRow(meal: meal)), ], ); } diff --git a/lib/ui/components/meal/meal_item_row.dart b/lib/ui/components/meal/meal_item_row.dart index ae1ee9c..4178145 100644 --- a/lib/ui/components/meal/meal_item_row.dart +++ b/lib/ui/components/meal/meal_item_row.dart @@ -1,10 +1,12 @@ -import 'package:bobmoo/ui/theme/app_colors.dart'; import 'package:bobmoo/models/menu_model.dart'; +import 'package:bobmoo/ui/theme/app_colors.dart'; +import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class MealItemRow extends StatelessWidget { final MealItem meal; + const MealItemRow({super.key, required this.meal}); @override @@ -17,7 +19,7 @@ class MealItemRow extends StatelessWidget { // 1. 코스 (A, B, C...) Text( "${meal.course} ", - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w500), + style: AppTypography.caption.m15, ), // 2. 메뉴 이름 Expanded( @@ -32,28 +34,18 @@ class MealItemRow extends StatelessWidget { .map((entry) { return Text( "${entry.value} ", - style: TextStyle( - fontSize: 15.sp, - fontWeight: FontWeight.w400, - ), + style: AppTypography.caption.r15, ); }) .toList(), ), ), Padding( - padding: EdgeInsets.zero, + padding: EdgeInsets.only(top: 3.h), child: Text( "${meal.price}원", - style: TextStyle( - fontSize: 11.sp, - fontFamily: 'NanumSquareRound', - fontWeight: FontWeight.w700, - color: AppColors.greyTextColor, - // 자간 5% - letterSpacing: 11.sp * 0.02, - // 행간 21px - height: 2.0, + style: AppTypography.button.sb11.copyWith( + color: AppColors.colorGray3, ), ), ), diff --git a/lib/ui/components/meal/open_status_badge.dart b/lib/ui/components/meal/open_status_badge.dart index 87e69cb..a2c8eb1 100644 --- a/lib/ui/components/meal/open_status_badge.dart +++ b/lib/ui/components/meal/open_status_badge.dart @@ -1,5 +1,6 @@ import 'package:bobmoo/ui/theme/app_colors.dart'; import 'package:bobmoo/models/menu_model.dart'; +import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:flutter/material.dart'; import 'package:bobmoo/utils/hours_parser.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -73,21 +74,16 @@ class _StatusBadge extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 1.5.h), + width: 60.w, + height: 24.h, decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(24.r), ), - child: Text( - text, - style: TextStyle( - fontSize: 11.sp, - fontWeight: FontWeight.w500, - // 자간 4% - letterSpacing: 11.sp * 0.04, - // 행간 21px - height: 2.0, - color: textColor, + child: Center( + child: Text( + text, + style: AppTypography.button.sb11.copyWith(color: textColor), ), ), ); From a08ad4c03a4391947ffb1a74ae095d26003a90a8 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 17:00:53 +0900 Subject: [PATCH 05/10] =?UTF-8?q?style:=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=B9=88=20=EC=83=81=ED=83=9C=20=EB=94=94=EC=9E=90=EC=9D=B8=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/screens/home_screen.dart | 128 ++++++++++++++++------------------- 1 file changed, 57 insertions(+), 71 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 832d4c7..7c28249 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -19,6 +19,7 @@ import 'package:bobmoo/utils/hours_parser.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:in_app_update/in_app_update.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -403,82 +404,67 @@ class _HomeScreenState extends State with WidgetsBindingObserver { } Widget _buildEmptyState() { - final Color univColor = context.watch().univColor; - - return Center( - child: Padding( - padding: EdgeInsets.all(32.w), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // 아이콘 - Container( - padding: EdgeInsets.all(24.w), - decoration: BoxDecoration( - color: univColor.withValues(alpha: 0.1), - shape: BoxShape.circle, - ), - child: Icon( - Icons.restaurant_menu, - size: 48.w, - color: univColor, - ), + return Container( + width: double.infinity, + margin: EdgeInsets.all(24.w), + decoration: BoxDecoration( + color: AppColors.colorWhite, + borderRadius: BorderRadius.circular(15.r), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 아이콘 + SvgPicture.asset( + 'assets/icons/icon_bob.svg', + width: 60.w, + ), + SizedBox(height: 24.h), + // 제목 + Text( + '등록된 식단이 없어요', + style: AppTypography.head.sb18, + ), + SizedBox(height: 21.h), + // 설명 + Text( + '식단 정보가 등록되지 않았어요.', + textAlign: TextAlign.center, + style: AppTypography.search.sb15.copyWith( + color: AppColors.colorGray3, ), - SizedBox(height: 24.h), - // 제목 - Text( - '등록된 식단이 없어요', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), + ), + SizedBox( + height: 4.h, + ), + Text( + '잠시 후 다시 확인해주세요.', + textAlign: TextAlign.center, + style: AppTypography.search.sb15.copyWith( + color: AppColors.colorGray3, ), - SizedBox(height: 8.h), - // 설명 - Text( - '아직 오늘의 메뉴가 등록되지 않았습니다.\n잠시 후 다시 확인해주세요.', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14.sp, - color: AppColors.greyTextColor, - height: 1.5, + ), + SizedBox(height: 118.h), + // 아래로 당겨 새로고침 + Column( + children: [ + Icon( + Icons.arrow_downward, + color: AppColors.colorGray3, + size: 32.w, ), - ), - SizedBox(height: 24.h), - // 새로고침 버튼 - TextButton( - onPressed: () => setState(() { - _refreshMeals(); - }), - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: univColor, - padding: EdgeInsets.symmetric( - horizontal: 24.w, - vertical: 12.h, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24.r), - ), + SizedBox( + height: 7.h, ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.refresh, size: 18.w), - SizedBox(width: 8.w), - Text( - '새로고침', - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w600, - ), - ), - ], + Text( + "아래로 당겨 새로고침", + style: AppTypography.button.sb11.copyWith( + color: AppColors.colorGray3, + ), ), - ), - ], - ), + ], + ), + ], ), ); } From 0a1dd5c4015f507c39e8235484403be0847d8401 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 17:18:24 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20=EC=96=B4=EB=94=94=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=82=98=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20RefreshIndicato?= =?UTF-8?q?r=20=EC=9C=84=EC=A0=AF=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 95 ++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 7c28249..cf75c06 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -474,54 +474,65 @@ class _HomeScreenState extends State with WidgetsBindingObserver { final groupedMeals = groupMeals(meals); final mealTypes = _orderedMealTypesByDynamicHours(groupedMeals); - return RefreshIndicator( - onRefresh: _refreshMeals, // 당겨서 새로고침 기능 연결 - child: ListView.builder( - itemCount: mealTypes.length, - padding: EdgeInsets.symmetric( - horizontal: 21.w, - vertical: 23.h, - ), - itemBuilder: (context, index) { - final mealType = mealTypes[index]; - final mealsByCafeteria = groupedMeals[mealType]; - - if (mealsByCafeteria == null || mealsByCafeteria.isEmpty) { - return const SizedBox.shrink(); - } - return Padding( - padding: EdgeInsets.only(bottom: 25.h), - child: TimeGroupedCard( - title: mealType, - mealData: mealsByCafeteria, - selectedDate: _selectedDate, - ), - ); - }, + return ListView.builder( + itemCount: mealTypes.length, + padding: EdgeInsets.symmetric( + horizontal: 21.w, + vertical: 23.h, ), + itemBuilder: (context, index) { + final mealType = mealTypes[index]; + final mealsByCafeteria = groupedMeals[mealType]; + + if (mealsByCafeteria == null || mealsByCafeteria.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(bottom: 25.h), + child: TimeGroupedCard( + title: mealType, + mealData: mealsByCafeteria, + selectedDate: _selectedDate, + ), + ); + }, ); } Widget _buildBody() { - return FutureBuilder>( - future: _mealFuture, - builder: (context, snapshot) { - // 로딩 중 - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } - // 에러 발생 - if (snapshot.hasError) { - return _buildErrorWidget(snapshot.error!); - } - // 데이터 없을 시 비어있음 표시 - if (!snapshot.hasData || snapshot.data!.isEmpty) { - return _buildEmptyState(); - } + return RefreshIndicator( + onRefresh: _refreshMeals, + child: FutureBuilder>( + future: _mealFuture, + builder: (context, snapshot) { + Widget child; + + // 로딩 중 + if (snapshot.connectionState == ConnectionState.waiting) { + child = const Center(child: CircularProgressIndicator()); + } + // 에러 발생 + else if (snapshot.hasError) { + child = _buildErrorWidget(snapshot.error!); + } + // 데이터 없을 시 비어있음 표시 + else if (!snapshot.hasData || snapshot.data!.isEmpty) { + child = _buildEmptyState(); + } + // 데이터 로딩 성공 -> MealList 위젯 생성 + else { + child = _buildMealList(snapshot.data!); + } - // 데이터 로딩 성공 -> MealList 위젯 생성 - return _buildMealList(snapshot.data!); - }, + return SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: MediaQuery.of(context).size.height - kToolbarHeight, + child: child, + ), + ); + }, + ), ); } From 17fdbbcadc0f6ddebccc96f31c69936374117f1c Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 17:41:14 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=EB=8B=B9=EA=B2=A8=EC=84=9C=20?= =?UTF-8?q?=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20SliverList=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 125 +++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index cf75c06..d011440 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -16,6 +16,7 @@ import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:bobmoo/utils/meal_utils.dart'; import 'package:bobmoo/ui/components/cards/time_grouped_card.dart'; import 'package:bobmoo/utils/hours_parser.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -474,61 +475,27 @@ class _HomeScreenState extends State with WidgetsBindingObserver { final groupedMeals = groupMeals(meals); final mealTypes = _orderedMealTypesByDynamicHours(groupedMeals); - return ListView.builder( - itemCount: mealTypes.length, + return SliverPadding( padding: EdgeInsets.symmetric( horizontal: 21.w, vertical: 23.h, ), - itemBuilder: (context, index) { - final mealType = mealTypes[index]; - final mealsByCafeteria = groupedMeals[mealType]; + sliver: SliverList.builder( + itemCount: mealTypes.length, - if (mealsByCafeteria == null || mealsByCafeteria.isEmpty) { - return const SizedBox.shrink(); - } - return Padding( - padding: EdgeInsets.only(bottom: 25.h), - child: TimeGroupedCard( - title: mealType, - mealData: mealsByCafeteria, - selectedDate: _selectedDate, - ), - ); - }, - ); - } + itemBuilder: (context, index) { + final mealType = mealTypes[index]; + final mealsByCafeteria = groupedMeals[mealType]; - Widget _buildBody() { - return RefreshIndicator( - onRefresh: _refreshMeals, - child: FutureBuilder>( - future: _mealFuture, - builder: (context, snapshot) { - Widget child; - - // 로딩 중 - if (snapshot.connectionState == ConnectionState.waiting) { - child = const Center(child: CircularProgressIndicator()); - } - // 에러 발생 - else if (snapshot.hasError) { - child = _buildErrorWidget(snapshot.error!); - } - // 데이터 없을 시 비어있음 표시 - else if (!snapshot.hasData || snapshot.data!.isEmpty) { - child = _buildEmptyState(); + if (mealsByCafeteria == null || mealsByCafeteria.isEmpty) { + return const SizedBox.shrink(); } - // 데이터 로딩 성공 -> MealList 위젯 생성 - else { - child = _buildMealList(snapshot.data!); - } - - return SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: SizedBox( - height: MediaQuery.of(context).size.height - kToolbarHeight, - child: child, + return Padding( + padding: EdgeInsets.only(bottom: 25.h), + child: TimeGroupedCard( + title: mealType, + mealData: mealsByCafeteria, + selectedDate: _selectedDate, ), ); }, @@ -536,6 +503,68 @@ class _HomeScreenState extends State with WidgetsBindingObserver { ); } + Widget _buildBody() { + return FutureBuilder>( + future: _mealFuture, + builder: (context, snapshot) { + return CustomScrollView( + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), + ), + slivers: [ + CupertinoSliverRefreshControl( + onRefresh: _refreshMeals, + builder: + ( + context, + refreshState, + pulledExtent, + refreshTriggerPullDistance, + refreshIndicatorExtent, + ) { + return Container( + margin: const EdgeInsets.all(20), + padding: EdgeInsets.all(33.h), + child: CupertinoActivityIndicator( + radius: 10.r, + ), + ); + }, + ), + // 케이스별로 다른 Sliver 추가 + // 로딩 중 + if (snapshot.connectionState == ConnectionState.waiting) + SliverToBoxAdapter( + child: SizedBox( + height: MediaQuery.of(context).size.height - kToolbarHeight, + child: const Center(child: CircularProgressIndicator()), + ), + ) + // 에러 발생 + else if (snapshot.hasError) + SliverToBoxAdapter( + child: SizedBox( + height: MediaQuery.of(context).size.height - kToolbarHeight, + child: _buildErrorWidget(snapshot.error!), + ), + ) + // 데이터 없을 시 비어있음 표시 + else if (!snapshot.hasData || snapshot.data!.isEmpty) + SliverToBoxAdapter( + child: SizedBox( + height: MediaQuery.of(context).size.height - kToolbarHeight, + child: _buildEmptyState(), + ), + ) + // 데이터 로딩 성공 -> MealList 위젯 생성 + else + _buildMealList(snapshot.data!), // Sliver 직접 추가 + ], + ); + }, + ); + } + PreferredSizeWidget _buildAppBar() { final UnivProvider univProvider = context.watch(); From c6d1772f270155402381fe20f742835e24435f1c Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 18:02:29 +0900 Subject: [PATCH 08/10] =?UTF-8?q?style:=20=EB=AF=B8=EB=AC=98=ED=95=9C=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ui/components/cards/time_grouped_card.dart | 2 +- lib/ui/components/meal/cafeteria_menu_column.dart | 3 --- lib/ui/components/meal/open_status_badge.dart | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/ui/components/cards/time_grouped_card.dart b/lib/ui/components/cards/time_grouped_card.dart index 24b657f..f2a0fa1 100644 --- a/lib/ui/components/cards/time_grouped_card.dart +++ b/lib/ui/components/cards/time_grouped_card.dart @@ -24,7 +24,7 @@ class TimeGroupedCard extends StatelessWidget { shadowColor: Colors.black.withValues(alpha: 0.5), elevation: 4, child: Padding( - padding: EdgeInsets.all(16.w), + padding: EdgeInsets.all(13.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/ui/components/meal/cafeteria_menu_column.dart b/lib/ui/components/meal/cafeteria_menu_column.dart index 7d28090..a1c9c98 100644 --- a/lib/ui/components/meal/cafeteria_menu_column.dart +++ b/lib/ui/components/meal/cafeteria_menu_column.dart @@ -47,7 +47,6 @@ class CafeteriaMenuColumn extends StatelessWidget { ), ], ), - SizedBox(height: 3.h), // 식당의 메뉴들 ListView.builder( padding: EdgeInsets.zero, @@ -64,8 +63,6 @@ class CafeteriaMenuColumn extends StatelessWidget { ); }, ), - - // ...data.meals.map((meal) => MealItemRow(meal: meal)), ], ); } diff --git a/lib/ui/components/meal/open_status_badge.dart b/lib/ui/components/meal/open_status_badge.dart index a2c8eb1..facc96e 100644 --- a/lib/ui/components/meal/open_status_badge.dart +++ b/lib/ui/components/meal/open_status_badge.dart @@ -74,7 +74,7 @@ class _StatusBadge extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - width: 60.w, + padding: EdgeInsets.symmetric(vertical: 1.5.h, horizontal: 10.w), height: 24.h, decoration: BoxDecoration( color: backgroundColor, From 2b56f47f17bc61de43e238b11597176737509d5d Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 18:52:11 +0900 Subject: [PATCH 09/10] =?UTF-8?q?style:=20=EC=8B=9D=EB=8B=A8=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20Padding=20=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ui/components/cards/time_grouped_card.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/ui/components/cards/time_grouped_card.dart b/lib/ui/components/cards/time_grouped_card.dart index f2a0fa1..b44a6ff 100644 --- a/lib/ui/components/cards/time_grouped_card.dart +++ b/lib/ui/components/cards/time_grouped_card.dart @@ -24,7 +24,12 @@ class TimeGroupedCard extends StatelessWidget { shadowColor: Colors.black.withValues(alpha: 0.5), elevation: 4, child: Padding( - padding: EdgeInsets.all(13.w), + padding: EdgeInsets.only( + top: 15.h, + bottom: 5.h, + left: 13.w, + right: 13.w, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -44,7 +49,7 @@ class TimeGroupedCard extends StatelessWidget { itemBuilder: (BuildContext context, int index) { // 각 인덱스에 해당하는 식당 메뉴 위젯을 반환 return Padding( - padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 3.w), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w), child: CafeteriaMenuColumn( data: mealData[index], mealType: title, From 1f5ea816b9f87d71b32b1ff7763e453c5c3ec119 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sat, 28 Feb 2026 18:52:49 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20=EB=8B=B9=EA=B2=A8=EC=84=9C?= =?UTF-8?q?=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=EC=97=90=20SliverFillRe?= =?UTF-8?q?maining=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index d011440..d85cc62 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -534,27 +534,21 @@ class _HomeScreenState extends State with WidgetsBindingObserver { // 케이스별로 다른 Sliver 추가 // 로딩 중 if (snapshot.connectionState == ConnectionState.waiting) - SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.of(context).size.height - kToolbarHeight, - child: const Center(child: CircularProgressIndicator()), - ), + SliverFillRemaining( + hasScrollBody: false, + child: const Center(child: CircularProgressIndicator()), ) // 에러 발생 else if (snapshot.hasError) - SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.of(context).size.height - kToolbarHeight, - child: _buildErrorWidget(snapshot.error!), - ), + SliverFillRemaining( + hasScrollBody: false, + child: _buildErrorWidget(snapshot.error!), ) // 데이터 없을 시 비어있음 표시 else if (!snapshot.hasData || snapshot.data!.isEmpty) - SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.of(context).size.height - kToolbarHeight, - child: _buildEmptyState(), - ), + SliverFillRemaining( + hasScrollBody: false, + child: _buildEmptyState(), ) // 데이터 로딩 성공 -> MealList 위젯 생성 else