diff --git a/.gitignore b/.gitignore index 29a3a50..79c113f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e1ca574..aa49780 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 1d6d19b..77f383c 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "com.android.application" version "8.2.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.10" apply false } include ":app" diff --git a/assets/bunked.png b/assets/bunked.png new file mode 100644 index 0000000..98fa16e Binary files /dev/null and b/assets/bunked.png differ diff --git a/assets/cancelled.png b/assets/cancelled.png new file mode 100644 index 0000000..5ed8b19 Binary files /dev/null and b/assets/cancelled.png differ diff --git a/assets/present.png b/assets/present.png new file mode 100644 index 0000000..2e40b11 Binary files /dev/null and b/assets/present.png differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 85fd7a9..8f830a8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -33,4 +33,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6199377..33d8405 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -11,11 +11,11 @@ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 78CA3B8C1258D30C1B468E48 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF5F228CFF479CA16E75E4E /* Pods_Runner.framework */; }; + 7D95F2611EED09C4BC280A07 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A7A67FA6D06C8F18C0F4B9F /* Pods_Runner.framework */; }; 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 */; }; - EA2A13BD4AAD20F195E81C45 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E53F2FEAFCE789B41710ADC /* Pods_RunnerTests.framework */; }; + C48CB6DF3049AEB7FA525E29 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F7F69BB54C179CC2820C3F /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,17 +42,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 09B5B2EC4AD0F34DD1DD51BB /* 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 = ""; }; + 013860BAAF11F9F127838ECE /* 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 = ""; }; 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 = ""; }; + 21F7F69BB54C179CC2820C3F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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; }; + 3A7A67FA6D06C8F18C0F4B9F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4BE8013C5BA25441CB6A3BB1 /* 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 = ""; }; + 3E55794EE01B1A5EB564D842 /* 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 = ""; }; 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 = ""; }; - 8A62884372337180989E41EA /* 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 = ""; }; 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; }; @@ -60,11 +61,10 @@ 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 = ""; }; - 9E53F2FEAFCE789B41710ADC /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B27684C6B76A4DA96004DBF9 /* 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 = ""; }; - B3C1D4624F64E6E408A6F97D /* 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 = ""; }; - B4E4542A08E8ABEB4F13CED1 /* 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 = ""; }; - DCF5F228CFF479CA16E75E4E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B0AFF00FC0A631FF8C4E3535 /* 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 = ""; }; + D8B6734CCAEBD74F2D147DD9 /* 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 = ""; }; + E44BC0CE27CD963FF60F41B5 /* 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 = ""; }; + FFB5C59B8C726A2936AA1710 /* 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,7 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EA2A13BD4AAD20F195E81C45 /* Pods_RunnerTests.framework in Frameworks */, + C48CB6DF3049AEB7FA525E29 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 78CA3B8C1258D30C1B468E48 /* Pods_Runner.framework in Frameworks */, + 7D95F2611EED09C4BC280A07 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,11 +95,11 @@ path = RunnerTests; sourceTree = ""; }; - 71A98B7CFFE6D29039C79488 /* Frameworks */ = { + 7FB23215744B69290EFE63BB /* Frameworks */ = { isa = PBXGroup; children = ( - DCF5F228CFF479CA16E75E4E /* Pods_Runner.framework */, - 9E53F2FEAFCE789B41710ADC /* Pods_RunnerTests.framework */, + 3A7A67FA6D06C8F18C0F4B9F /* Pods_Runner.framework */, + 21F7F69BB54C179CC2820C3F /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -107,12 +107,12 @@ 8B8EEA2F4298F832B04937D9 /* Pods */ = { isa = PBXGroup; children = ( - 8A62884372337180989E41EA /* Pods-Runner.debug.xcconfig */, - B27684C6B76A4DA96004DBF9 /* Pods-Runner.release.xcconfig */, - 09B5B2EC4AD0F34DD1DD51BB /* Pods-Runner.profile.xcconfig */, - 4BE8013C5BA25441CB6A3BB1 /* Pods-RunnerTests.debug.xcconfig */, - B3C1D4624F64E6E408A6F97D /* Pods-RunnerTests.release.xcconfig */, - B4E4542A08E8ABEB4F13CED1 /* Pods-RunnerTests.profile.xcconfig */, + D8B6734CCAEBD74F2D147DD9 /* Pods-Runner.debug.xcconfig */, + B0AFF00FC0A631FF8C4E3535 /* Pods-Runner.release.xcconfig */, + 013860BAAF11F9F127838ECE /* Pods-Runner.profile.xcconfig */, + 3E55794EE01B1A5EB564D842 /* Pods-RunnerTests.debug.xcconfig */, + E44BC0CE27CD963FF60F41B5 /* Pods-RunnerTests.release.xcconfig */, + FFB5C59B8C726A2936AA1710 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -136,7 +136,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, 8B8EEA2F4298F832B04937D9 /* Pods */, - 71A98B7CFFE6D29039C79488 /* Frameworks */, + 7FB23215744B69290EFE63BB /* Frameworks */, ); sourceTree = ""; }; @@ -171,7 +171,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 85BA5D1076183205F8381412 /* [CP] Check Pods Manifest.lock */, + E43C7C1A5ECBB369C2D6337E /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 2DED6C73C70465E27F295B49 /* Frameworks */, @@ -190,14 +190,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 7CDE82C4EF4AFEF23B40EAC6 /* [CP] Check Pods Manifest.lock */, + 8EC95ED5BDCED3778A800D4D /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - BE21667C6FF11987B726B421 /* [CP] Embed Pods Frameworks */, + 950CFE072B43DA02C69DAA2B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -285,7 +285,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 7CDE82C4EF4AFEF23B40EAC6 /* [CP] Check Pods Manifest.lock */ = { + 8EC95ED5BDCED3778A800D4D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -307,26 +307,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 85BA5D1076183205F8381412 /* [CP] Check Pods Manifest.lock */ = { + 950CFE072B43DA02C69DAA2B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { @@ -344,21 +339,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - BE21667C6FF11987B726B421 /* [CP] Embed Pods Frameworks */ = { + E43C7C1A5ECBB369C2D6337E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -435,6 +435,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -487,7 +488,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4BE8013C5BA25441CB6A3BB1 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 3E55794EE01B1A5EB564D842 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -505,7 +506,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B3C1D4624F64E6E408A6F97D /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = E44BC0CE27CD963FF60F41B5 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -521,7 +522,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B4E4542A08E8ABEB4F13CED1 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = FFB5C59B8C726A2936AA1710 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -559,6 +560,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -616,6 +618,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/lib/controllers/auth/login_controller.dart b/lib/controllers/auth/login_controller.dart index 6f9b038..b1b7919 100644 --- a/lib/controllers/auth/login_controller.dart +++ b/lib/controllers/auth/login_controller.dart @@ -8,7 +8,8 @@ import 'package:http/http.dart' as http; import 'dart:io'; class LoginController extends GetxController { - final CourseSummaryController courseSummaryController = Get.put(CourseSummaryController()); + final CourseSummaryController courseSummaryController = + Get.put(CourseSummaryController()); final _storage = const FlutterSecureStorage(); var isLogged = false.obs; @@ -18,34 +19,37 @@ class LoginController extends GetxController { Future getToken() async { return await _storage.read(key: 'token'); } -Future logoutfunction() async { - var headers = { HttpHeaders.authorizationHeader: "Token ${await getToken()}"}; - try { - var url = Uri.parse( - ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.logoutEmail); - http.Response response = - await http.post(url, headers: headers); - if (response.statusCode == 200) { - await _storage.delete(key: 'token'); - isLogged.value = false; - } else { - throw jsonDecode(response.body); - } - } catch (error) { - Get.back(); - showDialog( - context: Get.context!, - builder: (context) { - return SimpleDialog( - title: const Text('Error'), - contentPadding: const EdgeInsets.all(20), - children: [Text(error.toString())], - ); - }); + Future logoutfunction() async { + var headers = { + HttpHeaders.authorizationHeader: "Token ${await getToken()}" + }; + try { + var url = Uri.parse( + ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.logoutEmail); + http.Response response = await http.post(url, headers: headers); + if (response.statusCode == 200) { + await _storage.delete(key: 'token'); + await _storage.delete(key: 'onboarding'); + isLogged.value = false; + } else { + throw jsonDecode(response.body); } - return isLogged.value; + } catch (error) { + Get.back(); + showDialog( + context: Get.context!, + builder: (context) { + return SimpleDialog( + title: const Text('Error'), + contentPadding: const EdgeInsets.all(20), + children: [Text(error.toString())], + ); + }); } + return isLogged.value; + } + Future loginFunction() async { var headers = {"Content-Type": "application/json"}; try { @@ -80,6 +84,6 @@ Future logoutfunction() async { ); }); } - return isLogged.value ; + return isLogged.value; + } } -} \ No newline at end of file diff --git a/lib/controllers/auth/signup_controller.dart b/lib/controllers/auth/signup_controller.dart index 6da9348..13c0abb 100644 --- a/lib/controllers/auth/signup_controller.dart +++ b/lib/controllers/auth/signup_controller.dart @@ -28,7 +28,7 @@ class SignupController extends GetxController { if (response.statusCode == 201) { usernameController.clear(); passwordController.clear(); - Get.off(const AuthScreen()); + Get.off(const LoginScreen()); } else { throw jsonDecode(response.body) ?? 'Unknown Error occured' ; diff --git a/lib/controllers/navigation/navigation_controller.dart b/lib/controllers/navigation/navigation_controller.dart index 2630096..b0287e9 100644 --- a/lib/controllers/navigation/navigation_controller.dart +++ b/lib/controllers/navigation/navigation_controller.dart @@ -1,3 +1,5 @@ +import 'package:bunk_mate/screens/Status/status_page.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; class NavigationController extends GetxController { diff --git a/lib/controllers/onBoard/on_board_controller.dart b/lib/controllers/onBoard/on_board_controller.dart index 994c302..8bcb91f 100644 --- a/lib/controllers/onBoard/on_board_controller.dart +++ b/lib/controllers/onBoard/on_board_controller.dart @@ -39,7 +39,7 @@ class OnBoardController extends GetxController { ); _service.submitTimetable(timetable).then((_) { Get.snackbar("Success", "TimeTable has been created"); - Get.off(const Navigation()); + Get.off( Navigation()); }).catchError((error) { Get.snackbar("Error", "Could not create timetableld"); }); diff --git a/lib/main.dart b/lib/main.dart index c417f83..eda855f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,7 +15,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { const storage = FlutterSecureStorage(); - WidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); return FutureBuilder( future: storage.read(key: 'token'), builder: (BuildContext context, AsyncSnapshot snapshot) { @@ -37,7 +37,7 @@ class MyApp extends StatelessWidget { ColorScheme.fromSeed(seedColor: Colors.greenAccent), useMaterial3: true, ), - home: const Navigation(), + home: Navigation(), ); } else { return GetMaterialApp( @@ -49,7 +49,7 @@ class MyApp extends StatelessWidget { ColorScheme.fromSeed(seedColor: Colors.greenAccent), useMaterial3: true, ), - home: const AuthScreen(), + home: const LoginScreen(), ); } } diff --git a/lib/screens/Status/status_page.dart b/lib/screens/Status/status_page.dart index 8c3ef58..aaf255a 100644 --- a/lib/screens/Status/status_page.dart +++ b/lib/screens/Status/status_page.dart @@ -1,20 +1,33 @@ import 'package:bunk_mate/controllers/status/status_controller.dart'; +import 'package:bunk_mate/services/storage_service.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:line_icons/line_icons.dart'; import 'package:intl/intl.dart'; +import 'package:onboarding_overlay/onboarding_overlay.dart'; -class StatusView extends StatelessWidget { +class StatusView extends StatefulWidget { + final FocusNode focusNode; + StatusView({super.key, required this.focusNode}); + + @override + State createState() => StatusViewState(); +} + +class StatusViewState extends State { final StatusController controller = Get.put(StatusController(apiUrl: 'https://api.bunkmate.college')); + final Color bgColor = const Color(0xFF121212); + final Color cardColor = const Color(0xFF1E1E1E); + final Color accentColor = const Color(0xFF4CAF50); + final Color textColor = Colors.white; - final Color secondaryTextColor = Colors.white70; - StatusView({super.key}); + final Color secondaryTextColor = Colors.white70; @override Widget build(BuildContext context) { @@ -46,7 +59,15 @@ class StatusView extends StatelessWidget { ), ), const Spacer(), - _buildRewindTimeButton(context), + Row( + children: [ + _buildRewindTimeButton(context), + const SizedBox( + width: 10, + ), + _buildHelpButon(context) + ], + ), ], ), body: Obx( @@ -57,6 +78,41 @@ class StatusView extends StatelessWidget { ); } + void showPageGuide() async { + bool isOnboardingCompleted = await StorageService().getOnboardingComplete(); + final OnboardingState? onboarding = Onboarding.of(context); + if (onboarding != null && !isOnboardingCompleted) { + onboarding.show(); + await StorageService().setOnboardingComplete(); + } else { + print("Onboarding is null"); + } + } + + Widget _buildHelpButon(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(right: 20), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: bgColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + ), + onPressed: () { + final OnboardingState? onboarding = Onboarding.of(context); + if (onboarding != null) { + onboarding.show(); + } else { + print("Onboarding is null"); + } + }, + child: const Icon(Icons.help_outline, size: 40, color: Colors.white), + ), + ); + } + Widget _buildRewindTimeButton(BuildContext context) { return Padding( padding: const EdgeInsets.only(right: 20), @@ -79,6 +135,11 @@ class StatusView extends StatelessWidget { padding: const EdgeInsets.all(20.0), itemCount: controller.courses.length, itemBuilder: (BuildContext context, int index) { + if (index == 0) + return Focus( + focusNode: widget.focusNode, + child: _buildCourseItem(controller.courses[index]), + ); return _buildCourseItem(controller.courses[index]); }, ); diff --git a/lib/screens/auth/login_screen.dart b/lib/screens/auth/login_screen.dart index 56f96a4..7a826bd 100644 --- a/lib/screens/auth/login_screen.dart +++ b/lib/screens/auth/login_screen.dart @@ -6,14 +6,14 @@ import 'package:bunk_mate/controllers/auth/login_controller.dart'; import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; -class AuthScreen extends StatefulWidget { - const AuthScreen({super.key}); +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); @override - State createState() => _AuthScreenState(); + State createState() => _LoginScreenState(); } -class _AuthScreenState extends State +class _LoginScreenState extends State with SingleTickerProviderStateMixin { LoginController loginController = Get.put(LoginController()); late AnimationController _animationController; @@ -185,7 +185,7 @@ class _AuthScreenState extends State onPressed: () async { bool success = await loginController.loginFunction(); if (success) { - Get.off(const Navigation()); + Get.off( Navigation()); } }, style: ElevatedButton.styleFrom( diff --git a/lib/screens/auth/signup_screen.dart b/lib/screens/auth/signup_screen.dart index b14abb6..41d4625 100644 --- a/lib/screens/auth/signup_screen.dart +++ b/lib/screens/auth/signup_screen.dart @@ -199,7 +199,7 @@ class _SignupScreenState extends State with SingleTickerProviderSt ), GestureDetector( onTap: () { - Get.off(const AuthScreen()); + Get.off(const LoginScreen()); }, child: Text( "Sign in", diff --git a/lib/screens/homepage/homepage_screen.dart b/lib/screens/homepage/homepage_screen.dart index 5f9f2c0..ae146ae 100644 --- a/lib/screens/homepage/homepage_screen.dart +++ b/lib/screens/homepage/homepage_screen.dart @@ -248,12 +248,12 @@ class HomePageState extends State { Future _handleLogout() async { bool success = await loginController.logoutfunction(); if (!success) { - Get.off(const AuthScreen()); + Get.off(const LoginScreen()); Get.snackbar("Logout Successful", "You were logged out successfully"); Get.deleteAll(); } else { Get.snackbar("Error", "You weren't logged out. Try again."); - Get.to(const Navigation()); + Get.to( Navigation()); } } diff --git a/lib/screens/on_board_view.dart b/lib/screens/on_board_view.dart index 1dda40c..a1a2810 100644 --- a/lib/screens/on_board_view.dart +++ b/lib/screens/on_board_view.dart @@ -72,7 +72,7 @@ class _OnBoardViewState extends State { _service .timeTablePresets(value as int) .then((_) { - Get.off(const Navigation()); + Get.off( Navigation()); }).catchError((error) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index 1b839f9..6a18fac 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -14,4 +14,17 @@ class StorageService { Future deleteToken() async { await _storage.delete(key: 'token'); } + + Future setOnboardingComplete() async { + await _storage.write(key: 'onboarding', value: 'true'); + } + + Future deleteOnboarding() async { + await _storage.delete(key: 'onboarding'); + } + + Future getOnboardingComplete() async { + final onboarding = await _storage.read(key: 'onboarding'); + return onboarding == 'true'; + } } diff --git a/lib/utils/Navigation.dart b/lib/utils/Navigation.dart index 963a67c..20cb96e 100644 --- a/lib/utils/Navigation.dart +++ b/lib/utils/Navigation.dart @@ -4,10 +4,11 @@ import 'package:bunk_mate/screens/TimeTable/time_table_page.dart'; import 'package:bunk_mate/screens/homepage/homepage_screen.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:onboarding_overlay/onboarding_overlay.dart'; class Navigation extends StatefulWidget { - const Navigation({super.key}); - + Navigation({super.key}); + final GlobalKey onboardingKey = GlobalKey(); @override State createState() => _NavigationState(); } @@ -18,10 +19,25 @@ class _NavigationState extends State { final Color accentColor = const Color(0xFF4CAF50); final Color inactiveColor = Colors.white54; final GlobalKey homePageKey = GlobalKey(); + final GlobalKey statusPageKey = GlobalKey(); + late List focusNodes; + + @override + void initState() { + super.initState(); + focusNodes = List.generate( + 1, + (int i) => FocusNode(debugLabel: 'Onboarding Focus Node $i'), + growable: false, + ); + } void onTabTapped(int index) { if (index == 0) { homePageKey.currentState?.refreshData(); + } else if (index == 1) { + print("in if"); + statusPageKey.currentState?.showPageGuide(); } controller.updateIndex(index); } @@ -30,37 +46,119 @@ class _NavigationState extends State { Widget build(BuildContext context) { List children = [ HomePage(key: homePageKey), - StatusView(), + StatusView( + focusNode: focusNodes[0], + key: statusPageKey, + ), const TimeTablePage(), ]; - return Scaffold( - body: Obx( - () => IndexedStack( - index: controller.currentIndex.value, - children: children, + return Onboarding( + key: widget.onboardingKey, + autoSizeTexts: true, + steps: [ + OnboardingStep( + focusNode: focusNodes[0], + titleText: "", + bodyText: '', + overlayColor: Colors.black.withOpacity(0.7), + overlayShape: const RoundedRectangleBorder(), + overlayBehavior: HitTestBehavior.deferToChild, + stepBuilder: (context, renderInfo) { + return Scaffold( + backgroundColor: Colors.transparent, + body: SingleChildScrollView( + child: Center( + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: const EdgeInsets.all(25), + decoration: BoxDecoration( + color: const Color(0xFF121212), + border: Border.all(color: const Color(0xFF4CAF50)), + ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Guide to Status Page', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 20), + buildGuideStep( + 'Single Tap to mark bunked', + 'assets/bunked.png', + ), + buildGuideStep( + 'Double Tap to mark cancelled', + 'assets/cancelled.png', + ), + buildGuideStep( + 'Long Press to mark present again', + 'assets/present.png', // Make sure the path is correct. + ), + const SizedBox(height: 20), + Center( + child: ElevatedButton( + onPressed: renderInfo.close, // Close the guide + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF4CAF50), + ), + child: const Text( + "close", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + }, ), - ), - bottomNavigationBar: Container( - decoration: BoxDecoration( - color: bgColor, - boxShadow: [ - BoxShadow( - blurRadius: 20, - color: Colors.black.withOpacity(.1), - ) - ], + ], + child: Scaffold( + body: Obx( + () => IndexedStack( + index: controller.currentIndex.value, + children: children, + ), ), - child: SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildNavItem(0, Icons.home_rounded, 'Home'), - _buildNavItem(1, Icons.check_circle_outline_rounded, 'Status'), - _buildNavItem(2, Icons.calendar_today_rounded, 'Timetable'), - ], + bottomNavigationBar: Container( + decoration: BoxDecoration( + color: bgColor, + boxShadow: [ + BoxShadow( + blurRadius: 20, + color: Colors.black.withOpacity(.1), + ) + ], + ), + child: SafeArea( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildNavItem(0, Icons.home_rounded, 'Home'), + _buildNavItem( + 1, Icons.check_circle_outline_rounded, 'Status'), + _buildNavItem(2, Icons.calendar_today_rounded, 'Timetable'), + ], + ), ), ), ), @@ -107,4 +205,31 @@ class _NavigationState extends State { ), )); } + + Widget buildGuideStep(String text, String imagePath) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + text, + style: const TextStyle( + fontSize: 16, + color: Colors.white, + ), + ), + const SizedBox(height: 10), + Image.asset( + imagePath, + width: 300, + errorBuilder: (context, error, stackTrace) { + return const Text( + 'Image not found', + style: TextStyle(color: Colors.red), + ); + }, + ), + const SizedBox(height: 15), + ], + ); + } } diff --git a/pubspec.lock b/pubspec.lock index e25ccdb..be7d662 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + auto_size_text: + dependency: transitive + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -37,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" crypto: dependency: transitive description: @@ -252,18 +260,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -312,6 +320,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + onboarding_overlay: + dependency: "direct main" + description: + name: onboarding_overlay + sha256: "94626772a69494533f370ffe614882a7bf58d3bf964bfe39dcb480efa4c10444" + url: "https://pub.dev" + source: hosted + version: "3.2.2" password_strength: dependency: "direct main" description: @@ -452,7 +468,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -465,10 +481,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -481,10 +497,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" syncfusion_flutter_charts: dependency: "direct main" description: @@ -521,10 +537,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" typed_data: dependency: transitive description: @@ -553,10 +569,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 55a0b81..3673972 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: # date_field: ^5.2.1 tenor: ^1.0.9 flutter_spinkit: ^5.2.0 + onboarding_overlay: ^3.2.2 dev_dependencies: flutter_test: @@ -44,6 +45,9 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/pp.png + - assets/bunked.png + - assets/cancelled.png + - assets/present.png # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see