From 8241a434dd021bb342ea3673bb6ea1fcb1034aa7 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 18:11:44 +0900 Subject: [PATCH 1/8] =?UTF-8?q?add:=20flutter=5Fnative=5Fsplash=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 40 ++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 10 +++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index 4a366d7..252b39c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.2.0" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" + url: "https://pub.dev" + source: hosted + version: "2.0.3" archive: dependency: transitive description: @@ -185,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.7" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" cupertino_icons: dependency: "direct main" description: @@ -315,6 +331,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_native_splash: + dependency: "direct dev" + description: + name: flutter_native_splash + sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" + url: "https://pub.dev" + source: hosted + version: "2.4.7" flutter_screenutil: dependency: "direct main" description: @@ -381,6 +405,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.1" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" http: dependency: "direct main" description: @@ -874,6 +906,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 + url: "https://pub.dev" + source: hosted + version: "2.3.1" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 13f520f..bc6b149 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dev_dependencies: flutter_lints: ^6.0.0 flutter_launcher_icons: ^0.14.4 + flutter_native_splash: ^2.4.6 build_runner: any isar_community_generator: @@ -88,4 +89,11 @@ flutter_launcher_icons: remove_alpha_ios: true adaptive_icon_background: "#FFFFFF" - adaptive_icon_foreground: "assets/icons/icon.png" \ No newline at end of file + adaptive_icon_foreground: "assets/icons/icon.png" + +flutter_native_splash: + color: "#FFFFFF" + android: true + ios: true + android_12: + color: "#FFFFFF" \ No newline at end of file From c5b6fff5e99ff6aa3c597b54d93b535e825aaa19 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 18:13:33 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EB=A1=9C=EB=94=A9=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/loading_screen.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/screens/loading_screen.dart diff --git a/lib/screens/loading_screen.dart b/lib/screens/loading_screen.dart new file mode 100644 index 0000000..bfd1cf5 --- /dev/null +++ b/lib/screens/loading_screen.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class LoadingScreen extends StatelessWidget { + const LoadingScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + } +} From 8855755da62fb18cc27610537408343ad9d57a5d Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 18:14:08 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EC=8A=A4=ED=94=8C=EB=9E=98?= =?UTF-8?q?=EC=8B=9C=20=ED=99=94=EB=A9=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EB=93=9C=EC=9D=B8=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/app_gate.dart | 40 ++++++++++++++++------ lib/screens/splash_screen.dart | 61 ++++++++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/lib/screens/app_gate.dart b/lib/screens/app_gate.dart index cac6b9e..cb1edf7 100644 --- a/lib/screens/app_gate.dart +++ b/lib/screens/app_gate.dart @@ -1,4 +1,5 @@ import 'package:bobmoo/providers/univ_provider.dart'; +import 'package:bobmoo/screens/loading_screen.dart'; import 'package:bobmoo/screens/splash_screen.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -11,25 +12,30 @@ class AppGate extends StatefulWidget { } class _AppGateState extends State { + static const Duration _minimumSplashDuration = Duration(milliseconds: 1000); + // 한번만 실행되게 가드역할 bool _redirected = false; + bool _isSplashVisible = true; @override - void didChangeDependencies() { - super.didChangeDependencies(); - - final univProvider = context.watch(); + void initState() { + super.initState(); + Future.delayed(_minimumSplashDuration, () { + if (!mounted) return; + setState(() => _isSplashVisible = false); + _tryRedirect(); + }); + } - if (_redirected) return; + void _tryRedirect() { + if (_redirected || _isSplashVisible) return; - if (!univProvider.isInitialized) { - // 아직 초기화 전이면 UI만 보여주고 대기 - return; - } + final univProvider = context.read(); + if (!univProvider.isInitialized) return; _redirected = true; - // build 후에 이동 WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; @@ -45,6 +51,18 @@ class _AppGateState extends State { @override Widget build(BuildContext context) { - return const SplashScreen(); + final univProvider = context.watch(); + _tryRedirect(); + + if (_isSplashVisible) { + return const SplashScreen(); + } + + if (!univProvider.isInitialized) { + return const LoadingScreen(); + } + + // 라우트 이동 직전 프레임 + return const SizedBox.shrink(); } } diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index ff5c89b..a8bb98b 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -1,13 +1,70 @@ +import 'package:bobmoo/ui/theme/app_typography.dart'; import 'package:flutter/material.dart'; +import 'package:bobmoo/ui/theme/app_colors.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/svg.dart'; -class SplashScreen extends StatelessWidget { +class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _controller; + late final Animation _fadeAnimation; + late final Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 900), + ); + _fadeAnimation = CurvedAnimation( + parent: _controller, + curve: Curves.easeOutCubic, + ); + _scaleAnimation = Tween(begin: 0.97, end: 1.0).animate( + CurvedAnimation(parent: _controller, curve: Curves.easeOut), + ); + _controller.forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: AppColors.colorWhite, body: Center( - child: CircularProgressIndicator(), + child: FadeTransition( + opacity: _fadeAnimation, + child: ScaleTransition( + scale: _scaleAnimation, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + 'assets/icons/icon_bob.svg', + width: 107.w, + ), + SizedBox(height: 14.h), + Text( + "밥묵자", + style: AppTypography.head.b48, + ), + ], + ), + ), + ), ), ); } From 48cc3edbc82d9b4829ec85e45952f57fb2eebe85 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 18:15:10 +0900 Subject: [PATCH 4/8] =?UTF-8?q?delete:=20ios=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC/=ED=8F=B4=EB=8D=94=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/drawable-v21/background.png | Bin 0 -> 69 bytes .../res/drawable-v21/launch_background.xml | 12 +- .../app/src/main/res/drawable/background.png | Bin 0 -> 69 bytes .../main/res/drawable/launch_background.xml | 12 +- .../src/main/res/values-night-v31/styles.xml | 20 + .../app/src/main/res/values-night/styles.xml | 4 + .../app/src/main/res/values-v31/styles.xml | 20 + android/app/src/main/res/values/styles.xml | 4 + ios/.gitignore | 34 - ios/Flutter/AppFrameworkInfo.plist | 26 - ios/Flutter/Debug.xcconfig | 1 - ios/Flutter/Release.xcconfig | 1 - ios/Runner.xcodeproj/project.pbxproj | 616 ------------------ .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 101 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - ios/Runner/AppDelegate.swift | 13 - .../AppIcon.appiconset/Contents.json | 122 ---- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 295 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 450 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 282 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 462 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 704 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 586 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 1674 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 762 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 1226 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 1418 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 -- ios/Runner/Base.lproj/Main.storyboard | 26 - ios/Runner/Info.plist | 49 -- ios/Runner/Runner-Bridging-Header.h | 1 - ios/RunnerTests/RunnerTests.swift | 12 - pubspec.yaml | 2 +- 48 files changed, 55 insertions(+), 1132 deletions(-) create mode 100644 android/app/src/main/res/drawable-v21/background.png create mode 100644 android/app/src/main/res/drawable/background.png create mode 100644 android/app/src/main/res/values-night-v31/styles.xml create mode 100644 android/app/src/main/res/values-v31/styles.xml delete mode 100644 ios/.gitignore delete mode 100644 ios/Flutter/AppFrameworkInfo.plist delete mode 100644 ios/Flutter/Debug.xcconfig delete mode 100644 ios/Flutter/Release.xcconfig delete mode 100644 ios/Runner.xcodeproj/project.pbxproj delete mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 ios/Runner/AppDelegate.swift delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 ios/Runner/Base.lproj/Main.storyboard delete mode 100644 ios/Runner/Info.plist delete mode 100644 ios/Runner/Runner-Bridging-Header.h delete mode 100644 ios/RunnerTests/RunnerTests.swift diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 0000000000000000000000000000000000000000..8e21404806af591e5f4e4ac12eddcde9d2602abe GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryJf1F&Ar*6yfBye(&%nmV%p~xY RaWhby!PC{xWt~$(69CA855WKc literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml index f74085f..f88598c 100644 --- a/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -1,12 +1,6 @@ - - - - - + + + diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png new file mode 100644 index 0000000000000000000000000000000000000000..8e21404806af591e5f4e4ac12eddcde9d2602abe GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryJf1F&Ar*6yfBye(&%nmV%p~xY RaWhby!PC{xWt~$(69CA855WKc literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index 304732f..f88598c 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,6 @@ - - - - - + + + diff --git a/android/app/src/main/res/values-night-v31/styles.xml b/android/app/src/main/res/values-night-v31/styles.xml new file mode 100644 index 0000000..d0bac0f --- /dev/null +++ b/android/app/src/main/res/values-night-v31/styles.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml index 06952be..dbc9ea9 100644 --- a/android/app/src/main/res/values-night/styles.xml +++ b/android/app/src/main/res/values-night/styles.xml @@ -5,6 +5,10 @@ @drawable/launch_background + false + false + false + shortEdges + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index cb1ef88..0d1fa8f 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -5,6 +5,10 @@ @drawable/launch_background + false + false + false + shortEdges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c2851..0000000 --- a/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist deleted file mode 100644 index 4080f15..0000000 --- a/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Bobmoo Android - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - bobmoo_android - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a5..0000000 --- a/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift deleted file mode 100644 index 86a7c3b..0000000 --- a/ios/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Flutter -import UIKit -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/pubspec.yaml b/pubspec.yaml index bc6b149..0101630 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,6 +94,6 @@ flutter_launcher_icons: flutter_native_splash: color: "#FFFFFF" android: true - ios: true + ios: false android_12: color: "#FFFFFF" \ No newline at end of file From 8a6f2b67443881ff2497356c247620f3cea65766 Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 20:01:24 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EC=A2=8C=EC=9A=B0=20=EC=8A=A4?= =?UTF-8?q?=EC=99=80=EC=9D=B4=ED=94=84=EB=A1=9C=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 186 ++++++++++++++++++++++++++--------- 1 file changed, 138 insertions(+), 48 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 3538099..27ec3da 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -41,6 +41,13 @@ class _HomeScreenState extends State with WidgetsBindingObserver { /// 선택한 날짜 저장할 상태 변수 DateTime _selectedDate = DateTime.now(); + static const double _swipeTriggerDistance = 80; + static const double _maxVisualDragOffset = 110; + + double _horizontalDragOffset = 0; + bool _isHorizontalDragging = false; + int _dateTransitionDirection = 1; // 1: 다음날(왼쪽 스와이프), -1: 이전날 + /// 화면이 처음 나타날 때 데이터 불러오기 @override void initState() { @@ -243,6 +250,14 @@ class _HomeScreenState extends State with WidgetsBindingObserver { }); } + void _changeSelectedDateByDays(int days) { + setState(() { + _dateTransitionDirection = days >= 0 ? 1 : -1; + _selectedDate = _selectedDate.add(Duration(days: days)); + _mealFuture = _fetchData(); + }); + } + /// Pull-to-Refresh(당겨서 새로고침)을 위한 새로고침 함수 Future _refreshMeals() async { setState(() { @@ -280,8 +295,11 @@ class _HomeScreenState extends State with WidgetsBindingObserver { lastDate: DateTime(2030), ); if (picked != null && picked != _selectedDate) { - _selectedDate = picked; - _loadMeals(); // 새 날짜로 데이터 로드 + setState(() { + _dateTransitionDirection = picked.isAfter(_selectedDate) ? 1 : -1; + _selectedDate = picked; + _mealFuture = _fetchData(); + }); } } @@ -498,53 +516,125 @@ class _HomeScreenState extends State with WidgetsBindingObserver { 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, - ), - ); - }, + final currentDateKey = DateFormat('yyyy-MM-dd').format(_selectedDate); + final beginX = _dateTransitionDirection >= 0 ? 0.22 : -0.22; + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onHorizontalDragStart: (_) { + setState(() { + _isHorizontalDragging = true; + }); + }, + onHorizontalDragUpdate: (details) { + setState(() { + _horizontalDragOffset = (_horizontalDragOffset + details.delta.dx) + .clamp(-_maxVisualDragOffset, _maxVisualDragOffset); + }); + }, + onHorizontalDragCancel: () { + setState(() { + _isHorizontalDragging = false; + _horizontalDragOffset = 0; + }); + }, + onHorizontalDragEnd: (_) { + final dragOffset = _horizontalDragOffset; + int? dayDelta; + + if (dragOffset.abs() >= _swipeTriggerDistance) { + dayDelta = dragOffset < 0 ? 1 : -1; // 왼쪽 스와이프=다음 날 + } + + setState(() { + _isHorizontalDragging = false; + _horizontalDragOffset = 0; + }); + + if (dayDelta != null) { + _changeSelectedDateByDays(dayDelta); + } + }, + child: AnimatedContainer( + duration: _isHorizontalDragging + ? Duration.zero + : const Duration(milliseconds: 180), + curve: Curves.easeOutCubic, + transform: Matrix4.translationValues(_horizontalDragOffset, 0, 0), + child: ClipRect( + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 260), + switchInCurve: Curves.easeOutCubic, + switchOutCurve: Curves.easeOutCubic, + transitionBuilder: (child, animation) { + final isIncoming = child.key == ValueKey(currentDateKey); + final slide = isIncoming + ? Tween( + begin: Offset(beginX, 0), + end: Offset.zero, + ).animate(animation) + : Tween( + begin: Offset.zero, + end: Offset(-beginX, 0), + ).animate(ReverseAnimation(animation)); + + return FadeTransition( + opacity: animation, + child: SlideTransition(position: slide, child: child), + ); + }, + child: CustomScrollView( + key: ValueKey(currentDateKey), + 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) + SliverFillRemaining( + hasScrollBody: false, + child: const Center(child: CircularProgressIndicator()), + ) + // 에러 발생 + else if (snapshot.hasError) + SliverFillRemaining( + hasScrollBody: false, + child: _buildErrorWidget(snapshot.error!), + ) + // 데이터 없을 시 비어있음 표시 + else if (!snapshot.hasData || snapshot.data!.isEmpty) + SliverFillRemaining( + hasScrollBody: false, + child: _buildEmptyState(), + ) + // 데이터 로딩 성공 -> MealList 위젯 생성 + else + _buildMealList(snapshot.data!), // Sliver 직접 추가 + ], + ), + ), ), - // 케이스별로 다른 Sliver 추가 - // 로딩 중 - if (snapshot.connectionState == ConnectionState.waiting) - SliverFillRemaining( - hasScrollBody: false, - child: const Center(child: CircularProgressIndicator()), - ) - // 에러 발생 - else if (snapshot.hasError) - SliverFillRemaining( - hasScrollBody: false, - child: _buildErrorWidget(snapshot.error!), - ) - // 데이터 없을 시 비어있음 표시 - else if (!snapshot.hasData || snapshot.data!.isEmpty) - SliverFillRemaining( - hasScrollBody: false, - child: _buildEmptyState(), - ) - // 데이터 로딩 성공 -> MealList 위젯 생성 - else - _buildMealList(snapshot.data!), // Sliver 직접 추가 - ], + ), ); }, ); From 2fdfbc21455a469135b6ce6708b02a3daa6444da Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 20:06:10 +0900 Subject: [PATCH 6/8] =?UTF-8?q?style:=20=ED=99=88=ED=99=94=EB=A9=B4=20UI?= =?UTF-8?q?=20=EB=AF=B8=EC=84=B8=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 27ec3da..7e25b9d 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -42,7 +42,7 @@ class _HomeScreenState extends State with WidgetsBindingObserver { DateTime _selectedDate = DateTime.now(); static const double _swipeTriggerDistance = 80; - static const double _maxVisualDragOffset = 110; + static const double _maxVisualDragOffset = 90; double _horizontalDragOffset = 0; bool _isHorizontalDragging = false; @@ -678,7 +678,7 @@ class _HomeScreenState extends State with WidgetsBindingObserver { ), padding: EdgeInsets.symmetric( horizontal: 12.w, - vertical: 1.h, + vertical: 3.h, ), minimumSize: Size.zero, // 최소 사이즈 제거 tapTargetSize: MaterialTapTargetSize.shrinkWrap, // 탭 영역을 최소화 From 9eea161a3a1598ef67e6a1ece8fca20d07df021d Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 20:11:45 +0900 Subject: [PATCH 7/8] =?UTF-8?q?style:=20=ED=95=99=EA=B5=90=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=ED=99=94=EB=A9=B4=20UI=20=EB=AF=B8=EC=84=B8=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/select_school_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/screens/select_school_screen.dart b/lib/screens/select_school_screen.dart index 9c2ba63..2a52562 100644 --- a/lib/screens/select_school_screen.dart +++ b/lib/screens/select_school_screen.dart @@ -107,6 +107,7 @@ class _SelectSchoolScreenState extends State { final university = univs[index]; return ListTile( + contentPadding: EdgeInsets.symmetric(horizontal: 3.w), minTileHeight: 58.h, trailing: _selectedUniv == university ? Icon(Icons.check, size: 25.h) @@ -126,8 +127,7 @@ class _SelectSchoolScreenState extends State { ); }, separatorBuilder: (context, index) => Divider( - // TODO: 두께 들쭉날쭉 관련 논의 - thickness: 2.5, + thickness: 1, color: AppColors.colorGray5, ), ), From 11669bae99d8385dc64fadeb3b126903601d81fa Mon Sep 17 00:00:00 2001 From: hwoo7449 Date: Sun, 1 Mar 2026 20:25:46 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EC=8B=9C=20=EC=B4=88=EA=B8=B0=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=ED=95=99=EA=B5=90=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/select_school_screen.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/screens/select_school_screen.dart b/lib/screens/select_school_screen.dart index 2a52562..73daa02 100644 --- a/lib/screens/select_school_screen.dart +++ b/lib/screens/select_school_screen.dart @@ -1,5 +1,6 @@ import 'package:bobmoo/models/university.dart'; import 'package:bobmoo/providers/search_provider.dart'; +import 'package:bobmoo/providers/univ_provider.dart'; import 'package:bobmoo/ui/components/buttons/primary_button.dart'; import 'package:bobmoo/ui/theme/app_colors.dart'; import 'package:bobmoo/ui/theme/app_shadow.dart'; @@ -42,6 +43,12 @@ class _SelectSchoolScreenState extends State { ); } + @override + void initState() { + super.initState(); + _selectedUniv = context.read().selectedUniversity; + } + @override Widget build(BuildContext context) { final univs = context.select((SearchProvider p) => p.filteredItems);