diff --git a/asset/WT01_loading_layers.png b/asset/WT01_loading_layers.png new file mode 100644 index 0000000..0e9d6bf Binary files /dev/null and b/asset/WT01_loading_layers.png differ diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index d1f80c4..0000000 --- a/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,503 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 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 = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; 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 = ""; }; - 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; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 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 = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1020; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.aitrainer_app; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.aitrainer_app; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.aitrainer_app; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index a28140c..0000000 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..360f9ef 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "group:/Volumes/aitrainer/src/aitrainer_app/ios/ .xcodeproj"> diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index f9b0d7c..6b30c74 100644 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -2,6 +2,8 @@ + BuildSystemType + Original PreviewsEnabled diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index d36b1fa..65b74d7 100644 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,122 +1 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} \ 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 deleted file mode 100644 index dc9ada4..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null 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 deleted file mode 100644 index 28c6bf0..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null 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 deleted file mode 100644 index 2ccbfd9..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null 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 deleted file mode 100644 index f091b6b..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null 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 deleted file mode 100644 index 4cde121..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null 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 deleted file mode 100644 index d0ef06e..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null 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 deleted file mode 100644 index dcdc230..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null 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 deleted file mode 100644 index 2ccbfd9..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null 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 deleted file mode 100644 index c8f9ed8..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null 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 deleted file mode 100644 index a6d6b86..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null 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 deleted file mode 100644 index a6d6b86..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null 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 deleted file mode 100644 index 75b2d16..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null 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 deleted file mode 100644 index c4df70d..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null 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 deleted file mode 100644 index 6a84f41..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null 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 deleted file mode 100644 index d0e1f58..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard index f3c2851..25fb9f8 100644 --- a/ios/Runner/Base.lproj/Main.storyboard +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -1,8 +1,10 @@ - - + + + - + + @@ -14,13 +16,14 @@ - + - + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index f64123a..d9c6492 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -11,7 +11,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - aitrainer_app + com.aitrainer.app CFBundlePackageType APPL CFBundleShortVersionString diff --git a/lib/main.dart b/lib/main.dart index 2bec108..9320d2b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,23 +1,32 @@ +import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/service/api.dart'; +import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_new_page.dart'; +import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/exercise_new_page.dart'; import 'package:aitrainer_app/view/exercise_type_modify_page.dart'; import 'package:aitrainer_app/view/exercise_type_new_page.dart'; +import 'package:aitrainer_app/view/registration.dart'; import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart'; import 'package:flutter/material.dart'; import 'package:aitrainer_app/view/customer_list_page.dart'; import 'package:aitrainer_app/view/exercise_type_list_page.dart'; import 'package:aitrainer_app/widgets/nav_drawer.dart'; +import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp( - ChangeNotifierProvider( + MultiProvider( // Initialize the model in the builder. That way, Provider // can own Models's lifecycle, making sure to call `dispose` // when not needed anymore. - create: (context) => ExerciseChangingViewModel(null), + providers: [ + ChangeNotifierProvider(create: (context) => ExerciseChangingViewModel(null)), + ], child: AitrainerApp(), ), ); @@ -48,6 +57,8 @@ class AitrainerApp extends StatelessWidget { 'exerciseTypeNewPage': (context) => ExerciseTypeNewPage(), 'exerciseTypeModifyPage': (context) => ExerciseTypeModifyPage(), 'exerciseNewPage': (context) => ExerciseNewPage(), + 'login': (context) => LoginPage(), + 'registration': (context) => RegistrationPage(), }, initialRoute: 'home', title: 'Aitrainer Demo', @@ -59,20 +70,117 @@ class AitrainerApp extends StatelessWidget { } } -class AitrainerHome extends StatelessWidget { - final _biggerFont = const TextStyle(fontSize: 24.0, color: Color.fromRGBO(94, 123, 122, 0.9)); +class AitrainerHome extends StatefulWidget { + static final String routeName = 'home'; + + + @override + State createState() { + return new _HomePageState(); + } +} + +class _HomePageState extends State { + GlobalKey _scaffoldKey = new GlobalKey(); + Future _prefs = SharedPreferences.getInstance(); + Auth _auth = Auth(); + SharedPreferences _sharedPreferences; + var _authToken; + + + @override + void initState() { + super.initState(); + _fetchSessionAndNavigate(); + } + + _fetchSessionAndNavigate() async { + _sharedPreferences = await _prefs; + String authToken = Auth.getToken(_sharedPreferences); + var customerId = _sharedPreferences.getInt(Auth.customerIdKey); + + if ( _auth.firstLoad ) { + _fetchToken(_sharedPreferences); + } + + } + + /* + Auth flow of the user, see auth.dart + */ + _fetchToken(SharedPreferences prefs) async { + var responseJson = await APIClient.authenticateUser( + Auth.username, + Auth.password + ); + + if(responseJson['error'] != null) { + showSnackBar(_scaffoldKey, responseJson['error']); + print("************** Here big error - no authentication"); + } else if (responseJson['token'] != null) { + prefs.setString(Auth.authTokenKey, responseJson['token']); + Auth auth = Auth(); + auth.authToken = responseJson['token']; + if ( prefs.get(Auth.customerIdKey) == null ) { + print("************** Registration"); + // registration + Navigator.of(context).pushNamed('registration'); + prefs.setBool(Auth.isRegisteredKey, true); + } else { + DateTime now = DateTime.now(); + DateTime lastStoreDate = DateTime.parse( prefs.get(Auth.lastStoreDateKey) ); + DateTime minStoreDate = now.add(Duration(days: -10)); + + if ( lastStoreDate == null || + lastStoreDate.difference(minStoreDate) > Duration(days: 10) || + prefs.get(Auth.isLoggedInKey) == null || + prefs.get(Auth.isLoggedInKey) == false ) { + print("************* Login"); + Navigator.of(context).pushNamed('login'); + + } else { + print("************** Store SharedPreferences"); + // get API customer + await CustomerApi().getCustomer( prefs.getInt(Auth.customerIdKey)); + + } + } + + setState(() { + _auth.firstLoad = false; + _authToken = auth.authToken; + _sharedPreferences.setString(Auth.authTokenKey, _authToken); + }); + + } + } + + + static showSnackBar(GlobalKey scaffoldKey, String message) { + scaffoldKey.currentState.showSnackBar( + new SnackBar( + content: new Text(message ?? 'You are offline'), + ) + ); + } + @override Widget build(BuildContext context) { return Scaffold( + key: _scaffoldKey, drawer: NavDrawer(), appBar: AppBar( - title: Text('Menu'), + title: Text('Home'), + backgroundColor: Colors.transparent ), - body: Center( - child: Text( - 'Welcome to the AITRAINER', - style: _biggerFont), + body: Image.asset('asset/WT01_loading_layers.png', + fit: BoxFit.fill, + height: double.infinity, + width: double.infinity, + alignment: Alignment.center, ), + + ); } } diff --git a/lib/model/auth.dart b/lib/model/auth.dart new file mode 100644 index 0000000..ddf3ebc --- /dev/null +++ b/lib/model/auth.dart @@ -0,0 +1,105 @@ +import 'package:aitrainer_app/model/customer.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +enum SharePrefsChange { + login, + registration, + logout, +} +/* + Auth flow of the app + 1. During the login screen the authentication will be executed + - if not successful: message: Network error, try again later + - if successful + - get the stored shared preferences and customer id + - if customer_id not present -> registration page + - if present, check if the expiration_date > 10 days -> login page + - else get the API customer by the stored customer_id + - After registration / login store the preferences: + - AuthToken + - customer_id + - last_store_date + - is_registered + - is_logged_in + */ + +class Auth { + static final Auth _singleton = Auth._internal(); + + // Keys to store and fetch data from SharedPreferences + static final String authTokenKey = 'auth_token'; + static final String customerIdKey = 'customer_id'; + static final String lastStoreDateKey = 'last_date'; + static final String isRegisteredKey = 'is_registered'; + static final String isLoggedInKey = 'is_logged_in'; + + static final String _baseUrl = 'http://andio.eu:8888/api/'; + static final String username = 'bosi'; + static final String password = 'andio2009'; + + String authToken = ""; + Customer userLoggedIn; + bool firstLoad = true; + + factory Auth() { + return _singleton; + } + + Auth._internal(); + + String getAuthToken() { + return this.authToken; + } + + static String getToken(SharedPreferences prefs) { + return prefs.getString(authTokenKey); + } + + static String getBaseUrl() { + return _baseUrl; + } + + afterRegistration(Customer customer) { + Future prefs = SharedPreferences.getInstance(); + + userLoggedIn = customer; + setPreferences(prefs, SharePrefsChange.registration, customer.customerId); + } + + afterLogin(Customer customer) { + Future prefs = SharedPreferences.getInstance(); + + userLoggedIn = customer; + setPreferences(prefs, SharePrefsChange.login, customer.customerId); + } + + logout(){ + userLoggedIn = null; + authToken = ""; + //firstLoad = true; + Future prefs = SharedPreferences.getInstance(); + setPreferences(prefs, SharePrefsChange.logout, 0); + } + + setPreferences(Future prefs, + SharePrefsChange type, + int customerId) async { + SharedPreferences sharedPreferences; + sharedPreferences = await prefs; + + DateTime now = DateTime.now(); + sharedPreferences.setString(Auth.lastStoreDateKey, now.toString()); + if ( type == SharePrefsChange.registration ) { + sharedPreferences.setInt(Auth.customerIdKey, customerId); + sharedPreferences.setBool(Auth.isRegisteredKey, true); + sharedPreferences.setBool(Auth.isLoggedInKey, true); + } else if ( type == SharePrefsChange.login ) { + + sharedPreferences.setInt(Auth.customerIdKey, customerId); + sharedPreferences.setBool(Auth.isLoggedInKey, true); + } else if ( type == SharePrefsChange.login ) { + sharedPreferences.setBool(Auth.isLoggedInKey, false); + sharedPreferences.setInt(Auth.customerIdKey, 0); + } + } +} \ No newline at end of file diff --git a/lib/model/customer.dart b/lib/model/customer.dart index a4f6dc9..f725a5e 100644 --- a/lib/model/customer.dart +++ b/lib/model/customer.dart @@ -6,9 +6,10 @@ class Customer { int age; String active; int customerId; + String password; - Customer({this.customerId, this.name, this.firstName, this.email, this.sex, this.age, this.active}); + Customer({this.customerId, this.name, this.firstName, this.email, this.sex, this.age, this.active, this.password}); Customer.fromJson(Map json) { this.customerId = json['customer_id']; @@ -27,6 +28,7 @@ class Customer { "email": email, "age": age, "sex": sex, - "active": 'Y' + "active": 'Y', + "password": password, }; } \ No newline at end of file diff --git a/lib/model/user.dart b/lib/model/user.dart new file mode 100644 index 0000000..0d83fef --- /dev/null +++ b/lib/model/user.dart @@ -0,0 +1,15 @@ +class User { + String email; + String password; + int customerId; + + + User({this.customerId, this.email, this.password}); + + + Map toJson() => + { + "username": email, + "password": password, + }; +} \ No newline at end of file diff --git a/lib/service/api.dart b/lib/service/api.dart index 46b2b25..bacdaec 100644 --- a/lib/service/api.dart +++ b/lib/service/api.dart @@ -1,12 +1,19 @@ import 'dart:convert'; +import 'package:flutter/cupertino.dart'; import 'package:http/http.dart' as http; +import 'package:aitrainer_app/model/auth.dart'; -class APIClient { - final String _baseUrl = 'http://andio.eu:8888/api/'; +class APIClient extends ChangeNotifier { Future get(String endPoint, String param) async { - final url = _baseUrl + endPoint + param; - final response = await http.get(url, headers: {'Content-Type': 'application/json'}); + final url = Auth.getBaseUrl() + endPoint + param; + Auth auth = Auth(); + final response = await http.get(url, + headers: { + 'Content-Type': 'application/json', + 'Authorization' : "Bearer " + auth.getAuthToken() } + ); + notifyListeners(); if(response.statusCode == 200) { return utf8.decode(response.bodyBytes); } else { @@ -15,14 +22,69 @@ class APIClient { } Future post(String endPoint, String body) async { - final url = _baseUrl + endPoint; - print(" ------------ http/post endpoint $endPoint body $body"); + final url = Auth.getBaseUrl() + endPoint; + print(" ------------ http/post endpoint $endPoint body $body - url: $url "); + Auth auth = Auth(); final response = await http.post(url, - headers: {'Content-Type': 'application/json'}, + headers: { + 'Content-Type': 'application/json', + 'Authorization' : "Bearer " + auth.getAuthToken() + }, body: body ); print(" ------------ response: " + response.body.toString()); + notifyListeners(); return response.body; } + static dynamic authenticateUser(String email, String password) async { + var uri = Auth.getBaseUrl() + "authenticate"; + + try { + final body = '{"username":"$email", "password":"$password"}'; + final response = await http.post( + uri, + headers: { + 'Authorization': '1', + 'Content-Type': 'application/json' + }, + body: body + ); + final responseCode = response.statusCode; + if ( responseCode != 200) { + return { "error" : "Authentication error, total failure", }; + } + + final responseJson = json.decode(response.body); + return responseJson; + + } catch (exception) { + return { "error" : "Network error, try again later"}; + } + } + + static fetch(var authToken, var endPoint) async { + var uri = Auth.getBaseUrl() + endPoint; + + try { + final response = await http.get( + uri, + headers: { + 'Authorization': authToken + }, + ); + + final responseJson = json.decode(response.body); + return responseJson; + + } catch (exception) { + print(exception); + if(exception.toString().contains('SocketException')) { + return 'NetworkError'; + } else { + return null; + } + } + } + } \ No newline at end of file diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index 700fc7f..2de8f5e 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -1,7 +1,8 @@ import 'dart:convert'; import 'package:aitrainer_app/model/customer.dart'; +import 'package:aitrainer_app/model/user.dart'; import 'package:aitrainer_app/service/api.dart'; - +import 'package:aitrainer_app/model/auth.dart'; class CustomerApi { final APIClient _client=new APIClient(); @@ -29,4 +30,38 @@ class CustomerApi { "customers", body); } + + Future addUser(User user) async { + String body = JsonEncoder().convert(user.toJson()); + print(" ===== register new user: " + body ); + final String responseBody = await _client.post( + "registration", + body); + Auth auth = Auth(); + Customer customer = Customer.fromJson(jsonDecode(responseBody)); + auth.afterRegistration(customer); + + } + + Future getUser(User user) async { + String body = JsonEncoder().convert(user.toJson()); + print(" ===== login the user: " + body ); + final String responseBody = await _client.post( + "login", + body); + Auth auth = Auth(); + Customer customer = Customer.fromJson(jsonDecode(responseBody)); + auth.afterRegistration(customer); + } + + Future getCustomer(int customerId) async { + String body = ""; + print(" ===== get the customer by id: " + customerId.toString() ); + final String responseBody = await _client.get( + "customers/"+customerId.toString(), + body); + Auth auth = Auth(); + Customer customer = Customer.fromJson(jsonDecode(responseBody)); + auth.afterRegistration(customer); + } } \ No newline at end of file diff --git a/lib/util/common.dart b/lib/util/common.dart new file mode 100644 index 0000000..a430238 --- /dev/null +++ b/lib/util/common.dart @@ -0,0 +1,16 @@ + +import 'dart:collection'; + +class Common { + + + static String toJson( Map map ) { + String rc = "{"; + map.forEach((key, value) { + rc += "'$key':'$value'"; + }); + rc += "}"; + return rc; + } + +} \ No newline at end of file diff --git a/lib/view/login.dart b/lib/view/login.dart new file mode 100644 index 0000000..8d71dbd --- /dev/null +++ b/lib/view/login.dart @@ -0,0 +1,81 @@ + +import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart'; +import 'package:aitrainer_app/viewmodel/user_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:aitrainer_app/widgets/nav_drawer.dart'; + +class LoginPage extends StatefulWidget{ + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final UserViewModel user = UserViewModel(); + bool _obscureText = true; + + final _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + UserChangingViewModel model = UserChangingViewModel(user); + user.createNew(); + return Scaffold( + drawer: NavDrawer(), + appBar: AppBar( + title: Text('Login'), + ), + body: Center( + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email', + icon: const Padding( + padding:const EdgeInsets.only(left: 20.0, top: 50.0), + child: const Icon(Icons.people) + ) + ), + validator: (String input) { + RegExp exp = new RegExp(r"[\w._]+\@[\w._]+.[a-z]+", + caseSensitive: false, + multiLine: false,); + String ret = exp.hasMatch(input) == true ? + null: + "Please type an email address"; + return ret; + }, + onChanged: (input) => user.setEmail(input), + ), + new TextFormField( + decoration: const InputDecoration( + labelText: 'Password', + icon: const Padding( + padding: const EdgeInsets.only(left: 20.0, top: 15.0), + child: const Icon(Icons.lock))), + validator: (val) => val.length < 6 ? 'Password too short.' : null, + obscureText: _obscureText, + onChanged: (input) => user.setPassword(input), + ), + new InkWell( + child: new Text('SignUp'), + onTap: () => Navigator.of(context).pushNamed('registration'), + ), + new FloatingActionButton( + child: Icon(Icons.cloud_done,), + onPressed:() => { + if (_formKey.currentState.validate()) { + model = UserChangingViewModel(user), + model.getUser(), + Navigator.pop(context), + } + }) + ]) + ), + ), + + ); + } +} \ No newline at end of file diff --git a/lib/view/registration.dart b/lib/view/registration.dart new file mode 100644 index 0000000..881874b --- /dev/null +++ b/lib/view/registration.dart @@ -0,0 +1,85 @@ + +import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart'; +import 'package:aitrainer_app/viewmodel/user_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:aitrainer_app/widgets/nav_drawer.dart'; + +class RegistrationPage extends StatefulWidget{ + _RegistrationPageState createState() => _RegistrationPageState(); +} + +class _RegistrationPageState extends State { + final UserViewModel user = UserViewModel(); + bool _obscureText = true; + + final _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + UserChangingViewModel model = UserChangingViewModel(user); + user.createNew(); + return Scaffold( + drawer: NavDrawer(), + appBar: AppBar( + title: Text('Registration'), + ), + body: Center( + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email', + icon: const Padding( + padding:const EdgeInsets.only(left: 20.0, top: 50.0), + child: const Icon(Icons.people) + ) + ), + /* validator: (String input) { + RegExp exp = new RegExp(r"[\w._]+\@[\w._]+.[a-z]+", + caseSensitive: false, + multiLine: false,); + String ret = exp.hasMatch(input) == true ? + null: + "Please type an email address"; + return ret; + },*/ + onChanged: (input) => user.setEmail(input), + ), + new TextFormField( + decoration: const InputDecoration( + labelText: 'Password', + icon: const Padding( + padding: const EdgeInsets.only(left: 20.0, top: 15.0), + child: const Icon(Icons.lock))), + /* validator: (String input) { + String rc = input.length < 4 ? 'Password too short.' : null; + return rc; + }, */ + onChanged: (input) => user.setPassword(input), + obscureText: _obscureText, + ), + new InkWell( + child: new Text('I have an account'), + onTap: () => Navigator.of(context).pushNamed('login'), + ), + new FloatingActionButton( + child: Icon(Icons.cloud_done,), + onPressed:() => { + if (_formKey.currentState.validate()) { + model = UserChangingViewModel(user), + model.addUser(), + Navigator.pop(context), + } + } + ) + ]) + ), + ), + + ); + } +} \ No newline at end of file diff --git a/lib/viewmodel/user_changing_view_model.dart b/lib/viewmodel/user_changing_view_model.dart new file mode 100644 index 0000000..bfc4b9d --- /dev/null +++ b/lib/viewmodel/user_changing_view_model.dart @@ -0,0 +1,22 @@ +import 'package:aitrainer_app/model/user.dart'; +import 'package:aitrainer_app/service/customer_service.dart'; +import 'package:aitrainer_app/viewmodel/user_view_model.dart'; +import 'package:flutter/cupertino.dart'; + +class UserChangingViewModel extends ChangeNotifier { + UserViewModel userViewModel = UserViewModel(); + + UserChangingViewModel(userViewModel) { + this.userViewModel = userViewModel; + } + + Future addUser() async { + final User modelUser = userViewModel.getUser(); + await CustomerApi().addUser(modelUser); + } + + Future getUser() async { + final User modelUser = userViewModel.getUser(); + await CustomerApi().getUser(modelUser); + } +} \ No newline at end of file diff --git a/lib/viewmodel/user_view_model.dart b/lib/viewmodel/user_view_model.dart new file mode 100644 index 0000000..20586d3 --- /dev/null +++ b/lib/viewmodel/user_view_model.dart @@ -0,0 +1,23 @@ +import 'package:aitrainer_app/model/user.dart'; + +class UserViewModel { + User user; + + UserViewModel({this.user}); + + setEmail(String email) { + this.user.email = email; + } + + setPassword(String password) { + this.user.password = password; + } + + User getUser() { + return this.user; + } + + createNew() { + this.user = User(); + } +} diff --git a/lib/widgets/customer_list_widget.dart b/lib/widgets/customer_list_widget.dart index 3503ef4..54a1cf0 100644 --- a/lib/widgets/customer_list_widget.dart +++ b/lib/widgets/customer_list_widget.dart @@ -57,7 +57,7 @@ class _CustomerListWidget extends State { ), ), onTap: () { setState( () { - customer.visibleDetails = true; + customer.visibleDetails = customer.visibleDetails ? false : true; }); }, ); diff --git a/lib/widgets/nav_drawer.dart b/lib/widgets/nav_drawer.dart index c84bb61..43f1698 100644 --- a/lib/widgets/nav_drawer.dart +++ b/lib/widgets/nav_drawer.dart @@ -1,6 +1,8 @@ +import 'package:aitrainer_app/model/auth.dart'; import 'package:flutter/material.dart'; class NavDrawer extends StatelessWidget { + final Auth auth = Auth(); @override Widget build(BuildContext context) { return Drawer( @@ -37,6 +39,19 @@ class NavDrawer extends StatelessWidget { title: Text("TRAINING!"), onTap: () => Navigator.of(context).pushNamed('exerciseNewPage'), ), + ListTile( + leading: Icon(Icons.perm_identity), + title: Text('Login'), + onTap: () => Navigator.of(context).pushNamed('login'), + ), + ListTile( + leading: Icon(Icons.cancel), + title: Text("Logout"), + onTap: () => { + auth.logout(), + Navigator.of(context).pushNamed('home'), + } + ), ], ), ); diff --git a/pubspec.lock b/pubspec.lock index 22be0b8..c609b3c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -135,6 +135,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.2.0" + shared_preferences: + dependency: "direct dev" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 3dd4280..fbccdba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dev_dependencies: intl: 0.16.1 flutter_datetime_picker: ^1.3.8 datetime_picker_formfield: ^1.0.0 + shared_preferences: ^0.4.1 # For information on the generic Dart part of this file, see the @@ -54,8 +55,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg + assets: + - asset/WT01_loading_layers.png # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see