diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png index 15668959..ef8b1932 100644 Binary files a/android/app/src/main/res/drawable-hdpi/splash.png and b/android/app/src/main/res/drawable-hdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png index e04417ac..dc3eea0d 100644 Binary files a/android/app/src/main/res/drawable-mdpi/splash.png and b/android/app/src/main/res/drawable-mdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png index 139dd5e2..28d5e57e 100644 Binary files a/android/app/src/main/res/drawable-xhdpi/splash.png and b/android/app/src/main/res/drawable-xhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png index d445b92a..a1996bf3 100644 Binary files a/android/app/src/main/res/drawable-xxhdpi/splash.png and b/android/app/src/main/res/drawable-xxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png index b4c1874e..0d405700 100644 Binary files a/android/app/src/main/res/drawable-xxxhdpi/splash.png and b/android/app/src/main/res/drawable-xxxhdpi/splash.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index d08fafc1..91c2d8b8 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 2341e2ed..a146aea6 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 06e8f99e..c3379ea5 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 110971cf..6130a555 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index cf1ea232..31ac45f9 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/images/ground_flip_app_icon.png b/assets/images/ground_flip_app_icon.png new file mode 100644 index 00000000..bbb21a9b Binary files /dev/null and b/assets/images/ground_flip_app_icon.png differ diff --git a/flutter_launcher_icons.yaml b/flutter_launcher_icons.yaml index 96eb549e..18b7ce68 100644 --- a/flutter_launcher_icons.yaml +++ b/flutter_launcher_icons.yaml @@ -1,5 +1,5 @@ flutter_icons: ios: true android: true - image_path: "assets/images/app_icon.png" + image_path: "assets/images/ground_flip_app_icon.png" remove_alpha_ios: true \ No newline at end of file diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 316c49bf..46ade383 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index df2390b9..637db2ae 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 6162c2cc..ed823268 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 7a11ade0..e2fc3ab2 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 869a5b77..639f1a2a 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index a965691a..af914455 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 56f7ad58..94a25a26 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 6162c2cc..ed823268 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 5a6c0a47..2872fe58 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 0431ba8c..4f3f027e 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png index 8200c6df..a018a81b 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png index 9f4b4030..fd5819bf 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png index 858c49af..7ce65c8a 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png index e46bdfdd..296de1bc 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 0431ba8c..4f3f027e 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index d6dad8ac..566e3798 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png index 1a62e602..dbbc9e23 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png index 688418a1..233d0404 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 4da0e146..991e0277 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 7d06c3b4..3d0753c6 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index db40f313..6d68c2f7 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png index e04417ac..dc3eea0d 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png index 139dd5e2..28d5e57e 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png index d445b92a..a1996bf3 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard index 4cc69847..8d2b7d51 100644 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -38,7 +38,7 @@ - + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 44ac26de..78d87435 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -56,7 +56,7 @@ NSLocationAlwaysAndWhenInUseUsageDescription This app needs GPS NSLocationWhenInUseUsageDescription - This app needs GPS + This app needs GPS UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName diff --git a/lib/constants/app_colors.dart b/lib/constants/app_colors.dart index 80280fdb..80d01218 100644 --- a/lib/constants/app_colors.dart +++ b/lib/constants/app_colors.dart @@ -16,5 +16,5 @@ class AppColors { static const Color boxColor = Color(0xFF1F1F1F); static const Color boxColorSecond = Color(0xFF555555); static const Color buttonColor = Colors.white; - static const Color navigationBarColor = Color(0xA6212121); + static const Color navigationBarColor = Color(0xFF1D1D1D); } diff --git a/lib/constants/text_styles.dart b/lib/constants/text_styles.dart index e8031857..2cab5abe 100644 --- a/lib/constants/text_styles.dart +++ b/lib/constants/text_styles.dart @@ -57,6 +57,12 @@ class TextStyles { fontWeight: FontWeight.w500, ); + static TextStyle fs14w500cTextSecondary = TextStyle( + color: AppColors.textSecondary, + fontSize: 14, + fontWeight: FontWeight.w500, + ); + static TextStyle fs14w400cTextSecondary = TextStyle( color: AppColors.textSecondary, fontSize: 14, @@ -75,6 +81,12 @@ class TextStyles { fontWeight: FontWeight.w800, ); + static TextStyle fs24w900cTextPrimary = TextStyle( + color: AppColors.textPrimary, + fontSize: 24, + fontWeight: FontWeight.w900, + ); + static TextStyle fs32w400cTextSecondary = TextStyle( color: AppColors.textSecondary, fontSize: 32, diff --git a/lib/controllers/bottom_sheet_controller.dart b/lib/controllers/bottom_sheet_controller.dart new file mode 100644 index 00000000..7c6efb5c --- /dev/null +++ b/lib/controllers/bottom_sheet_controller.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../enums/pixel_mode.dart'; +import '../models/individual_history_pixel_info.dart'; +import '../models/individual_mode_pixel_info.dart'; +import '../service/pixel_service.dart'; +import '../widgets/map/bottom_sheet/individual_history_list.dart'; +import '../widgets/map/bottom_sheet/pixel_info_header.dart'; +import '../widgets/map/bottom_sheet/step_stats.dart'; +import '../widgets/map/bottom_sheet/visited_user_list.dart'; + +class BottomSheetController extends GetxController { + final PixelService pixelService = PixelService(); + final DraggableScrollableController draggableController = + DraggableScrollableController(); + Widget currentBody = StepStatsBody(); + Widget currentHeader = StepStats(); + RxInt mode = 0.obs; + RxBool changeVar = true.obs; + double size = 1.1; + + void showIndividualHistoryPixelInfo(IndividualHistoryPixelInfo pixelInfo) { + mode.value = 1; + changeVar.value = changeVar.value ? false : true; + draggableController.animateTo( + 0.6, + duration: Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + currentHeader = PixelInfoHeader( + address: pixelInfo.address, + visitCount: pixelInfo.visitCount!, + mode: PixelMode.individualHistory, + ); + currentBody = IndividualHistoryList( + visitList: pixelInfo.visitList ?? [], + ); + mode.value = 1; + } + + void showIndividualModePixelInfo(IndividualModePixelInfo pixelInfo) { + mode.value = 2; + changeVar.value = changeVar.value ? false : true; + draggableController.animateTo( + 0.6, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + currentHeader = PixelInfoHeader( + address: pixelInfo.address, + visitCount: pixelInfo.visitCount!, + mode: PixelMode.individualMode, + ); + currentBody = VisitedUserList( + pixelOwnerUser: pixelInfo.pixelOwnerUser!, + visitList: pixelInfo.visitList ?? [], + ); + mode.value = 2; + } + + minimize() { + currentHeader = StepStats(); + currentBody = StepStatsBody(); + mode.value = 0; + draggableController.animateTo( + 0.1, + duration: Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + } + + void changeStepStatIfMinimized() { + if (draggableController.size <= 0.111 && size > draggableController.size) { + currentHeader = StepStats(); + currentBody = StepStatsBody(); + mode.value = 0; + } + size = draggableController.size; + } + + getBody() { + if (mode.value == 0 && changeVar.value) { + // changeVar.value = changeVar.value ? false : true; + } + return currentBody; + } + + getHeader() { + if (mode.value == 0 && changeVar.value) { + // changeVar.value = changeVar.value ? false : true; + } + return currentHeader; + } +} diff --git a/lib/controllers/map_controller.dart b/lib/controllers/map_controller.dart index c4ad0c36..6e883b02 100644 --- a/lib/controllers/map_controller.dart +++ b/lib/controllers/map_controller.dart @@ -9,12 +9,19 @@ import 'package:location/location.dart'; import '../enums/pixel_mode.dart'; import '../models/individual_history_pixel.dart'; import '../models/individual_mode_pixel.dart'; +import '../models/user_pixel_count.dart'; +import '../service/location_service.dart'; import '../service/pixel_service.dart'; +import '../service/user_service.dart'; import '../utils/user_manager.dart'; import '../widgets/pixel.dart'; +import 'bottom_sheet_controller.dart'; class MapController extends GetxController { final PixelService pixelService = PixelService(); + final UserService userService = UserService(); + final BottomSheetController bottomSheetController = + Get.find(); static const String darkMapStylePath = 'assets/map_style/dark_map_style_with_landmarks.txt'; @@ -35,6 +42,9 @@ class MapController extends GetxController { RxList pixels = [].obs; RxList markers = [].obs; RxBool isLoading = true.obs; + final RxInt selectedType = 0.obs; + final RxInt currentPixelCount = 0.obs; + final RxInt accumulatePixelCount = 0.obs; Timer? _cameraIdleTimer; @@ -44,6 +54,7 @@ class MapController extends GetxController { await _loadMapStyle(); await initCurrentLocation(); _updateLatestPixel(); + await updateCurrentPixel(); await occupyPixel(); updatePixels(); _createUserMarker(); @@ -51,6 +62,20 @@ class MapController extends GetxController { _trackPixels(); } + onHidden() { + bottomSheetController.minimize(); + } + + updateCurrentPixel() async { + UserPixelCount pixelCount = await userService.getUserPixelCount(); + currentPixelCount.value = pixelCount.currentPixelCount!; + accumulatePixelCount.value = pixelCount.accumulatePixelCount!; + } + + getSelectedType() { + return selectedType.value; + } + void onCameraIdle() { _cameraIdleTimer = Timer(Duration(milliseconds: 300), updatePixels); } @@ -60,6 +85,16 @@ class MapController extends GetxController { _cameraIdleTimer?.cancel(); } + focusOnCurrentLocation() { + currentCameraPosition = CameraPosition( + target: LatLng(currentLocation.latitude!, currentLocation.longitude!), + zoom: 16.0, + ); + googleMapController?.animateCamera( + CameraUpdate.newCameraPosition(currentCameraPosition), + ); + } + void _trackUserLocation() { location.onLocationChanged.listen((newLocation) async { currentLocation = newLocation; @@ -73,7 +108,7 @@ class MapController extends GetxController { Future initCurrentLocation() async { try { - currentLocation = await location.getLocation(); + currentLocation = LocationService().currentLocation!; currentCameraPosition = CameraPosition( target: LatLng(currentLocation.latitude!, currentLocation.longitude!), zoom: 16.0, @@ -182,6 +217,7 @@ class MapController extends GetxController { currentLongitude: currentLocation.longitude!, ); updatePixels(); + await updateCurrentPixel(); } isPixelChanged() { @@ -194,8 +230,10 @@ class MapController extends GetxController { latestPixel['y'] != currentPixel['y']; } - void changePixelMode(String pixelModeKrName) { - currentPixelMode.value = PixelMode.fromKrName(pixelModeKrName); + void changePixelMode(int type) { + selectedType.value = type; + currentPixelMode.value = PixelMode.fromInt(type); + bottomSheetController.minimize(); updatePixels(); } @@ -234,4 +272,12 @@ class MapController extends GetxController { double _toRadians(double degree) { return degree * math.pi / 180; } + + getPixelCount() { + if (currentPixelMode.value == PixelMode.individualHistory) { + return accumulatePixelCount.value; + } else { + return currentPixelCount.value; + } + } } diff --git a/lib/controllers/my_page_controller.dart b/lib/controllers/my_page_controller.dart index 4e9f07d3..18466c76 100644 --- a/lib/controllers/my_page_controller.dart +++ b/lib/controllers/my_page_controller.dart @@ -1,5 +1,4 @@ import 'package:get/get.dart'; -import 'package:location/location.dart'; import '../models/user.dart'; import '../models/user_pixel_count.dart'; @@ -17,13 +16,6 @@ class MyPageController extends GetxController { User userInfo = await userService.getCurrentUserInfo(); currentUserInfo.value = userInfo; await _updatePixelCount(); - _trackPixelCount(); - } - - _trackPixelCount() { - Location().onLocationChanged.listen((newLocation) async { - await _updatePixelCount(); - }); } _updatePixelCount() async { diff --git a/lib/controllers/navigation_controller.dart b/lib/controllers/navigation_controller.dart index 3f2eca2c..b556c3e4 100644 --- a/lib/controllers/navigation_controller.dart +++ b/lib/controllers/navigation_controller.dart @@ -5,10 +5,12 @@ import '../screens/map_screen.dart'; import '../screens/my_page_screen.dart'; import '../screens/ranking_screen.dart'; import '../widgets/common/app_bar.dart'; +import 'map_controller.dart'; import 'ranking_controller.dart'; class NavigationController extends GetxController { final RankingController rankingController = Get.find(); + final MapController mapController = Get.find(); final RxInt selectedIndex = 0.obs; static List tabPages = [ const MapScreen(), @@ -24,10 +26,13 @@ class NavigationController extends GetxController { ]; void changeIndex(int index) { - selectedIndex(index); - if (index == 1) { + if (selectedIndex.value != 1 && index == 1) { rankingController.onVisible(); } + if (selectedIndex.value == 0 && index != 0) { + mapController.onHidden(); + } + selectedIndex(index); } Widget getCurrentPage() { diff --git a/lib/enums/pixel_mode.dart b/lib/enums/pixel_mode.dart index bfef0193..a543f287 100644 --- a/lib/enums/pixel_mode.dart +++ b/lib/enums/pixel_mode.dart @@ -1,15 +1,26 @@ enum PixelMode { - individualMode(koreanName : '개인전'), - individualHistory(koreanName : '개인 기록'), - groupMode(koreanName : '그룹전'); + individualMode(koreanName: '개인전'), + individualHistory(koreanName: '개인 기록'), + groupMode(koreanName: '그룹전'); const PixelMode({required this.koreanName}); final String koreanName; static PixelMode fromKrName(String krName) { - return PixelMode.values.firstWhere((mode) => mode.koreanName == krName, - orElse: () => throw ArgumentError('No enum value with krName $krName'), + return PixelMode.values.firstWhere( + (mode) => mode.koreanName == krName, + orElse: () => throw ArgumentError('No enum value with krName $krName'), ); } + + static PixelMode fromInt(int number) { + if (number == 0) { + return PixelMode.individualHistory; + } else if (number == 1) { + return PixelMode.individualMode; + } else { + return PixelMode.groupMode; + } + } } diff --git a/lib/main.dart b/lib/main.dart index 22c37954..4f957b29 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,16 +11,15 @@ import 'screens/policy_screen.dart'; import 'screens/setting_screen.dart'; import 'screens/sign_up_screen.dart'; import 'service/auth_service.dart'; +import 'service/location_service.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: ".env"); await GetStorage.init(); KakaoSdk.init(nativeAppKey: dotenv.env['NATIVE_APP_KEY']!); - + await LocationService().initCurrentLocation(); String initialRoute = await AuthService().isLogin() ? '/main' : '/permission'; - - await Future.delayed(Duration(seconds: 2)); runApp( MyApp( initialRoute: initialRoute, @@ -35,7 +34,6 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MediaQuery( data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), child: GetMaterialApp( @@ -51,7 +49,10 @@ class MyApp extends StatelessWidget { GetPage(name: '/setting', page: () => const SettingScreen()), GetPage(name: '/signup', page: () => const SignUpScreen()), GetPage(name: '/policy', page: () => const PolicyScreen()), - GetPage(name: '/permission', page: () => const PermissionRequestScreen()), + GetPage( + name: '/permission', + page: () => const PermissionRequestScreen(), + ), ], ), ); diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 6f108118..89e6060c 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -2,31 +2,46 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../constants/app_colors.dart'; +import '../controllers/bottom_sheet_controller.dart'; +import '../controllers/map_controller.dart'; import '../controllers/my_page_controller.dart'; import '../controllers/navigation_controller.dart'; import '../controllers/ranking_controller.dart'; import '../widgets/common/naviagtion_bar.dart'; -class MainScreen extends StatelessWidget { +class MainScreen extends StatefulWidget { const MainScreen({super.key}); + @override + State createState() => _MainScreenState(); +} + +class _MainScreenState extends State { @override Widget build(BuildContext context) { Get.put(MyPageController()); Get.put(RankingController()); + Get.put(BottomSheetController()); + Get.put(MapController()); final NavigationController navigationController = Get.put(NavigationController()); return Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(kToolbarHeight), - child: Obx(() => navigationController.getCurrentAppBar()), - ), + appBar: navigationController.selectedIndex.value == 0 + ? null + : PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: Obx(() => navigationController.getCurrentAppBar()), + ), body: Obx( - () => SafeArea(child: navigationController.getCurrentPage()), + () => navigationController.getCurrentPage(), ), backgroundColor: AppColors.background, - bottomNavigationBar: const CustomBottomNavigationBar(), + bottomNavigationBar: CustomBottomNavigationBar((index) { + setState(() { + navigationController.changeIndex(index); + }); + }), ); } } diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index b6e0a4c6..77c62b4b 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -5,15 +5,17 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import '../controllers/map_controller.dart'; import '../controllers/pixel_info_controller.dart'; import '../controllers/walking_controller.dart'; -import '../widgets/map/mode_change_button.dart'; -import '../widgets/map/step_stats.dart'; +import '../widgets/map/bottom_sheet/map_bottom_sheet.dart'; +import '../widgets/map/current_location_button.dart'; +import '../widgets/map/mode_change_toggle.dart'; +import '../widgets/map/pixel_count_info.dart'; class MapScreen extends StatelessWidget { const MapScreen({super.key}); @override Widget build(BuildContext context) { - final MapController mapController = Get.put(MapController()); + final MapController mapController = Get.find(); Get.put(PixelInfoController()); Get.put(WalkingController()); @@ -29,8 +31,6 @@ class MapScreen extends StatelessWidget { Obx(() { return GoogleMap( mapType: MapType.normal, - myLocationButtonEnabled: true, - myLocationEnabled: true, initialCameraPosition: CameraPosition( target: LatLng( mapController.currentLocation.latitude!, @@ -50,16 +50,23 @@ class MapScreen extends StatelessWidget { }), Column( children: [ - const Padding( - padding: EdgeInsets.only(top: 65.0), - ), - ModeChangeButton(), - const Padding( - padding: EdgeInsets.only(bottom: 32), - child: StepStats(), + SizedBox(height: 60), + ModeChangeToggle(), + Spacer(), + Container( + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + PixelCountInfo(count: 128), + CurrentLocationButton(), + ], + ), ), + SizedBox(height: MediaQuery.of(context).size.height * 0.11), ], ), + MapBottomSheet(), ], ); } diff --git a/lib/service/location_service.dart b/lib/service/location_service.dart new file mode 100644 index 00000000..661e4380 --- /dev/null +++ b/lib/service/location_service.dart @@ -0,0 +1,17 @@ +import 'package:location/location.dart'; + +class LocationService { + static final LocationService _instance = LocationService._internal(); + final Location location = Location(); + LocationData? currentLocation; + + LocationService._internal(); + + factory LocationService() { + return _instance; + } + + initCurrentLocation() async { + currentLocation = await location.getLocation(); + } +} diff --git a/lib/widgets/common/app_bar.dart b/lib/widgets/common/app_bar.dart index f716f35e..17a3e85d 100644 --- a/lib/widgets/common/app_bar.dart +++ b/lib/widgets/common/app_bar.dart @@ -9,10 +9,7 @@ class MapAppBar extends StatelessWidget { @override Widget build(BuildContext context) { - return AppBar( - backgroundColor: AppColors.background, - title: AppBarTitle(title: "지도"), - ); + return Container(); } } diff --git a/lib/widgets/common/naviagtion_bar.dart b/lib/widgets/common/naviagtion_bar.dart index b3f00f8a..249446eb 100644 --- a/lib/widgets/common/naviagtion_bar.dart +++ b/lib/widgets/common/naviagtion_bar.dart @@ -5,27 +5,36 @@ import '../../constants/app_colors.dart'; import '../../controllers/navigation_controller.dart'; class CustomBottomNavigationBar extends GetView { - const CustomBottomNavigationBar({super.key}); + const CustomBottomNavigationBar(this.onTap, {super.key}); + + final Function(int) onTap; @override Widget build(BuildContext context) { return Obx( - () => BottomNavigationBar( - type: BottomNavigationBarType.fixed, - currentIndex: controller.selectedIndex.value, - onTap: controller.changeIndex, - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.map), label: "지도"), - BottomNavigationBarItem(icon: Icon(Icons.leaderboard), label: "랭킹"), - // BottomNavigationBarItem(icon: Icon(Icons.group), label: "그룹"), - BottomNavigationBarItem( - icon: Icon(Icons.account_circle), - label: "마이", + () => Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide(color: AppColors.backgroundThird, width: 0.5), ), - ], - selectedItemColor: AppColors.primary, - unselectedItemColor: AppColors.textSecondary, - backgroundColor: AppColors.navigationBarColor, + ), + child: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: controller.selectedIndex.value, + onTap: onTap, + items: const [ + BottomNavigationBarItem(icon: Icon(Icons.map), label: "지도"), + BottomNavigationBarItem(icon: Icon(Icons.leaderboard), label: "랭킹"), + // BottomNavigationBarItem(icon: Icon(Icons.group), label: "그룹"), + BottomNavigationBarItem( + icon: Icon(Icons.account_circle), + label: "마이", + ), + ], + selectedItemColor: AppColors.primary, + unselectedItemColor: AppColors.textSecondary, + backgroundColor: AppColors.navigationBarColor, + ), ), ); } diff --git a/lib/widgets/map/bottom_sheet/individual_history_list.dart b/lib/widgets/map/bottom_sheet/individual_history_list.dart new file mode 100644 index 00000000..2b3775ea --- /dev/null +++ b/lib/widgets/map/bottom_sheet/individual_history_list.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +import '../../../constants/app_colors.dart'; +import '../../../constants/text_styles.dart'; + +class IndividualHistoryList extends StatelessWidget { + const IndividualHistoryList({super.key, required this.visitList}); + + final List visitList; + + @override + Widget build(BuildContext context) { + return SliverList.list( + children: [ + SizedBox(height: 10), + for (int i = 0; i < visitList.length; i++) + IndividualHistoryListElement( + historyDate: visitList[i], + ), + ], + ); + } +} + +class IndividualHistoryListElement extends StatelessWidget { + IndividualHistoryListElement({super.key, required this.historyDate}); + + final DateTime historyDate; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(20, 5, 20, 5), + child: Container( + padding: EdgeInsets.symmetric(vertical: 16, horizontal: 20), + decoration: BoxDecoration( + color: AppColors.backgroundThird, + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: Row( + children: [ + Icon( + Icons.access_time_filled, + size: 16, + color: AppColors.textSecondary, + ), + SizedBox( + width: 20, + ), + Text( + '${historyDate.year}년 ${historyDate.month}월 ${historyDate.day}일', + style: TextStyles.fs17w400cTextPrimary, + ), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/map/bottom_sheet/map_bottom_sheet.dart b/lib/widgets/map/bottom_sheet/map_bottom_sheet.dart new file mode 100644 index 00000000..bdfe9e42 --- /dev/null +++ b/lib/widgets/map/bottom_sheet/map_bottom_sheet.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../constants/app_colors.dart'; +import '../../../controllers/bottom_sheet_controller.dart'; + +class MapBottomSheet extends StatelessWidget { + const MapBottomSheet({super.key}); + + @override + Widget build(BuildContext context) { + var bottomSheetController = Get.find(); + + return NotificationListener( + onNotification: (DraggableScrollableNotification notification) { + bottomSheetController.changeStepStatIfMinimized(); + return true; + }, + child: DraggableScrollableSheet( + controller: bottomSheetController.draggableController, + snap: true, + initialChildSize: 0.11, + minChildSize: 0.11, + maxChildSize: 0.6, + builder: (BuildContext context, scrollController) { + return Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: AppColors.backgroundSecondary, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(32), + topRight: Radius.circular(32), + ), + ), + child: CustomScrollView( + controller: scrollController, + slivers: [ + SliverToBoxAdapter( + child: Center( + child: Container( + decoration: BoxDecoration( + color: AppColors.backgroundThird, + borderRadius: + const BorderRadius.all(Radius.circular(10)), + ), + height: 4, + width: 40, + margin: const EdgeInsets.fromLTRB(0, 10, 0, 0), + ), + ), + ), + SliverToBoxAdapter( + child: Obx( + () => Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: bottomSheetController.getHeader(), + ), + ), + ), + Obx(() { + return bottomSheetController.getBody(); + }), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/widgets/map/bottom_sheet/pixel_info_header.dart b/lib/widgets/map/bottom_sheet/pixel_info_header.dart new file mode 100644 index 00000000..88371df7 --- /dev/null +++ b/lib/widgets/map/bottom_sheet/pixel_info_header.dart @@ -0,0 +1,42 @@ +import 'package:flutter/cupertino.dart'; + +import '../../../constants/text_styles.dart'; +import '../../../enums/pixel_mode.dart'; + +class PixelInfoHeader extends StatelessWidget { + const PixelInfoHeader({ + super.key, + required this.address, + required this.visitCount, + required this.mode, + }); + + final String? address; + final int visitCount; + final PixelMode mode; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Text( + address ?? "대한민국", + style: TextStyles.fs24w900cTextPrimary, + ), + Spacer(), + Text( + createSubPixelInfo(), + style: TextStyles.fs17w400cTextSecondary, + ), + ], + ); + } + + createSubPixelInfo() { + if (mode == PixelMode.individualHistory) { + return "$visitCount번째 방문"; + } else if (mode == PixelMode.individualMode) { + return "오늘 $visitCount명이 점령"; + } + } +} diff --git a/lib/widgets/map/bottom_sheet/step_stats.dart b/lib/widgets/map/bottom_sheet/step_stats.dart new file mode 100644 index 00000000..d9105feb --- /dev/null +++ b/lib/widgets/map/bottom_sheet/step_stats.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +import '../../../constants/text_styles.dart'; +import '../../../controllers/walking_controller.dart'; + +class StepStats extends StatelessWidget { + const StepStats({super.key}); + + @override + Widget build(BuildContext context) { + var walkingController = Get.find(); + return Obx( + () => Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + NumberFormat('###,###,###') + .format(walkingController.currentStep.value), + style: TextStyles.fs24w900cTextPrimary, + ), + Text( + ' 걸음', + style: TextStyles.fs17w400cTextSecondary, + ), + Spacer(), + Text( + '${walkingController.getCurrentCalorie()} kcal · ${walkingController.getCurrentTravelDistance()}km', + style: TextStyles.fs17w400cTextSecondary, + ), + ], + ), + ); + } +} + +// Todo: 추후에 대시보드 등의 형식으로 변경 필요 +class StepStatsBody extends StatelessWidget { + const StepStatsBody({super.key}); + + @override + Widget build(BuildContext context) { + return SliverToBoxAdapter( + child: SizedBox( + width: 10, + ), + ); + } +} diff --git a/lib/widgets/map/bottom_sheet/visited_user_list.dart b/lib/widgets/map/bottom_sheet/visited_user_list.dart new file mode 100644 index 00000000..f22140bd --- /dev/null +++ b/lib/widgets/map/bottom_sheet/visited_user_list.dart @@ -0,0 +1,190 @@ +import 'package:flutter/cupertino.dart'; +import 'package:intl/intl.dart'; + +import '../../../constants/app_colors.dart'; +import '../../../constants/text_styles.dart'; +import '../../../models/individual_mode_pixel_info.dart'; + +class VisitedUserList extends StatelessWidget { + final PixelOwnerUser pixelOwnerUser; + final List visitList; + + const VisitedUserList({ + super.key, + required this.visitList, + required this.pixelOwnerUser, + }); + + @override + Widget build(BuildContext context) { + return SliverList.list( + children: [ + SizedBox(height: 10), + OwnerInfo( + pixelOwnerUser: pixelOwnerUser, + ), + if (visitList.isEmpty) NoVisitedUserMessage(), + for (int i = 0; i < visitList.length; i++) + VisitedUserListElement( + visitedUser: visitList[i], + ), + ], + ); + } +} + +class OwnerInfo extends StatelessWidget { + const OwnerInfo({ + super.key, + required this.pixelOwnerUser, + }); + + final PixelOwnerUser pixelOwnerUser; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Container( + decoration: BoxDecoration( + color: AppColors.backgroundThird, + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + padding: EdgeInsets.all(20), + child: Row( + children: [ + ClipOval( + child: pixelOwnerUser.profileImageUrl != null + ? Image.network( + pixelOwnerUser.profileImageUrl!, + cacheWidth: 100, + width: 44, + height: 44, + fit: BoxFit.cover, + ) + : Image.asset( + 'assets/images/default_profile_image.png', + width: 44, + height: 44, + ), + ), + SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + pixelOwnerUser.nickname ?? "알 수 없음", + style: TextStyles.fs17w600cTextPrimary, + ), + Row( + children: [ + Text( + '${NumberFormat('###,###,###').format(pixelOwnerUser.currentPixelCount)}px', + style: TextStyles.fs14w500cPrimary, + ), + Text( + ' · 누적 ${NumberFormat('###,###,###').format(pixelOwnerUser.accumulatePixelCount)}px', + style: TextStyles.fs14w500cTextSecondary, + ), + ], + ), + ], + ), + ), + Image.asset( + 'assets/images/current_pixel_icon.png', + width: 44, + height: 44, + ), + ], + ), + ), + ); + } +} + +class VisitedUserListElement extends StatelessWidget { + const VisitedUserListElement({ + super.key, + required this.visitedUser, + }); + + final VisitList visitedUser; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 40.0), + child: Container( + padding: EdgeInsets.symmetric(vertical: 20), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: AppColors.backgroundThird, + width: 0.5, + ), + ), + ), + child: Row( + children: [ + ClipOval( + child: visitedUser.profileImageUrl != null + ? Image.network( + visitedUser.profileImageUrl!, + cacheWidth: 100, + width: 44, + height: 44, + fit: BoxFit.cover, + ) + : Image.asset( + 'assets/images/default_profile_image.png', + width: 44, + height: 44, + ), + ), + SizedBox(width: 10), + Expanded( + child: Text( + visitedUser.nickname ?? "알 수 없음", + style: TextStyles.fs17w600cTextPrimary, + ), + ), + ], + ), + ), + ); + } +} + +class NoVisitedUserMessage extends StatelessWidget { + const NoVisitedUserMessage({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(20.0), + child: Container( + padding: const EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: AppColors.backgroundThird, + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + child: Center( + child: Column( + children: [ + Text( + "오늘 방문한 사람이 없습니다.", + style: TextStyles.fs20w700cTextPrimary, + ), + Text( + "걸어서 픽셀을 차지 해보세요!", + style: TextStyles.fs17w700cPrimary, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/map/current_location_button.dart b/lib/widgets/map/current_location_button.dart new file mode 100644 index 00000000..40044a82 --- /dev/null +++ b/lib/widgets/map/current_location_button.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../constants/app_colors.dart'; +import '../../controllers/map_controller.dart'; + +class CurrentLocationButton extends StatefulWidget { + const CurrentLocationButton({super.key}); + + @override + createState() => _CurrentLocationButtonState(); +} + +class _CurrentLocationButtonState extends State { + final MapController mapController = Get.find(); + Color _buttonColor = AppColors.backgroundSecondary; // 기본 배경 색상 + final Color _pressedColor = Colors.grey; // 눌렀을 때의 배경 색상 + + void _onPointerDown(DragDownDetails event) { + setState(() { + _buttonColor = _pressedColor; + }); + } + + void _onPointerUp() { + setState(() { + _buttonColor = AppColors.backgroundSecondary; + }); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + mapController.focusOnCurrentLocation(); + }, + onPanDown: _onPointerDown, + onPanEnd: (details) => _onPointerUp(), + onPanCancel: () => _onPointerUp(), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + color: _buttonColor, + ), + padding: EdgeInsets.all(10), + child: Icon( + Icons.my_location, + color: Colors.white, + size: 20, // 아이콘 크기 + ), + ), + ); + } +} diff --git a/lib/widgets/map/individual_history_pixel_info_bottom_sheet.dart b/lib/widgets/map/individual_history_pixel_info_bottom_sheet.dart deleted file mode 100644 index 9a800e6c..00000000 --- a/lib/widgets/map/individual_history_pixel_info_bottom_sheet.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../models/individual_history_pixel_info.dart'; -import 'individual_visit_history_list_view.dart'; - -class IndividualHistoryPixelInfoBottomSheet extends StatelessWidget { - const IndividualHistoryPixelInfoBottomSheet( - {super.key, required this.pixelInfo,}); - - final IndividualHistoryPixelInfo pixelInfo; - - @override - Widget build(BuildContext context) { - return Container( - decoration: const BoxDecoration( - color: Color(0xFF374957), - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), - ), - child: SizedBox( - height: 400, - width: 380, - child: Padding( - padding: const EdgeInsets.fromLTRB(30, 20, 30, 0), - child: Column( - children: [ - Align( - alignment: Alignment.topLeft, - child: Text( - '${pixelInfo.address ?? '대한민국'} ${pixelInfo.addressNumber ?? 'n'}번째 픽셀', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - Align( - alignment: Alignment.topLeft, - child: Text( - '${pixelInfo.visitCount}번 방문했어요!', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white,), - ), - ), - SizedBox( - height: 10, - ), - IndividualVisitHistoryListView(visitList: pixelInfo.visitList!), - ], - ), - ), - ), - ); - } -} diff --git a/lib/widgets/map/individual_mode_pixel_info_bottom_sheet.dart b/lib/widgets/map/individual_mode_pixel_info_bottom_sheet.dart deleted file mode 100644 index 44c1ee9f..00000000 --- a/lib/widgets/map/individual_mode_pixel_info_bottom_sheet.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../models/individual_mode_pixel_info.dart'; -import 'pixel_owner_info.dart'; -import 'visited_user_list_view.dart'; - -class IndividualModePixelInfoBottomSheet extends StatelessWidget { - IndividualModePixelInfoBottomSheet({super.key, required this.pixelInfo,}); - - final IndividualModePixelInfo pixelInfo; - - @override - Widget build(BuildContext context) { - return Container( - decoration: const BoxDecoration( - color: Color(0xFF374957), - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), - ), - child: SizedBox( - height: 400, - width: 380, - child: Padding( - padding: const EdgeInsets.fromLTRB(30, 20, 30, 0), - child: Column( - children: [ - Align( - alignment: Alignment.topLeft, - child: Text( - '${pixelInfo.address ?? '대한민국'} ${pixelInfo.addressNumber ?? 'n'}번째 픽셀', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - SizedBox( - height: 10, - ), - PixelOwnerInfo(pixelOwnerUser: pixelInfo.pixelOwnerUser!,), - const SizedBox(height: 10), - Align( - alignment: Alignment.topLeft, - child: Text( - '오늘 ${pixelInfo.visitCount}명이 차지했었어요!', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - SizedBox( - height: 10, - ), - VisitedUserListView(visitList: pixelInfo.visitList!,), - ], - ), - ), - ), - ); - } -} diff --git a/lib/widgets/map/individual_visit_history_list_view.dart b/lib/widgets/map/individual_visit_history_list_view.dart deleted file mode 100644 index ca818a4f..00000000 --- a/lib/widgets/map/individual_visit_history_list_view.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; - -class IndividualVisitHistoryListView extends StatelessWidget { - const IndividualVisitHistoryListView({super.key, required this.visitList}); - - final List visitList; - - @override - Widget build(BuildContext context) { - return Expanded( - child: Container( - decoration: const BoxDecoration( - color: Color(0xFF8B8B8B), - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: ListView.builder( - itemCount: visitList.length, - itemBuilder: (context, int index) { - return Padding( - padding: const EdgeInsets.all(5.0), - child: Text( - "${visitList[index].year}년 ${visitList[index].month}월 ${visitList[index].day}일", - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ); - }, - shrinkWrap: true, - ), - ), - ), - ); - } -} diff --git a/lib/widgets/map/mode_change_button.dart b/lib/widgets/map/mode_change_button.dart deleted file mode 100644 index 75bbdd80..00000000 --- a/lib/widgets/map/mode_change_button.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -import '../../controllers/map_controller.dart'; -import '../../enums/pixel_mode.dart'; - -class ModeChangeButton extends StatelessWidget { - const ModeChangeButton({super.key}); - - @override - Widget build(BuildContext context) { - var mapController = Get.find(); - - return Expanded( - child: Align( - alignment: Alignment.topRight, - child: DropdownButtonHideUnderline( - child: Obx(() { - return DropdownButton2( - alignment: Alignment.center, - isExpanded: true, - items: PixelMode.values - .map( - (PixelMode pixelMode) => DropdownMenuItem( - value: pixelMode.koreanName, - child: Text( - pixelMode.koreanName, - style: const TextStyle( - fontSize: 14, - color: Colors.white, - ), - ), - ), - ) - .toList(), - onChanged: (pixelModeKrName) { - mapController.changePixelMode(pixelModeKrName!); - }, - value: mapController.currentPixelMode.value.koreanName, - buttonStyleData: ButtonStyleData( - padding: EdgeInsets.symmetric(horizontal: 16), - height: 40, - width: 120, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: Colors.blueAccent, - ), - ), - dropdownStyleData: DropdownStyleData( - maxHeight: 200, - width: 120, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: Colors.blueAccent, - ), - scrollbarTheme: ScrollbarThemeData( - radius: const Radius.circular(40), - ), - ), - menuItemStyleData: const MenuItemStyleData( - height: 40, - ), - ); - }), - ), - ), - ); - } -} diff --git a/lib/widgets/map/mode_change_toggle.dart b/lib/widgets/map/mode_change_toggle.dart new file mode 100644 index 00000000..c60efb63 --- /dev/null +++ b/lib/widgets/map/mode_change_toggle.dart @@ -0,0 +1,58 @@ +import 'package:animated_toggle_switch/animated_toggle_switch.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../constants/app_colors.dart'; +import '../../controllers/map_controller.dart'; + +class ModeChangeToggle extends StatelessWidget { + ModeChangeToggle({super.key}); + + @override + Widget build(BuildContext context) { + final MapController mapController = Get.find(); + return Obx(() { + return Center( + child: AnimatedToggleSwitch.size( + current: mapController.getSelectedType(), + style: ToggleStyle( + backgroundColor: Color(0xF21D1D1D), + indicatorColor: AppColors.buttonColor, + borderColor: Colors.transparent, + borderRadius: BorderRadius.circular(24.0), + indicatorBorderRadius: BorderRadius.circular(24.0), + ), + // values: const [0, 1, 2], + values: const [0, 1], + iconOpacity: 1.0, + selectedIconScale: 1.0, + indicatorSize: Size.fromWidth(90), + height: 40, + iconAnimationType: AnimationType.onHover, + styleAnimationType: AnimationType.onHover, + spacing: 2.0, + customIconBuilder: (context, local, global) { + // final text = const ['개인 기록', '개인전', '그룹전'][local.index]; + final text = const ['개인 기록', '개인전'][local.index]; + return Center( + child: Text( + text, + style: TextStyle( + color: Color.lerp( + Colors.white, + Colors.black, + local.animationValue, + ), + fontSize: 14, + fontWeight: FontWeight.w700, + ), + ), + ); + }, + borderWidth: 0.0, + onChanged: (i) => mapController.changePixelMode(i), + ), + ); + }); + } +} diff --git a/lib/widgets/map/pixel_count_info.dart b/lib/widgets/map/pixel_count_info.dart new file mode 100644 index 00000000..85dcdbcc --- /dev/null +++ b/lib/widgets/map/pixel_count_info.dart @@ -0,0 +1,51 @@ +import 'package:flutter/cupertino.dart'; +import 'package:get/get.dart'; + +import '../../constants/app_colors.dart'; +import '../../constants/text_styles.dart'; +import '../../controllers/map_controller.dart'; +import '../../enums/pixel_mode.dart'; + +class PixelCountInfo extends StatelessWidget { + const PixelCountInfo({ + super.key, + required this.count, + }); + + final int count; + + @override + Widget build(BuildContext context) { + final MapController mapController = Get.find(); + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + color: AppColors.backgroundSecondary, + ), + padding: EdgeInsets.symmetric(vertical: 10, horizontal: 8), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Obx(() { + return Image.asset( + mapController.currentPixelMode.value == + PixelMode.individualHistory + ? "assets/images/accumulate_pixel_icon.png" + : "assets/images/current_pixel_icon.png", + width: 28, + ); + }), + SizedBox( + width: 10, + ), + Obx(() { + return Text( + "${mapController.getPixelCount()}", + style: TextStyles.fs17w600cTextPrimary, + ); + }), + ], + ), + ); + } +} diff --git a/lib/widgets/map/pixel_owner_info.dart b/lib/widgets/map/pixel_owner_info.dart deleted file mode 100644 index e55c3b50..00000000 --- a/lib/widgets/map/pixel_owner_info.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../models/individual_mode_pixel_info.dart'; - -class PixelOwnerInfo extends StatelessWidget { - const PixelOwnerInfo({super.key, required this.pixelOwnerUser}); - - final PixelOwnerUser pixelOwnerUser; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ClipOval( - child: pixelOwnerUser.profileImageUrl != null - ? Image.network( - pixelOwnerUser.profileImageUrl!, - width: 50, - height: 50, - fit: BoxFit.cover, - ) - : Image.asset( - 'assets/images/default_profile_image.png', - width: 50, - height: 50, - ), - ), - Text( - pixelOwnerUser.nickname!, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - Text( - pixelOwnerUser.currentPixelCount.toString(), - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - Text( - pixelOwnerUser.accumulatePixelCount.toString(), - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ], - ); - } -} diff --git a/lib/widgets/map/step_stats.dart b/lib/widgets/map/step_stats.dart deleted file mode 100644 index 7d0999af..00000000 --- a/lib/widgets/map/step_stats.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -import '../../controllers/walking_controller.dart'; - -class StepStats extends StatelessWidget { - const StepStats({super.key}); - - @override - Widget build(BuildContext context) { - var walkController = Get.find(); - return Obx( - () => Column( - children: [ - Text( - '${walkController.getCurrentStep()} 걸음', - style: TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - SizedBox( - height: 5, - ), - Container( - padding: EdgeInsets.symmetric(horizontal: 7, vertical: 5), - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(20.0), - ), - child: Text( - '${walkController.getCurrentCalorie()} kcal | ${walkController.getCurrentTravelDistance()}km', - style: - TextStyle(color: Colors.white, fontWeight: FontWeight.bold), - ), - ), - ], - ), - ); - } -} diff --git a/lib/widgets/map/visited_user_list_view.dart b/lib/widgets/map/visited_user_list_view.dart deleted file mode 100644 index 43cb2ea0..00000000 --- a/lib/widgets/map/visited_user_list_view.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../models/individual_mode_pixel_info.dart'; - -class VisitedUserListView extends StatelessWidget { - final List visitList; - - VisitedUserListView({super.key, required this.visitList}); - - @override - Widget build(BuildContext context) { - return Expanded( - child: Container( - decoration: const BoxDecoration( - color: Color(0xFF8B8B8B), - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: ListView.builder( - itemCount: visitList.length, - itemBuilder: (context, int index) { - return Padding( - padding: const EdgeInsets.all(5.0), - child: Row( - children: [ - ClipOval( - child: visitList[index].profileImageUrl != null - ? Image.network( - visitList[index].profileImageUrl!, - width: 50, - height: 50, - fit: BoxFit.cover, - ) - : Image.asset( - 'assets/images/default_profile_image.png', - width: 50, - height: 50, - ), - ), - SizedBox( - width: 20, - ), - Text( - visitList[index].nickname!, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ], - ), - ); - }, - shrinkWrap: true, - ), - ), - ), - ); - } -} diff --git a/lib/widgets/my_page/pixel_dash_board.dart b/lib/widgets/my_page/pixel_dash_board.dart index 8a11d824..ef440258 100644 --- a/lib/widgets/my_page/pixel_dash_board.dart +++ b/lib/widgets/my_page/pixel_dash_board.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../controllers/my_page_controller.dart'; +import '../../controllers/map_controller.dart'; import '../../controllers/walking_controller.dart'; import 'pixel_dash_board_widget.dart'; @@ -10,7 +10,7 @@ class PixelDashBoard extends StatelessWidget { super.key, }); - final MyPageController myPageController = Get.find(); + final MapController mapController = Get.find(); final WalkingController walkingController = Get.find(); @override @@ -21,13 +21,13 @@ class PixelDashBoard extends StatelessWidget { PixelDashBoardWidget( textValue: "현재 px", iconImageUrl: "assets/images/current_pixel_icon.png", - countValue: myPageController.currentPixelCount, + countValue: mapController.currentPixelCount, ), SizedBox(width: 20), PixelDashBoardWidget( textValue: "누적 px", iconImageUrl: "assets/images/accumulate_pixel_icon.png", - countValue: myPageController.accumulatePixelCount, + countValue: mapController.accumulatePixelCount, ), ], ); diff --git a/lib/widgets/pixel.dart b/lib/widgets/pixel.dart index 986bedf5..3f82cc47 100644 --- a/lib/widgets/pixel.dart +++ b/lib/widgets/pixel.dart @@ -1,15 +1,16 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import '../controllers/bottom_sheet_controller.dart'; import '../controllers/pixel_info_controller.dart'; import '../models/individual_history_pixel.dart'; import '../models/individual_history_pixel_info.dart'; import '../models/individual_mode_pixel.dart'; import '../models/individual_mode_pixel_info.dart'; import '../utils/user_manager.dart'; -import 'map/individual_history_pixel_info_bottom_sheet.dart'; -import 'map/individual_mode_pixel_info_bottom_sheet.dart'; class Pixel extends Polygon { static const double latPerPixel = 0.000724; @@ -53,21 +54,17 @@ class Pixel extends Polygon { topLeftPoint: LatLng(pixel.latitude, pixel.longitude), ), fillColor: isMyPixel - ? Colors.blue.withOpacity(0.3) - : Colors.red.withOpacity(0.3), - strokeColor: isMyPixel ? Colors.blue : Colors.red, - strokeWidth: 1, + ? Color(0xFF0DF69E) + .withOpacity(0.3 + (Random().nextDouble() * (0.6 - 0.3))) + : Colors.red.withOpacity(0.3 + (Random().nextDouble() * (0.6 - 0.3))), + strokeColor: isMyPixel ? Color(0xFF0DF69E) : Colors.red, + strokeWidth: 2, onTap: (int pixelId) async { IndividualModePixelInfo pixelInfo = await Get.find() .getIndividualModePixelInfo(pixelId); - Get.bottomSheet( - IndividualModePixelInfoBottomSheet(pixelInfo: pixelInfo), - clipBehavior: Clip.hardEdge, - backgroundColor: Colors.white, - enterBottomSheetDuration: Duration(milliseconds: 100), - exitBottomSheetDuration: Duration(milliseconds: 100), - ); + Get.find() + .showIndividualModePixelInfo(pixelInfo); }, ); } @@ -83,23 +80,18 @@ class Pixel extends Polygon { points: _getRectangleFromLatLng( topLeftPoint: LatLng(pixel.latitude, pixel.longitude), ), - fillColor: Colors.blue.withOpacity(0.3), - strokeColor: Colors.blue, - strokeWidth: 1, + fillColor: Color(0xFF0DF69E) + .withOpacity(0.3 + (Random().nextDouble() * (0.6 - 0.3))), + strokeColor: Color(0xFF0DF69E), + strokeWidth: 2, onTap: (int pixelId) async { IndividualHistoryPixelInfo pixelInfo = await Get.find().getIndividualHistoryPixelInfo( pixelId, UserManager().getUserId()!, ); - - Get.bottomSheet( - IndividualHistoryPixelInfoBottomSheet(pixelInfo: pixelInfo), - clipBehavior: Clip.hardEdge, - backgroundColor: Colors.white, - enterBottomSheetDuration: Duration(milliseconds: 100), - exitBottomSheetDuration: Duration(milliseconds: 100), - ); + Get.find() + .showIndividualHistoryPixelInfo(pixelInfo); }, ); } diff --git a/pubspec.yaml b/pubspec.yaml index e71c1fbd..0c39417a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,4 +53,4 @@ flutter: flutter_native_splash: color: "#000000" - image: assets/images/groundflip_logo.png \ No newline at end of file + image: "assets/images/ground_flip_app_icon.png" \ No newline at end of file