diff --git a/assets/images/icons/gray_line_graph_icon.svg b/assets/images/icons/gray_line_graph_icon.svg
index 77bd908..5c284c6 100644
--- a/assets/images/icons/gray_line_graph_icon.svg
+++ b/assets/images/icons/gray_line_graph_icon.svg
@@ -1,4 +1,4 @@
diff --git a/assets/images/icons/gray_line_graph_icon_dark.svg b/assets/images/icons/gray_line_graph_icon_dark.svg
new file mode 100644
index 0000000..78196d7
--- /dev/null
+++ b/assets/images/icons/gray_line_graph_icon_dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/icons/green_line_graph_icon_dark.svg b/assets/images/icons/green_line_graph_icon_dark.svg
new file mode 100644
index 0000000..2b92ce5
--- /dev/null
+++ b/assets/images/icons/green_line_graph_icon_dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 9269530..fd2bfa6 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -25,10 +25,10 @@ PODS:
- Firebase/Messaging (12.15.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 12.15.0)
- - firebase_core (4.11.0):
+ - firebase_core (4.12.1):
- Firebase/CoreOnly (= 12.15.0)
- Flutter
- - firebase_messaging (16.4.1):
+ - firebase_messaging (16.4.3):
- Firebase/Messaging (= 12.15.0)
- firebase_core
- Flutter
@@ -58,7 +58,6 @@ PODS:
- Flutter
- flutter_image_compress_common (1.0.0):
- Flutter
- - Mantle
- SDWebImage
- SDWebImageWebPCoder
- flutter_native_splash (2.4.3):
@@ -66,6 +65,9 @@ PODS:
- flutter_secure_storage_darwin (10.0.0):
- Flutter
- FlutterMacOS
+ - gal (1.0.0):
+ - Flutter
+ - FlutterMacOS
- google_sign_in_ios (0.0.1):
- Flutter
- FlutterMacOS
@@ -129,9 +131,6 @@ PODS:
- libwebp/sharpyuv (1.5.0)
- libwebp/webp (1.5.0):
- libwebp/sharpyuv
- - Mantle (2.2.0):
- - Mantle/extobjc (= 2.2.0)
- - Mantle/extobjc (2.2.0)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
@@ -139,10 +138,7 @@ PODS:
- nanopb/encode (3.30910.0)
- native_camera_sound (0.0.1):
- Flutter
- - path_provider_foundation (0.0.1):
- - Flutter
- - FlutterMacOS
- - photo_manager (3.10.0):
+ - photo_manager (3.11.0):
- Flutter
- FlutterMacOS
- PromisesObjC (2.4.1)
@@ -178,12 +174,12 @@ DEPENDENCIES:
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
+ - gal (from `.symlinks/plugins/gal/darwin`)
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
- image_gallery_saver_plus (from `.symlinks/plugins/image_gallery_saver_plus/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- kakao_flutter_sdk_common (from `.symlinks/plugins/kakao_flutter_sdk_common/ios`)
- native_camera_sound (from `.symlinks/plugins/native_camera_sound/ios`)
- - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- photo_manager (from `.symlinks/plugins/photo_manager/darwin`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
@@ -205,7 +201,6 @@ SPEC REPOS:
- GTMAppAuth
- GTMSessionFetcher
- libwebp
- - Mantle
- nanopb
- PromisesObjC
- PromisesSwift
@@ -236,6 +231,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_native_splash/ios"
flutter_secure_storage_darwin:
:path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin"
+ gal:
+ :path: ".symlinks/plugins/gal/darwin"
google_sign_in_ios:
:path: ".symlinks/plugins/google_sign_in_ios/darwin"
image_gallery_saver_plus:
@@ -246,8 +243,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/kakao_flutter_sdk_common/ios"
native_camera_sound:
:path: ".symlinks/plugins/native_camera_sound/ios"
- path_provider_foundation:
- :path: ".symlinks/plugins/path_provider_foundation/darwin"
photo_manager:
:path: ".symlinks/plugins/photo_manager/darwin"
share_plus:
@@ -265,19 +260,20 @@ SPEC CHECKSUMS:
AppCheckCore: e215d35177a9cf469927863e69c13e220df32a8b
audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5
camera_avfoundation: 5675ca25298b6f81fa0a325188e7df62cc217741
- device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
+ device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
Firebase: a8539b633d474fbeb654c7043f9c1649e274045b
- firebase_core: fc23178af8ea070194d09031ae4198a9608a3d22
- firebase_messaging: 870b2939a7f7cc52bc38ca0250127c45b42f4bad
+ firebase_core: 1c1e250a77e2df9e80bb8bf0875c8b427044a697
+ firebase_messaging: 90cf2b62f5058120ab19f29ceff1503ba0f51ba7
FirebaseCore: 2e86a4ea1684d4381707069e4a6d89ac808e901e
FirebaseCoreInternal: 6ab6a02c94446c026d2cf35cf5383842ebaa4992
FirebaseInstallations: eb29ccbf64eaedf86fd5b2ccc7fabde567660b52
FirebaseMessaging: 40017d7bc8457ee295b0f41d480a80fdabc9994e
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_appauth: d4abcf54856e5d8ba82ed7646ffc83245d4aa448
- flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
+ flutter_image_compress_common: 11d5dcfb36f4ded92320bfa896261813a073240e
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23
+ gal: baecd024ebfd13c441269ca7404792a7152fde89
google_sign_in_ios: 000870aa06da9b28d1d0bf7ef70ff0213059dd28
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleSignIn: fcee2257188d5eda57a5e2b6a715550ffff9206d
@@ -288,11 +284,9 @@ SPEC CHECKSUMS:
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
kakao_flutter_sdk_common: 682b3606698f87467788598dc2dc09d4e6867fbd
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
- Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
native_camera_sound: 6304ac5ca3b45d88b1fba6f1bd9c6da9dbf947f1
- path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
- photo_manager: 93cc83778e33d5fd49dfd7e34b73f8f5a4f10aaf
+ photo_manager: 6ab48c2ce7ec21aa06d59e6cc049f0b6d9ba7f94
PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273
PromisesSwift: 217dea0fd5d2ad65222a109c48698add13cc1c5b
RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 47ae92a..981c011 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -8,15 +8,15 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 1E1273485725C2A64084771D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2854B46F269B9E374FB19610 /* Pods_RunnerTests.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 426B7EB1D329128F59D73DBA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B3F97F5F02273ADB06E13B6 /* Pods_Runner.framework */; };
645EEB062FBCBFE300FC5FD1 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 645EEB052FBCBFE300FC5FD1 /* GoogleService-Info.plist */; };
- 6D6C320DFB58EC02A307DA65 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B7938CD66B50F327A0E94C8 /* Pods_RunnerTests.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- B4C01431C955B9FADF842796 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D41E1C008ED228BEB320CCC0 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -43,20 +43,21 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 06623951D74E8295B77CDFA6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
- 222AF339DB600A2B5E01EE0F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ 2854B46F269B9E374FB19610 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 2E68AA69DC79680F084578A6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 38DD50535F97A74EA0DE4185 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 5B0ED7BBC442AE978AE8D417 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
645EEB052FBCBFE300FC5FD1 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
- 66D0A08EDFDC6A4F81AD876C /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
- 696A10F514DF2CD03037CA9C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
- 6B7938CD66B50F327A0E94C8 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 8B3F97F5F02273ADB06E13B6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8F8E2745537E5F76E469599B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -64,9 +65,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 9CB79A0D157D1F0B25AC4A41 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
- A037740DFB6F8686FADEEDEA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
- D41E1C008ED228BEB320CCC0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ ABFEB1C4FB8F956B28267714 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ F2A895843BD3292018B207E3 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -74,7 +74,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- B4C01431C955B9FADF842796 /* Pods_Runner.framework in Frameworks */,
+ 426B7EB1D329128F59D73DBA /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -82,7 +82,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 6D6C320DFB58EC02A307DA65 /* Pods_RunnerTests.framework in Frameworks */,
+ 1E1273485725C2A64084771D /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -97,15 +97,6 @@
path = RunnerTests;
sourceTree = "";
};
- 95F3614CB6DD26EA78562BF5 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- D41E1C008ED228BEB320CCC0 /* Pods_Runner.framework */,
- 6B7938CD66B50F327A0E94C8 /* Pods_RunnerTests.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -125,7 +116,7 @@
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
F53435CEBB51953C132E7EEE /* Pods */,
- 95F3614CB6DD26EA78562BF5 /* Frameworks */,
+ C800AE767162B9A72D222CA0 /* Frameworks */,
);
sourceTree = "";
};
@@ -154,15 +145,24 @@
path = Runner;
sourceTree = "";
};
+ C800AE767162B9A72D222CA0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 8B3F97F5F02273ADB06E13B6 /* Pods_Runner.framework */,
+ 2854B46F269B9E374FB19610 /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
F53435CEBB51953C132E7EEE /* Pods */ = {
isa = PBXGroup;
children = (
- 5B0ED7BBC442AE978AE8D417 /* Pods-Runner.debug.xcconfig */,
- A037740DFB6F8686FADEEDEA /* Pods-Runner.release.xcconfig */,
- 696A10F514DF2CD03037CA9C /* Pods-Runner.profile.xcconfig */,
- 66D0A08EDFDC6A4F81AD876C /* Pods-RunnerTests.debug.xcconfig */,
- 222AF339DB600A2B5E01EE0F /* Pods-RunnerTests.release.xcconfig */,
- 9CB79A0D157D1F0B25AC4A41 /* Pods-RunnerTests.profile.xcconfig */,
+ 06623951D74E8295B77CDFA6 /* Pods-Runner.debug.xcconfig */,
+ 2E68AA69DC79680F084578A6 /* Pods-Runner.release.xcconfig */,
+ 8F8E2745537E5F76E469599B /* Pods-Runner.profile.xcconfig */,
+ ABFEB1C4FB8F956B28267714 /* Pods-RunnerTests.debug.xcconfig */,
+ F2A895843BD3292018B207E3 /* Pods-RunnerTests.release.xcconfig */,
+ 38DD50535F97A74EA0DE4185 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "";
@@ -174,7 +174,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
- 91F3200276A4E86A18DFE0E4 /* [CP] Check Pods Manifest.lock */,
+ F04B7F7A5F9BFA0D3EFFE1DA /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
EB3CC2CAF7B78D46445B9C5A /* Frameworks */,
@@ -193,15 +193,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- BABF811E74C9070047A08B2E /* [CP] Check Pods Manifest.lock */,
+ 7CB04BF6E2E10F1ECF3D924D /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 0B268321AD0C0BA397BF1DEB /* [CP] Embed Pods Frameworks */,
- 2AA5B2287AC283419474F813 /* [CP] Copy Pods Resources */,
+ 3594C6D699A409224AA82FE3 /* [CP] Embed Pods Frameworks */,
+ EDD5B35332DD8BDF7C9C3C60 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -274,7 +274,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 0B268321AD0C0BA397BF1DEB /* [CP] Embed Pods Frameworks */ = {
+ 3594C6D699A409224AA82FE3 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -291,23 +291,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 2AA5B2287AC283419474F813 /* [CP] Copy Pods Resources */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
- );
- name = "[CP] Copy Pods Resources";
- outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
- showEnvVarsInLog = 0;
- };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -324,7 +307,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- 91F3200276A4E86A18DFE0E4 /* [CP] Check Pods Manifest.lock */ = {
+ 7CB04BF6E2E10F1ECF3D924D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -339,7 +322,7 @@
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -361,7 +344,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
- BABF811E74C9070047A08B2E /* [CP] Check Pods Manifest.lock */ = {
+ EDD5B35332DD8BDF7C9C3C60 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ F04B7F7A5F9BFA0D3EFFE1DA /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -376,7 +376,7 @@
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -435,7 +435,7 @@
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 696A10F514DF2CD03037CA9C /* Pods-Runner.profile.xcconfig */;
+ baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
@@ -488,12 +488,12 @@
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 696A10F514DF2CD03037CA9C /* Pods-Runner.profile.xcconfig */;
+ baseConfigurationReference = 8F8E2745537E5F76E469599B /* Pods-Runner.profile.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 25VAHVQXN9;
+ DEVELOPMENT_TEAM = 382P985AXP;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
@@ -501,7 +501,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.nuk.haenaem;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.haenaem;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -511,7 +511,7 @@
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 66D0A08EDFDC6A4F81AD876C /* Pods-RunnerTests.debug.xcconfig */;
+ baseConfigurationReference = ABFEB1C4FB8F956B28267714 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -529,7 +529,7 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 222AF339DB600A2B5E01EE0F /* Pods-RunnerTests.release.xcconfig */;
+ baseConfigurationReference = F2A895843BD3292018B207E3 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -545,7 +545,7 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9CB79A0D157D1F0B25AC4A41 /* Pods-RunnerTests.profile.xcconfig */;
+ baseConfigurationReference = 38DD50535F97A74EA0DE4185 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -561,7 +561,7 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 5B0ED7BBC442AE978AE8D417 /* Pods-Runner.debug.xcconfig */;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
@@ -619,7 +619,7 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = A037740DFB6F8686FADEEDEA /* Pods-Runner.release.xcconfig */;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
@@ -674,12 +674,12 @@
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 5B0ED7BBC442AE978AE8D417 /* Pods-Runner.debug.xcconfig */;
+ baseConfigurationReference = 06623951D74E8295B77CDFA6 /* Pods-Runner.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 25VAHVQXN9;
+ DEVELOPMENT_TEAM = 382P985AXP;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
@@ -687,7 +687,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.nuk.haenaem;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.haenaem;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -698,12 +698,12 @@
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = A037740DFB6F8686FADEEDEA /* Pods-Runner.release.xcconfig */;
+ baseConfigurationReference = 2E68AA69DC79680F084578A6 /* Pods-Runner.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 25VAHVQXN9;
+ DEVELOPMENT_TEAM = 382P985AXP;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
@@ -711,7 +711,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.nuk.haenaem;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.haenaem;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart
index a776dc8..7917b36 100644
--- a/lib/core/theme/app_colors.dart
+++ b/lib/core/theme/app_colors.dart
@@ -3,8 +3,10 @@
import 'package:flutter/material.dart';
class AppColors {
- // Grayscale
+ static const Color white = Color(0xFFFAFAFA);
static const Color black = Color(0xFF1b1d1b);
+
+ // Grayscale
static const Color gray1 = Color(0xFF444444);
static const Color gray2 = Color(0xFF616161);
static const Color gray3 = Color(0xFF8c8c8c);
@@ -24,3 +26,129 @@ class AppColors {
static const Color notification = Color(0xffD11E1B);
static const Color blue = Color(0xff4589FF);
}
+
+class AppColorsExtension extends ThemeExtension {
+ final Color blackToWhite;
+ final Color whiteToBlack;
+ final Color gray1;
+ final Color gray2;
+ final Color gray3;
+ final Color gray4;
+ final Color gray5;
+ final Color primaryAble;
+ final Color selected;
+ final Color disable;
+ final Color success;
+ final Color warning;
+ final Color notification;
+
+ const AppColorsExtension({
+ required this.blackToWhite,
+ required this.whiteToBlack,
+ required this.gray1,
+ required this.gray2,
+ required this.gray3,
+ required this.gray4,
+ required this.gray5,
+ required this.primaryAble,
+ required this.selected,
+ required this.disable,
+ required this.success,
+ required this.warning,
+ required this.notification,
+ });
+
+ // 라이트 모드 매핑
+ factory AppColorsExtension.light() {
+ return const AppColorsExtension(
+ blackToWhite: AppColors.black,
+ whiteToBlack: AppColors.white,
+ gray1: AppColors.gray1,
+ gray2: AppColors.gray2,
+ gray3: AppColors.gray3,
+ gray4: AppColors.gray4,
+ gray5: AppColors.gray5,
+ primaryAble: AppColors.primaryAble,
+ selected: AppColors.selected,
+ disable: AppColors.disable,
+ success: AppColors.success,
+ warning: AppColors.warning,
+ notification: AppColors.notification,
+ );
+ }
+
+ // 다크 모드 매핑 (요청하신 계획안 적용)
+ factory AppColorsExtension.dark() {
+ return const AppColorsExtension(
+ blackToWhite: AppColors.white, // black -> white
+ whiteToBlack: AppColors.black, // white -> black
+ gray1: AppColors.gray4, // gray1 -> gray4
+ gray2: AppColors.gray4, // gray2 -> gray4
+ gray3: AppColors.gray3, // gray3 변경없음
+ gray4: AppColors.gray2, // gray4 -> gray2
+ gray5: Color(0xFF2A2A2A), // gray5 -> d-gray5
+ primaryAble: Color(0xFF0EBC6A), // primarygreen -> d-primarygreen
+ selected: Color(0xFF1D2C23), // selected -> d-selected
+ disable: Color(0xFF697C73), // disable -> d-disable
+ success: Color(0xFF1A3A1E), // success -> d-success
+ warning: Color(0xFF3A2522), // warning -> d-warning
+ notification: Color(0xFFEE4750), // notification -> d-notification
+ );
+ }
+
+ @override
+ ThemeExtension copyWith({
+ Color? blackToWhite,
+ Color? whiteToBlack,
+ Color? gray1,
+ Color? gray2,
+ Color? gray3,
+ Color? gray4,
+ Color? gray5,
+ Color? primaryAble,
+ Color? selected,
+ Color? disable,
+ Color? success,
+ Color? warning,
+ Color? notification,
+ }) {
+ return AppColorsExtension(
+ blackToWhite: blackToWhite ?? this.blackToWhite,
+ whiteToBlack: whiteToBlack ?? this.whiteToBlack,
+ gray1: gray1 ?? this.gray1,
+ gray2: gray2 ?? this.gray2,
+ gray3: gray3 ?? this.gray3,
+ gray4: gray4 ?? this.gray4,
+ gray5: gray5 ?? this.gray5,
+ primaryAble: primaryAble ?? this.primaryAble,
+ selected: selected ?? this.selected,
+ disable: disable ?? this.disable,
+ success: success ?? this.success,
+ warning: warning ?? this.warning,
+ notification: notification ?? this.notification,
+ );
+ }
+
+ @override
+ ThemeExtension lerp(
+ covariant ThemeExtension? other,
+ double t,
+ ) {
+ if (other is! AppColorsExtension) return this;
+ return AppColorsExtension(
+ blackToWhite: Color.lerp(blackToWhite, other.blackToWhite, t)!,
+ whiteToBlack: Color.lerp(whiteToBlack, other.whiteToBlack, t)!,
+ gray1: Color.lerp(gray1, other.gray1, t)!,
+ gray2: Color.lerp(gray2, other.gray2, t)!,
+ gray3: Color.lerp(gray3, other.gray3, t)!,
+ gray4: Color.lerp(gray4, other.gray4, t)!,
+ gray5: Color.lerp(gray5, other.gray5, t)!,
+ primaryAble: Color.lerp(primaryAble, other.primaryAble, t)!,
+ selected: Color.lerp(selected, other.selected, t)!,
+ disable: Color.lerp(disable, other.disable, t)!,
+ success: Color.lerp(success, other.success, t)!,
+ warning: Color.lerp(warning, other.warning, t)!,
+ notification: Color.lerp(notification, other.notification, t)!,
+ );
+ }
+}
diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart
index 9cb8630..248a39b 100644
--- a/lib/core/theme/app_theme.dart
+++ b/lib/core/theme/app_theme.dart
@@ -7,9 +7,12 @@ class AppTheme {
static ThemeData lightTheme = ThemeData(
useMaterial3: true,
fontFamily: 'Pretendard',
- scaffoldBackgroundColor: Colors.white,
+ scaffoldBackgroundColor: AppColors.white,
brightness: Brightness.light, // 다크모드 판별을 위해 필요
- // 스피너랑 커서 우리 앱 초록색으로 변경
+ // ThemeExtension 등록
+ extensions: [AppColorsExtension.light()],
+
+ // 스피너랑 커서 앱 초록색으로 변경
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: AppColors.primaryAble,
),
@@ -21,8 +24,8 @@ class AppTheme {
// 상단 AppBar 테마 설정
appBarTheme: const AppBarTheme(
- backgroundColor: Colors.white,
- surfaceTintColor: Colors.white, // 스크롤 시 색상 변함 방지
+ backgroundColor: AppColors.white,
+ surfaceTintColor: AppColors.white, // 스크롤 시 색상 변함 방지
elevation: 0,
//scrolledUnderElevation: 0, // 스크롤 시 그림자 생김 방지
centerTitle: true,
@@ -30,36 +33,38 @@ class AppTheme {
// 하단 NavigationBar 테마 설정
navigationBarTheme: const NavigationBarThemeData(
- backgroundColor: Colors.white,
- surfaceTintColor: Colors.white,
+ backgroundColor: AppColors.white,
+ surfaceTintColor: AppColors.white,
elevation: 0,
),
// 하단 BottomNavigationBar 테마 설정 (Legacy 방식)
// NavigationBar가 아닌 BottomNavigationBar를 쓸 경우를 대비
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
- backgroundColor: Colors.white,
+ backgroundColor: AppColors.white,
elevation: 0,
- selectedItemColor: Colors.white,
- unselectedItemColor: Colors.grey,
+ selectedItemColor: AppColors.white,
+ unselectedItemColor: AppColors.gray3,
),
);
- // 🖤 다크모드 기본 골격
- // TODO: 화면 점검하면서 색상값 다듬기
+ // 다크모드
static ThemeData darkTheme = ThemeData(
useMaterial3: true,
fontFamily: 'Pretendard',
scaffoldBackgroundColor: const Color(0xFF121212),
brightness: Brightness.dark,
+ // ThemeExtension 등록
+ extensions: [AppColorsExtension.dark()],
+
progressIndicatorTheme: const ProgressIndicatorThemeData(
- color: AppColors.primaryAble,
+ color: Color(0xFF0EBC6A), // 다크모드 primarygreen
),
textSelectionTheme: TextSelectionThemeData(
- cursorColor: AppColors.primaryAble,
- selectionHandleColor: AppColors.primaryAble,
- selectionColor: AppColors.primaryAble.withValues(alpha: 0.3),
+ cursorColor: const Color(0xFF0EBC6A),
+ selectionHandleColor: const Color(0xFF0EBC6A),
+ selectionColor: const Color(0xFF0EBC6A).withValues(alpha: 0.3),
),
appBarTheme: const AppBarTheme(
@@ -78,7 +83,7 @@ class AppTheme {
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: Color(0xFF1E1E1E),
elevation: 0,
- selectedItemColor: Colors.white,
+ selectedItemColor: AppColors.white,
unselectedItemColor: Colors.grey,
),
);
diff --git a/lib/features/auth/login/login_screen.dart b/lib/features/auth/login/login_screen.dart
index 414cf58..dfc24e5 100644
--- a/lib/features/auth/login/login_screen.dart
+++ b/lib/features/auth/login/login_screen.dart
@@ -29,6 +29,7 @@ class _LoginScreenState extends State {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
// 공통으로 사용할 네비게이션 함수
// void navigateToSignup() {
// Navigator.push(
@@ -45,7 +46,6 @@ class _LoginScreenState extends State {
}
return Scaffold(
- backgroundColor: Colors.white,
body: SafeArea(
// 스크롤 가능
child: SingleChildScrollView(
@@ -74,7 +74,9 @@ class _LoginScreenState extends State {
Text(
"오늘의 '해냄'을 위해\n지금 시작해보세요",
textAlign: TextAlign.center,
- style: AppTypography.h2.copyWith(color: Colors.black),
+ style: AppTypography.h2.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
const SizedBox(height: 120), // 중간 여백
@@ -82,9 +84,7 @@ class _LoginScreenState extends State {
Text(
'간편하게 시작하기',
textAlign: TextAlign.center,
- style: AppTypography.b2.copyWith(
- color: const Color(0xFf6A7282),
- ),
+ style: AppTypography.b2.copyWith(color: appColors.gray3),
),
const SizedBox(height: 10),
@@ -206,9 +206,7 @@ class _LoginScreenState extends State {
Text(
'계속 진행하면 서비스 이용약관 및\n개인정보 처리방침에 동의하는 것으로 간주됩니다',
textAlign: TextAlign.center,
- style: AppTypography.b2.copyWith(
- color: const Color(0xFF6A7282),
- ),
+ style: AppTypography.b2.copyWith(color: appColors.gray3),
),
const SizedBox(height: 40), // 하단 여백
],
diff --git a/lib/features/auth/signup/screens/bio_setup_screen.dart b/lib/features/auth/signup/screens/bio_setup_screen.dart
index bdadd25..7e3f196 100644
--- a/lib/features/auth/signup/screens/bio_setup_screen.dart
+++ b/lib/features/auth/signup/screens/bio_setup_screen.dart
@@ -62,6 +62,7 @@ class _BioSetupScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
// 사용자가 한 글자라도 입력했는지 여부를 판단하여 버튼 문구 결정
final String dynamicButtonText = _bioController.text.isEmpty
? '건너뛰기'
@@ -82,10 +83,10 @@ class _BioSetupScreenState extends ConsumerState {
maxLength: 50,
maxLines: null,
minLines: 1,
- style: AppTypography.b2.copyWith(color: AppColors.black),
+ style: AppTypography.b2.copyWith(color: appColors.blackToWhite),
decoration: InputDecoration(
hintText: '한 줄 소개를 입력해주세요',
- hintStyle: AppTypography.b2.copyWith(color: AppColors.gray3),
+ hintStyle: AppTypography.b2.copyWith(color: appColors.gray3),
counterText: '',
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
@@ -93,12 +94,12 @@ class _BioSetupScreenState extends ConsumerState {
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
- borderSide: const BorderSide(color: AppColors.gray4),
+ borderSide: BorderSide(color: appColors.gray4),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
- borderSide: const BorderSide(
- color: AppColors.primaryAble,
+ borderSide: BorderSide(
+ color: appColors.primaryAble,
width: 1.5,
),
),
@@ -107,7 +108,7 @@ class _BioSetupScreenState extends ConsumerState {
const SizedBox(height: 8),
Text(
'${_bioController.text.length}/50',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/auth/signup/screens/nickname_setup_screen.dart b/lib/features/auth/signup/screens/nickname_setup_screen.dart
index fd24bf1..f2ecfd7 100644
--- a/lib/features/auth/signup/screens/nickname_setup_screen.dart
+++ b/lib/features/auth/signup/screens/nickname_setup_screen.dart
@@ -97,6 +97,8 @@ class _NicknameSetupScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// 어떤 에러 메시지를 보여줄지 결정 (형식 에러가 중복 에러보다 우선순위 높음)
String? errorMessage;
if (_isInvalidFormat) {
@@ -116,10 +118,10 @@ class _NicknameSetupScreenState extends ConsumerState {
TextField(
controller: _nicknameController,
maxLength: 15,
- style: AppTypography.b2.copyWith(color: AppColors.black),
+ style: AppTypography.b2.copyWith(color: appColors.blackToWhite),
decoration: InputDecoration(
hintText: '닉네임을 입력해주세요',
- hintStyle: AppTypography.b2.copyWith(color: AppColors.gray3),
+ hintStyle: AppTypography.b2.copyWith(color: appColors.gray3),
counterText: '',
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
@@ -129,16 +131,16 @@ class _NicknameSetupScreenState extends ConsumerState {
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: (errorMessage != null)
- ? AppColors.notification
- : AppColors.gray4,
+ ? appColors.notification
+ : appColors.gray4,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: (errorMessage != null)
- ? AppColors.notification
- : AppColors.primaryAble,
+ ? appColors.notification
+ : appColors.primaryAble,
width: 1.5,
),
),
@@ -160,7 +162,7 @@ class _NicknameSetupScreenState extends ConsumerState {
Text(
'* ',
style: AppTypography.c1.copyWith(
- color: AppColors.notification,
+ color: appColors.notification,
height: 1.5,
),
),
@@ -170,7 +172,7 @@ class _NicknameSetupScreenState extends ConsumerState {
// 기존 메시지에서 '* '를 제거하고 전달
errorMessage.replaceFirst('* ', ''),
style: AppTypography.c1.copyWith(
- color: AppColors.notification,
+ color: appColors.notification,
height: 1.5,
),
),
@@ -184,7 +186,7 @@ class _NicknameSetupScreenState extends ConsumerState {
// 글자수 카운터
Text(
'${_nicknameController.text.length}/15',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/auth/signup/screens/signup_main_screen.dart b/lib/features/auth/signup/screens/signup_main_screen.dart
index d4d1909..b7a5f0b 100644
--- a/lib/features/auth/signup/screens/signup_main_screen.dart
+++ b/lib/features/auth/signup/screens/signup_main_screen.dart
@@ -70,6 +70,8 @@ class _SignupMainScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
final bool isSuccessStep = _currentStep == _totalSteps;
final bool isLoading = ref.watch(signupProvider).isLoading;
@@ -82,8 +84,8 @@ class _SignupMainScreenState extends ConsumerState {
child: Stack(
children: [
Scaffold(
- backgroundColor: isSuccessStep ? null : Colors.white,
- appBar: _buildAppBar(isSuccessStep),
+ backgroundColor: isSuccessStep ? null : appColors.whiteToBlack,
+ appBar: _buildAppBar(isSuccessStep, appColors),
body: Column(
children: [
if (!isSuccessStep) _buildProgressBar(),
@@ -106,18 +108,21 @@ class _SignupMainScreenState extends ConsumerState {
}
// 상단 앱바 구성
- PreferredSizeWidget? _buildAppBar(bool isSuccessStep) {
+ PreferredSizeWidget? _buildAppBar(
+ bool isSuccessStep,
+ AppColorsExtension appColors,
+ ) {
if (isSuccessStep) return null;
return AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
leading: IconButton(
- icon: const Icon(Icons.arrow_back, color: AppColors.black),
+ icon: Icon(Icons.arrow_back, color: appColors.blackToWhite),
onPressed: _prevPage,
),
title: Text(
'회원가입',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
);
diff --git a/lib/features/auth/signup/screens/signup_success_screen.dart b/lib/features/auth/signup/screens/signup_success_screen.dart
index fc8d8b8..1f5d9f0 100644
--- a/lib/features/auth/signup/screens/signup_success_screen.dart
+++ b/lib/features/auth/signup/screens/signup_success_screen.dart
@@ -7,6 +7,7 @@ import '../providers/signup_provider.dart';
import '../widgets/emoji_firework_widget.dart';
import 'package:haenaem/features/auth/signup/widgets/signup_progress.bar.dart';
import 'package:haenaem/features/main/screens/main_screen.dart';
+import 'package:haenaem/core/theme/app_colors.dart';
// 회원가입 4단계를 모두 끝낸 후 띄우는 가입 성공 화면
class SignupSuccessScreen extends ConsumerStatefulWidget {
@@ -77,7 +78,7 @@ class _SignupSuccessScreenState extends ConsumerState {
alignment: Alignment.center,
child: Text(
'회원가입',
- style: AppTypography.h3.copyWith(color: Colors.white),
+ style: AppTypography.h3.copyWith(color: AppColors.white),
),
),
// 진행률 바 배치
@@ -86,8 +87,8 @@ class _SignupSuccessScreenState extends ConsumerState {
child: SignupProgressBar(
currentStep: 5,
totalSteps: 5,
- activeColor: Colors.white,
- inactiveColor: Colors.white,
+ activeColor: AppColors.white,
+ inactiveColor: AppColors.white,
),
),
@@ -106,14 +107,14 @@ class _SignupSuccessScreenState extends ConsumerState {
Text(
'가입을 해냈어요! 🎉\n당신의 첫 번째 성취를 축하합니다.',
style: AppTypography.h2.copyWith(
- color: Colors.white,
+ color: AppColors.white,
),
),
const SizedBox(height: 10),
Text(
'멋진 챌린지들이 기다리고 있어요.\n함께 해낼 준비 됐나요?',
style: AppTypography.h3.copyWith(
- color: Colors.white,
+ color: AppColors.white,
),
),
],
@@ -145,7 +146,9 @@ class _SignupSuccessScreenState extends ConsumerState {
child: Text(
'시작하기',
- style: AppTypography.h3.copyWith(color: Colors.white),
+ style: AppTypography.h3.copyWith(
+ color: AppColors.white,
+ ),
),
),
),
diff --git a/lib/features/auth/signup/screens/tag_screen.dart b/lib/features/auth/signup/screens/tag_screen.dart
index b2563e1..440420a 100644
--- a/lib/features/auth/signup/screens/tag_screen.dart
+++ b/lib/features/auth/signup/screens/tag_screen.dart
@@ -42,6 +42,8 @@ class _TagScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
final signupState = ref.watch(signupProvider);
final categorizedTags = signupState.categorizedTags;
final selectedTags = signupState.tags;
@@ -91,7 +93,7 @@ class _TagScreenState extends ConsumerState {
Text(
TagMapper.getKoreanCategory(categoryKey),
style: AppTypography.b2.copyWith(
- color: AppColors.black,
+ color: appColors.blackToWhite,
),
),
const SizedBox(height: 12),
diff --git a/lib/features/auth/signup/widgets/next_button.dart b/lib/features/auth/signup/widgets/next_button.dart
index 29b5c25..23d2573 100644
--- a/lib/features/auth/signup/widgets/next_button.dart
+++ b/lib/features/auth/signup/widgets/next_button.dart
@@ -18,8 +18,10 @@ class NextButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Material(
- color: isEnabled ? AppColors.primaryAble : AppColors.disable,
+ color: isEnabled ? appColors.primaryAble : appColors.disable,
borderRadius: BorderRadius.circular(10),
child: InkWell(
onTap: isEnabled ? onPressed : null,
@@ -30,7 +32,7 @@ class NextButton extends StatelessWidget {
alignment: Alignment.center,
child: Text(
text,
- style: AppTypography.h3.copyWith(color: Colors.white),
+ style: AppTypography.h3.copyWith(color: appColors.whiteToBlack),
),
),
),
diff --git a/lib/features/auth/signup/widgets/signup_page_layout.dart b/lib/features/auth/signup/widgets/signup_page_layout.dart
index 3722a1b..14553a1 100644
--- a/lib/features/auth/signup/widgets/signup_page_layout.dart
+++ b/lib/features/auth/signup/widgets/signup_page_layout.dart
@@ -26,6 +26,8 @@ class SignupPageLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Stack(
children: [
// 콘텐츠 영역 (스크롤 가능)
@@ -39,12 +41,16 @@ class SignupPageLayout extends StatelessWidget {
// 타이틀 + 서브타이틀
Text(
title,
- style: AppTypography.h2.copyWith(color: AppColors.black),
+ style: AppTypography.h2.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
const SizedBox(height: 10),
Text(
subTitle,
- style: AppTypography.b1.copyWith(color: AppColors.black),
+ style: AppTypography.b1.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
const SizedBox(height: 42),
child, // 단계별 본문 콘텐츠
diff --git a/lib/features/auth/signup/widgets/signup_progress.bar.dart b/lib/features/auth/signup/widgets/signup_progress.bar.dart
index 81d1069..b88e5d4 100644
--- a/lib/features/auth/signup/widgets/signup_progress.bar.dart
+++ b/lib/features/auth/signup/widgets/signup_progress.bar.dart
@@ -19,6 +19,8 @@ class SignupProgressBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Row(
children: List.generate(totalSteps, (index) {
final bool isCompleted = index < currentStep;
@@ -29,8 +31,8 @@ class SignupProgressBar extends StatelessWidget {
decoration: BoxDecoration(
// 지정된 색상이 없으면 기본 테마 색상 사용
color: isCompleted
- ? (activeColor ?? AppColors.primaryAble)
- : (inactiveColor ?? AppColors.gray4),
+ ? (activeColor ?? appColors.primaryAble)
+ : (inactiveColor ?? appColors.gray4),
borderRadius: BorderRadius.circular(1000),
),
),
diff --git a/lib/features/challenge/create/screens/challenge_create_screen.dart b/lib/features/challenge/create/screens/challenge_create_screen.dart
index fad21f7..c313fd3 100644
--- a/lib/features/challenge/create/screens/challenge_create_screen.dart
+++ b/lib/features/challenge/create/screens/challenge_create_screen.dart
@@ -325,8 +325,10 @@ class _ChallengeCreateScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
// 뒤로 가기 버튼
leading: IconButton(
@@ -335,17 +337,21 @@ class _ChallengeCreateScreenState extends ConsumerState {
'assets/images/icons/arrow_left.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
),
title: Text(
"챌린지 만들기",
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
elevation: 0,
scrolledUnderElevation: 0,
- surfaceTintColor: Colors.white,
- backgroundColor: Colors.white,
+ surfaceTintColor: appColors.whiteToBlack,
+ backgroundColor: appColors.whiteToBlack,
),
// 입력 폼
@@ -374,8 +380,8 @@ class _ChallengeCreateScreenState extends ConsumerState {
hintText: _dateHintText,
// 선택된 날짜가 없으면 gray3, 날짜가 선택되면 black으로 설정
textColor: _selectedDay == null
- ? AppColors.gray3
- : AppColors.black,
+ ? appColors.gray3
+ : appColors.blackToWhite,
iconPath: 'assets/images/icons/big_down_arrow.svg',
onTap: _showCalendarBottomSheet,
),
@@ -386,8 +392,8 @@ class _ChallengeCreateScreenState extends ConsumerState {
ChallengeInputBox(
hintText: _selectedDuration ?? '인증 기간을 선택하세요',
textColor: _selectedDuration == null
- ? AppColors.gray3
- : AppColors.black,
+ ? appColors.gray3
+ : appColors.blackToWhite,
iconPath: 'assets/images/icons/big_down_arrow.svg',
onTap: showDurationBottomSheet,
),
@@ -398,8 +404,8 @@ class _ChallengeCreateScreenState extends ConsumerState {
ChallengeInputBox(
hintText: _selectedFrequency ?? '인증 빈도를 선택하세요',
textColor: _selectedFrequency == null
- ? AppColors.gray3
- : AppColors.black,
+ ? appColors.gray3
+ : appColors.blackToWhite,
iconPath: 'assets/images/icons/big_down_arrow.svg',
onTap: showFrequencyBottomSheet,
),
diff --git a/lib/features/challenge/create/widgets/ai_notice_box.dart b/lib/features/challenge/create/widgets/ai_notice_box.dart
index 75ea836..7744c9a 100644
--- a/lib/features/challenge/create/widgets/ai_notice_box.dart
+++ b/lib/features/challenge/create/widgets/ai_notice_box.dart
@@ -13,13 +13,15 @@ class AiNoticeBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(8),
- border: Border.all(color: AppColors.gray4, width: 0.75),
+ border: Border.all(color: appColors.gray4, width: 0.75),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // 아이콘이 문구 상단에 맞게 정렬
@@ -28,10 +30,7 @@ class AiNoticeBox extends StatelessWidget {
'assets/images/icons/ai_notice.svg',
width: 20,
height: 20,
- colorFilter: const ColorFilter.mode(
- AppColors.gray1,
- BlendMode.srcIn,
- ),
+ colorFilter: ColorFilter.mode(appColors.gray1, BlendMode.srcIn),
),
const SizedBox(width: 5),
Expanded(
@@ -40,14 +39,14 @@ class AiNoticeBox extends StatelessWidget {
children: [
Text(
'정확한 인증을 위해 AI 검증 단계를 거치게 됩니다.\n환경에 따라 인식이 지연되거나 재촬영이 필요할 수 있습니다.',
- style: AppTypography.c1.copyWith(color: AppColors.gray1),
+ style: AppTypography.c1.copyWith(color: appColors.gray1),
),
if (autoVerifiable == false) ...[
const SizedBox(height: 4),
Text(
'현재 입력한 챌린지 이름은 AI가 자동으로 판별하기 어려운 주제예요. 인증 시 지연되거나 재촬영이 필요할 가능성이 높아요.',
style: AppTypography.c1.copyWith(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
),
),
],
diff --git a/lib/features/challenge/create/widgets/challenge_calendar_bottom_sheet.dart b/lib/features/challenge/create/widgets/challenge_calendar_bottom_sheet.dart
index c7dfb2b..ce2e149 100644
--- a/lib/features/challenge/create/widgets/challenge_calendar_bottom_sheet.dart
+++ b/lib/features/challenge/create/widgets/challenge_calendar_bottom_sheet.dart
@@ -39,6 +39,8 @@ class _ChallengeCalendarBottomSheetState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
bool isFirstMonth =
_focusedDay.year == _today.year && _focusedDay.month == _today.month;
@@ -64,7 +66,7 @@ class _ChallengeCalendarBottomSheetState
width: 44.62,
height: 44.62,
decoration: ShapeDecoration(
- color: const Color(0xFFE8F5E9), // 해냄-green-selected
+ color: appColors.selected,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
@@ -73,7 +75,7 @@ class _ChallengeCalendarBottomSheetState
child: Text(
'${date.day}',
style: AppTypography.b1.copyWith(
- color: const Color(0xFF009951), // 해냄-green-primary-able
+ color: appColors.primaryAble, // 해냄-green-primary-able
fontWeight: FontWeight.bold,
),
),
@@ -89,7 +91,9 @@ class _ChallengeCalendarBottomSheetState
child: Center(
child: Text(
'${date.day}',
- style: AppTypography.b1.copyWith(color: AppColors.black),
+ style: AppTypography.b1.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
),
),
@@ -102,14 +106,16 @@ class _ChallengeCalendarBottomSheetState
headerPadding: const EdgeInsets.only(top: 0, bottom: 5),
formatButtonVisible: false,
titleCentered: true,
- titleTextStyle: AppTypography.h3.copyWith(color: AppColors.black),
+ titleTextStyle: AppTypography.h3.copyWith(
+ color: appColors.blackToWhite,
+ ),
leftChevronIcon: _buildChevron(
'assets/images/icons/tab_previous.svg',
- isFirstMonth ? AppColors.gray3 : AppColors.black,
+ isFirstMonth ? appColors.gray3 : appColors.blackToWhite,
),
rightChevronIcon: _buildChevron(
'assets/images/icons/tab_next.svg',
- AppColors.black,
+ appColors.blackToWhite,
),
leftChevronMargin: const EdgeInsets.only(left: 50),
rightChevronMargin: const EdgeInsets.only(right: 50),
@@ -135,7 +141,7 @@ class _ChallengeCalendarBottomSheetState
),
disabledTextStyle: AppTypography.b1.copyWith(
- color: AppColors.gray3,
+ color: appColors.gray3,
),
// 오늘 날짜 디자인
@@ -144,22 +150,28 @@ class _ChallengeCalendarBottomSheetState
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(8),
),
- todayTextStyle: AppTypography.b1.copyWith(color: AppColors.black),
+ todayTextStyle: AppTypography.b1.copyWith(
+ color: appColors.blackToWhite,
+ ),
// 선택된 날짜 디자인
selectedDecoration: BoxDecoration(
- color: AppColors.selected,
+ color: appColors.selected,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(8),
),
selectedTextStyle: AppTypography.b1.copyWith(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
fontWeight: FontWeight.bold,
),
- defaultTextStyle: AppTypography.b1.copyWith(color: AppColors.black),
- weekendTextStyle: AppTypography.b1.copyWith(color: AppColors.black),
- outsideTextStyle: AppTypography.b1.copyWith(color: AppColors.gray3),
+ defaultTextStyle: AppTypography.b1.copyWith(
+ color: appColors.blackToWhite,
+ ),
+ weekendTextStyle: AppTypography.b1.copyWith(
+ color: appColors.blackToWhite,
+ ),
+ outsideTextStyle: AppTypography.b1.copyWith(color: appColors.gray3),
),
// 과거 날짜 선택 차단
diff --git a/lib/features/challenge/create/widgets/challenge_duration_bottom_sheet.dart b/lib/features/challenge/create/widgets/challenge_duration_bottom_sheet.dart
index 168c26d..9e0745d 100644
--- a/lib/features/challenge/create/widgets/challenge_duration_bottom_sheet.dart
+++ b/lib/features/challenge/create/widgets/challenge_duration_bottom_sheet.dart
@@ -64,6 +64,8 @@ class _ChallengeDurationBottomSheetState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return CustomBottomSheet(
title: "인증 기간 선택",
heightFactor: isCustomMode ? 0.80 : 0.55,
@@ -73,6 +75,7 @@ class _ChallengeDurationBottomSheetState
// 1. 기본 기간 리스트
...["7일", "30일", "50일", "100일", "365일"].map((item) {
return _buildListItem(
+ appColors,
label: item,
isSelected: _tempSelectedDuration == item && !isCustomMode,
onTap: () {
@@ -86,15 +89,16 @@ class _ChallengeDurationBottomSheetState
// 2. 직접 입력 섹션
Container(
width: double.infinity,
- color: isCustomMode ? AppColors.selected : Colors.transparent,
+ color: isCustomMode ? appColors.selected : Colors.transparent,
child: Column(
children: [
_buildListItem(
+ appColors,
label: "직접 입력",
isSelected: isCustomMode,
onTap: () => setState(() => isCustomMode = true),
),
- if (isCustomMode) _buildCustomInputForm(),
+ if (isCustomMode) _buildCustomInputForm(appColors),
],
),
),
@@ -105,7 +109,7 @@ class _ChallengeDurationBottomSheetState
}
// 직접 입력 폼 위젯
- Widget _buildCustomInputForm() {
+ Widget _buildCustomInputForm(AppColorsExtension appColors) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 23),
child: Column(
@@ -114,20 +118,20 @@ class _ChallengeDurationBottomSheetState
Text(
'• 1일 이상의 기간을 입력해주세요\n• 1~365일까지 설정할 수 있습니다.',
style: AppTypography.b2.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
height: 1.5,
),
),
const SizedBox(height: 24),
Text(
'인증 기간 (일)',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
const SizedBox(height: 8),
Container(
- decoration: const BoxDecoration(
+ decoration: BoxDecoration(
border: Border(
- bottom: BorderSide(color: AppColors.gray3, width: 0.75),
+ bottom: BorderSide(color: appColors.gray3, width: 0.75),
),
),
child: Row(
@@ -141,7 +145,7 @@ class _ChallengeDurationBottomSheetState
decoration: InputDecoration(
hintText: "숫자를 입력하세요",
hintStyle: AppTypography.b1.copyWith(
- color: AppColors.gray3,
+ color: appColors.gray3,
),
border: InputBorder.none,
isDense: true,
@@ -150,7 +154,7 @@ class _ChallengeDurationBottomSheetState
),
Text(
'일',
- style: AppTypography.b1.copyWith(color: AppColors.gray1),
+ style: AppTypography.b1.copyWith(color: appColors.gray1),
),
],
),
@@ -169,13 +173,13 @@ class _ChallengeDurationBottomSheetState
alignment: Alignment.center,
decoration: BoxDecoration(
color: isButtonEnabled
- ? AppColors.primaryAble
- : AppColors.disable,
+ ? appColors.primaryAble
+ : appColors.disable,
borderRadius: BorderRadius.circular(10),
),
child: Text(
'완료',
- style: AppTypography.b1.copyWith(color: Colors.white),
+ style: AppTypography.b1.copyWith(color: appColors.whiteToBlack),
),
),
),
@@ -186,7 +190,8 @@ class _ChallengeDurationBottomSheetState
}
// 내부 리스트 아이템 빌더
- Widget _buildListItem({
+ Widget _buildListItem(
+ AppColorsExtension appColors, {
required String label,
required bool isSelected,
required VoidCallback onTap,
@@ -196,12 +201,12 @@ class _ChallengeDurationBottomSheetState
child: Container(
width: double.infinity,
height: 54,
- color: isSelected ? AppColors.selected : Colors.transparent,
+ color: isSelected ? appColors.selected : Colors.transparent,
alignment: Alignment.center,
child: Text(
label,
style: AppTypography.b1.copyWith(
- color: isSelected ? AppColors.primaryAble : AppColors.black,
+ color: isSelected ? appColors.primaryAble : appColors.blackToWhite,
),
),
),
diff --git a/lib/features/challenge/create/widgets/challenge_frequency_bottom_sheet.dart b/lib/features/challenge/create/widgets/challenge_frequency_bottom_sheet.dart
index 1bc29c9..794c66b 100644
--- a/lib/features/challenge/create/widgets/challenge_frequency_bottom_sheet.dart
+++ b/lib/features/challenge/create/widgets/challenge_frequency_bottom_sheet.dart
@@ -18,6 +18,8 @@ class ChallengeFrequencyBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// 선택지 리스트 정의
final items = ["매일", "주 1회", "주 2회", "주 3회", "주 4회", "주 5회", "주 6회"];
@@ -40,13 +42,15 @@ class ChallengeFrequencyBottomSheet extends StatelessWidget {
width: double.infinity,
height: 54,
decoration: BoxDecoration(
- color: isSelected ? AppColors.selected : Colors.transparent,
+ color: isSelected ? appColors.selected : Colors.transparent,
),
alignment: Alignment.center,
child: Text(
item,
style: AppTypography.b1.copyWith(
- color: isSelected ? AppColors.primaryAble : AppColors.black,
+ color: isSelected
+ ? appColors.primaryAble
+ : appColors.blackToWhite,
),
),
),
diff --git a/lib/features/challenge/create/widgets/challenge_select_button.dart b/lib/features/challenge/create/widgets/challenge_select_button.dart
index 2597dd5..971837c 100644
--- a/lib/features/challenge/create/widgets/challenge_select_button.dart
+++ b/lib/features/challenge/create/widgets/challenge_select_button.dart
@@ -19,6 +19,8 @@ class ChallengeSelectButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Expanded(
child: GestureDetector(
onTap: onTap,
@@ -26,11 +28,11 @@ class ChallengeSelectButton extends StatelessWidget {
height: 48,
decoration: BoxDecoration(
// 활성화 시 selected 색상, 비활성 시 gray5
- color: isSelected ? AppColors.selected : AppColors.gray5,
+ color: isSelected ? appColors.selected : appColors.gray5,
borderRadius: BorderRadius.circular(8),
// 활성화 시에만 primaryAble 외곽선 추가
border: isSelected
- ? Border.all(color: AppColors.primaryAble, width: 2)
+ ? Border.all(color: appColors.primaryAble, width: 2)
: null,
),
alignment: Alignment.center,
@@ -39,7 +41,9 @@ class ChallengeSelectButton extends StatelessWidget {
style: AppTypography.b2.copyWith(
// 활성화 시 Bold + primaryAble 색상
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
- color: isSelected ? AppColors.primaryAble : AppColors.black,
+ color: isSelected
+ ? appColors.primaryAble
+ : appColors.blackToWhite,
),
),
),
diff --git a/lib/features/challenge/create/widgets/challenge_tag_bottom_sheet.dart b/lib/features/challenge/create/widgets/challenge_tag_bottom_sheet.dart
index 2697dcf..1c78e1d 100644
--- a/lib/features/challenge/create/widgets/challenge_tag_bottom_sheet.dart
+++ b/lib/features/challenge/create/widgets/challenge_tag_bottom_sheet.dart
@@ -64,6 +64,8 @@ class _ChallengeTagBottomSheetState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
final tagsAsync = ref.watch(allTagsProvider);
// 💡 화면 전체 높이의 95% 계산
@@ -72,8 +74,8 @@ class _ChallengeTagBottomSheetState
return Container(
height: targetHeight, // 높이를 95%로 고정
- decoration: const BoxDecoration(
- color: Colors.white,
+ decoration: BoxDecoration(
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
@@ -117,17 +119,14 @@ class _ChallengeTagBottomSheetState
Text(
'챌린지 태그 선택',
style: AppTypography.h3.copyWith(
- color: const Color(0xFF1A1D1B),
+ color: appColors.blackToWhite,
),
),
IconButton(
onPressed: () => Navigator.pop(context),
- icon: const Text(
+ icon: Text(
'×',
- style: TextStyle(
- fontSize: 28,
- color: Color(0xFF616161),
- ),
+ style: TextStyle(fontSize: 28, color: appColors.gray2),
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
@@ -137,13 +136,13 @@ class _ChallengeTagBottomSheetState
),
// 2. 서브 타이틀
- const Padding(
+ Padding(
padding: EdgeInsets.symmetric(vertical: 2),
child: Text(
'태그를 통해 관심 분야를 알려주세요\n1~2개를 골라주세요',
textAlign: TextAlign.center,
style: TextStyle(
- color: Color(0xFF1A1D1B),
+ color: appColors.blackToWhite,
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1.5,
@@ -184,7 +183,7 @@ class _ChallengeTagBottomSheetState
Text(
TagMapper.getKoreanCategory(categoryKey),
style: AppTypography.b2.copyWith(
- color: AppColors.black,
+ color: appColors.blackToWhite,
),
),
const SizedBox(height: 10),
diff --git a/lib/features/challenge/create/widgets/challenge_visibility_selector.dart b/lib/features/challenge/create/widgets/challenge_visibility_selector.dart
index e8e4c10..9221ac7 100644
--- a/lib/features/challenge/create/widgets/challenge_visibility_selector.dart
+++ b/lib/features/challenge/create/widgets/challenge_visibility_selector.dart
@@ -18,6 +18,8 @@ class ChallengeVisibilitySelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -46,7 +48,7 @@ class ChallengeVisibilitySelector extends StatelessWidget {
const SizedBox(height: 4),
Text(
'비공개, 친구 공개 시 챌린지 검색에서 제외됩니다.',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
);
diff --git a/lib/features/challenge/detail/screens/challenge_main_screen.dart b/lib/features/challenge/detail/screens/challenge_main_screen.dart
index 514628c..b61faab 100644
--- a/lib/features/challenge/detail/screens/challenge_main_screen.dart
+++ b/lib/features/challenge/detail/screens/challenge_main_screen.dart
@@ -81,14 +81,16 @@ class _ChallengeDetailScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
final detailAsync = ref.watch(
challengeDetailProvider(challengeId: widget.challengeId),
);
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
scrolledUnderElevation: 0,
leading: IconButton(
@@ -96,6 +98,10 @@ class _ChallengeDetailScreenState extends ConsumerState {
icon: SvgPicture.asset(
'assets/images/icons/arrow_left.svg',
width: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
),
title: Text(widget.challengeTitle ?? "챌린지 상세", style: AppTypography.h3),
@@ -144,11 +150,11 @@ class _ChallengeDetailScreenState extends ConsumerState {
),
],
),
- bottomNavigationBar: _buildBottomButton(),
+ bottomNavigationBar: _buildBottomButton(appColors),
);
}
- Widget _buildBottomButton() {
+ Widget _buildBottomButton(AppColorsExtension appColors) {
final myInfo = ref.watch(currentUserProvider);
final detailAsync = ref.watch(
challengeDetailProvider(challengeId: widget.challengeId),
@@ -181,10 +187,10 @@ class _ChallengeDetailScreenState extends ConsumerState {
return BottomActionButton(
text: isMemberTab ? '내 순위 확인하기' : (isDisabled ? '인증 완료!' : '인증하기'),
backgroundColor: isDisabled
- ? AppColors.disable
- : (isMemberTab ? Colors.white : AppColors.primaryAble),
- textColor: isMemberTab ? AppColors.primaryAble : Colors.white,
- borderColor: isMemberTab ? AppColors.primaryAble : null,
+ ? appColors.disable
+ : (isMemberTab ? appColors.whiteToBlack : appColors.primaryAble),
+ textColor: isMemberTab ? appColors.primaryAble : appColors.whiteToBlack,
+ borderColor: isMemberTab ? appColors.primaryAble : null,
onPressed: isDisabled
? null // 비활성화 시 탭 자체를 막음 (토스트 제거)
: () {
diff --git a/lib/features/challenge/detail/screens/member_ranking_screen.dart b/lib/features/challenge/detail/screens/member_ranking_screen.dart
index 8947eee..e2d4853 100644
--- a/lib/features/challenge/detail/screens/member_ranking_screen.dart
+++ b/lib/features/challenge/detail/screens/member_ranking_screen.dart
@@ -13,23 +13,31 @@ class MemberRankingScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
// 1. 프로바이더 구독 (API 호출 및 상태 관리)
final rankingAsync = ref.watch(
challengeRankingNotifierProvider(challengeId),
);
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
leading: IconButton(
- icon: SvgPicture.asset('assets/images/icons/arrow_left.svg'),
+ icon: SvgPicture.asset(
+ 'assets/images/icons/arrow_left.svg',
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
+ ),
onPressed: () => Navigator.pop(context),
),
title: Text(
'멤버 순위',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
),
@@ -49,20 +57,23 @@ class MemberRankingScreen extends ConsumerWidget {
child: Stack(
children: [
ListView(), // RefreshIndicator를 동작하게 하기 위한 빈 리스트뷰
- const Center(
+ Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.leaderboard_outlined,
size: 64,
- color: Colors.grey,
+ color: appColors.gray2,
),
SizedBox(height: 16),
Text(
'아직 랭킹 데이터가 없어요!\n첫 번째로 인증을 완료해 보세요.',
textAlign: TextAlign.center,
- style: TextStyle(color: Colors.grey, fontSize: 16),
+ style: TextStyle(
+ color: appColors.gray2,
+ fontSize: 16,
+ ),
),
],
),
@@ -87,6 +98,7 @@ class MemberRankingScreen extends ConsumerWidget {
delegate: SliverChildBuilderDelegate((context, index) {
final user = rankingData.topRankings[index];
return _buildRankingItem(
+ appColors,
rank: user.rank,
userName: user.nickname,
profileImageUrl: user.profileImageUrl,
@@ -107,7 +119,8 @@ class MemberRankingScreen extends ConsumerWidget {
}
// 랭킹 아이템 리스트 위젯
- Widget _buildRankingItem({
+ Widget _buildRankingItem(
+ AppColorsExtension appColors, {
required int rank,
required String userName,
required String? profileImageUrl,
@@ -123,10 +136,10 @@ class MemberRankingScreen extends ConsumerWidget {
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
- color: isMe ? AppColors.selected : Colors.white,
+ color: isMe ? appColors.selected : appColors.whiteToBlack,
borderRadius: BorderRadius.circular(20),
border: Border.all(
- color: isMe ? AppColors.primaryAble : AppColors.gray4,
+ color: isMe ? appColors.primaryAble : appColors.gray4,
width: 1,
),
boxShadow: [
@@ -139,16 +152,16 @@ class MemberRankingScreen extends ConsumerWidget {
),
child: Row(
children: [
- SizedBox(width: 28, child: _buildRankBadge(rank)),
+ SizedBox(width: 28, child: _buildRankBadge(rank, appColors)),
const SizedBox(width: 13),
- _buildProfileImage(profileImageUrl, isMe: isMe),
+ _buildProfileImage(profileImageUrl, appColors, isMe: isMe),
const SizedBox(width: 12),
Expanded(
child: Text(
userName,
style: TextStyle(
fontSize: 15,
- color: isMe ? AppColors.primaryAble : Colors.black,
+ color: isMe ? appColors.primaryAble : appColors.blackToWhite,
),
),
),
@@ -156,13 +169,15 @@ class MemberRankingScreen extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
_buildStatInfo(
+ appColors,
svgAsset: 'assets/images/icons/mini_success_icon.svg',
value: totalCount,
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
isActive: totalCount > 0,
),
const SizedBox(height: 6),
_buildStatInfo(
+ appColors,
svgAsset: 'assets/images/icons/small_fire_icon.svg',
value: streakCount,
color: AppColors.fire,
@@ -176,7 +191,11 @@ class MemberRankingScreen extends ConsumerWidget {
}
// 헬퍼 메서드들
- Widget _buildProfileImage(String? url, {required bool isMe}) {
+ Widget _buildProfileImage(
+ String? url,
+ AppColorsExtension appColors, {
+ required bool isMe,
+ }) {
// isMe 여부에 따라 사이즈 결정
final double size = isMe ? 50 : 40;
@@ -188,7 +207,7 @@ class MemberRankingScreen extends ConsumerWidget {
color: Colors.grey[200],
// 내 순위일 때만 테두리 적용
border: Border.all(
- color: isMe ? AppColors.disable : Colors.transparent,
+ color: isMe ? appColors.disable : Colors.transparent,
width: isMe ? 1 : 0,
),
),
@@ -228,13 +247,13 @@ class MemberRankingScreen extends ConsumerWidget {
);
}
- Widget _buildRankBadge(int rank) {
+ Widget _buildRankBadge(int rank, AppColorsExtension appColors) {
if (rank <= 3)
return SvgPicture.asset(_getMedalSvg(rank), width: 30, height: 30);
return Text(
rank.toString(),
- style: AppTypography.b3.copyWith(color: AppColors.gray2),
+ style: AppTypography.b3.copyWith(color: appColors.gray2),
textAlign: TextAlign.center,
);
}
@@ -252,7 +271,8 @@ class MemberRankingScreen extends ConsumerWidget {
}
}
- Widget _buildStatInfo({
+ Widget _buildStatInfo(
+ AppColorsExtension appColors, {
required String svgAsset,
required int value,
required Color color,
@@ -266,7 +286,7 @@ class MemberRankingScreen extends ConsumerWidget {
width: 20,
height: 20,
colorFilter: ColorFilter.mode(
- isActive ? color : AppColors.gray4,
+ isActive ? color : appColors.gray4,
BlendMode.srcIn,
),
),
@@ -276,7 +296,7 @@ class MemberRankingScreen extends ConsumerWidget {
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
- color: isActive ? color : AppColors.gray4,
+ color: isActive ? color : appColors.gray4,
),
),
],
diff --git a/lib/features/challenge/detail/views/calendar_view.dart b/lib/features/challenge/detail/views/calendar_view.dart
index 902c8d9..7ee780d 100644
--- a/lib/features/challenge/detail/views/calendar_view.dart
+++ b/lib/features/challenge/detail/views/calendar_view.dart
@@ -77,6 +77,7 @@ class _CalendarViewState extends ConsumerState {
// 1. 공유 버튼 클릭 이벤트 핸들러
Future _handleShare() async {
+ final appColors = Theme.of(context).extension()!;
// 로딩 중 및 안내 문구 표시 다이얼로그
showDialog(
context: context,
@@ -87,21 +88,21 @@ class _CalendarViewState extends ConsumerState {
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
- decoration: const BoxDecoration(
- color: Colors.white,
+ decoration: BoxDecoration(
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.all(Radius.circular(16)),
),
- child: const Column(
+ child: Column(
mainAxisSize: MainAxisSize.min,
children: [
- CircularProgressIndicator(color: AppColors.black),
+ CircularProgressIndicator(color: appColors.primaryAble),
SizedBox(height: 16),
Text(
'이미지를 생성하고 있습니다...\n(약 5~10초 정도 소요될 수 있습니다)',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
- color: AppColors.black,
+ color: appColors.blackToWhite,
height: 1.4,
),
),
@@ -155,6 +156,8 @@ class _CalendarViewState extends ConsumerState {
// 모달 위젯을 띄우는 함수
void _showSharePreviewModal(String imageUrl) {
+ final appColors = Theme.of(context).extension()!;
+
showDialog(
context: context,
barrierColor: Colors.black.withOpacity(0.75), // 어두운 백그라운드
@@ -231,6 +234,7 @@ class _CalendarViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
// 1. 데이터 구독
final statsAsync = ref.watch(challengeStatsProvider(widget.challengeId));
@@ -244,20 +248,20 @@ class _CalendarViewState extends ConsumerState {
// 2. 통합 로딩/에러/데이터 처리
return statsAsync.when(
- loading: () => const Scaffold(
- backgroundColor: Colors.white,
+ loading: () => Scaffold(
+ backgroundColor: appColors.whiteToBlack,
body: Center(child: CircularProgressIndicator()),
),
error: (e, s) => Scaffold(body: Center(child: Text('통계 로드 실패: $e'))),
data: (stats) => postsAsync.when(
- loading: () => const Scaffold(
- backgroundColor: Colors.white,
+ loading: () => Scaffold(
+ backgroundColor: appColors.whiteToBlack,
body: Center(child: CircularProgressIndicator()),
),
error: (e, s) => Scaffold(body: Center(child: Text('인증글 로드 실패: $e'))),
data: (posts) {
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
// [추가] 당겨서 새로고침 위젯
body: RefreshIndicator(
onRefresh: () async {
@@ -279,11 +283,12 @@ class _CalendarViewState extends ConsumerState {
_buildStatCards(
stats.totalSuccessDays,
stats.currentStreakDays,
+ appColors,
),
const SizedBox(height: 20),
- _buildCalendarHeader(_focusedDay),
+ _buildCalendarHeader(_focusedDay, appColors),
const SizedBox(height: 10),
- _buildWeekdayHeader(),
+ _buildWeekdayHeader(appColors),
const SizedBox(height: 10),
SizedBox(
@@ -339,10 +344,10 @@ class _CalendarViewState extends ConsumerState {
),
const SizedBox(height: 20),
- _buildPostsHeaderForData(posts.length),
+ _buildPostsHeaderForData(posts.length, appColors),
const SizedBox(height: 16),
posts.isEmpty
- ? _buildEmptyState()
+ ? _buildEmptyState(appColors)
: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
@@ -360,17 +365,25 @@ class _CalendarViewState extends ConsumerState {
);
}
- Widget _buildStatCards(int totalSuccessDays, int currentStreakDays) {
+ Widget _buildStatCards(
+ int totalSuccessDays,
+ int currentStreakDays,
+ AppColorsExtension appColors,
+ ) {
return Row(
children: [
- _buildStatCard(totalSuccessDays.toString(), '완료 일수'),
+ _buildStatCard(totalSuccessDays.toString(), '완료 일수', appColors),
const SizedBox(width: 12),
- _buildStatCard(currentStreakDays.toString(), '연속 일수'),
+ _buildStatCard(currentStreakDays.toString(), '연속 일수', appColors),
],
);
}
- Widget _buildStatCard(String value, String label) {
+ Widget _buildStatCard(
+ String value,
+ String label,
+ AppColorsExtension appColors,
+ ) {
final intValue = int.tryParse(value) ?? 0;
final bool showFire = label == '연속 일수' && intValue > 0;
@@ -378,7 +391,7 @@ class _CalendarViewState extends ConsumerState {
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
borderRadius: BorderRadius.circular(12),
),
child: Column(
@@ -405,7 +418,7 @@ class _CalendarViewState extends ConsumerState {
),
Text(
label,
- style: const TextStyle(color: AppColors.black, fontSize: 13),
+ style: TextStyle(color: appColors.blackToWhite, fontSize: 13),
),
],
),
@@ -413,7 +426,7 @@ class _CalendarViewState extends ConsumerState {
);
}
- Widget _buildCalendarHeader(DateTime date) {
+ Widget _buildCalendarHeader(DateTime date, AppColorsExtension appColors) {
final now = DateTime.now();
bool isFuture =
date.year > now.year ||
@@ -438,7 +451,9 @@ class _CalendarViewState extends ConsumerState {
width: 24,
height: 24,
colorFilter: ColorFilter.mode(
- isAtStart ? AppColors.gray3 : AppColors.black, // 색상도 비활성 표시
+ isAtStart
+ ? appColors.gray3
+ : appColors.blackToWhite, // 색상도 비활성 표시
BlendMode.srcIn,
),
),
@@ -446,7 +461,7 @@ class _CalendarViewState extends ConsumerState {
const SizedBox(width: 20),
Text(
'${date.year}년 ${date.month}월',
- style: AppTypography.b1.copyWith(color: AppColors.black),
+ style: AppTypography.b1.copyWith(color: appColors.blackToWhite),
),
const SizedBox(width: 20),
IconButton(
@@ -456,7 +471,7 @@ class _CalendarViewState extends ConsumerState {
width: 24,
height: 24,
colorFilter: ColorFilter.mode(
- isFuture ? AppColors.gray3 : AppColors.black,
+ isFuture ? appColors.gray3 : appColors.blackToWhite,
BlendMode.srcIn,
),
),
@@ -470,13 +485,17 @@ class _CalendarViewState extends ConsumerState {
'assets/images/icons/share_icon.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
),
],
);
}
- Widget _buildWeekdayHeader() {
+ Widget _buildWeekdayHeader(AppColorsExtension appColors) {
final weekdays = ['일', '월', '화', '수', '목', '금', '토'];
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
@@ -484,28 +503,28 @@ class _CalendarViewState extends ConsumerState {
.map(
(day) => Text(
day,
- style: AppTypography.b1.copyWith(color: AppColors.black),
+ style: AppTypography.b1.copyWith(color: appColors.blackToWhite),
),
)
.toList(),
);
}
- Widget _buildPostsHeaderForData(int count) {
+ Widget _buildPostsHeaderForData(int count, AppColorsExtension appColors) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('내 인증글', style: AppTypography.b1),
Text(
'총 $count개',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
);
}
- Widget _buildEmptyState() => const Padding(
+ Widget _buildEmptyState(AppColorsExtension appColors) => Padding(
padding: EdgeInsets.symmetric(vertical: 40),
- child: Text('이번 달 인증글이 없습니다.', style: TextStyle(color: AppColors.gray2)),
+ child: Text('이번 달 인증글이 없습니다.', style: TextStyle(color: appColors.gray2)),
);
}
diff --git a/lib/features/challenge/detail/views/information_view.dart b/lib/features/challenge/detail/views/information_view.dart
index 175852d..4cf9731 100644
--- a/lib/features/challenge/detail/views/information_view.dart
+++ b/lib/features/challenge/detail/views/information_view.dart
@@ -17,21 +17,23 @@ class InformationView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
final challengeAsync = ref.watch(
challengeDetailProvider(challengeId: challengeId),
);
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
body: challengeAsync.when(
- loading: () => const Center(
- child: CircularProgressIndicator(color: AppColors.primaryAble),
+ loading: () => Center(
+ child: CircularProgressIndicator(color: appColors.primaryAble),
),
error: (error, stack) => Center(
child: Text(
'데이터를 불러오지 못했습니다.\n잠시 후 다시 시도해주세요.',
textAlign: TextAlign.center,
- style: AppTypography.b1.copyWith(color: AppColors.gray2),
+ style: AppTypography.b1.copyWith(color: appColors.gray2),
),
),
data: (challenge) => ChallengeDetailContent(
diff --git a/lib/features/challenge/detail/views/member_view.dart b/lib/features/challenge/detail/views/member_view.dart
index 00af3ea..5762919 100644
--- a/lib/features/challenge/detail/views/member_view.dart
+++ b/lib/features/challenge/detail/views/member_view.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:haenaem/features/feed/provider/feed_provider.dart';
import 'package:haenaem/features/feed/views/share_feed_view.dart';
+import 'package:haenaem/core/theme/app_colors.dart';
class MemberView extends ConsumerStatefulWidget {
final int challengeId;
@@ -30,11 +31,13 @@ class _MemberViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// challengeId별로 독립적인 상태를 가진 프로바이더를 참조합니다.
final provider = memberFeedProvider(widget.challengeId);
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
body: ShareFeedView(
scrollController: widget.scrollController,
provider: provider,
diff --git a/lib/features/challenge/detail/widgets/calendar_grid.dart b/lib/features/challenge/detail/widgets/calendar_grid.dart
index b050209..6e52e87 100644
--- a/lib/features/challenge/detail/widgets/calendar_grid.dart
+++ b/lib/features/challenge/detail/widgets/calendar_grid.dart
@@ -115,23 +115,29 @@ class _CalendarCell extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return GestureDetector(
onTap: onTap,
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
// 인증: 초록색 / 미인증: 회색
- color: isCertified ? AppColors.primaryAble : AppColors.gray5,
+ color: isCertified ? appColors.primaryAble : appColors.gray5,
borderRadius: BorderRadius.circular(5),
// 오늘이면서 미인증인 경우 테두리 표시
border: (isToday && !isCertified)
- ? Border.all(color: AppColors.gray2, width: 1)
+ ? Border.all(color: appColors.gray2, width: 1)
: null,
// 인증 사진이 있으면 썸네일로 표시
image: (isCertified && imageUrl != null)
? DecorationImage(
image: NetworkImage(imageUrl!),
fit: BoxFit.cover,
+ colorFilter: ColorFilter.mode(
+ Colors.black.withValues(alpha: 0.2),
+ BlendMode.darken,
+ ),
)
: null,
),
@@ -139,7 +145,7 @@ class _CalendarCell extends StatelessWidget {
'$day',
style: TextStyle(
// 인증: 흰색 / 미인증: 회색
- color: isCertified ? Colors.white : AppColors.gray2,
+ color: isCertified ? AppColors.white : appColors.gray2,
fontWeight: isToday ? FontWeight.bold : FontWeight.normal,
),
),
diff --git a/lib/features/challenge/detail/widgets/calendar_post_card.dart b/lib/features/challenge/detail/widgets/calendar_post_card.dart
index 52f7143..e282b25 100644
--- a/lib/features/challenge/detail/widgets/calendar_post_card.dart
+++ b/lib/features/challenge/detail/widgets/calendar_post_card.dart
@@ -17,6 +17,8 @@ class CalendarPostCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
final String formattedDate = DateFormat(
'M월 d일',
).format(DateTime.parse(post.postDate));
@@ -32,9 +34,9 @@ class CalendarPostCard extends StatelessWidget {
margin: const EdgeInsets.only(bottom: 15),
padding: const EdgeInsets.all(13),
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(8),
- border: Border.all(color: AppColors.gray4),
+ border: Border.all(color: appColors.gray4),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -61,12 +63,16 @@ class CalendarPostCard extends StatelessWidget {
'assets/images/icons/green_calendar.svg',
width: 12,
height: 12,
+ colorFilter: ColorFilter.mode(
+ appColors.primaryAble,
+ BlendMode.srcIn,
+ ),
),
const SizedBox(width: 4),
Text(
formattedDate,
style: AppTypography.c1.copyWith(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
),
),
],
@@ -74,7 +80,9 @@ class CalendarPostCard extends StatelessWidget {
const SizedBox(height: 4),
Text(
post.content,
- style: AppTypography.b2.copyWith(color: AppColors.black),
+ style: AppTypography.b2.copyWith(
+ color: appColors.blackToWhite,
+ ),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
diff --git a/lib/features/challenge/detail/widgets/calendar_share_preview.dart b/lib/features/challenge/detail/widgets/calendar_share_preview.dart
index e3229b3..24e4c99 100644
--- a/lib/features/challenge/detail/widgets/calendar_share_preview.dart
+++ b/lib/features/challenge/detail/widgets/calendar_share_preview.dart
@@ -18,6 +18,8 @@ class CalendarSharePreview extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Dialog(
backgroundColor: Colors.transparent, // 배경 투명 (barrierColor가 어둡게 처리)
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40),
@@ -56,18 +58,23 @@ class CalendarSharePreview extends StatelessWidget {
if (loadingProgress == null) return child;
return Container(
height: 300,
- color: AppColors.gray5,
- child: const Center(
+ color: appColors.gray5,
+ child: Center(
child: CircularProgressIndicator(
- color: AppColors.black,
+ color: appColors.blackToWhite,
),
),
);
},
errorBuilder: (context, error, stackTrace) => Container(
height: 200,
- color: AppColors.gray5,
- child: const Center(child: Text('이미지를 불러올 수 없습니다.')),
+ color: appColors.gray5,
+ child: Center(
+ child: Text(
+ '이미지를 불러올 수 없습니다.',
+ style: TextStyle(color: appColors.blackToWhite),
+ ),
+ ),
),
),
),
@@ -85,8 +92,8 @@ class CalendarSharePreview extends StatelessWidget {
text: '저장하기',
icon: Icons.download_rounded,
onPressed: onSave,
- backgroundColor: Colors.white.withValues(alpha: 0.2),
- textColor: Colors.white,
+ backgroundColor: appColors.gray2,
+ textColor: appColors.whiteToBlack,
showContainerDecoration: false, // Row 안에서 배경 중첩 방지
),
),
@@ -98,8 +105,8 @@ class CalendarSharePreview extends StatelessWidget {
text: '공유하기',
icon: Icons.share_rounded,
onPressed: onShare,
- backgroundColor: AppColors.primaryAble,
- textColor: Colors.white,
+ backgroundColor: appColors.primaryAble,
+ textColor: appColors.whiteToBlack,
showContainerDecoration: false, // Row 안에서 배경 중첩 방지
),
),
diff --git a/lib/features/challenge/detail/widgets/challenge_create_success_dialog.dart b/lib/features/challenge/detail/widgets/challenge_create_success_dialog.dart
index c91b8ed..ef5c697 100644
--- a/lib/features/challenge/detail/widgets/challenge_create_success_dialog.dart
+++ b/lib/features/challenge/detail/widgets/challenge_create_success_dialog.dart
@@ -18,6 +18,8 @@ class ChallengeCreateSuccessDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Dialog(
alignment: const Alignment(0, -0.3),
insetPadding: const EdgeInsets.symmetric(horizontal: 20),
@@ -27,7 +29,7 @@ class ChallengeCreateSuccessDialog extends StatelessWidget {
height: 700,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(16),
),
child: Column(
@@ -47,7 +49,7 @@ class ChallengeCreateSuccessDialog extends StatelessWidget {
),
),
// 하단 닫기 버튼
- buildLaterButton(context),
+ buildLaterButton(context, appColors),
],
),
),
@@ -76,13 +78,13 @@ class ChallengeCreateSuccessDialog extends StatelessWidget {
const SizedBox(height: 12),
Text(
'챌린지 생성 완료!',
- style: AppTypography.h2.copyWith(color: Colors.white),
+ style: AppTypography.h2.copyWith(color: AppColors.white),
),
const SizedBox(height: 4),
Text(
'친구들을 초대해서 함께 도전해보세요',
style: AppTypography.b1.copyWith(
- color: Colors.white.withAlpha(200),
+ color: AppColors.white.withAlpha(200),
),
),
],
@@ -91,7 +93,7 @@ class ChallengeCreateSuccessDialog extends StatelessWidget {
}
// 나중에 초대하기 버튼
- Widget buildLaterButton(BuildContext context) {
+ Widget buildLaterButton(BuildContext context, AppColorsExtension appColors) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
@@ -102,12 +104,12 @@ class ChallengeCreateSuccessDialog extends StatelessWidget {
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
- border: Border.all(color: AppColors.gray2),
+ border: Border.all(color: appColors.gray2),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'나중에 초대하기',
- style: AppTypography.b1.copyWith(color: AppColors.gray2),
+ style: AppTypography.b1.copyWith(color: appColors.gray2),
),
),
),
diff --git a/lib/features/challenge/detail/widgets/challenge_popup_menu.dart b/lib/features/challenge/detail/widgets/challenge_popup_menu.dart
index b31039f..cb25632 100644
--- a/lib/features/challenge/detail/widgets/challenge_popup_menu.dart
+++ b/lib/features/challenge/detail/widgets/challenge_popup_menu.dart
@@ -26,6 +26,8 @@ class ChallengePopupMenu extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
// WidgetRef 추가
return PopupMenuButton(
popUpAnimationStyle: const AnimationStyle(
@@ -36,11 +38,12 @@ class ChallengePopupMenu extends ConsumerWidget {
'assets/images/icons/dots_vert_icon.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(appColors.blackToWhite, BlendMode.srcIn),
),
- color: Colors.white,
+ color: appColors.whiteToBlack,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
- side: const BorderSide(color: AppColors.gray4, width: 1),
+ side: BorderSide(color: appColors.gray4, width: 1),
),
offset: const Offset(0, 40),
@@ -51,12 +54,14 @@ class ChallengePopupMenu extends ConsumerWidget {
'알림 설정',
'assets/images/icons/notification_icon.svg',
'notification',
+ appColors,
),
const PopupMenuDivider(height: 1),
_buildPopupItem(
'챌린지 초대',
'assets/images/icons/share_icon.svg',
'invite',
+ appColors,
),
const PopupMenuDivider(height: 1),
@@ -66,6 +71,7 @@ class ChallengePopupMenu extends ConsumerWidget {
'챌린지 설정',
'assets/images/icons/black_settings_icon.svg', // 방장용 설정 아이콘
'settings',
+ appColors,
)
else
_buildPopupItem(
@@ -73,12 +79,13 @@ class ChallengePopupMenu extends ConsumerWidget {
'assets/images/icons/exit_icon.svg',
'leave',
isDanger: true,
+ appColors,
),
];
},
onSelected: (String value) =>
- _handleMenuSelection(context, ref, value), // ref 전달
+ _handleMenuSelection(context, ref, value, appColors), // ref 전달
);
}
@@ -86,10 +93,13 @@ class ChallengePopupMenu extends ConsumerWidget {
PopupMenuItem _buildPopupItem(
String title,
String iconPath,
- String value, {
+ String value,
+ AppColorsExtension appColors, {
bool isDanger = false,
}) {
- final Color itemColor = isDanger ? AppColors.notification : AppColors.black;
+ final Color itemColor = isDanger
+ ? appColors.notification
+ : appColors.blackToWhite;
return PopupMenuItem(
value: value,
@@ -118,6 +128,7 @@ class ChallengePopupMenu extends ConsumerWidget {
BuildContext context,
WidgetRef ref,
String value,
+ AppColorsExtension appColors,
) async {
// async 추가
switch (value) {
@@ -162,11 +173,11 @@ class ChallengePopupMenu extends ConsumerWidget {
emoji: '🥺',
title: '챌린지를 나가시겠어요?',
content: '지금까지의 진행 상황이 모두 사라지며,\n복구할 수 없습니다.',
- contentColor: AppColors.notification,
+ contentColor: appColors.notification,
confirmText: '취소',
cancelText: '나가기',
- cancelBackgroundColor: AppColors.notification, // 나가기 버튼은 경고색으로 강조
- cancelTextColor: Colors.white,
+ cancelBackgroundColor: appColors.notification, // 나가기 버튼은 경고색으로 강조
+ cancelTextColor: appColors.whiteToBlack, // 나가기 버튼 텍스트는 흰색
onCancel: () async {
debugPrint('나가기 버튼 눌림');
final success = await ref
diff --git a/lib/features/challenge/detail/widgets/notification_settings_dialog.dart b/lib/features/challenge/detail/widgets/notification_settings_dialog.dart
index 85cd219..cc0b8a0 100644
--- a/lib/features/challenge/detail/widgets/notification_settings_dialog.dart
+++ b/lib/features/challenge/detail/widgets/notification_settings_dialog.dart
@@ -214,10 +214,12 @@ class _NotificationSettingsDialogState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Dialog(
insetPadding: const EdgeInsets.symmetric(horizontal: 20),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
clipBehavior: Clip.antiAlias,
child: Column(
@@ -227,10 +229,10 @@ class _NotificationSettingsDialogState
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
- decoration: const BoxDecoration(
- color: Colors.white,
+ decoration: BoxDecoration(
+ color: appColors.whiteToBlack,
border: Border(
- bottom: BorderSide(width: 1, color: AppColors.gray4),
+ bottom: BorderSide(width: 1, color: appColors.gray4),
),
),
child: Row(
@@ -238,7 +240,9 @@ class _NotificationSettingsDialogState
children: [
Text(
'챌린지 알림 설정',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
GestureDetector(
onTap: () => Navigator.pop(context),
@@ -247,6 +251,10 @@ class _NotificationSettingsDialogState
height: 24,
child: SvgPicture.asset(
'assets/images/icons/close_icon.svg',
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
),
),
@@ -256,10 +264,10 @@ class _NotificationSettingsDialogState
// ── 본문 ─────────────────────────────────────
_isLoading
- ? const Padding(
+ ? Padding(
padding: EdgeInsets.symmetric(vertical: 40),
child: CircularProgressIndicator(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
),
)
: Padding(
@@ -275,12 +283,14 @@ class _NotificationSettingsDialogState
'모든 알림 받기',
allNotifications,
_toggleAll,
+ appColors,
),
- _buildDailyReminderSection(),
+ _buildDailyReminderSection(appColors),
_buildSwitchRowWithGlobalWarning(
'내 글 좋아요',
"내 게시물에 '좋아요' 반응이 올 때 알림",
likesNotification,
+ appColors,
_likesDisabledByGlobal ? null : _toggleLikes,
disabledByGlobal: _likesDisabledByGlobal,
),
@@ -288,6 +298,7 @@ class _NotificationSettingsDialogState
'댓글',
'내 글에 새로운 댓글이 달릴 때 알림',
commentsNotification,
+ appColors,
_commentsDisabledByGlobal ? null : _toggleComments,
disabledByGlobal: _commentsDisabledByGlobal,
),
@@ -295,6 +306,7 @@ class _NotificationSettingsDialogState
'멤버 인증 소식',
'다른 참여자들이 인증 완료 시 알림',
mateVerification,
+ appColors,
_verificationDisabledByGlobal
? null
: _toggleVerification,
@@ -308,8 +320,8 @@ class _NotificationSettingsDialogState
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
- decoration: const BoxDecoration(
- border: Border(top: BorderSide(width: 1, color: AppColors.gray4)),
+ decoration: BoxDecoration(
+ border: Border(top: BorderSide(width: 1, color: appColors.gray4)),
),
child: SizedBox(
width: double.infinity,
@@ -317,16 +329,16 @@ class _NotificationSettingsDialogState
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
- backgroundColor: AppColors.primaryAble,
+ backgroundColor: appColors.primaryAble,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 0,
),
- child: const Text(
+ child: Text(
'완료',
style: TextStyle(
- color: Colors.white,
+ color: appColors.whiteToBlack,
fontSize: 16,
fontWeight: FontWeight.w500,
),
@@ -346,6 +358,7 @@ class _NotificationSettingsDialogState
String subtitle,
bool value,
ValueChanged? onChanged,
+ AppColorsExtension appColors,
) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -357,16 +370,16 @@ class _NotificationSettingsDialogState
children: [
Text(
title,
- style: AppTypography.b1.copyWith(color: AppColors.black),
+ style: AppTypography.b1.copyWith(color: appColors.blackToWhite),
),
Text(
subtitle,
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
],
),
),
- _buildSwitch(value, onChanged),
+ _buildSwitch(value, onChanged, appColors),
],
);
}
@@ -377,6 +390,7 @@ class _NotificationSettingsDialogState
String title,
String subtitle,
bool value,
+ AppColorsExtension appColors,
ValueChanged? onChanged, {
bool disabledByGlobal = false,
}) {
@@ -395,13 +409,13 @@ class _NotificationSettingsDialogState
title,
style: AppTypography.b1.copyWith(
color: disabledByGlobal
- ? AppColors.gray2
- : AppColors.black,
+ ? appColors.gray2
+ : appColors.blackToWhite,
),
),
Text(
subtitle,
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
],
),
@@ -409,6 +423,7 @@ class _NotificationSettingsDialogState
_buildSwitch(
value,
(_isLoading || disabledByGlobal) ? null : onChanged,
+ appColors,
),
],
),
@@ -417,7 +432,7 @@ class _NotificationSettingsDialogState
padding: const EdgeInsets.only(top: 4),
child: Text(
'전체 푸시 알림 설정에서 해당 알림이 꺼져 있습니다.',
- style: AppTypography.c1.copyWith(color: AppColors.primaryAble),
+ style: AppTypography.c1.copyWith(color: appColors.primaryAble),
),
),
],
@@ -426,27 +441,31 @@ class _NotificationSettingsDialogState
// ── 공통 스위치 위젯 ────────────────────────────────────
- Widget _buildSwitch(bool value, ValueChanged? onChanged) {
+ Widget _buildSwitch(
+ bool value,
+ ValueChanged? onChanged,
+ AppColorsExtension appColors,
+ ) {
return Transform.scale(
scale: 0.8,
alignment: Alignment.centerRight,
child: Switch(
value: value,
onChanged: onChanged,
- activeTrackColor: AppColors.primaryAble,
- activeThumbColor: Colors.white,
- inactiveTrackColor: AppColors.disable,
- inactiveThumbColor: Colors.white,
+ activeTrackColor: appColors.primaryAble,
+ activeThumbColor: appColors.whiteToBlack,
+ inactiveTrackColor: appColors.disable,
+ inactiveThumbColor: appColors.whiteToBlack,
trackOutlineColor: const WidgetStatePropertyAll(Colors.transparent),
thumbIcon: WidgetStateProperty.all(const Icon(null)),
- thumbColor: const WidgetStatePropertyAll(Colors.white),
+ thumbColor: const WidgetStatePropertyAll(AppColors.white),
),
);
}
// ── 일일 리마인더 섹션 ──────────────────────────────────
- Widget _buildDailyReminderSection() {
+ Widget _buildDailyReminderSection(AppColorsExtension appColors) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -462,13 +481,13 @@ class _NotificationSettingsDialogState
'일일 리마인더',
style: AppTypography.b1.copyWith(
color: _reminderDisabledByGlobal
- ? AppColors.gray2
- : AppColors.black,
+ ? appColors.gray2
+ : appColors.blackToWhite,
),
),
Text(
'매일 $selectedTime 알림',
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
],
),
@@ -478,18 +497,19 @@ class _NotificationSettingsDialogState
(_isLoading || _reminderDisabledByGlobal)
? null
: _toggleReminder,
+ appColors,
),
],
),
if (dailyReminder && !_reminderDisabledByGlobal) ...[
const SizedBox(height: 4),
GestureDetector(
- onTap: () => _showTimePicker(context),
+ onTap: () => _showTimePicker(context, appColors),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
borderRadius: BorderRadius.circular(8),
),
child: Row(
@@ -497,7 +517,7 @@ class _NotificationSettingsDialogState
children: [
Text(
selectedTime,
- style: AppTypography.b2.copyWith(color: AppColors.gray3),
+ style: AppTypography.b2.copyWith(color: appColors.gray3),
),
Opacity(
opacity: 0.5,
@@ -505,8 +525,8 @@ class _NotificationSettingsDialogState
'assets/images/icons/big_down_arrow.svg',
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
+ colorFilter: ColorFilter.mode(
+ appColors.gray2,
BlendMode.srcIn,
),
),
@@ -521,7 +541,7 @@ class _NotificationSettingsDialogState
padding: const EdgeInsets.only(top: 4),
child: Text(
'전체 푸시 알림 설정에서 해당 알림이 꺼져 있습니다.',
- style: AppTypography.c1.copyWith(color: AppColors.primaryAble),
+ style: AppTypography.c1.copyWith(color: appColors.primaryAble),
),
),
],
@@ -530,7 +550,7 @@ class _NotificationSettingsDialogState
// ── 시간 피커 ───────────────────────────────────────────
- void _showTimePicker(BuildContext context) {
+ void _showTimePicker(BuildContext context, AppColorsExtension appColors) {
final List hours = List.generate(12, (i) => '${i + 1}시');
String currentPeriod = selectedTime.contains('오후') ? '오후' : '오전';
String currentHour = selectedTime.split(' ').last;
@@ -539,13 +559,13 @@ class _NotificationSettingsDialogState
showDialog(
context: context,
- barrierColor: Colors.black.withAlpha(100),
+ barrierColor: appColors.blackToWhite.withAlpha(100),
builder: (context) => StatefulBuilder(
builder: (context, setDialogState) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
contentPadding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
content: SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
@@ -558,6 +578,7 @@ class _NotificationSettingsDialogState
currentPeriod,
(newPeriod) =>
setDialogState(() => currentPeriod = newPeriod),
+ appColors,
),
),
const SizedBox(height: 24),
@@ -570,7 +591,7 @@ class _NotificationSettingsDialogState
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
- color: AppColors.gray4.withAlpha(100),
+ color: appColors.gray4.withAlpha(100),
borderRadius: BorderRadius.circular(10),
),
),
@@ -614,7 +635,7 @@ class _NotificationSettingsDialogState
},
style: TextButton.styleFrom(
backgroundColor: Colors.transparent,
- foregroundColor: AppColors.primaryAble,
+ foregroundColor: appColors.primaryAble,
minimumSize: const Size(double.infinity, 52),
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
@@ -624,7 +645,7 @@ class _NotificationSettingsDialogState
child: Text(
'완료',
style: AppTypography.b1.copyWith(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
fontWeight: FontWeight.bold,
),
),
@@ -641,12 +662,13 @@ class _NotificationSettingsDialogState
Widget _buildAnimatedPeriodSelector(
String currentPeriod,
Function(String) onPeriodChanged,
+ AppColorsExtension appColors,
) {
return Container(
height: 48,
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
- color: AppColors.gray4,
+ color: appColors.gray4,
borderRadius: BorderRadius.circular(12),
),
child: LayoutBuilder(
@@ -664,7 +686,7 @@ class _NotificationSettingsDialogState
width: width - 4,
height: double.infinity,
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
@@ -687,7 +709,9 @@ class _NotificationSettingsDialogState
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: AppTypography.b2.copyWith(
- color: isSelected ? Colors.black : AppColors.gray2,
+ color: isSelected
+ ? appColors.blackToWhite
+ : appColors.gray2,
fontWeight: isSelected
? FontWeight.bold
: FontWeight.normal,
diff --git a/lib/features/challenge/invite/screens/challenge_invite_screen.dart b/lib/features/challenge/invite/screens/challenge_invite_screen.dart
index b6093bd..a3f8c68 100644
--- a/lib/features/challenge/invite/screens/challenge_invite_screen.dart
+++ b/lib/features/challenge/invite/screens/challenge_invite_screen.dart
@@ -13,19 +13,27 @@ class ChallengeInviteScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
scrolledUnderElevation: 0,
leading: IconButton(
- icon: SvgPicture.asset('assets/images/icons/arrow_left.svg'),
+ icon: SvgPicture.asset(
+ 'assets/images/icons/arrow_left.svg',
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
+ ),
onPressed: () => Navigator.pop(context),
),
title: Text(
'챌린지 초대',
- style: AppTypography.h3.copyWith(color: Colors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
),
diff --git a/lib/features/challenge/invite/widgets/challenge_invite_content.dart b/lib/features/challenge/invite/widgets/challenge_invite_content.dart
index 646562e..28557f5 100644
--- a/lib/features/challenge/invite/widgets/challenge_invite_content.dart
+++ b/lib/features/challenge/invite/widgets/challenge_invite_content.dart
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter/services.dart';
+import 'package:haenaem/app.dart';
import 'package:haenaem/core/theme/app_colors.dart';
import 'package:haenaem/core/theme/app_typography.dart';
import 'package:haenaem/core/utils/korean_string_utils.dart';
@@ -85,6 +86,8 @@ class _ChallengeInviteContentState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// 챌린지 전용 초대 정보 Provider 사용
final inviteInfoAsync = ref.watch(
challengeInviteProvider(widget.challengeId),
@@ -97,15 +100,15 @@ class _ChallengeInviteContentState
return Column(
children: [
// 1. 링크 공유 섹션 (API에서 받아온 링크 사용)
- _buildLinkShareBox(info.challengeLink),
+ _buildLinkShareBox(info.challengeLink, appColors),
const SizedBox(height: 10),
// 2. 검색창
- _buildSearchBar(),
+ _buildSearchBar(appColors),
const SizedBox(height: 10),
// 3. 친구 리스트 (API 데이터 + 검색 필터링)
- Expanded(child: _buildFriendList(info.friends)),
+ Expanded(child: _buildFriendList(info.friends, appColors)),
],
);
},
@@ -114,9 +117,9 @@ class _ChallengeInviteContentState
debugPrint('⏳ [UI 로딩 중...]');
return Column(
children: [
- _buildLinkShareBox(widget.challengeUrl),
+ _buildLinkShareBox(widget.challengeUrl, appColors),
const SizedBox(height: 10),
- _buildSearchBar(),
+ _buildSearchBar(appColors),
const SizedBox(height: 50),
const CircularProgressIndicator(),
],
@@ -127,7 +130,7 @@ class _ChallengeInviteContentState
return Center(
child: Text(
'정보를 불러오지 못했습니다.',
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
);
},
@@ -135,7 +138,10 @@ class _ChallengeInviteContentState
}
// 친구 리스트 빌더 메서드 분리
- Widget _buildFriendList(List friends) {
+ Widget _buildFriendList(
+ List friends,
+ AppColorsExtension appColors,
+ ) {
debugPrint('🔍 [검색어] "$_searchQuery"');
// 검색 필터링
final filteredFriends = friends.where((friend) {
@@ -157,7 +163,7 @@ class _ChallengeInviteContentState
padding: const EdgeInsets.only(top: 50),
child: Text(
friends.isEmpty ? '초대할 수 있는 친구가 없습니다.' : '검색 결과가 없습니다.',
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
);
}
@@ -166,13 +172,13 @@ class _ChallengeInviteContentState
shrinkWrap: true,
itemCount: filteredFriends.length,
itemBuilder: (context, index) {
- return _buildFriendInviteItem(filteredFriends[index]);
+ return _buildFriendInviteItem(filteredFriends[index], appColors);
},
);
}
// 링크 공유 박스 UI
- Widget _buildLinkShareBox(String link) {
+ Widget _buildLinkShareBox(String link, AppColorsExtension appColors) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10),
@@ -186,7 +192,7 @@ class _ChallengeInviteContentState
children: [
Text(
'챌린지 링크 공유',
- style: AppTypography.b2.copyWith(color: AppColors.gray1),
+ style: AppTypography.b2.copyWith(color: appColors.gray1),
),
const SizedBox(height: 8),
// 링크 표시창
@@ -194,9 +200,9 @@ class _ChallengeInviteContentState
width: double.infinity,
padding: const EdgeInsets.all(10),
decoration: ShapeDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
shape: RoundedRectangleBorder(
- side: const BorderSide(width: 1, color: AppColors.gray4),
+ side: BorderSide(width: 1, color: appColors.gray4),
borderRadius: BorderRadius.circular(10),
),
),
@@ -214,8 +220,9 @@ class _ChallengeInviteContentState
children: [
Expanded(
child: _buildActionItem(
+ appColors,
label: '복사',
- color: AppColors.gray5,
+ color: appColors.gray5,
iconPath: 'assets/images/icons/link_copy.svg',
onTap: () => _copyToClipboard(link),
),
@@ -223,8 +230,9 @@ class _ChallengeInviteContentState
const SizedBox(width: 10),
Expanded(
child: _buildActionItem(
+ appColors,
label: '공유',
- color: AppColors.gray5,
+ color: appColors.gray5,
iconPath: 'assets/images/icons/share_icon.svg',
onTap: () {
_shareSystemLink(link);
@@ -240,7 +248,8 @@ class _ChallengeInviteContentState
}
// 공통 버튼 아이템 빌더 (아이콘 + 텍스트)
- Widget _buildActionItem({
+ Widget _buildActionItem(
+ AppColorsExtension appColors, {
required String label,
required Color color,
required String iconPath,
@@ -266,16 +275,13 @@ class _ChallengeInviteContentState
iconPath,
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
- BlendMode.srcIn,
- ),
+ colorFilter: ColorFilter.mode(appColors.gray2, BlendMode.srcIn),
),
const SizedBox(width: 4),
Text(
label,
textAlign: TextAlign.center,
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
],
),
@@ -284,13 +290,13 @@ class _ChallengeInviteContentState
}
// 검색창 UI
- Widget _buildSearchBar() {
+ Widget _buildSearchBar(AppColorsExtension appColors) {
return TextField(
controller: _searchController,
decoration: InputDecoration(
isDense: true,
hintText: '친구 검색',
- hintStyle: AppTypography.b2.copyWith(color: AppColors.gray3),
+ hintStyle: AppTypography.b2.copyWith(color: appColors.gray3),
prefixIcon: Padding(
padding: const EdgeInsets.all(14.0),
child: SvgPicture.asset('assets/images/icons/search_icon.svg'),
@@ -298,18 +304,21 @@ class _ChallengeInviteContentState
contentPadding: const EdgeInsets.symmetric(vertical: 8),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
- borderSide: const BorderSide(color: AppColors.gray4),
+ borderSide: BorderSide(color: appColors.gray4),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
- borderSide: const BorderSide(color: AppColors.gray4),
+ borderSide: BorderSide(color: appColors.gray4),
),
),
);
}
// 친구 리스트 아이템 + 실제 API 로직
- Widget _buildFriendInviteItem(InviteFriend friend) {
+ Widget _buildFriendInviteItem(
+ InviteFriend friend,
+ AppColorsExtension appColors,
+ ) {
// 1. 서버에서 온 상태(friend.isInvited)이거나
// 2. 방금 내가 버튼 눌러서 초대한 상태(_newlyInvitedFriends 포함)인지 확인
bool isInvited =
@@ -376,8 +385,8 @@ class _ChallengeInviteContentState
},
style: ElevatedButton.styleFrom(
backgroundColor: isInvited
- ? AppColors.disable
- : AppColors.primaryAble,
+ ? appColors.disable
+ : appColors.primaryAble,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
@@ -387,7 +396,7 @@ class _ChallengeInviteContentState
child: Text(
isInvited ? '초대됨' : '초대',
style: AppTypography.c1.copyWith(
- color: isInvited ? AppColors.gray2 : Colors.white,
+ color: isInvited ? appColors.gray2 : Colors.white,
),
),
),
diff --git a/lib/features/challenge/settings/screens/challenge_members_screen.dart b/lib/features/challenge/settings/screens/challenge_members_screen.dart
index ed75edd..110616b 100644
--- a/lib/features/challenge/settings/screens/challenge_members_screen.dart
+++ b/lib/features/challenge/settings/screens/challenge_members_screen.dart
@@ -70,6 +70,8 @@ class _ScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// 클라이언트 필터링을 위해 nickname에 null 전달
// (서버에서 전체 목록을 받아온 후 앱 내에서 초성 검색 수행)
final filter = MemberFilter(
@@ -81,17 +83,17 @@ class _ScreenState extends ConsumerState {
final currentUserId = ref.watch(currentUserProvider)?.id;
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
leading: IconButton(
icon: SvgPicture.asset(
'assets/images/icons/arrow_left.svg',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(
- AppColors.black,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
BlendMode.srcIn,
),
),
@@ -100,7 +102,7 @@ class _ScreenState extends ConsumerState {
centerTitle: true,
title: Text(
'챌린지 멤버 관리',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
),
body: Column(
@@ -109,7 +111,7 @@ class _ScreenState extends ConsumerState {
Center(
child: Text(
'멤버를 검색하고 관리할 수 있습니다.',
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
),
const SizedBox(height: 10),
@@ -120,7 +122,7 @@ class _ScreenState extends ConsumerState {
height: 46,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(9.5),
- border: Border.all(color: AppColors.gray4),
+ border: Border.all(color: appColors.gray4),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
@@ -128,8 +130,8 @@ class _ScreenState extends ConsumerState {
SvgPicture.asset(
'assets/images/icons/search_icon.svg',
width: 18,
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
+ colorFilter: ColorFilter.mode(
+ appColors.gray2,
BlendMode.srcIn,
),
),
@@ -146,13 +148,15 @@ class _ScreenState extends ConsumerState {
decoration: InputDecoration(
hintText: '닉네임으로 검색',
hintStyle: AppTypography.b2.copyWith(
- color: AppColors.gray3,
+ color: appColors.gray3,
),
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.zero,
),
- style: AppTypography.b2.copyWith(color: AppColors.black),
+ style: AppTypography.b2.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
),
],
@@ -203,7 +207,7 @@ class _ScreenState extends ConsumerState {
child: Text(
'멤버 ${filteredMembers.length}',
style: AppTypography.b2.copyWith(
- color: AppColors.black,
+ color: appColors.blackToWhite,
),
),
),
@@ -223,7 +227,7 @@ class _ScreenState extends ConsumerState {
return _MemberTile(
member: member,
isMe: isMe, // 상태 전달
- notificationRed: AppColors.notification,
+ notificationRed: appColors.notification,
// 콜백 함수 전달
onKick: () =>
_handleKickMember(member.id, member.nickname),
@@ -257,6 +261,8 @@ class _MemberTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: Row(
@@ -270,12 +276,12 @@ class _MemberTile extends StatelessWidget {
children: [
Text(
member.nickname,
- style: AppTypography.b2.copyWith(color: AppColors.black),
+ style: AppTypography.b2.copyWith(color: appColors.blackToWhite),
),
Text(
'사용자 칭호',
// TODO: 실제 칭호 데이터로 교체 필요
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
@@ -286,12 +292,12 @@ class _MemberTile extends StatelessWidget {
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
- color: AppColors.selected,
+ color: appColors.selected,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'챌린지장',
- style: AppTypography.c1.copyWith(color: AppColors.primaryAble),
+ style: AppTypography.c1.copyWith(color: appColors.primaryAble),
),
)
else
@@ -303,7 +309,7 @@ class _MemberTile extends StatelessWidget {
title: '강제 퇴장',
content: '\'${member.nickname}\' 님을 강퇴하시겠습니까?',
confirmText: '강제 퇴장',
- confirmTextColor: AppColors.notification,
+ confirmTextColor: appColors.notification,
onConfirm: () {
onKick();
},
diff --git a/lib/features/challenge/settings/screens/challenge_settings_screen.dart b/lib/features/challenge/settings/screens/challenge_settings_screen.dart
index 95bbb5d..e3a5b58 100644
--- a/lib/features/challenge/settings/screens/challenge_settings_screen.dart
+++ b/lib/features/challenge/settings/screens/challenge_settings_screen.dart
@@ -20,18 +20,20 @@ class ChallengeSettingsScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
leading: IconButton(
icon: SvgPicture.asset(
'assets/images/icons/arrow_left.svg',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(
- AppColors.black,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
BlendMode.srcIn,
),
),
@@ -39,7 +41,7 @@ class ChallengeSettingsScreen extends ConsumerWidget {
),
title: Text(
'챌린지 설정',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
),
@@ -50,9 +52,10 @@ class ChallengeSettingsScreen extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- 챌린지 운영 관리 섹션 ---
- _buildSectionTitle('챌린지 운영 관리', AppColors.black),
+ _buildSectionTitle('챌린지 운영 관리', appColors.blackToWhite),
const SizedBox(height: 8),
_buildManageTile(
+ appColors,
title: '챌린지 멤버 관리',
iconPath: 'assets/images/icons/friend_icon_on.svg',
onTap: () {
@@ -70,12 +73,9 @@ class ChallengeSettingsScreen extends ConsumerWidget {
const SizedBox(height: 30),
// --- 위험 구역 섹션 (통합 박스 구조) ---
- Text(
- '위험구역',
- style: AppTypography.b3.copyWith(color: AppColors.notification),
- ),
+ _buildSectionTitle('위험구역', appColors.notification),
const SizedBox(height: 10),
- _buildDangerZoneContainer(context, ref),
+ _buildDangerZoneContainer(context, ref, appColors),
],
),
),
@@ -85,11 +85,12 @@ class ChallengeSettingsScreen extends ConsumerWidget {
// 섹션 타이틀 헬퍼
Widget _buildSectionTitle(String title, Color color) {
- return Text(title, style: AppTypography.b3.copyWith(color: color));
+ return Text(title, style: AppTypography.h3.copyWith(color: color));
}
// 관리 메뉴 타일
- Widget _buildManageTile({
+ Widget _buildManageTile(
+ AppColorsExtension appColors, {
required String title,
required String iconPath,
required VoidCallback onTap,
@@ -100,7 +101,7 @@ class ChallengeSettingsScreen extends ConsumerWidget {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
borderRadius: BorderRadius.circular(10),
),
child: Row(
@@ -112,21 +113,21 @@ class ChallengeSettingsScreen extends ConsumerWidget {
iconPath,
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- AppColors.black,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
BlendMode.srcIn,
),
),
const SizedBox(width: 10),
- Text(title, style: AppTypography.b2),
+ Text(title, style: AppTypography.b1),
],
),
SvgPicture.asset(
'assets/images/icons/right_arrow_icon.svg',
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- AppColors.black,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
BlendMode.srcIn,
),
),
@@ -137,13 +138,17 @@ class ChallengeSettingsScreen extends ConsumerWidget {
}
// 위험 구역 통합 컨테이너
- Widget _buildDangerZoneContainer(BuildContext context, WidgetRef ref) {
+ Widget _buildDangerZoneContainer(
+ BuildContext context,
+ WidgetRef ref,
+ AppColorsExtension appColors,
+ ) {
return Container(
width: double.infinity,
decoration: ShapeDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
shape: RoundedRectangleBorder(
- side: const BorderSide(width: 1, color: AppColors.gray4),
+ side: BorderSide(width: 1, color: appColors.gray4),
borderRadius: BorderRadius.circular(10),
),
),
@@ -151,6 +156,7 @@ class ChallengeSettingsScreen extends ConsumerWidget {
children: [
// 상단: 챌린지 나가기 구역
_buildDangerItem(
+ appColors,
context: context,
title: '챌린지 나가기',
description: '다른 멤버에게 챌린지장을 위임한 후, 챌린지를 나갑니다.',
@@ -163,8 +169,8 @@ class ChallengeSettingsScreen extends ConsumerWidget {
title: '챌린지장 위임하기',
content: '다른 멤버에게 챌린지장을 위임하고\n정말 이 챌린지에서 나가시겠습니까?',
confirmText: '나가기',
- confirmBackgroundColor: AppColors.notification,
- confirmTextColor: Colors.white,
+ confirmBackgroundColor: appColors.notification,
+ confirmTextColor: appColors.whiteToBlack,
cancelText: '취소',
onConfirm: () async {
final currentFilter = MemberFilter(
@@ -188,13 +194,14 @@ class ChallengeSettingsScreen extends ConsumerWidget {
),
// 중간 구분선
- const Padding(
+ Padding(
padding: EdgeInsets.symmetric(horizontal: 0),
- child: Divider(height: 1, color: AppColors.gray4),
+ child: Divider(height: 1, color: appColors.gray4),
),
// 하단: 챌린지 삭제 구역
_buildDangerItem(
+ appColors,
context: context,
title: '챌린지 삭제',
description:
@@ -211,10 +218,10 @@ class ChallengeSettingsScreen extends ConsumerWidget {
content:
'이 작업은 되돌릴 수 없습니다.\n\n삭제 시 영향:\n• 모든 멤버가 퇴장됩니다.\n• 성취 그래프와 인증 기록이 삭제됩니다.\n• 챌린지 정보가 완전히 제거됩니다.',
confirmText: '영구 삭제',
- confirmBackgroundColor: AppColors.notification,
- confirmTextColor: Colors.white,
+ confirmBackgroundColor: appColors.notification,
+ confirmTextColor: appColors.whiteToBlack,
cancelText: '취소',
- cancelTextColor: AppColors.gray2,
+ cancelTextColor: appColors.gray2,
onConfirm: () async {
// 기존 다이얼로그에 있던 삭제 로직 그대로 이식
final success = await ref
@@ -241,7 +248,8 @@ class ChallengeSettingsScreen extends ConsumerWidget {
}
// 위험 구역 내부 아이템 빌더
- Widget _buildDangerItem({
+ Widget _buildDangerItem(
+ AppColorsExtension appColors, {
required BuildContext context,
required String title,
required String description,
@@ -256,25 +264,27 @@ class ChallengeSettingsScreen extends ConsumerWidget {
children: [
Text(
title,
- style: AppTypography.b2.copyWith(color: AppColors.notification),
+ style: AppTypography.b3.copyWith(color: appColors.notification),
),
const SizedBox(height: 4),
Text(
description,
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(
- backgroundColor: isDelete ? AppColors.notification : Colors.white,
+ backgroundColor: isDelete
+ ? appColors.notification
+ : appColors.whiteToBlack,
elevation: 0,
minimumSize: const Size(double.infinity, 40),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: isDelete
? BorderSide.none
- : const BorderSide(color: AppColors.gray4),
+ : BorderSide(color: appColors.gray4),
),
),
child: Row(
@@ -285,8 +295,8 @@ class ChallengeSettingsScreen extends ConsumerWidget {
'assets/images/icons/small_trash_icon.svg',
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- Colors.white,
+ colorFilter: ColorFilter.mode(
+ appColors.whiteToBlack,
BlendMode.srcIn,
),
),
@@ -294,8 +304,10 @@ class ChallengeSettingsScreen extends ConsumerWidget {
],
Text(
buttonText,
- style: AppTypography.b2.copyWith(
- color: isDelete ? Colors.white : AppColors.black,
+ style: AppTypography.b1.copyWith(
+ color: isDelete
+ ? appColors.whiteToBlack
+ : appColors.blackToWhite,
),
),
],
diff --git a/lib/features/challenge/verification/screens/challenge_verification_screen.dart b/lib/features/challenge/verification/screens/challenge_verification_screen.dart
index 99c4da9..fe0272d 100644
--- a/lib/features/challenge/verification/screens/challenge_verification_screen.dart
+++ b/lib/features/challenge/verification/screens/challenge_verification_screen.dart
@@ -1,6 +1,7 @@
// 최초 작성자 : 김채영
import 'dart:io';
import 'package:flutter/material.dart';
+import 'package:haenaem/app.dart';
import 'package:haenaem/core/utils/image_utils.dart';
import 'package:image_picker/image_picker.dart';
import 'package:photo_manager/photo_manager.dart';
@@ -242,7 +243,7 @@ class _ChallengeVerificationScreenState
}
// 사진 업로드/미리보기
- Widget _buildPhotoUploadBox() {
+ Widget _buildPhotoUploadBox(AppColorsExtension appColors) {
return SizedBox(
height: 100,
child: ListView.builder(
@@ -253,7 +254,7 @@ class _ChallengeVerificationScreenState
if (index < _newImages.length) {
return _buildImagePreview(index);
} else {
- return _buildAddPhotoButton();
+ return _buildAddPhotoButton(appColors);
}
},
),
@@ -261,16 +262,16 @@ class _ChallengeVerificationScreenState
}
// 사진 추가 버튼 (+)
- Widget _buildAddPhotoButton() {
+ Widget _buildAddPhotoButton(AppColorsExtension appColors) {
return GestureDetector(
onTap: _showImageSourceSheet,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(12),
- border: Border.all(color: AppColors.gray3, width: 1.08),
+ border: Border.all(color: appColors.gray3, width: 1.08),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -279,15 +280,12 @@ class _ChallengeVerificationScreenState
'assets/images/icons/plus.svg',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(
- AppColors.gray3,
- BlendMode.srcIn,
- ),
+ colorFilter: ColorFilter.mode(appColors.gray3, BlendMode.srcIn),
),
const SizedBox(height: 2),
Text(
'사진 추가',
- style: AppTypography.c1.copyWith(color: AppColors.gray3),
+ style: AppTypography.c1.copyWith(color: appColors.gray3),
),
],
),
@@ -339,7 +337,7 @@ class _ChallengeVerificationScreenState
}
// 입력창 하단 정보 영역
- Widget _buildInputFooter() {
+ Widget _buildInputFooter(AppColorsExtension appColors) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 4),
@@ -356,7 +354,7 @@ class _ChallengeVerificationScreenState
top: -3.19,
child: Text(
'챌린지 목표 달성을 자유롭게 표현해주세요',
- style: AppTypography.c1.copyWith(color: AppColors.gray3),
+ style: AppTypography.c1.copyWith(color: appColors.gray3),
),
),
],
@@ -367,7 +365,7 @@ class _ChallengeVerificationScreenState
child: Text(
'${_contentController.text.length}/500',
textAlign: TextAlign.right,
- style: AppTypography.c1.copyWith(color: AppColors.gray3),
+ style: AppTypography.c1.copyWith(color: appColors.gray3),
),
),
],
@@ -376,7 +374,11 @@ class _ChallengeVerificationScreenState
}
// 인증 사진 옆 사진 개수
- Widget _buildDynamicLabel(String label, int count) {
+ Widget _buildDynamicLabel(
+ String label,
+ int count,
+ AppColorsExtension appColors,
+ ) {
return Container(
width: double.infinity,
margin: const EdgeInsets.only(bottom: 8), // 아래 박스와의 간격
@@ -385,11 +387,14 @@ class _ChallengeVerificationScreenState
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(label, style: AppTypography.b3.copyWith(color: AppColors.black)),
+ Text(
+ label,
+ style: AppTypography.b3.copyWith(color: appColors.blackToWhite),
+ ),
const SizedBox(width: 4),
Text(
'($count)',
- style: AppTypography.b1.copyWith(color: AppColors.primaryAble),
+ style: AppTypography.b1.copyWith(color: appColors.primaryAble),
),
],
),
@@ -571,7 +576,7 @@ class _ChallengeVerificationScreenState
}
// 기존 이미지 리스트 UI (수정 모드일 때만 호출)
- Widget _buildExistingImagesList() {
+ Widget _buildExistingImagesList(AppColorsExtension appColors) {
// articleImageUrl이 String 리스트이므로 인덱스로 관리하거나 ID가 필요함
// 만약 post.images 객체 리스트가 있다면 ID 추출이 가능합니다.
return Column(
@@ -580,6 +585,7 @@ class _ChallengeVerificationScreenState
_buildDynamicLabel(
'기존 인증 사진',
_existingImages.length - _imageIdsToDelete.length,
+ appColors,
),
SizedBox(
height: 100,
@@ -638,7 +644,7 @@ class _ChallengeVerificationScreenState
}
// 인증글 공통 팝업 호출 로직 (작성 취소할 경우, 수정 취소할 경우)
- void _handleBackAction() async {
+ void _handleBackAction(AppColorsExtension appColors) async {
final bool? shouldExit = await showDialog(
context: context,
barrierColor: const Color(0x7F1A1D1B),
@@ -648,7 +654,7 @@ class _ChallengeVerificationScreenState
? '지금 나가면 수정 중인 내용은\n저장되지 않고 모두 삭제됩니다.'
: '지금 나가면 작성 중인 내용은\n저장되지 않고 모두 삭제됩니다.',
confirmText: isEditMode ? '수정 취소' : '작성 취소',
- confirmTextColor: AppColors.notification,
+ confirmTextColor: appColors.notification,
onConfirm: () {
Navigator.pop(context);
},
@@ -668,31 +674,37 @@ class _ChallengeVerificationScreenState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
- _handleBackAction();
+ _handleBackAction(appColors);
},
child: Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
leading: IconButton(
- onPressed: _handleBackAction,
+ onPressed: () => _handleBackAction(appColors),
icon: SvgPicture.asset(
'assets/images/icons/arrow_left.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
),
title: Text(
isEditMode ? '인증글 수정' : '챌린지 인증하기',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
scrolledUnderElevation: 0,
elevation: 0,
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
),
body: Column(
children: [
@@ -707,12 +719,15 @@ class _ChallengeVerificationScreenState
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isEditMode && _existingImages.isNotEmpty)
- _buildExistingImagesList(),
+ _buildExistingImagesList(
+ appColors,
+ ), // 기존 이미지 리스트 UI (수정 모드 전용)
_buildDynamicLabel(
isEditMode ? '새 사진 추가' : '인증 사진',
_newImages.length,
+ appColors,
),
- _buildPhotoUploadBox(), // 헬퍼 함수 호출
+ _buildPhotoUploadBox(appColors), // 헬퍼 함수 호출
const SizedBox(height: 8),
// 검증 상태에 따른 박스 로직
@@ -727,7 +742,7 @@ class _ChallengeVerificationScreenState
height: 176,
),
const SizedBox(height: 8),
- _buildInputFooter(), // 헬퍼 함수 호출
+ _buildInputFooter(appColors), // 헬퍼 함수 호출
const SizedBox(height: 40),
],
),
diff --git a/lib/features/challenge/verification/screens/custom_gallery_screen.dart b/lib/features/challenge/verification/screens/custom_gallery_screen.dart
index 6b5c74f..b0b0ae8 100644
--- a/lib/features/challenge/verification/screens/custom_gallery_screen.dart
+++ b/lib/features/challenge/verification/screens/custom_gallery_screen.dart
@@ -142,12 +142,14 @@ class _CustomGalleryScreenState extends State {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
leading: IconButton(
onPressed: () => Navigator.pop(context),
- icon: const Icon(Icons.arrow_back, color: AppColors.black),
+ icon: Icon(Icons.arrow_back, color: appColors.blackToWhite),
),
title: GestureDetector(
onTap: _showFolderList, // 클릭 시 폴더 목록 표시
@@ -158,7 +160,7 @@ class _CustomGalleryScreenState extends State {
(_selectedPath?.isAll == true)
? '갤러리'
: (_selectedPath?.name ?? '갤러리'),
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
const SizedBox(width: 4),
SvgPicture.asset(
@@ -200,13 +202,13 @@ class _CustomGalleryScreenState extends State {
'완료',
style: AppTypography.b1.copyWith(
color: _selectedAssets.isNotEmpty
- ? AppColors.black
- : AppColors.gray3,
+ ? appColors.blackToWhite
+ : appColors.gray3,
),
),
),
],
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
scrolledUnderElevation: 0,
elevation: 0,
),
@@ -257,7 +259,7 @@ class _CustomGalleryScreenState extends State {
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected
- ? AppColors.primaryAble
+ ? appColors.primaryAble
: Colors.transparent,
border: Border.all(color: Colors.white, width: 2),
),
@@ -266,7 +268,7 @@ class _CustomGalleryScreenState extends State {
? Text(
'$selectOrder',
style: const TextStyle(
- color: Colors.white,
+ color: AppColors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
diff --git a/lib/features/challenge/verification/widgets/ai_fail_box.dart b/lib/features/challenge/verification/widgets/ai_fail_box.dart
index 0458e56..b1f1026 100644
--- a/lib/features/challenge/verification/widgets/ai_fail_box.dart
+++ b/lib/features/challenge/verification/widgets/ai_fail_box.dart
@@ -10,11 +10,13 @@ class AiFailBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: AppColors.warning,
+ color: appColors.warning,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
@@ -24,6 +26,10 @@ class AiFailBox extends StatelessWidget {
'assets/images/icons/warning.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.notification,
+ BlendMode.srcIn,
+ ),
),
const SizedBox(width: 10),
Expanded(
@@ -34,12 +40,12 @@ class AiFailBox extends StatelessWidget {
Text(
'챌린지에 맞는 사진인지 확인해 주세요!',
style: AppTypography.b2.copyWith(
- color: AppColors.notification,
+ color: appColors.notification,
),
),
Text(
'다른 사진으로 다시 시도해주세요.',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/challenge/verification/widgets/ai_success_box.dart b/lib/features/challenge/verification/widgets/ai_success_box.dart
index 8b49e52..e18771c 100644
--- a/lib/features/challenge/verification/widgets/ai_success_box.dart
+++ b/lib/features/challenge/verification/widgets/ai_success_box.dart
@@ -10,11 +10,13 @@ class AiSuccessBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: AppColors.selected,
+ color: appColors.selected,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
@@ -24,6 +26,10 @@ class AiSuccessBox extends StatelessWidget {
'assets/images/icons/success_check.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.primaryAble,
+ BlendMode.srcIn,
+ ),
),
const SizedBox(width: 10),
Expanded(
@@ -34,12 +40,12 @@ class AiSuccessBox extends StatelessWidget {
Text(
'챌린지 내용과 적합합니다!',
style: AppTypography.b2.copyWith(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
),
),
Text(
'인증 사진이 검증되었습니다. 인증을 완료하세요.',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/challenge/verification/widgets/ai_verification_box.dart b/lib/features/challenge/verification/widgets/ai_verification_box.dart
index b0c8349..8336ee4 100644
--- a/lib/features/challenge/verification/widgets/ai_verification_box.dart
+++ b/lib/features/challenge/verification/widgets/ai_verification_box.dart
@@ -10,22 +10,24 @@ class AiVerificationBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- const SizedBox(
+ SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2, // 원의 선 두께
- valueColor: AlwaysStoppedAnimation(AppColors.gray2),
+ valueColor: AlwaysStoppedAnimation(appColors.gray2),
),
),
const SizedBox(width: 10),
@@ -36,11 +38,11 @@ class AiVerificationBox extends StatelessWidget {
children: [
Text(
'AI가 사진을 검증 중입니다...',
- style: AppTypography.b2.copyWith(color: AppColors.gray1),
+ style: AppTypography.b2.copyWith(color: appColors.gray1),
),
Text(
'챌린지 내용과 적합한지 확인하고 있어요',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/challenge/verification/widgets/photo_free_box.dart b/lib/features/challenge/verification/widgets/photo_free_box.dart
index 63b4780..9592391 100644
--- a/lib/features/challenge/verification/widgets/photo_free_box.dart
+++ b/lib/features/challenge/verification/widgets/photo_free_box.dart
@@ -9,11 +9,13 @@ class PhotoFreeBox extends StatelessWidget {
const PhotoFreeBox({super.key});
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
@@ -23,6 +25,7 @@ class PhotoFreeBox extends StatelessWidget {
'assets/images/icons/gallery_icon.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(appColors.gray1, BlendMode.srcIn),
),
const SizedBox(width: 10),
Expanded(
@@ -31,11 +34,11 @@ class PhotoFreeBox extends StatelessWidget {
children: [
Text(
'사진 첨부는 선택이에요',
- style: AppTypography.b2.copyWith(color: AppColors.gray1),
+ style: AppTypography.b2.copyWith(color: appColors.gray1),
),
Text(
'사진을 함께 올리면 더 생생한 인증글이 완성돼요😎',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/challenge/verification/widgets/reverification_guide_box.dart b/lib/features/challenge/verification/widgets/reverification_guide_box.dart
index 791067a..fe82b68 100644
--- a/lib/features/challenge/verification/widgets/reverification_guide_box.dart
+++ b/lib/features/challenge/verification/widgets/reverification_guide_box.dart
@@ -10,10 +10,12 @@ class ReverificationGuideBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: AppColors.warning,
+ color: appColors.warning,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
@@ -23,6 +25,10 @@ class ReverificationGuideBox extends StatelessWidget {
'assets/images/icons/tip_bulb.svg',
width: 18,
height: 18,
+ colorFilter: ColorFilter.mode(
+ appColors.notification,
+ BlendMode.srcIn,
+ ),
),
const SizedBox(width: 10),
Expanded(
@@ -34,14 +40,14 @@ class ReverificationGuideBox extends StatelessWidget {
Text(
'재인증 가이드',
style: AppTypography.b2.copyWith(
- color: AppColors.notification,
+ color: appColors.notification,
),
),
const SizedBox(height: 4),
// 가이드 상세 내용
Text(
'• 챌린지 활동이 명확히 보이는 사진을 촬영해주세요.\n• 흐릿하거나 어두운 사진은 인식되지 않을 수 있습니다.\n• 다른 각도나 조명에서 다시 시도해보세요.',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/challenge/verification/widgets/verification_info_box.dart b/lib/features/challenge/verification/widgets/verification_info_box.dart
index 8d468fd..0f75a8c 100644
--- a/lib/features/challenge/verification/widgets/verification_info_box.dart
+++ b/lib/features/challenge/verification/widgets/verification_info_box.dart
@@ -9,11 +9,13 @@ class VerificationInfoBox extends StatelessWidget {
const VerificationInfoBox({super.key});
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
@@ -23,6 +25,7 @@ class VerificationInfoBox extends StatelessWidget {
'assets/images/icons/gallery_icon.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(appColors.gray1, BlendMode.srcIn),
),
const SizedBox(width: 10),
Expanded(
@@ -31,11 +34,11 @@ class VerificationInfoBox extends StatelessWidget {
children: [
Text(
'사진을 추가해주세요',
- style: AppTypography.b2.copyWith(color: AppColors.gray1),
+ style: AppTypography.b2.copyWith(color: appColors.gray1),
),
Text(
'챌린지를 수행한 증거 사진을 올려주세요. \n최대 3장 첨부할 수 있습니다.',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/challenge/verification/widgets/verification_tip_box.dart b/lib/features/challenge/verification/widgets/verification_tip_box.dart
index 526670c..1e4b4ce 100644
--- a/lib/features/challenge/verification/widgets/verification_tip_box.dart
+++ b/lib/features/challenge/verification/widgets/verification_tip_box.dart
@@ -9,11 +9,16 @@ class VerificationTipBox extends StatelessWidget {
const VerificationTipBox({super.key});
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+ Color tipboxYellow = Theme.of(context).brightness == Brightness.light
+ ? const Color(0xFFFFF4D1)
+ : const Color(0xFF575046);
+
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
- color: const Color(0xFFFFF4D1),
+ color: tipboxYellow,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Row(
@@ -38,7 +43,7 @@ class VerificationTipBox extends StatelessWidget {
const SizedBox(height: 4),
Text(
'• 날짜/시간이 보이는 사진이 더 좋아요.\n• 구체적인 내용을 작성하면 동기부여에 도움이 됩니다.\n• 긍정적인 에너지를 공유해보세요!',
- style: AppTypography.c1.copyWith(color: AppColors.gray2),
+ style: AppTypography.c1.copyWith(color: appColors.gray2),
),
],
),
diff --git a/lib/features/feed/screens/challenge_search_screen.dart b/lib/features/feed/screens/challenge_search_screen.dart
index 1415487..04850dd 100644
--- a/lib/features/feed/screens/challenge_search_screen.dart
+++ b/lib/features/feed/screens/challenge_search_screen.dart
@@ -31,6 +31,8 @@ class _ChallengeSearchScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// 사용자 프로필 정보 가져오기 (헤더 이름용)
final currentUser = ref.watch(currentUserProvider);
@@ -40,18 +42,18 @@ class _ChallengeSearchScreenState extends ConsumerState {
);
return Scaffold(
- backgroundColor: Color(0xFFE0E2DC).withValues(alpha: 50),
+ backgroundColor: appColors.gray5,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
- surfaceTintColor: Colors.white,
+ surfaceTintColor: appColors.whiteToBlack,
leading: IconButton(
icon: SvgPicture.asset(
'assets/images/icons/arrow_left.svg',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(
- AppColors.black,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
BlendMode.srcIn,
),
),
@@ -59,20 +61,20 @@ class _ChallengeSearchScreenState extends ConsumerState {
),
title: Text(
'챌린지 탐색',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
),
body: Column(
children: [
- const Divider(height: 1, color: AppColors.gray4),
// 검색창 영역
- _buildSearchBar(),
+ _buildSearchBar(appColors),
// 챌린지 카드 리스트 영역
Expanded(
child: _currentKeyword.isEmpty
? _buildRecommendationSection(
currentUser,
+ appColors,
) // 💡 검색어 없을 때 추천 섹션 노출
: _buildSearchResults(searchResults), // 검색어 있을 때 결과 노출
),
@@ -82,7 +84,10 @@ class _ChallengeSearchScreenState extends ConsumerState {
}
// ── AI 추천 섹션 구현 ────────────────────────────
- Widget _buildRecommendationSection(User? currentUser) {
+ Widget _buildRecommendationSection(
+ User? currentUser,
+ AppColorsExtension appColors,
+ ) {
final userName = currentUser?.nickname ?? "해냄";
final recommendedAsync = ref.watch(recommendedChallengesProvider);
@@ -98,7 +103,9 @@ class _ChallengeSearchScreenState extends ConsumerState {
alignment: Alignment.centerLeft,
child: Text(
"추천 챌린지",
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
),
),
@@ -120,7 +127,7 @@ class _ChallengeSearchScreenState extends ConsumerState {
}
// 검색창 위젯 분리 (가독성용)
- Widget _buildSearchBar() {
+ Widget _buildSearchBar(AppColorsExtension appColors) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: TextField(
@@ -132,18 +139,14 @@ class _ChallengeSearchScreenState extends ConsumerState {
},
decoration: InputDecoration(
hintText: '이름으로 탐색하기',
- hintStyle: AppTypography.b2.copyWith(color: AppColors.gray3),
+ hintStyle: AppTypography.b2.copyWith(color: appColors.gray3),
prefixIcon: Padding(
padding: const EdgeInsets.all(14.0),
child: SvgPicture.asset('assets/images/icons/search_icon.svg'),
),
suffixIcon: _searchController.text.isNotEmpty
? IconButton(
- icon: const Icon(
- Icons.clear,
- size: 20,
- color: AppColors.gray3,
- ),
+ icon: Icon(Icons.clear, size: 20, color: appColors.gray3),
onPressed: () {
_searchController.clear();
setState(() => _currentKeyword = "");
@@ -151,15 +154,15 @@ class _ChallengeSearchScreenState extends ConsumerState {
)
: null,
filled: true,
- fillColor: Colors.white,
+ fillColor: appColors.whiteToBlack,
contentPadding: const EdgeInsets.symmetric(vertical: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(9.5),
- borderSide: const BorderSide(color: AppColors.gray4),
+ borderSide: BorderSide(color: appColors.gray4),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(9.5),
- borderSide: BorderSide(color: Colors.grey.shade300),
+ borderSide: BorderSide(color: appColors.gray4),
),
),
),
diff --git a/lib/features/feed/screens/feed_screen.dart b/lib/features/feed/screens/feed_screen.dart
index 43fdea3..eda57f1 100644
--- a/lib/features/feed/screens/feed_screen.dart
+++ b/lib/features/feed/screens/feed_screen.dart
@@ -32,15 +32,17 @@ class _FeedScreenState extends State
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
scrolledUnderElevation: 0,
title: Text(
'피드',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
actions: [
@@ -62,8 +64,8 @@ class _FeedScreenState extends State
'assets/images/icons/search_icon.svg',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(
- AppColors.black,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
BlendMode.srcIn,
),
),
diff --git a/lib/features/feed/screens/post_detail_screen.dart b/lib/features/feed/screens/post_detail_screen.dart
index c49d268..2a9b05c 100644
--- a/lib/features/feed/screens/post_detail_screen.dart
+++ b/lib/features/feed/screens/post_detail_screen.dart
@@ -28,15 +28,17 @@ class PostDetailScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
// 상세 정보 및 댓글 구독
final detailAsync = ref.watch(postDetailProvider(postId: postId));
final commentsAsync = ref.watch(postCommentsProvider(postId: postId));
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
- surfaceTintColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
+ surfaceTintColor: appColors.whiteToBlack,
scrolledUnderElevation: 0,
elevation: 0,
leading: IconButton(
@@ -44,12 +46,16 @@ class PostDetailScreen extends ConsumerWidget {
'assets/images/icons/arrow_left.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
onPressed: () => Navigator.pop(context),
),
title: Text(
'피드',
- style: AppTypography.h3.copyWith(color: AppColors.black),
+ style: AppTypography.h3.copyWith(color: appColors.blackToWhite),
),
centerTitle: true,
),
@@ -84,7 +90,8 @@ class PostDetailScreen extends ConsumerWidget {
child: Center(child: Text('댓글 로드 실패')),
),
data: (comments) {
- if (comments.isEmpty) return _buildEmptyComments();
+ if (comments.isEmpty)
+ return _buildEmptyComments(appColors);
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
@@ -113,11 +120,11 @@ class PostDetailScreen extends ConsumerWidget {
);
}
- Widget _buildEmptyComments() {
- return const Padding(
+ Widget _buildEmptyComments(AppColorsExtension appColors) {
+ return Padding(
padding: EdgeInsets.symmetric(vertical: 60),
child: Center(
- child: Text('첫 댓글을 남겨보세요!', style: TextStyle(color: AppColors.gray2)),
+ child: Text('첫 댓글을 남겨보세요!', style: TextStyle(color: appColors.gray2)),
),
);
}
diff --git a/lib/features/feed/views/share_feed_view.dart b/lib/features/feed/views/share_feed_view.dart
index 1ffb1fc..47ba1bb 100644
--- a/lib/features/feed/views/share_feed_view.dart
+++ b/lib/features/feed/views/share_feed_view.dart
@@ -64,6 +64,8 @@ class _ShareFeedViewState extends ConsumerState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
super.build(context);
final feedState = ref.watch(widget.provider);
@@ -84,7 +86,7 @@ class _ShareFeedViewState extends ConsumerState
Center(
child: Text(
widget.emptyMessage,
- style: AppTypography.b1.copyWith(color: AppColors.gray4),
+ style: AppTypography.b1.copyWith(color: appColors.gray4),
),
),
],
diff --git a/lib/features/feed/widgets/challenge_search_card.dart b/lib/features/feed/widgets/challenge_search_card.dart
index 35d4e7c..38be531 100644
--- a/lib/features/feed/widgets/challenge_search_card.dart
+++ b/lib/features/feed/widgets/challenge_search_card.dart
@@ -13,11 +13,13 @@ class ChallengeSearchCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.fromLTRB(16, 10, 0, 10),
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
@@ -37,7 +39,9 @@ class ChallengeSearchCard extends StatelessWidget {
// 챌린지 제목
Text(
challenge.base.title,
- style: AppTypography.b3.copyWith(color: AppColors.black),
+ style: AppTypography.b3.copyWith(
+ color: appColors.blackToWhite,
+ ),
),
const SizedBox(height: 10),
@@ -45,6 +49,7 @@ class ChallengeSearchCard extends StatelessWidget {
Row(
children: [
_buildInfoItem(
+ appColors,
iconPath: 'assets/images/icons/person_icon.svg',
text: '${challenge.participantCount}명/50명',
),
@@ -53,7 +58,7 @@ class ChallengeSearchCard extends StatelessWidget {
Text(
'완료까지 D-${challenge.dDay}',
style: AppTypography.b2.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
),
),
],
@@ -94,10 +99,7 @@ class ChallengeSearchCard extends StatelessWidget {
},
icon: SvgPicture.asset(
'assets/images/icons/thick_right_arrow_icon.svg',
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
- BlendMode.srcIn,
- ),
+ colorFilter: ColorFilter.mode(appColors.gray2, BlendMode.srcIn),
),
),
],
@@ -107,17 +109,21 @@ class ChallengeSearchCard extends StatelessWidget {
}
// 정보 아이템(인원수 등) 위젯
- Widget _buildInfoItem({required String iconPath, required String text}) {
+ Widget _buildInfoItem(
+ AppColorsExtension appColors, {
+ required String iconPath,
+ required String text,
+ }) {
return Row(
children: [
SvgPicture.asset(
iconPath,
width: 18,
height: 18,
- colorFilter: const ColorFilter.mode(AppColors.gray2, BlendMode.srcIn),
+ colorFilter: ColorFilter.mode(appColors.gray2, BlendMode.srcIn),
),
const SizedBox(width: 2),
- Text(text, style: AppTypography.b2.copyWith(color: AppColors.gray2)),
+ Text(text, style: AppTypography.b2.copyWith(color: appColors.gray2)),
],
);
}
diff --git a/lib/features/feed/widgets/comment_input_field.dart b/lib/features/feed/widgets/comment_input_field.dart
index 10c2890..9880ede 100644
--- a/lib/features/feed/widgets/comment_input_field.dart
+++ b/lib/features/feed/widgets/comment_input_field.dart
@@ -40,6 +40,8 @@ class _CommentInputFieldState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
// 키보드가 올라왔을 때 하단 여백 자동 조절
final double systemBottomPadding =
MediaQuery.of(context).viewInsets.bottom > 0
@@ -54,7 +56,7 @@ class _CommentInputFieldState extends ConsumerState {
bottom: systemBottomPadding,
),
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
@@ -62,7 +64,7 @@ class _CommentInputFieldState extends ConsumerState {
blurRadius: 4,
),
],
- border: Border(top: BorderSide(color: Colors.grey.shade200)),
+ border: Border(top: BorderSide(color: appColors.gray4)),
),
child: Row(
children: [
@@ -71,9 +73,9 @@ class _CommentInputFieldState extends ConsumerState {
controller: _commentController,
decoration: InputDecoration(
hintText: '댓글을 입력하세요...',
- hintStyle: AppTypography.b2.copyWith(color: AppColors.gray3),
+ hintStyle: AppTypography.b2.copyWith(color: appColors.gray3),
filled: true,
- fillColor: AppColors.gray5,
+ fillColor: appColors.gray5,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
@@ -113,10 +115,14 @@ class _CommentInputFieldState extends ConsumerState {
child: CircleAvatar(
radius: 22,
backgroundColor: _isButtonActive
- ? AppColors.primaryAble
- : AppColors.disable,
+ ? appColors.primaryAble
+ : appColors.disable,
child: SvgPicture.asset(
'assets/images/icons/comment_upload_icon.svg',
+ colorFilter: ColorFilter.mode(
+ appColors.whiteToBlack,
+ BlendMode.srcIn,
+ ),
),
),
),
diff --git a/lib/features/feed/widgets/comment_item.dart b/lib/features/feed/widgets/comment_item.dart
index 69b127a..9f9473f 100644
--- a/lib/features/feed/widgets/comment_item.dart
+++ b/lib/features/feed/widgets/comment_item.dart
@@ -22,87 +22,100 @@ class CommentItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
- String commentDate = DateFormat('yyyy.MM.dd HH:mm').format(comment.date);
+ final appColors = Theme.of(context).extension()!;
+
+ // 피그마 디자인에 맞춰 날짜 포맷 변경
+ String commentDate = DateFormat('yyyy년 MM월 dd일 HH:mm').format(comment.date);
return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
- child: Column(
- mainAxisSize: MainAxisSize.min, // 💡 무한 루프 방지: 최소 크기만 확보
+ padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
+ child: Row(
+ // 💡 중요: 아바타와 텍스트가 모두 위쪽을 기준으로 정렬되도록 수정
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- // 1층: 프로필 이미지 + 닉네임 (세로 중앙 정렬) + 우측 팝업 메뉴
- Row(
- crossAxisAlignment:
- CrossAxisAlignment.center, // 💡 이름이 이미지 중간 위치에 오도록 설정
- children: [
- // 프로필 이미지
- CircleAvatar(
- radius: 18,
- backgroundImage:
- (comment.writer.profileUrl != null &&
- comment.writer.profileUrl!.isNotEmpty)
- ? NetworkImage(comment.writer.profileUrl!) as ImageProvider
- : null,
- child:
- (comment.writer.profileUrl == null ||
- comment.writer.profileUrl!.isEmpty)
- ? SvgPicture.asset(
- 'assets/images/icons/default_profile_icon.svg',
- )
- : null,
- ),
- const SizedBox(width: 12),
-
- // 유저 닉네임
- Expanded(
- child: Text(
- comment.writer.nickname,
- style: AppTypography.b1,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- ),
-
- // 우측 팝업 메뉴 (크기를 제한하여 레이아웃 루프 차단)
- SizedBox(
- width: 40,
- height: 40,
- child: CommentPopupMenu(
- postId: postId,
- comment: comment,
- feedProvider: feedProvider,
- ),
- ),
- ],
+ // 1. 좌측: 프로필 이미지
+ CircleAvatar(
+ radius: 20, // 40x40 크기
+ backgroundImage:
+ (comment.writer.profileUrl != null &&
+ comment.writer.profileUrl!.isNotEmpty)
+ ? NetworkImage(comment.writer.profileUrl!) as ImageProvider
+ : null,
+ backgroundColor: appColors.gray5,
+ child:
+ (comment.writer.profileUrl == null ||
+ comment.writer.profileUrl!.isEmpty)
+ ? SvgPicture.asset(
+ 'assets/images/icons/default_profile_icon.svg',
+ width: 40,
+ height: 40,
+ )
+ : null,
),
- const SizedBox(width: 12),
- // 댓글 내용 및 팝업 메뉴
+
+ const SizedBox(width: 10), // 피그마 spacing: 10 적용
+ // 2. 우측: 닉네임, 내용, 날짜 (Expanded는 여기서 가로 공간만 차지하므로 안전함)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min, // 💡 세로 무한 루프 에러 완전 차단
children: [
+ // 2-1. 상단 헤더: 닉네임 + 서브텍스트 + 팝업 메뉴
Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(comment.writer.nickname, style: AppTypography.b1),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ comment.writer.nickname,
+ style: AppTypography.b3.copyWith(
+ color: appColors.blackToWhite,
+ ),
+ ),
+ const SizedBox(height: 2), // 피그마 spacing: 6 적용
+ // 2-2. 본문 내용 (누락되었던 content 복구)
+ Text(
+ comment.content,
+ style: AppTypography.b2.copyWith(
+ color: appColors.blackToWhite,
+ height: 1.5,
+ ),
+ ),
+ ],
+ ),
+ ),
+ // 우측 팝업 메뉴
SizedBox(
- height: 24,
- width: 24,
child: CommentPopupMenu(
postId: postId,
comment: comment,
feedProvider: feedProvider,
),
),
+ ],
+ ),
+
+ const SizedBox(height: 8), // 내용과 날짜 사이 간격
+ // 2-3. 하단 날짜
+ Row(
+ children: [
+ Text(
+ commentDate,
+ style: AppTypography.c1.copyWith(
+ color: appColors.gray2,
+ height: 1.5,
+ ),
+ ),
+ const SizedBox(width: 20), // 날짜와 '수정됨' 사이 간격
if (comment.isEdited)
- Padding(
- padding: const EdgeInsets.only(right: 8.0),
- child: Text(
- '수정됨',
- style: AppTypography.b2.copyWith(
- color: AppColors.gray3,
- ),
+ Text(
+ '수정됨',
+ style: AppTypography.c1.copyWith(
+ color: appColors.gray3,
),
),
],
diff --git a/lib/features/feed/widgets/comment_popup_menu.dart b/lib/features/feed/widgets/comment_popup_menu.dart
index dcca180..69a5a01 100644
--- a/lib/features/feed/widgets/comment_popup_menu.dart
+++ b/lib/features/feed/widgets/comment_popup_menu.dart
@@ -31,18 +31,21 @@ class CommentPopupMenu extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
return PopupMenuButton(
- icon: SvgPicture.asset(
+ child: SvgPicture.asset(
'assets/images/icons/dots_vert_icon.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(appColors.gray3, BlendMode.srcIn),
),
offset: const Offset(0, 40),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
- side: const BorderSide(color: AppColors.gray4, width: 1),
+ side: BorderSide(color: appColors.gray4, width: 1),
),
- color: Colors.white,
+ color: appColors.whiteToBlack,
itemBuilder: (context) {
// 내 댓글인 경우 : 수정/삭제
if (comment.isMine) {
@@ -51,12 +54,14 @@ class CommentPopupMenu extends ConsumerWidget {
'수정하기',
'assets/images/icons/edit_icon.svg',
'edit',
+ appColors,
),
const PopupMenuDivider(height: 1),
_buildPopupItem(
'삭제하기',
'assets/images/icons/small_trash_icon.svg',
'delete',
+ appColors,
isDanger: true,
),
];
@@ -68,22 +73,27 @@ class CommentPopupMenu extends ConsumerWidget {
'신고하기',
'assets/images/icons/complaint.svg',
'report',
+ appColors,
isDanger: true,
),
];
}
},
- onSelected: (value) => _handleMenuSelection(context, ref, value),
+ onSelected: (value) =>
+ _handleMenuSelection(context, ref, value, appColors),
);
}
PopupMenuItem _buildPopupItem(
String title,
String iconPath,
- String value, {
+ String value,
+ AppColorsExtension appColors, {
bool isDanger = false,
}) {
- final Color itemColor = isDanger ? AppColors.notification : AppColors.black;
+ final Color itemColor = isDanger
+ ? appColors.notification
+ : appColors.blackToWhite;
return PopupMenuItem(
value: value,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
@@ -107,6 +117,7 @@ class CommentPopupMenu extends ConsumerWidget {
BuildContext context,
WidgetRef ref,
String value,
+ AppColorsExtension appColors,
) async {
switch (value) {
case 'edit':
@@ -141,7 +152,7 @@ class CommentPopupMenu extends ConsumerWidget {
title: '댓글을 삭제하시겠습니까?',
content: '작성하신 댓글을 삭제하시겠습니까?\n삭제된 댓글은 되돌릴 수 없습니다.',
confirmText: '삭제',
- confirmTextColor: AppColors.notification,
+ confirmTextColor: appColors.notification,
cancelText: '취소',
onCancel: () {
Navigator.of(context).pop(true);
diff --git a/lib/features/feed/widgets/edit_article_dialog.dart b/lib/features/feed/widgets/edit_article_dialog.dart
index 6c88b56..fbb6c29 100644
--- a/lib/features/feed/widgets/edit_article_dialog.dart
+++ b/lib/features/feed/widgets/edit_article_dialog.dart
@@ -22,7 +22,10 @@ class _EditArticleDialogState extends State {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Dialog(
+ backgroundColor: appColors.whiteToBlack,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(20),
@@ -37,7 +40,7 @@ class _EditArticleDialogState extends State {
maxLines: 5,
decoration: InputDecoration(
filled: true,
- fillColor: AppColors.gray5,
+ fillColor: appColors.gray5,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
@@ -50,24 +53,21 @@ class _EditArticleDialogState extends State {
Expanded(
child: TextButton(
onPressed: () => Navigator.pop(context),
- child: const Text(
- '취소',
- style: TextStyle(color: AppColors.gray2),
- ),
+ child: Text('취소', style: TextStyle(color: appColors.gray2)),
),
),
Expanded(
child: ElevatedButton(
onPressed: () => Navigator.pop(context, _controller.text),
style: ElevatedButton.styleFrom(
- backgroundColor: AppColors.primaryAble,
+ backgroundColor: appColors.primaryAble,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
- child: const Text(
+ child: Text(
'수정완료',
- style: TextStyle(color: Colors.white),
+ style: TextStyle(color: appColors.whiteToBlack),
),
),
),
diff --git a/lib/features/feed/widgets/enter_confirm_dialog.dart b/lib/features/feed/widgets/enter_confirm_dialog.dart
index 7e72c08..85316b2 100644
--- a/lib/features/feed/widgets/enter_confirm_dialog.dart
+++ b/lib/features/feed/widgets/enter_confirm_dialog.dart
@@ -21,12 +21,13 @@ class EnterConfirmDialog extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
final state = ref.watch(challengeParticipateNotifierProvider);
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
- backgroundColor: Colors.white,
- surfaceTintColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
+ surfaceTintColor: appColors.whiteToBlack,
insetPadding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Padding(
padding: const EdgeInsets.fromLTRB(15, 25, 15, 15),
@@ -41,24 +42,28 @@ class EnterConfirmDialog extends ConsumerWidget {
'assets/images/icons/round_check_icon.svg',
width: 42,
height: 42,
+ colorFilter: ColorFilter.mode(
+ appColors.primaryAble,
+ BlendMode.srcIn,
+ ),
),
),
const SizedBox(height: 10),
// 제목
Text(
'챌린지 참여 완료!',
- style: AppTypography.h2.copyWith(color: AppColors.black),
+ style: AppTypography.h2.copyWith(color: appColors.blackToWhite),
),
const SizedBox(height: 3),
// 챌린지 명 넣기
Text(
'‘$challengeTitle’',
- style: AppTypography.b3.copyWith(color: AppColors.gray1),
+ style: AppTypography.b3.copyWith(color: appColors.gray1),
textAlign: TextAlign.center,
),
Text(
'지금부터 함께 도전해요!',
- style: AppTypography.b1.copyWith(color: AppColors.gray1),
+ style: AppTypography.b1.copyWith(color: appColors.gray1),
),
const SizedBox(height: 24),
// 확인 버튼
@@ -81,8 +86,8 @@ class EnterConfirmDialog extends ConsumerWidget {
route.isFirst, // 못 찾으면(다른 경로로 들어온 경우) 기존처럼 맨 처음까지
),
style: ElevatedButton.styleFrom(
- backgroundColor: AppColors.primaryAble,
- foregroundColor: Colors.white,
+ backgroundColor: appColors.primaryAble,
+ foregroundColor: appColors.whiteToBlack,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
@@ -90,7 +95,9 @@ class EnterConfirmDialog extends ConsumerWidget {
),
child: Text(
'확인',
- style: AppTypography.b1.copyWith(color: Colors.white),
+ style: AppTypography.b1.copyWith(
+ color: appColors.whiteToBlack,
+ ),
),
),
),
diff --git a/lib/features/feed/widgets/feed_post_card.dart b/lib/features/feed/widgets/feed_post_card.dart
index 7cf89d3..00af64a 100644
--- a/lib/features/feed/widgets/feed_post_card.dart
+++ b/lib/features/feed/widgets/feed_post_card.dart
@@ -28,6 +28,7 @@ class FeedPostCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
/*
final displayDate = post.updatedAt ?? post.createdAt;
@@ -106,7 +107,7 @@ class FeedPostCard extends ConsumerWidget {
child: Text(
post.title,
style: AppTypography.b3.copyWith(
- color: AppColors.black,
+ color: appColors.blackToWhite,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -118,7 +119,7 @@ class FeedPostCard extends ConsumerWidget {
child: Text(
'수정됨',
style: AppTypography.b2.copyWith(
- color: AppColors.gray3,
+ color: appColors.gray3,
), // 조금 연한 회색 스타일 적용
),
),
@@ -127,7 +128,7 @@ class FeedPostCard extends ConsumerWidget {
const SizedBox(height: 4), // 간격 추가
Text(
post.content,
- style: AppTypography.b1.copyWith(color: AppColors.gray1),
+ style: AppTypography.b1.copyWith(color: appColors.gray1),
// 사진이 없을 때는 본문을 더 길게 보여줘도 좋습니다.
maxLines: post.hasImage ? 3 : 10,
overflow: TextOverflow.ellipsis,
@@ -170,8 +171,8 @@ class FeedPostCard extends ConsumerWidget {
height: 20,
colorFilter: ColorFilter.mode(
post.isLiked
- ? AppColors.notification
- : AppColors.gray2,
+ ? appColors.notification
+ : appColors.gray2,
BlendMode.srcIn,
),
),
@@ -180,8 +181,8 @@ class FeedPostCard extends ConsumerWidget {
post.likeCount.toString(),
style: AppTypography.b2.copyWith(
color: post.isLiked
- ? AppColors.notification
- : AppColors.gray2,
+ ? appColors.notification
+ : appColors.gray2,
),
),
],
@@ -191,11 +192,12 @@ class FeedPostCard extends ConsumerWidget {
_buildIconInfo(
'assets/images/icons/comment_icon.svg',
post.commentCount.toString(),
+ appColors,
),
const Spacer(),
Text(
formattedDate,
- style: AppTypography.b2.copyWith(color: AppColors.gray2),
+ style: AppTypography.b2.copyWith(color: appColors.gray2),
),
],
),
@@ -207,19 +209,19 @@ class FeedPostCard extends ConsumerWidget {
Widget _buildIconInfo(
String iconPath,
- String count, {
- Color color = AppColors.gray2,
- }) {
+ String count,
+ AppColorsExtension appColors,
+ ) {
return Row(
children: [
SvgPicture.asset(
iconPath,
width: 20,
height: 20,
- colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
+ colorFilter: ColorFilter.mode(appColors.gray2, BlendMode.srcIn),
),
const SizedBox(width: 4),
- Text(count, style: AppTypography.b2.copyWith(color: color)),
+ Text(count, style: AppTypography.b2.copyWith(color: appColors.gray2)),
],
);
}
@@ -240,6 +242,8 @@ class _PostImageSliderState extends State<_PostImageSlider> {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Column(
children: [
SizedBox(
@@ -273,15 +277,15 @@ class _PostImageSliderState extends State<_PostImageSlider> {
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
- color: AppColors.gray5,
+ color: appColors.gray5,
child: const Center(child: CircularProgressIndicator()),
);
},
errorBuilder: (context, error, stackTrace) {
return Container(
- color: AppColors.gray5,
- child: const Center(
- child: Icon(Icons.broken_image, color: AppColors.gray3),
+ color: appColors.gray5,
+ child: Center(
+ child: Icon(Icons.broken_image, color: appColors.gray3),
),
);
},
diff --git a/lib/features/feed/widgets/post_popup_menu.dart b/lib/features/feed/widgets/post_popup_menu.dart
index e2a9736..296c537 100644
--- a/lib/features/feed/widgets/post_popup_menu.dart
+++ b/lib/features/feed/widgets/post_popup_menu.dart
@@ -27,6 +27,8 @@ class PostPopupMenu extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
// 💡 Post 객체 내부 데이터 상태 확인용 로그
debugPrint('--- [Post Data Check] ---');
debugPrint('Post ID: ${post.id}');
@@ -60,16 +62,17 @@ class PostPopupMenu extends ConsumerWidget {
constraints: const BoxConstraints(maxWidth: 206),
offset: const Offset(0, 40), // 버튼 아래로 띄우기
shape: RoundedRectangleBorder(
- side: const BorderSide(width: 1, color: AppColors.gray4),
+ side: BorderSide(width: 1, color: appColors.gray4),
borderRadius: BorderRadius.circular(10),
),
- color: Colors.white,
+ color: appColors.whiteToBlack,
elevation: 4, // 그림자 효과
icon: SvgPicture.asset(
'assets/images/icons/dots_vert_icon.svg',
width: 24,
height: 24,
+ colorFilter: ColorFilter.mode(appColors.blackToWhite, BlendMode.srcIn),
),
// 내 글이냐 아니냐에 따라 아이템 리스트만 교체
@@ -82,6 +85,7 @@ class PostPopupMenu extends ConsumerWidget {
'수정하기',
'assets/images/icons/edit_icon.svg',
'edit',
+ appColors,
),
_buildDivider(),
_buildPopupItem(
@@ -89,6 +93,7 @@ class PostPopupMenu extends ConsumerWidget {
'assets/images/icons/small_trash_icon.svg',
'delete',
isDanger: true,
+ appColors,
),
];
} else {
@@ -99,6 +104,7 @@ class PostPopupMenu extends ConsumerWidget {
'챌린지 보기',
'assets/images/icons/eye.svg',
'view_challenge',
+ appColors,
),
];
}
@@ -109,6 +115,7 @@ class PostPopupMenu extends ConsumerWidget {
'챌린지 보기',
'assets/images/icons/eye.svg',
'view_challenge',
+ appColors,
),
_buildDivider(),
_buildPopupItem(
@@ -116,21 +123,26 @@ class PostPopupMenu extends ConsumerWidget {
'assets/images/icons/complaint.svg',
'report',
isDanger: true,
+ appColors,
),
];
}
},
- onSelected: (value) => _handleMenuSelection(context, ref, value),
+ onSelected: (value) =>
+ _handleMenuSelection(context, ref, value, appColors),
);
}
PopupMenuItem _buildPopupItem(
String title,
String iconPath,
- String value, {
+ String value,
+ AppColorsExtension appColors, {
bool isDanger = false,
}) {
- final Color textColor = isDanger ? AppColors.notification : AppColors.black;
+ final Color textColor = isDanger
+ ? appColors.notification
+ : appColors.blackToWhite;
return PopupMenuItem(
value: value,
@@ -170,6 +182,7 @@ class PostPopupMenu extends ConsumerWidget {
BuildContext context,
WidgetRef ref,
String value,
+ AppColorsExtension appColors,
) async {
FocusManager.instance.primaryFocus?.unfocus();
@@ -195,7 +208,7 @@ class PostPopupMenu extends ConsumerWidget {
title: '인증글 삭제',
content: '작성하신 인증글을 삭제하시겠습니까?\n삭제된 인증글은 되돌릴 수 없습니다.',
confirmText: '삭제',
- confirmTextColor: AppColors.notification,
+ confirmTextColor: appColors.notification,
cancelText: '취소',
onConfirm: () async {
try {
diff --git a/lib/features/feed/widgets/recommended_challenge_card.dart b/lib/features/feed/widgets/recommended_challenge_card.dart
index 08479c2..a3d2a35 100644
--- a/lib/features/feed/widgets/recommended_challenge_card.dart
+++ b/lib/features/feed/widgets/recommended_challenge_card.dart
@@ -25,6 +25,7 @@ class _RecommendedChallengeCardState extends State {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
final isExpanded = _expandedStates[_currentIndex] ?? false;
final currentItem = widget.items[_currentIndex];
@@ -34,7 +35,7 @@ class _RecommendedChallengeCardState extends State {
// ✅ 패딩 제거 → 이미지가 카드 전체 너비를 꽉 채움(풀블리드)
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
@@ -79,7 +80,7 @@ class _RecommendedChallengeCardState extends State {
child: Text(
currentItem.recommendReason,
style: AppTypography.b2.copyWith(
- color: AppColors.gray1,
+ color: appColors.gray1,
),
),
)
@@ -97,7 +98,7 @@ class _RecommendedChallengeCardState extends State {
isExpanded ? '추천 이유 닫기' : '추천 이유 보기',
textAlign: TextAlign.right,
style: AppTypography.b2.copyWith(
- color: AppColors.gray1,
+ color: appColors.gray1,
decoration: TextDecoration.underline,
),
),
@@ -127,6 +128,8 @@ class _RecommendedChallengeTop extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -142,16 +145,16 @@ class _RecommendedChallengeTop extends StatelessWidget {
loadingBuilder: (context, child, progress) {
if (progress == null) return child;
return Container(
- color: AppColors.gray4,
+ color: appColors.gray4,
child: const Center(child: CircularProgressIndicator()),
);
},
errorBuilder: (context, error, stackTrace) => Container(
- color: AppColors.gray4,
+ color: appColors.gray4,
child: const Icon(Icons.image_not_supported_outlined),
),
)
- : Container(color: AppColors.gray4),
+ : Container(color: appColors.gray4),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
@@ -162,7 +165,7 @@ class _RecommendedChallengeTop extends StatelessWidget {
// ── 제목 ──
Text(
item.title,
- style: AppTypography.b3.copyWith(color: AppColors.black),
+ style: AppTypography.b3.copyWith(color: appColors.blackToWhite),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -183,15 +186,15 @@ class _RecommendedChallengeTop extends StatelessWidget {
'assets/images/icons/person_icon.svg',
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
+ colorFilter: ColorFilter.mode(
+ appColors.gray2,
BlendMode.srcIn,
),
),
Text(
'${item.participantNumber}명',
style: AppTypography.b2.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
),
),
],
@@ -199,7 +202,7 @@ class _RecommendedChallengeTop extends StatelessWidget {
Text(
'완료까지 D-${item.remainingDays}',
style: AppTypography.b2.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
),
),
],
@@ -228,8 +231,8 @@ class _RecommendedChallengeTop extends StatelessWidget {
child: SizedBox(
child: SvgPicture.asset(
'assets/images/icons/thick_right_arrow_icon.svg',
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
+ colorFilter: ColorFilter.mode(
+ appColors.gray2,
BlendMode.srcIn,
),
),
@@ -243,11 +246,11 @@ class _RecommendedChallengeTop extends StatelessWidget {
children: [
Text(
'챌린지 설명',
- style: AppTypography.b1.copyWith(color: AppColors.gray1),
+ style: AppTypography.b1.copyWith(color: appColors.gray1),
),
Text(
item.content,
- style: AppTypography.b2.copyWith(color: AppColors.gray1),
+ style: AppTypography.b2.copyWith(color: appColors.gray1),
),
],
),
diff --git a/lib/features/home/screens/home_screen.dart b/lib/features/home/screens/home_screen.dart
index e274f1f..46ea8eb 100644
--- a/lib/features/home/screens/home_screen.dart
+++ b/lib/features/home/screens/home_screen.dart
@@ -19,6 +19,8 @@ class HomeScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
+
final homeDataAsync = ref.watch(homeNotifierProvider);
DateTime now = DateTime.now();
@@ -29,7 +31,7 @@ class HomeScreen extends ConsumerWidget {
);
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
body: SafeArea(
child: homeDataAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
@@ -130,6 +132,8 @@ class HomeScreen extends ConsumerWidget {
WidgetRef ref,
int count,
) {
+ final appColors = Theme.of(context).extension()!;
+
return Stack(
alignment: Alignment.center,
children: [
@@ -162,7 +166,13 @@ class HomeScreen extends ConsumerWidget {
}
}
},
- icon: SvgPicture.asset('assets/images/icons/home_notice_icon.svg'),
+ icon: SvgPicture.asset(
+ 'assets/images/icons/home_notice_icon.svg',
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
+ ),
),
if (count > 0)
Positioned(
@@ -170,13 +180,13 @@ class HomeScreen extends ConsumerWidget {
top: 3,
child: Container(
padding: const EdgeInsets.all(4),
- decoration: const BoxDecoration(
- color: AppColors.notification,
+ decoration: BoxDecoration(
+ color: appColors.notification,
shape: BoxShape.circle,
),
child: Text(
'$count',
- style: AppTypography.c1.copyWith(color: Colors.white),
+ style: AppTypography.c1.copyWith(color: AppColors.white),
),
),
),
@@ -186,6 +196,14 @@ class HomeScreen extends ConsumerWidget {
// FAB 빌더
Widget _buildFAB(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+ Color FABColor = Theme.of(context).brightness == Brightness.light
+ ? AppColors.white
+ : appColors.primaryAble;
+ Color FABplusColor = Theme.of(context).brightness == Brightness.light
+ ? appColors.primaryAble
+ : AppColors.black;
+
return Positioned(
right: 20,
bottom: 30,
@@ -196,9 +214,9 @@ class HomeScreen extends ConsumerWidget {
builder: (context) => const ChallengeCreateScreen(),
),
),
- backgroundColor: Colors.white,
+ backgroundColor: FABColor,
shape: const CircleBorder(),
- child: const Icon(Icons.add, size: 32, color: Colors.green),
+ child: Icon(Icons.add, size: 32, color: FABplusColor),
),
);
}
@@ -217,7 +235,9 @@ class ChallengeListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
- if (challenges.isEmpty) return _buildEmptyState();
+ final appColors = Theme.of(context).extension()!;
+
+ if (challenges.isEmpty) return _buildEmptyState(appColors);
return ListView.builder(
padding: const EdgeInsets.only(bottom: 100),
@@ -228,14 +248,14 @@ class ChallengeListView extends StatelessWidget {
);
}
- Widget _buildEmptyState() {
+ Widget _buildEmptyState(AppColorsExtension appColors) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
borderRadius: BorderRadius.circular(12),
),
child: const Text(
diff --git a/lib/features/home/widgets/challenge_card.dart b/lib/features/home/widgets/challenge_card.dart
index 1f36216..82efb62 100644
--- a/lib/features/home/widgets/challenge_card.dart
+++ b/lib/features/home/widgets/challenge_card.dart
@@ -12,14 +12,16 @@ class ChallengeCard extends StatelessWidget {
const ChallengeCard({super.key, required this.challenge});
- Color get _cardColor {
- if (challenge.isDone) return AppColors.success;
- if (challenge.warning) return AppColors.warning;
- return AppColors.gray5;
+ Color _getCardColor(AppColorsExtension appColors) {
+ if (challenge.isDone) return appColors.success;
+ if (challenge.warning) return appColors.warning;
+ return appColors.gray5;
}
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return GestureDetector(
onTap: () {
Navigator.push(
@@ -38,7 +40,7 @@ class ChallengeCard extends StatelessWidget {
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
- color: _cardColor,
+ color: _getCardColor(appColors),
borderRadius: BorderRadius.circular(12),
),
child: IntrinsicHeight(
@@ -62,12 +64,12 @@ class ChallengeCard extends StatelessWidget {
if (challenge.warning) ...[
const SizedBox(height: 4),
- _buildWarningText(),
+ _buildWarningText(appColors),
],
],
),
),
- _buildDivider(),
+ _buildDivider(appColors),
SizedBox(width: 44, child: Center(child: _buildStatusIcon())),
],
),
@@ -121,20 +123,20 @@ class ChallengeCard extends StatelessWidget {
);
}
- Widget _buildWarningText() {
- return const Text(
+ Widget _buildWarningText(AppColorsExtension appColors) {
+ return Text(
'오늘 챌린지를 하지 않으면 실패해요!',
- style: TextStyle(color: AppColors.notification, fontSize: 12),
+ style: TextStyle(color: appColors.notification, fontSize: 12),
);
}
- Widget _buildDivider() {
+ Widget _buildDivider(AppColorsExtension appColors) {
return SizedBox(
width: 40,
child: Center(
child: CustomPaint(
size: const Size(1, double.infinity),
- painter: VerticalDashPainter(),
+ painter: VerticalDashPainter(color: appColors.gray3),
),
),
);
@@ -150,14 +152,18 @@ class ChallengeCard extends StatelessWidget {
}
class VerticalDashPainter extends CustomPainter {
+ final Color color;
+
+ VerticalDashPainter({required this.color});
+
@override
void paint(Canvas canvas, Size size) {
double dashHeight = 5, dashSpace = 3, startY = 0;
// 캔버스의 중앙 X축 계산
final double centerX = size.width / 2;
final paint = Paint()
- ..color = AppColors
- .gray3 // 점선 색상 농도 조절
+ ..color =
+ color // 점선 색상 농도 조절
..strokeWidth = 1;
while (startY < size.height) {
diff --git a/lib/features/home/widgets/day_chip.dart b/lib/features/home/widgets/day_chip.dart
index 120e1d4..f5de400 100644
--- a/lib/features/home/widgets/day_chip.dart
+++ b/lib/features/home/widgets/day_chip.dart
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:haenaem/core/theme/app_colors.dart';
import 'package:haenaem/core/theme/app_typography.dart';
-// import 'package:haenaem/features/challenge/models/challenge_model.dart';
class DayChip extends StatelessWidget {
final DateTime date;
@@ -17,7 +16,9 @@ class DayChip extends StatelessWidget {
@override
Widget build(BuildContext context) {
- const Color gray2 = AppColors.gray2;
+ final appColors = Theme.of(context).extension()!;
+
+ Color gray2 = appColors.gray2;
// 요일 레이블 매핑
const weekdayLabels = ['일', '월', '화', '수', '목', '금', '토'];
@@ -32,18 +33,21 @@ class DayChip extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
- Text(label, style: AppTypography.b1.copyWith(color: AppColors.black)),
+ Text(
+ label,
+ style: AppTypography.b1.copyWith(color: appColors.blackToWhite),
+ ),
const SizedBox(height: 8),
Container(
width: 40,
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 9),
decoration: ShapeDecoration(
- color: getBackgroundColor(),
+ color: getBackgroundColor(appColors),
shape: RoundedRectangleBorder(
// 오늘 날짜이면서 색상이 안 들어간 상태일 때만 테두리 적용
side: showBorder
- ? const BorderSide(
+ ? BorderSide(
width: 1,
strokeAlign: BorderSide.strokeAlignOutside,
color: gray2,
@@ -56,7 +60,7 @@ class DayChip extends StatelessWidget {
child: Text(
day,
style: TextStyle(
- color: isColoredStatus ? Colors.white : gray2,
+ color: isColoredStatus ? appColors.whiteToBlack : gray2,
fontSize: 14,
fontFamily: 'Pretendard',
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w400,
@@ -69,16 +73,16 @@ class DayChip extends StatelessWidget {
);
}
- Color getBackgroundColor() {
+ Color getBackgroundColor(AppColorsExtension appColors) {
switch (status) {
case 'RED':
- return AppColors.notification;
+ return appColors.notification;
case 'GREEN':
- return AppColors.primaryAble;
+ return appColors.primaryAble;
case 'GRAY':
case 'NONE':
default:
- return AppColors.gray5;
+ return appColors.gray5;
}
}
}
diff --git a/lib/features/main/widgets/bottom_nav_bar.dart b/lib/features/main/widgets/bottom_nav_bar.dart
index 7819d8f..95ebdbe 100644
--- a/lib/features/main/widgets/bottom_nav_bar.dart
+++ b/lib/features/main/widgets/bottom_nav_bar.dart
@@ -17,37 +17,55 @@ class MainBottomNavBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BottomNavigationBar(
- type: BottomNavigationBarType.fixed,
- currentIndex: currentIndex,
- selectedItemColor: AppColors.black,
- unselectedItemColor: AppColors.gray3,
- selectedLabelStyle: AppTypography.c2,
- unselectedLabelStyle: AppTypography.c2,
- onTap: onTap,
- items: [
- _buildNavItem('home_icon_off.svg', '홈'),
- _buildNavItem('graph_icon.svg', '통계'),
- _buildNavItem('feed_icon.svg', '피드'),
- _buildNavItem('friend_icon_off.svg', '친구'),
- _buildNavItem('user_icon.svg', '내 페이지'),
- ],
+ final appColors = Theme.of(context).extension()!;
+
+ return Container(
+ decoration: BoxDecoration(
+ // 상단에만 외곽선 추가
+ border: Border(
+ top: BorderSide(
+ color: appColors.gray5, // 원하는 테두리 색상으로 변경 가능
+ width: 1.0, // 테두리 두께 설정
+ ),
+ ),
+ ),
+ child: BottomNavigationBar(
+ backgroundColor: appColors.whiteToBlack,
+ type: BottomNavigationBarType.fixed,
+ currentIndex: currentIndex,
+ selectedItemColor: appColors.blackToWhite,
+ unselectedItemColor: appColors.gray3,
+ selectedLabelStyle: AppTypography.c2,
+ unselectedLabelStyle: AppTypography.c2,
+ onTap: onTap,
+ items: [
+ _buildNavItem('home_icon_off.svg', '홈', appColors),
+ _buildNavItem('graph_icon.svg', '통계', appColors),
+ _buildNavItem('feed_icon.svg', '피드', appColors),
+ _buildNavItem('friend_icon_off.svg', '친구', appColors),
+ _buildNavItem('user_icon.svg', '내 페이지', appColors),
+ ],
+ ),
);
}
- BottomNavigationBarItem _buildNavItem(String iconName, String label) {
+ BottomNavigationBarItem _buildNavItem(
+ String iconName,
+ String label,
+ AppColorsExtension appColors,
+ ) {
return BottomNavigationBarItem(
icon: SvgPicture.asset(
'assets/images/icons/$iconName',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(AppColors.gray3, BlendMode.srcIn),
+ colorFilter: ColorFilter.mode(appColors.gray3, BlendMode.srcIn),
),
activeIcon: SvgPicture.asset(
'assets/images/icons/$iconName',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(AppColors.black, BlendMode.srcIn),
+ colorFilter: ColorFilter.mode(appColors.blackToWhite, BlendMode.srcIn),
),
label: label,
);
diff --git a/lib/features/notification/screens/challenge_invite_detail_screen.dart b/lib/features/notification/screens/challenge_invite_detail_screen.dart
index 9b3f686..6cdb6e7 100644
--- a/lib/features/notification/screens/challenge_invite_detail_screen.dart
+++ b/lib/features/notification/screens/challenge_invite_detail_screen.dart
@@ -30,15 +30,16 @@ class ChallengeInviteDetailScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
// 💡 기존 InformationView에서 쓰던 Provider를 똑같이 사용하여 상세 데이터를 불러옵니다.
final challengeAsync = ref.watch(
challengeDetailProvider(challengeId: challengeId),
);
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
scrolledUnderElevation: 0,
leading: IconButton(
@@ -46,14 +47,18 @@ class ChallengeInviteDetailScreen extends ConsumerWidget {
icon: SvgPicture.asset(
'assets/images/icons/arrow_left.svg', // 기존 앱바 아이콘 통일
width: 24,
+ colorFilter: ColorFilter.mode(
+ appColors.blackToWhite,
+ BlendMode.srcIn,
+ ),
),
),
title: const Text('챌린지 상세정보', style: AppTypography.h3),
centerTitle: true,
),
body: challengeAsync.when(
- loading: () => const Center(
- child: CircularProgressIndicator(color: AppColors.primaryAble),
+ loading: () => Center(
+ child: CircularProgressIndicator(color: appColors.primaryAble),
),
error: (error, stack) {
// 에러 메시지에 404 또는 NOT_FOUND가 포함되어 있는지 확인합니다.
@@ -68,7 +73,7 @@ class ChallengeInviteDetailScreen extends ConsumerWidget {
? '이미 삭제되었거나\n존재하지 않는 챌린지입니다.'
: '데이터를 불러오지 못했습니다.\n잠시 후 다시 시도해주세요.',
textAlign: TextAlign.center,
- style: AppTypography.b1.copyWith(color: AppColors.gray2),
+ style: AppTypography.b1.copyWith(color: appColors.gray2),
),
);
},
@@ -84,7 +89,7 @@ class ChallengeInviteDetailScreen extends ConsumerWidget {
const SizedBox(width: 8),
Text(
'$inviterName님의 초대',
- style: AppTypography.b1.copyWith(color: AppColors.gray2),
+ style: AppTypography.b1.copyWith(color: appColors.gray2),
),
],
),
@@ -107,8 +112,8 @@ class ChallengeInviteDetailScreen extends ConsumerWidget {
// 데이터 로드 성공 시에만 버튼 표시
data: (challenge) => BottomActionButton(
text: '초대 수락하기',
- backgroundColor: AppColors.primaryAble,
- textColor: Colors.white,
+ backgroundColor: appColors.primaryAble,
+ textColor: appColors.whiteToBlack,
onPressed: () async {
// 💡 1. 다이얼로그에 넘겨줄 챌린지 제목을 가져옵니다.
final challenge = ref
diff --git a/lib/features/notification/screens/notification_main_screen.dart b/lib/features/notification/screens/notification_main_screen.dart
index 6220ffc..2103d29 100644
--- a/lib/features/notification/screens/notification_main_screen.dart
+++ b/lib/features/notification/screens/notification_main_screen.dart
@@ -34,16 +34,18 @@ class _NotificationMainScreenState
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Scaffold(
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
appBar: AppBar(
leading: IconButton(
- icon: const Icon(Icons.arrow_back, color: AppColors.black),
+ icon: Icon(Icons.arrow_back, color: appColors.blackToWhite),
onPressed: () => Navigator.of(context).pop(),
),
title: const Text('알림', style: AppTypography.h3),
centerTitle: true,
- backgroundColor: Colors.white,
+ backgroundColor: appColors.whiteToBlack,
elevation: 0,
scrolledUnderElevation: 0,
),
diff --git a/lib/features/notification/views/all_notifications_view.dart b/lib/features/notification/views/all_notifications_view.dart
index ffae915..8ccfc7a 100644
--- a/lib/features/notification/views/all_notifications_view.dart
+++ b/lib/features/notification/views/all_notifications_view.dart
@@ -66,16 +66,19 @@ class _AllNotificationsViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
final state = ref.watch(notificationProvider);
if (state.isLoading) {
- return const Center(
- child: CircularProgressIndicator(color: AppColors.primaryAble),
+ return Center(
+ child: CircularProgressIndicator(color: appColors.primaryAble),
);
}
if (state.notifications.isEmpty) {
- return const Center(child: Text('새로운 알림이 없습니다.'));
+ return Center(
+ child: Text('새로운 알림이 없습니다.', style: TextStyle(color: appColors.gray2)),
+ );
}
// 날짜별로 그룹화
@@ -112,11 +115,7 @@ class _AllNotificationsViewState extends ConsumerState {
listItems.add(
InkWell(
onTap: () {
- // 1. 클릭 시 읽음 처리 (그냥 하얀색으로 바뀜)
- // TODO API 구현된 게 없으므로 임시 설정
- // ref.read(notificationProvider.notifier).markAsRead(noti);
-
- // 2. 알림 타입(type)별 페이지 이동 분기
+ // 알림 타입(type)별 페이지 이동 분기
if (noti.type == 'FRIEND_REQUEST') {
// 친구 요청 알림 -> 소셜(친구추가) 페이지로 이동
Navigator.push(
@@ -196,17 +195,17 @@ class _AllNotificationsViewState extends ConsumerState {
return RefreshIndicator(
onRefresh: () => ref.read(notificationProvider.notifier).refresh(),
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
child: ListView.builder(
controller: _scrollController,
itemCount: listItems.length + (state.isFetchingMore ? 1 : 0),
itemBuilder: (context, index) {
// 마지막 아이템이고 추가 로딩 중이면 로딩 인디케이터 표시
if (index == listItems.length) {
- return const Padding(
+ return Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Center(
- child: CircularProgressIndicator(color: AppColors.primaryAble),
+ child: CircularProgressIndicator(color: appColors.primaryAble),
),
);
}
diff --git a/lib/features/notification/views/challenge_invites_view.dart b/lib/features/notification/views/challenge_invites_view.dart
index 8014493..1099956 100644
--- a/lib/features/notification/views/challenge_invites_view.dart
+++ b/lib/features/notification/views/challenge_invites_view.dart
@@ -13,102 +13,110 @@ class ChallengeInvitesView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final appColors = Theme.of(context).extension()!;
// 데이터 감지
final state = ref.watch(challengeInviteProvider);
final notifier = ref.read(challengeInviteProvider.notifier);
+ Widget content;
+
if (state.isLoading) {
- return const Center(
- child: CircularProgressIndicator(color: AppColors.primaryAble),
+ return Center(
+ child: CircularProgressIndicator(color: appColors.primaryAble),
);
- }
-
- if (state.invites.isEmpty) {
- return const Center(child: Text('받은 챌린지 초대가 없습니다.'));
- }
-
- return RefreshIndicator(
- onRefresh: () => notifier.fetchInvites(),
- color: AppColors.primaryAble,
- child: ListView.builder(
- padding: const EdgeInsets.only(top: 10, bottom: 40),
- itemCount: state.invites.length,
- itemBuilder: (context, index) {
- final invite = state.invites[index];
+ } else if (state.invites.isEmpty) {
+ return Center(
+ child: Text(
+ '받은 챌린지 초대가 없습니다.',
+ style: TextStyle(color: appColors.gray2),
+ ),
+ );
+ } else {
+ content = RefreshIndicator(
+ onRefresh: () => notifier.fetchInvites(),
+ color: appColors.primaryAble,
+ child: ListView.builder(
+ padding: const EdgeInsets.only(top: 10, bottom: 40),
+ itemCount: state.invites.length,
+ itemBuilder: (context, index) {
+ final invite = state.invites[index];
- return ChallengeInviteCard(
- inviteChallenge: invite,
- // 수락 콜백 연결
- onAccept: () async {
- try {
- await notifier.acceptInvite(invite.challengeInfo.base.id);
+ return ChallengeInviteCard(
+ inviteChallenge: invite,
+ // 수락 콜백 연결
+ onAccept: () async {
+ try {
+ await notifier.acceptInvite(invite.challengeInfo.base.id);
- ref.read(needsHomeRefreshProvider.notifier).state =
- true; // 홈 화면 새로고침 필요 플래그 켜기
+ ref.read(needsHomeRefreshProvider.notifier).state =
+ true; // 홈 화면 새로고침 필요 플래그 켜기
- if (context.mounted) {
- // 위젯이 살아있을 때 안전한 Navigator를 미리 변수로 캡처해 둡니다.
- final safeNavigator = Navigator.of(context);
+ if (context.mounted) {
+ // 위젯이 살아있을 때 안전한 Navigator를 미리 변수로 캡처해 둡니다.
+ final safeNavigator = Navigator.of(context);
- // 다이얼로그(showDialog) 대신 스낵바를 띄웁니다.
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(
- '${invite.challengeInfo.base.title} 초대를 수락했습니다.',
- ),
- behavior: SnackBarBehavior.floating, // 화면 아래에 살짝 떠 있는 스타일
- duration: const Duration(seconds: 3),
- // ✨ 꿀팁: 스낵바 우측에 '이동' 버튼 추가
- action: SnackBarAction(
- label: '이동',
- textColor: AppColors.primaryAble, // 앱 테마색 (초록)
- onPressed: () {
- // '이동'을 누르면 해당 챌린지 방으로 라우팅
- safeNavigator.push(
- MaterialPageRoute(
- builder: (context) => ChallengeMainScreen(
- challengeId: invite.challengeInfo.base.id,
+ // 다이얼로그(showDialog) 대신 스낵바를 띄웁니다.
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(
+ '${invite.challengeInfo.base.title} 초대를 수락했습니다.',
+ ),
+ behavior:
+ SnackBarBehavior.floating, // 화면 아래에 살짝 떠 있는 스타일
+ duration: const Duration(seconds: 3),
+ // 스낵바 우측에 '이동' 버튼 추가
+ action: SnackBarAction(
+ label: '이동',
+ textColor: appColors.primaryAble, // 앱 테마색 (초록)
+ onPressed: () {
+ // '이동'을 누르면 해당 챌린지 방으로 라우팅
+ safeNavigator.push(
+ MaterialPageRoute(
+ builder: (context) => ChallengeMainScreen(
+ challengeId: invite.challengeInfo.base.id,
+ ),
),
- ),
- );
- },
+ );
+ },
+ ),
),
- ),
- );
- }
- } catch (e) {
- // 수락 실패 시 에러 메시지 띄우기
- if (context.mounted) {
- final errorMsg = e.toString().replaceAll('Exception: ', '');
- displayToast(context, errorMsg);
+ );
+ }
+ } catch (e) {
+ // 수락 실패 시 에러 메시지 띄우기
+ if (context.mounted) {
+ final errorMsg = e.toString().replaceAll('Exception: ', '');
+ displayToast(context, errorMsg);
+ }
}
- }
- },
- // 거절 콜백 연결
- onReject: () async {
- try {
- await notifier.rejectInvite(invite.challengeInfo.base.id);
+ },
+ // 거절 콜백 연결
+ onReject: () async {
+ try {
+ await notifier.rejectInvite(invite.challengeInfo.base.id);
- ref.read(needsHomeRefreshProvider.notifier).state =
- true; // 홈 화면 새로고침 필요 플래그 켜기
+ ref.read(needsHomeRefreshProvider.notifier).state =
+ true; // 홈 화면 새로고침 필요 플래그 켜기
- if (context.mounted) {
- displayToast(
- context,
- '${invite.challengeInfo.base.title} 초대를 거절했습니다.',
- );
- }
- } catch (e) {
- // 거절 실패 시 에러 메시지 띄우기
- if (context.mounted) {
- final errorMsg = e.toString().replaceAll('Exception: ', '');
- displayToast(context, errorMsg);
+ if (context.mounted) {
+ displayToast(
+ context,
+ '${invite.challengeInfo.base.title} 초대를 거절했습니다.',
+ );
+ }
+ } catch (e) {
+ // 거절 실패 시 에러 메시지 띄우기
+ if (context.mounted) {
+ final errorMsg = e.toString().replaceAll('Exception: ', '');
+ displayToast(context, errorMsg);
+ }
}
- }
- },
- );
- },
- ),
- );
+ },
+ );
+ },
+ ),
+ );
+ }
+ return Container(color: appColors.gray5, child: content);
}
}
diff --git a/lib/features/notification/widgets/challenge_invite_card.dart b/lib/features/notification/widgets/challenge_invite_card.dart
index 0494432..ba16f50 100644
--- a/lib/features/notification/widgets/challenge_invite_card.dart
+++ b/lib/features/notification/widgets/challenge_invite_card.dart
@@ -21,6 +21,7 @@ class ChallengeInviteCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
final challengeInfo = inviteChallenge.challengeInfo;
final inviterUser = inviteChallenge.inviterUser;
@@ -29,7 +30,7 @@ class ChallengeInviteCard extends StatelessWidget {
// 디자인 가이드에 맞춘 패딩 적용
padding: const EdgeInsets.only(top: 10, left: 16, right: 4, bottom: 10),
decoration: BoxDecoration(
- color: Colors.white,
+ color: appColors.whiteToBlack,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
@@ -56,12 +57,12 @@ class ChallengeInviteCard extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
- _buildIconBox(),
+ _buildIconBox(appColors), // 프로필 이미지 또는 기본 아이콘
const SizedBox(width: 6),
Text(
'${inviterUser.nickname}님의 초대',
style: AppTypography.b2.copyWith(
- color: AppColors.gray1, // 디자인 코드 참조
+ color: appColors.gray1, // 디자인 코드 참조
),
),
],
@@ -69,7 +70,9 @@ class ChallengeInviteCard extends StatelessWidget {
),
Text(
challengeInfo.base.title,
- style: AppTypography.b3.copyWith(color: AppColors.black),
+ style: AppTypography.b3.copyWith(
+ color: appColors.blackToWhite,
+ ),
overflow: TextOverflow.ellipsis,
),
@@ -81,15 +84,15 @@ class ChallengeInviteCard extends StatelessWidget {
'assets/images/icons/person_icon.svg',
width: 16,
height: 16,
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
+ colorFilter: ColorFilter.mode(
+ appColors.gray2,
BlendMode.srcIn,
),
),
Text(
'${challengeInfo.participantCount.toString()}명',
style: AppTypography.b2.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
),
),
const SizedBox(width: 16),
@@ -98,7 +101,7 @@ class ChallengeInviteCard extends StatelessWidget {
? '오늘 완료'
: '완료까지 D-${challengeInfo.dDay}',
style: AppTypography.b2.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
),
),
],
@@ -115,13 +118,13 @@ class ChallengeInviteCard extends StatelessWidget {
vertical: 4,
), // 내부 여백
decoration: BoxDecoration(
- color: AppColors.selected,
+ color: appColors.selected,
borderRadius: BorderRadius.circular(16), // 모서리 둥글기
),
child: Text(
tag.tag,
style: AppTypography.b2.copyWith(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
),
),
);
@@ -152,8 +155,8 @@ class ChallengeInviteCard extends StatelessWidget {
},
icon: SvgPicture.asset(
'assets/images/icons/thick_right_arrow_icon.svg',
- colorFilter: const ColorFilter.mode(
- AppColors.gray2,
+ colorFilter: ColorFilter.mode(
+ appColors.gray2,
BlendMode.srcIn,
),
),
@@ -177,13 +180,13 @@ class ChallengeInviteCard extends StatelessWidget {
height: 48,
alignment: Alignment.center,
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
borderRadius: BorderRadius.circular(10),
),
child: Text(
'거절',
style: AppTypography.b1.copyWith(
- color: AppColors.gray2,
+ color: appColors.gray2,
),
),
),
@@ -199,12 +202,14 @@ class ChallengeInviteCard extends StatelessWidget {
height: 48,
alignment: Alignment.center,
decoration: BoxDecoration(
- color: AppColors.primaryAble,
+ color: appColors.primaryAble,
borderRadius: BorderRadius.circular(10),
),
child: Text(
'수락',
- style: AppTypography.b1.copyWith(color: Colors.white),
+ style: AppTypography.b1.copyWith(
+ color: appColors.whiteToBlack,
+ ),
),
),
),
@@ -218,12 +223,12 @@ class ChallengeInviteCard extends StatelessWidget {
}
// 아이콘 또는 프로필 이미지 렌더링 영역
- Widget _buildIconBox() {
+ Widget _buildIconBox(AppColorsExtension appColors) {
return Container(
width: 24,
height: 24,
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
shape: BoxShape.circle,
image:
inviteChallenge.inviterUser.profileUrl != null &&
diff --git a/lib/features/notification/widgets/notification_date_header.dart b/lib/features/notification/widgets/notification_date_header.dart
index 8d5afd3..677598f 100644
--- a/lib/features/notification/widgets/notification_date_header.dart
+++ b/lib/features/notification/widgets/notification_date_header.dart
@@ -11,11 +11,13 @@ class NotificationDateHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Padding(
padding: const EdgeInsets.only(top: 16, left: 20, right: 20, bottom: 4),
child: Text(
dateText,
- style: AppTypography.h3.copyWith(color: AppColors.gray1),
+ style: AppTypography.h3.copyWith(color: appColors.gray1),
),
);
}
diff --git a/lib/features/notification/widgets/notification_list_tile.dart b/lib/features/notification/widgets/notification_list_tile.dart
index ad802b4..0771a49 100644
--- a/lib/features/notification/widgets/notification_list_tile.dart
+++ b/lib/features/notification/widgets/notification_list_tile.dart
@@ -23,21 +23,23 @@ class NotificationListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final appColors = Theme.of(context).extension()!;
+
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
// isRead 값에 따라 배경색 분기 처리
- color: isRead ? Colors.white : AppColors.selected,
+ color: isRead ? appColors.whiteToBlack : appColors.selected,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- _buildIconBox(),
+ _buildIconBox(appColors), // 아이콘 또는 프로필 이미지 렌더링
const SizedBox(width: 16),
// 긴 텍스트가 화면을 넘어갈 때 줄바꿈되도록 Expanded 사용
Expanded(
child: Text(
message,
- style: AppTypography.b1.copyWith(color: AppColors.black),
+ style: AppTypography.b1.copyWith(color: appColors.blackToWhite),
),
),
],
@@ -46,14 +48,14 @@ class NotificationListTile extends StatelessWidget {
}
// 아이콘 또는 프로필 이미지 렌더링 영역
- Widget _buildIconBox() {
+ Widget _buildIconBox(AppColorsExtension appColors) {
// 1. 프로필 타입일 경우 (친구 페이지 로직 적용)
if (iconType == NotiIconType.profile) {
return Container(
width: 40,
height: 40,
decoration: BoxDecoration(
- color: AppColors.gray5,
+ color: appColors.gray5,
shape: BoxShape.circle,
image: profileImageUrl != null && profileImageUrl!.startsWith('http')
? DecorationImage(
@@ -79,7 +81,7 @@ class NotificationListTile extends StatelessWidget {
}
// 2. 시스템 알림 (성공, 실패, 일반) 로직
- Color bgColor = AppColors.gray5;
+ Color bgColor = appColors.gray5;
Widget iconWidget = const Icon(
Icons.eco,
color: Colors.white,
@@ -90,7 +92,7 @@ class NotificationListTile extends StatelessWidget {
bgColor = AppColors.blue;
iconWidget = const Icon(Icons.star, color: Colors.white, size: 20);
} else if (iconType == NotiIconType.fail) {
- bgColor = AppColors.notification;
+ bgColor = appColors.notification;
iconWidget = const Icon(Icons.close, color: Colors.white, size: 20);
}
diff --git a/lib/features/report/screens/report_screen.dart b/lib/features/report/screens/report_screen.dart
index db46f99..5e9b454 100644
--- a/lib/features/report/screens/report_screen.dart
+++ b/lib/features/report/screens/report_screen.dart
@@ -27,7 +27,6 @@ class _ReportScreenState extends ConsumerState {
int? _selectedReasonIndex;
final TextEditingController _otherReasonController = TextEditingController();
- // 백엔드 API 명세에 맞게 keys 값을 꼭 수정해주세요!
final List