diff --git a/.metadata b/.metadata index 784ce129..6eb54a17 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" channel: "stable" project_type: app @@ -13,11 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: ios + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: linux + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: macos + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - platform: web - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: windows + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 # User provided section diff --git a/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt b/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt new file mode 100644 index 00000000..018e286b --- /dev/null +++ b/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.get_flutter_fire + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt b/android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt new file mode 100644 index 00000000..a235e9f2 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.sharekhan_dhruv + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png b/assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png new file mode 100644 index 00000000..5e50fd3e Binary files /dev/null and b/assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png differ diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..6b4c2aa6 --- /dev/null +++ b/firebase.json @@ -0,0 +1,16 @@ +{ + "flutter": { + "platforms": { + "dart": { + "lib/firebase_options.dart": { + "projectId": "sharekhan-dhruv", + "configurations": { + "android": "1:64647061409:android:a52c96120dd0ace789f5e0", + "web": "1:64647061409:web:a5241dce8216b26989f5e0", + "windows": "1:64647061409:web:4e3c3388406d0e2789f5e0" + } + } + } + } + } +} \ No newline at end of file diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig new file mode 100644 index 00000000..6ebce106 --- /dev/null +++ b/ios/Flutter/Generated.xcconfig @@ -0,0 +1,14 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter +FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib\main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100644 index 00000000..7bf44cf1 --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter" +export "FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..d97f17e2 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..6afd14af --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,346 @@ +PODS: + - AppAuth (1.7.5): + - AppAuth/Core (= 1.7.5) + - AppAuth/ExternalUserAgent (= 1.7.5) + - AppAuth/Core (1.7.5) + - AppAuth/ExternalUserAgent (1.7.5): + - AppAuth/Core + - desktop_webview_auth (0.0.1): + - Flutter + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - Firebase/Analytics (10.25.0): + - Firebase/Core + - Firebase/Auth (10.25.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 10.25.0) + - Firebase/Core (10.25.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 10.25.0) + - Firebase/CoreOnly (10.25.0): + - FirebaseCore (= 10.25.0) + - Firebase/DynamicLinks (10.25.0): + - Firebase/CoreOnly + - FirebaseDynamicLinks (~> 10.25.0) + - Firebase/RemoteConfig (10.25.0): + - Firebase/CoreOnly + - FirebaseRemoteConfig (~> 10.25.0) + - Firebase/Storage (10.25.0): + - Firebase/CoreOnly + - FirebaseStorage (~> 10.25.0) + - firebase_analytics (10.10.7): + - Firebase/Analytics (= 10.25.0) + - firebase_core + - Flutter + - firebase_auth (4.20.0): + - Firebase/Auth (= 10.25.0) + - firebase_core + - Flutter + - firebase_core (2.32.0): + - Firebase/CoreOnly (= 10.25.0) + - Flutter + - firebase_dynamic_links (5.5.7): + - Firebase/DynamicLinks (= 10.25.0) + - firebase_core + - Flutter + - firebase_remote_config (4.4.7): + - Firebase/RemoteConfig (= 10.25.0) + - firebase_core + - Flutter + - firebase_storage (11.7.7): + - Firebase/Storage (= 10.25.0) + - firebase_core + - Flutter + - FirebaseABTesting (10.29.0): + - FirebaseCore (~> 10.0) + - FirebaseAnalytics (10.25.0): + - FirebaseAnalytics/AdIdSupport (= 10.25.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.25.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.25.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseAppCheckInterop (10.29.0) + - FirebaseAuth (10.25.0): + - FirebaseAppCheckInterop (~> 10.17) + - FirebaseCore (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/Environment (~> 7.8) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - RecaptchaInterop (~> 100.0) + - FirebaseAuthInterop (10.29.0) + - FirebaseCore (10.25.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GoogleUtilities/Logger (~> 7.12) + - FirebaseCoreExtension (10.29.0): + - FirebaseCore (~> 10.0) + - FirebaseCoreInternal (10.29.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseDynamicLinks (10.25.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (10.29.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - FirebaseRemoteConfig (10.25.0): + - FirebaseABTesting (~> 10.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - FirebaseRemoteConfigInterop (~> 10.23) + - FirebaseSharedSwift (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseRemoteConfigInterop (10.29.0) + - FirebaseSharedSwift (10.29.0) + - FirebaseStorage (10.25.0): + - FirebaseAppCheckInterop (~> 10.0) + - FirebaseAuthInterop (~> 10.25) + - FirebaseCore (~> 10.0) + - FirebaseCoreExtension (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - Flutter (1.0.0) + - google_sign_in_ios (0.0.1): + - AppAuth (>= 1.7.4) + - Flutter + - FlutterMacOS + - GoogleSignIn (~> 7.1) + - GTMSessionFetcher (>= 3.4.0) + - GoogleAppMeasurement (10.25.0): + - GoogleAppMeasurement/AdIdSupport (= 10.25.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.25.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.25.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.25.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleSignIn (7.1.0): + - AppAuth (< 2.0, >= 1.7.3) + - GTMAppAuth (< 5.0, >= 4.1.1) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (7.13.3): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (7.13.3): + - GoogleUtilities/Privacy + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.13.3): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (7.13.3): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.13.3)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (7.13.3) + - GoogleUtilities/Reachability (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GTMAppAuth (4.1.1): + - AppAuth/Core (~> 1.7) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher (3.5.0): + - GTMSessionFetcher/Full (= 3.5.0) + - GTMSessionFetcher/Core (3.5.0) + - GTMSessionFetcher/Full (3.5.0): + - GTMSessionFetcher/Core + - image_picker_ios (0.0.1): + - Flutter + - nanopb (2.30910.0): + - nanopb/decode (= 2.30910.0) + - nanopb/encode (= 2.30910.0) + - nanopb/decode (2.30910.0) + - nanopb/encode (2.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - PromisesObjC (2.4.0) + - RecaptchaInterop (100.0.0) + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) + - SwiftyGif (5.4.5) + +DEPENDENCIES: + - desktop_webview_auth (from `.symlinks/plugins/desktop_webview_auth/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_dynamic_links (from `.symlinks/plugins/firebase_dynamic_links/ios`) + - firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`) + - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) + - Flutter (from `Flutter`) + - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + +SPEC REPOS: + trunk: + - AppAuth + - DKImagePickerController + - DKPhotoGallery + - Firebase + - FirebaseABTesting + - FirebaseAnalytics + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseDynamicLinks + - FirebaseInstallations + - FirebaseRemoteConfig + - FirebaseRemoteConfigInterop + - FirebaseSharedSwift + - FirebaseStorage + - GoogleAppMeasurement + - GoogleSignIn + - GoogleUtilities + - GTMAppAuth + - GTMSessionFetcher + - nanopb + - PromisesObjC + - RecaptchaInterop + - SDWebImage + - SwiftyGif + +EXTERNAL SOURCES: + desktop_webview_auth: + :path: ".symlinks/plugins/desktop_webview_auth/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_dynamic_links: + :path: ".symlinks/plugins/firebase_dynamic_links/ios" + firebase_remote_config: + :path: ".symlinks/plugins/firebase_remote_config/ios" + firebase_storage: + :path: ".symlinks/plugins/firebase_storage/ios" + Flutter: + :path: Flutter + google_sign_in_ios: + :path: ".symlinks/plugins/google_sign_in_ios/darwin" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + +SPEC CHECKSUMS: + AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa + desktop_webview_auth: d645139460ef203d50bd0cdb33356785dd939cce + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + Firebase: 0312a2352584f782ea56f66d91606891d4607f06 + firebase_analytics: cc06e24d6a2343c44f845b3112143db72d10ef78 + firebase_auth: 5719ddc9f654b813405899480e84971bd8e61235 + firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197 + firebase_dynamic_links: 525e9c1b702d2ed2d9b0dbd342eee1e15a75e62d + firebase_remote_config: 7b05c80210ab558c80f7a756681022b4ee98eea0 + firebase_storage: 5c0f552d6b27d621429d7fd16ebab4be94a3c954 + FirebaseABTesting: d87f56707159bae64e269757a6e963d490f2eebe + FirebaseAnalytics: ec00fe8b93b41dc6fe4a28784b8e51da0647a248 + FirebaseAppCheckInterop: 6a1757cfd4067d8e00fccd14fcc1b8fd78cfac07 + FirebaseAuth: c0f93dcc570c9da2bffb576969d793e95c344fbb + FirebaseAuthInterop: 17db81e9b198afb0f95ce48c133825727eed55d3 + FirebaseCore: 7ec4d0484817f12c3373955bc87762d96842d483 + FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f + FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934 + FirebaseDynamicLinks: 12c9f5b643943e0565ed28080373f89cbcb914a3 + FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd + FirebaseRemoteConfig: 9f3935cefecd85d5b312192117f444957de24a75 + FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d + FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e + FirebaseStorage: 44f4e25073f6fa0d4d8c09f5bec299ee9e4eb985 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 + GoogleAppMeasurement: 9abf64b682732fed36da827aa2a68f0221fd2356 + GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 + GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + nanopb: 438bc412db1928dac798aa6fd75726007be04262 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 + +COCOAPODS: 1.14.2 diff --git a/ios/Runner/GeneratedPluginRegistrant.h b/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 00000000..7a890927 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 00000000..6f999d66 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,98 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#import "GeneratedPluginRegistrant.h" + +#if __has_include() +#import +#else +@import cloud_firestore; +#endif + +#if __has_include() +#import +#else +@import desktop_webview_auth; +#endif + +#if __has_include() +#import +#else +@import file_picker; +#endif + +#if __has_include() +#import +#else +@import firebase_analytics; +#endif + +#if __has_include() +#import +#else +@import firebase_auth; +#endif + +#if __has_include() +#import +#else +@import firebase_core; +#endif + +#if __has_include() +#import +#else +@import firebase_dynamic_links; +#endif + +#if __has_include() +#import +#else +@import firebase_remote_config; +#endif + +#if __has_include() +#import +#else +@import firebase_storage; +#endif + +#if __has_include() +#import +#else +@import google_sign_in_ios; +#endif + +#if __has_include() +#import +#else +@import image_picker_ios; +#endif + +#if __has_include() +#import +#else +@import path_provider_foundation; +#endif + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { + [FLTFirebaseFirestorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseFirestorePlugin"]]; + [DesktopWebviewAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"DesktopWebviewAuthPlugin"]]; + [FilePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FilePickerPlugin"]]; + [FLTFirebaseAnalyticsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAnalyticsPlugin"]]; + [FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]]; + [FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]]; + [FLTFirebaseDynamicLinksPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseDynamicLinksPlugin"]]; + [FLTFirebaseRemoteConfigPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseRemoteConfigPlugin"]]; + [FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]]; + [FLTGoogleSignInPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleSignInPlugin"]]; + [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; + [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; +} + +@end diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 00000000..78e3a245 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo + API_KEY + AIzaSyD12e9qHOj8ulb6cxP0DIWHW2tIeYzgKtc + GCM_SENDER_ID + 56053565487 + PLIST_VERSION + 1 + BUNDLE_ID + com.example.getFlutterFire + PROJECT_ID + sharekhan-nemin + STORAGE_BUCKET + sharekhan-nemin.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:56053565487:ios:3515d315addadb07cb5eda + + \ No newline at end of file diff --git a/lib/app/middleware/auth_middleware.dart b/lib/app/middleware/auth_middleware.dart index 827dd96c..37e0c1c6 100644 --- a/lib/app/middleware/auth_middleware.dart +++ b/lib/app/middleware/auth_middleware.dart @@ -6,9 +6,9 @@ import 'package:get_flutter_fire/app/routes/app_pages.dart'; Future loginVerify(bool check, GetNavConfig route, Future Function(GetNavConfig) redirector) async { - final newRoute = route.location == Routes.LOGIN + final newRoute = route.locationString == Routes.LOGIN ? Routes.LOGIN - : Routes.LOGIN_THEN(route.location); + : Routes.LOGIN_THEN(route.locationString); if (check) { return GetNavConfig.fromRoute(newRoute); } @@ -17,9 +17,9 @@ Future loginVerify(bool check, GetNavConfig route, // This will never get reached if server is sending error in login due to non verification // With customClaims status == "creating", it will reach here for SignUp case only if (!AuthService.to.isEmailVerified && !AuthService.to.registered.value) { - return GetNavConfig.fromRoute(route.location == Routes.REGISTER + return GetNavConfig.fromRoute(route.locationString == Routes.REGISTER ? Routes.REGISTER - : Routes.REGISTER_THEN(route.location)); + : Routes.REGISTER_THEN(route.locationString)); } return await redirector(route); @@ -68,7 +68,7 @@ class EnsureRoleMiddleware extends GetMiddleware { @override Future redirectDelegate(GetNavConfig route) async { if (!AuthService.to.isLoggedInValue || !AuthService.to.hasRole(role)) { - final newRoute = Routes.LOGIN_THEN(route.location); + final newRoute = Routes.LOGIN_THEN(route.locationString); return GetNavConfig.fromRoute(newRoute); } return await super.redirectDelegate(route); diff --git a/lib/app/modules/cart/controllers/cart_controller.dart b/lib/app/modules/cart/controllers/cart_controller.dart index c938ec4c..c4588ef5 100644 --- a/lib/app/modules/cart/controllers/cart_controller.dart +++ b/lib/app/modules/cart/controllers/cart_controller.dart @@ -1,23 +1,72 @@ import 'package:get/get.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import '../../../../models/cart_item.dart'; class CartController extends GetxController { - //TODO: Implement CartController + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + final cartItems = [].obs; - final count = 0.obs; @override void onInit() { super.onInit(); + fetchCartItems(); + + // Add a dummy item for demonstration purposes + final dummyItem = CartItem( + id: 'dummy-id', + name: 'Dummy Item', + quantity: 1, + ); + cartItems.add(dummyItem); } - @override - void onReady() { - super.onReady(); + Future fetchCartItems() async { + const userId = 'your-user-id'; // Replace with the actual user ID + final snapshot = await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .get(); + + final items = snapshot.docs + .map((doc) => CartItem.fromDocument(doc)) + .toList(); + + cartItems.assignAll(items); } - @override - void onClose() { - super.onClose(); + Future addCartItem(CartItem item) async { + const userId = 'your-user-id'; // Replace with the actual user ID + await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .add(item.toMap()); + + fetchCartItems(); } - void increment() => count.value++; + Future removeCartItem(String itemId) async { + const userId = 'your-user-id'; // Replace with the actual user ID + await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .doc(itemId) + .delete(); + + fetchCartItems(); + } + + Future updateCartItem(CartItem item) async { + const userId = 'your-user-id'; // Replace with the actual user ID + await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .doc(item.id) + .update(item.toMap()); + + fetchCartItems(); + } } diff --git a/lib/app/modules/cart/views/cart_view.dart b/lib/app/modules/cart/views/cart_view.dart index 3e048c79..05800d34 100644 --- a/lib/app/modules/cart/views/cart_view.dart +++ b/lib/app/modules/cart/views/cart_view.dart @@ -1,27 +1,78 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; -import 'package:get_flutter_fire/app/routes/app_pages.dart'; -import '../../../widgets/screen_widget.dart'; +import '../../../../models/cart_item.dart'; import '../../../../services/auth_service.dart'; import '../controllers/cart_controller.dart'; class CartView extends GetView { const CartView({super.key}); + @override Widget build(BuildContext context) { - return ScreenWidget( + return Scaffold( appBar: AppBar( title: Text('${AuthService.to.userName} Cart'), centerTitle: true, ), - body: const Center( - child: Text( - 'CartView is working', - style: TextStyle(fontSize: 20), - ), + body: Obx(() { + if (controller.cartItems.isEmpty) { + return const Center( + child: Text( + 'No items in cart', + style: TextStyle(fontSize: 20), + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.all(8.0), + itemCount: controller.cartItems.length, + itemBuilder: (context, index) { + final item = controller.cartItems[index]; + return Card( + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text('Quantity: ${item.quantity}'), + ], + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => controller.removeCartItem(item.id), + ), + ], + ), + ), + ); + }, + ); + }), + floatingActionButton: FloatingActionButton( + onPressed: () { + // Add new cart item + final newItem = CartItem( + id: DateTime.now().millisecondsSinceEpoch.toString(), // Unique ID + name: 'New Item', + quantity: 1, + ); + controller.addCartItem(newItem); + }, + child: const Icon(Icons.add), ), - screen: screen!, ); } } diff --git a/lib/app/modules/categories/bindings/categories_binding.dart b/lib/app/modules/categories/bindings/categories_binding.dart index 06e278c8..f69ba042 100644 --- a/lib/app/modules/categories/bindings/categories_binding.dart +++ b/lib/app/modules/categories/bindings/categories_binding.dart @@ -1,12 +1,11 @@ import 'package:get/get.dart'; - import '../controllers/categories_controller.dart'; class CategoriesBinding extends Bindings { @override void dependencies() { - Get.lazyPut( - () => CategoriesController(), + Get.lazyPut( + () => CategoryController(), ); } } diff --git a/lib/app/modules/categories/controllers/categories_controller.dart b/lib/app/modules/categories/controllers/categories_controller.dart index 6612e511..67150e2f 100644 --- a/lib/app/modules/categories/controllers/categories_controller.dart +++ b/lib/app/modules/categories/controllers/categories_controller.dart @@ -1,23 +1,26 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:get/get.dart'; +import 'package:get_flutter_fire/models/category.dart'; -class CategoriesController extends GetxController { - //TODO: Implement CategoriesController +class CategoryController extends GetxController { + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + var categories = [].obs; - final count = 0.obs; @override void onInit() { super.onInit(); + fetchCategories(); } - @override - void onReady() { - super.onReady(); + void fetchCategories() { + _firestore.collection('categories').snapshots().listen((snapshot) { + categories.value = snapshot.docs + .map((doc) => Category.fromMap(doc.data())) + .toList(); + }); } - @override - void onClose() { - super.onClose(); + void addCategory(Category category) async { + await _firestore.collection('categories').add(category.toMap()); } - - void increment() => count.value++; } diff --git a/lib/app/modules/categories/views/categories_view.dart b/lib/app/modules/categories/views/categories_view.dart index 97bfef38..87d862f6 100644 --- a/lib/app/modules/categories/views/categories_view.dart +++ b/lib/app/modules/categories/views/categories_view.dart @@ -1,24 +1,31 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; +import 'package:get_flutter_fire/app/modules/categories/controllers/categories_controller.dart'; -import '../controllers/categories_controller.dart'; - -class CategoriesView extends GetView { +class CategoriesView extends GetView { const CategoriesView({super.key}); + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('CategoriesView'), - centerTitle: true, - ), - body: const Center( - child: Text( - 'CategoriesView is working', - style: TextStyle(fontSize: 20), - ), - ), + appBar: AppBar(title: const Text('Categories')), + body: Obx(() { + if (controller.categories.isEmpty) { + return const Center(child: Text('No categories')); + } + return ListView.builder( + itemCount: controller.categories.length, + itemBuilder: (context, index) { + final category = controller.categories[index]; + return ExpansionTile( + title: Text(category.name), + children: category.subCategories.map((subCategory) => ListTile( + title: Text(subCategory.name), + )).toList(), + ); + }, + ); + }), ); } } diff --git a/lib/app/modules/login/views/login_view.dart b/lib/app/modules/login/views/login_view.dart index 00c3af3f..cdcb6537 100644 --- a/lib/app/modules/login/views/login_view.dart +++ b/lib/app/modules/login/views/login_view.dart @@ -39,8 +39,8 @@ class LoginView extends GetView { return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: action == AuthAction.signIn - ? const Text('Welcome to Get Flutter Fire, please sign in!') - : const Text('New to Get Flutter Fire, please sign up!'), + ? const Text('Welcome to Sharekhan App, please sign in!') + : const Text('New to Get Sharekhan App? please sign up!'), ); } diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index c26d11c1..834db809 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -1,12 +1,12 @@ // ignore_for_file: inference_failure_on_function_invocation +import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_ui_auth/firebase_ui_auth.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../../services/auth_service.dart'; import '../../../../models/screens.dart'; -import '../../../widgets/change_password_dialog.dart'; import '../../../widgets/image_picker_button.dart'; import '../controllers/profile_controller.dart'; @@ -17,11 +17,11 @@ class ProfileView extends GetView { Color get placeholderColor => Colors.grey; Widget _imageFrameBuilder( - BuildContext context, - Widget? child, - int? frame, - bool? _, - ) { + BuildContext context, + Widget? child, + int? frame, + bool? _, + ) { if (frame == null) { return Container(color: placeholderColor); } @@ -31,94 +31,113 @@ class ProfileView extends GetView { @override Widget build(BuildContext context) { - return Obx(() => profileScreen()); + return Obx(() => profileScreen(context)); } - Widget profileScreen() { + Widget profileScreen(BuildContext context) { return AuthService.to.isLoggedInValue ? ProfileScreen( - // We are using the Flutter Fire Profile Screen now but will change in subsequent steps. - // The issues are highlighted in comments here + // We are using the Flutter Fire Profile Screen now but will change in subsequent steps. + // The issues are highlighted in comments here - // appBar: AppBar( - // title: const Text('User Profile'), - // ), - avatar: SizedBox( - //null will give the profile image component but it does not refresh the pic when changed - height: size, + // appBar: AppBar( + // title: const Text('User Profile'), + // ), + avatar: SizedBox( + //null will give the profile image component but it does not refresh the pic when changed + height: size, + width: size, + child: ClipPath( + clipper: ShapeBorderClipper(shape: shape), + clipBehavior: Clip.hardEdge, + child: controller.photoURL != null + ? Image.network( + controller.photoURL!, + width: size, + height: size, + cacheWidth: size.toInt(), + cacheHeight: size.toInt(), + fit: BoxFit.contain, + frameBuilder: _imageFrameBuilder, + ) + : Center( + child: Image.asset( + 'assets/images/dash.png', width: size, - child: ClipPath( - clipper: ShapeBorderClipper(shape: shape), - clipBehavior: Clip.hardEdge, - child: controller.photoURL != null - ? Image.network( - controller.photoURL!, - width: size, - height: size, - cacheWidth: size.toInt(), - cacheHeight: size.toInt(), - fit: BoxFit.contain, - frameBuilder: _imageFrameBuilder, - ) - : Center( - child: Image.asset( - 'assets/images/dash.png', - width: size, - fit: BoxFit.contain, - ), - ), - ), + fit: BoxFit.contain, ), - // showDeleteConfirmationDialog: true, //this does not work properly. Possibly a bug in FlutterFire - actions: [ - SignedOutAction((context) { - Get.back(); - controller.logout(); - Get.rootDelegate.toNamed(Screen.PROFILE.route); - // Navigator.of(context).pop(); - }), - AccountDeletedAction((context, user) { - //If we don't include this the button is still shown but no action gets done. Ideally the button should also not be shown. Its a bug in FlutterFire - Get.defaultDialog( - //this is only called after the delete is done and not useful for confirmation of the delete action - title: 'Deleted Account of ${user.displayName}', - barrierDismissible: true, - navigatorKey: Get.nestedKey(Screen.HOME.route), - ); - }) - ], - children: [ - //This is to show that we can add custom content here - const Divider(), - controller.currentUser?.email != null - ? TextButton.icon( - onPressed: callChangePwdDialog, - label: const Text('Change Password'), - icon: const Icon(Icons.password_rounded), - ) - : const SizedBox.shrink(), - ImagePickerButton(callback: (String? path) async { - if (path != null) { - //Upload to Store - String? dest = await controller.uploadFile(path); - //attach it to User imageUrl - if (dest != null) { - await controller.updatePhotoURL(dest); - } - } - }) - ], - ) + ), + ), + ), + // showDeleteConfirmationDialog: true, //this does not work properly. Possibly a bug in FlutterFire + actions: [ + SignedOutAction((context) { + Get.back(); + controller.logout(); + Get.rootDelegate.toNamed(Screen.PROFILE.route); + // Navigator.of(context).pop(); + }), + AccountDeletedAction((context, user) { + //If we don't include this the button is still shown but no action gets done. Ideally the button should also not be shown. Its a bug in FlutterFire + Get.defaultDialog( + //this is only called after the delete is done and not useful for confirmation of the delete action + title: 'Deleted Account of ${user.displayName}', + barrierDismissible: true, + navigatorKey: Get.nestedKey(Screen.HOME.route), + ); + }) + ], + children: [ + //This is to show that we can add custom content here + const Divider(), + controller.currentUser?.email != null + ? TextButton.icon( + onPressed: () => _resetPasswordEmailVerification(context), + label: const Text('Reset Password'), + icon: const Icon(Icons.email_rounded), + ) + : const SizedBox.shrink(), + ImagePickerButton(callback: (String? path) async { + if (path != null) { + //Upload to Store + String? dest = await controller.uploadFile(path); + //attach it to User imageUrl + if (dest != null) { + await controller.updatePhotoURL(dest); + } + } + }) + ], + ) : const Scaffold(); } - void callChangePwdDialog() { - var dlg = ChangePasswordDialog(controller.currentUser!); - Get.defaultDialog( - title: "Change Password", - content: dlg, - textConfirm: "Submit", - textCancel: "Cancel", - onConfirm: dlg.onSubmit); + + + Future _resetPasswordEmailVerification(BuildContext context) async { + final email = controller.currentUser?.email; + if (email != null) { + try { + await FirebaseAuth.instance.sendPasswordResetEmail(email: email); + controller.logout(); + Get.snackbar( + 'Success', + 'Password reset email sent. Please check your inbox.', + snackPosition: SnackPosition.BOTTOM, + ); + } catch (e) { + Get.snackbar( + 'Error', + 'Failed to send password reset email: $e', + snackPosition: SnackPosition.BOTTOM, + ); + } + } else { + Get.snackbar( + 'Error', + 'No email associated with this account.', + snackPosition: SnackPosition.BOTTOM, + ); + } } } diff --git a/lib/app/modules/role_requests/bindings/role_requests_binding.dart b/lib/app/modules/role_requests/bindings/role_requests_binding.dart new file mode 100644 index 00000000..8f7233e2 --- /dev/null +++ b/lib/app/modules/role_requests/bindings/role_requests_binding.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; +import '../controllers/role_requests_controllers.dart'; + +class RoleRequestsBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => RoleRequestController(), + ); + } +} diff --git a/lib/app/modules/role_requests/controllers/role_requests_controllers.dart b/lib/app/modules/role_requests/controllers/role_requests_controllers.dart new file mode 100644 index 00000000..cc37f1b0 --- /dev/null +++ b/lib/app/modules/role_requests/controllers/role_requests_controllers.dart @@ -0,0 +1,33 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:get/get.dart'; +import 'package:get_flutter_fire/models/role_requests.dart'; + +class RoleRequestController extends GetxController { + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + var roleRequests = [].obs; + + @override + void onInit() { + super.onInit(); + fetchRoleRequests(); + } + + void fetchRoleRequests() { + _firestore.collection('role_requests').snapshots().listen((snapshot) { + roleRequests.value = snapshot.docs + .map((doc) => RoleRequest.fromMap(doc.data() as Map)) + .toList(); + }); + } + + void addRoleRequest(RoleRequest request) async { + await _firestore.collection('role_requests').add(request.toMap()); + } + + void approveRoleRequest(String requestId) async { + await _firestore + .collection('role_requests') + .doc(requestId) + .update({'approved': true}); + } +} diff --git a/lib/app/modules/role_requests/views/role_requests_view.dart b/lib/app/modules/role_requests/views/role_requests_view.dart new file mode 100644 index 00000000..821dfaa0 --- /dev/null +++ b/lib/app/modules/role_requests/views/role_requests_view.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../controllers/role_requests_controllers.dart'; + +class RoleRequestsView extends GetView { + const RoleRequestsView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Role Requests')), + body: Obx(() { + if (controller.roleRequests.isEmpty) { + return const Center(child: Text('No role requests')); + } + return ListView.builder( + itemCount: controller.roleRequests.length, + itemBuilder: (context, index) { + final request = controller.roleRequests[index]; + return ListTile( + title: Text('User: ${request.userId}'), + subtitle: Text('Requested Role: ${request.requestedRole}'), + trailing: request.approved + ? const Icon(Icons.check, color: Colors.green) + : ElevatedButton( + onPressed: () => + controller.approveRoleRequest(request.userId), + child: const Text('Approve'), + ), + ); + }, + ); + }), + ); + } +} diff --git a/lib/app/modules/root/views/drawer.dart b/lib/app/modules/root/views/drawer.dart index 908d0223..ed5eb929 100644 --- a/lib/app/modules/root/views/drawer.dart +++ b/lib/app/modules/root/views/drawer.dart @@ -7,7 +7,8 @@ import '../../../../models/role.dart'; import '../../../../services/auth_service.dart'; import '../../../../models/screens.dart'; -import '../controllers/my_drawer_controller.dart'; +import '../../../../services/remote_config.dart'; +import '../../../widgets/remotely_config_obx.dart'; class DrawerWidget extends StatelessWidget { const DrawerWidget({ @@ -16,23 +17,26 @@ class DrawerWidget extends StatelessWidget { @override Widget build(BuildContext context) { - MyDrawerController controller = Get.put(MyDrawerController([]), - permanent: true); //must make true else gives error - Screen.drawer().then((v) => {controller.values.value = v}); - return Obx(() => Drawer( - //changing the shape of the drawer - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(0), bottomRight: Radius.circular(20)), - ), - width: 200, - child: Column( - children: drawerItems(context, controller.values), - ), - )); + return RemotelyConfigObxVal.noparam( + (data) => Drawer( + //changing the shape of the drawer + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topRight: Radius.circular(0), bottomRight: Radius.circular(20)), + ), + width: 200, + child: Column( + children: drawerItems(context, data), + ), + ), + List.empty().obs, + "useBottomSheetForProfileOptions", + Typer.boolean, + func: Screen.drawer, + ); } - List drawerItems(BuildContext context, Rx> values) { + List drawerItems(BuildContext context, Iterable values) { List list = [ Container( height: 100, @@ -67,7 +71,7 @@ class DrawerWidget extends StatelessWidget { } } - for (Screen screen in values.value) { + for (Screen screen in values) { list.add(ListTile( title: Text(screen.label ?? ''), onTap: () { diff --git a/lib/app/modules/root/views/root_view.dart b/lib/app/modules/root/views/root_view.dart index 2bbf228c..c7c6c0ca 100644 --- a/lib/app/modules/root/views/root_view.dart +++ b/lib/app/modules/root/views/root_view.dart @@ -5,6 +5,8 @@ import 'package:get/get.dart'; import 'package:get_flutter_fire/services/auth_service.dart'; import '../../../routes/app_pages.dart'; import '../../../../models/screens.dart'; +import '../../../utils/icon_constants.dart'; +import '../../../widgets/screen_widget.dart'; import '../controllers/root_controller.dart'; import 'drawer.dart'; @@ -31,14 +33,14 @@ class RootView extends GetView { ) : IconButton( icon: ImageIcon( - const AssetImage("icons/logo.png"), + const AssetImage(IconConstants.logo), color: Colors.grey.shade800, ), onPressed: () => AuthService.to.isLoggedInValue ? controller.openDrawer() : {Screen.HOME.doAction()}, ), - actions: topRightMenuButtons(current), + actions: ScreenWidgetExtension.topRightMenuButtons(current), // automaticallyImplyLeading: false, //removes drawer icon ), body: GetRouterOutlet( @@ -52,13 +54,4 @@ class RootView extends GetView { }, ); } - -//This could be used to add icon buttons in expanded web view instead of the context menu - List topRightMenuButtons(GetNavConfig current) { - return [ - Container( - margin: const EdgeInsets.only(right: 15), - child: Screen.LOGIN.widget(current)) - ]; //TODO add seach button - } } diff --git a/lib/app/modules/search/bindings/search_binding.dart b/lib/app/modules/search/bindings/search_binding.dart new file mode 100644 index 00000000..64cfa36f --- /dev/null +++ b/lib/app/modules/search/bindings/search_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/search_controller.dart'; + +class SearchBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => SearchController(), + ); + } +} diff --git a/lib/app/modules/search/controllers/search_controller.dart b/lib/app/modules/search/controllers/search_controller.dart new file mode 100644 index 00000000..f282fabb --- /dev/null +++ b/lib/app/modules/search/controllers/search_controller.dart @@ -0,0 +1,23 @@ +import 'package:get/get.dart'; + +class SearchController extends GetxController { + //TODO: Implement SearchController + + final count = 0.obs; + @override + void onInit() { + super.onInit(); + } + + @override + void onReady() { + super.onReady(); + } + + @override + void onClose() { + super.onClose(); + } + + void increment() => count.value++; +} diff --git a/lib/app/modules/search/views/search_view.dart b/lib/app/modules/search/views/search_view.dart new file mode 100644 index 00000000..f0c3c16c --- /dev/null +++ b/lib/app/modules/search/views/search_view.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart' hide SearchController; + +import 'package:get/get.dart'; + +import '../controllers/search_controller.dart'; + +class SearchView extends GetView { + const SearchView({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('SearchView'), + centerTitle: true, + ), + body: const Center( + child: Text( + 'SearchView is working', + style: TextStyle(fontSize: 20), + ), + ), + ); + } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 7269755d..35e129f3 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; + import 'package:get/get.dart'; +import 'package:get_flutter_fire/app/modules/role_requests/bindings/role_requests_binding.dart'; +import 'package:get_flutter_fire/app/modules/role_requests/views/role_requests_view.dart'; import '../../models/access_level.dart'; import '../../models/role.dart'; +import '../../models/screens.dart'; import '../middleware/auth_middleware.dart'; import '../modules/cart/bindings/cart_binding.dart'; import '../modules/cart/views/cart_view.dart'; @@ -28,6 +32,8 @@ import '../modules/register/bindings/register_binding.dart'; import '../modules/register/views/register_view.dart'; import '../modules/root/bindings/root_binding.dart'; import '../modules/root/views/root_view.dart'; +import '../modules/search/bindings/search_binding.dart'; +import '../modules/search/views/search_view.dart'; import '../modules/settings/bindings/settings_binding.dart'; import '../modules/settings/views/settings_view.dart'; import '../modules/task_details/bindings/task_details_binding.dart'; @@ -36,7 +42,6 @@ import '../modules/tasks/bindings/tasks_binding.dart'; import '../modules/tasks/views/tasks_view.dart'; import '../modules/users/bindings/users_binding.dart'; import '../modules/users/views/users_view.dart'; -import '../../models/screens.dart'; part 'app_routes.dart'; part 'screen_extension.dart'; @@ -60,6 +65,10 @@ class AppPages { page: () => const LoginView(), binding: LoginBinding(), ), + Screen.LOGOUT.getPage( + page: () => const LoginView(), + binding: LoginBinding(), + ), Screen.REGISTER.getPage( page: () => const RegisterView(), binding: RegisterBinding(), @@ -146,9 +155,17 @@ class AppPages { ), ], ), + Screen.ROLE_REQUEST.getPage( + page: () => const RoleRequestsView(), + binding: RoleRequestsBinding(), + ), ], ) ], ), + Screen.SEARCH.getPage( + page: () => const SearchView(), + binding: SearchBinding(), + ), ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index f3129d21..1eb11696 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -17,6 +17,7 @@ abstract class Routes { // static const TASKS = _Paths.HOME + _Paths.TASKS; // static const USERS = _Paths.HOME + _Paths.USERS; // static const MY_PRODUCTS = _Paths.HOME + _Paths.MY_PRODUCTS; + static String ROLE_REQUEST = Screen.ROLE_REQUEST.route; static String PRODUCT_DETAILS(String productId) => '${Screen.PRODUCTS.route}/$productId'; @@ -30,6 +31,7 @@ abstract class Routes { '${Screen.LOGIN.route}?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; static String REGISTER_THEN(String afterSuccessfulLogin) => '${Screen.REGISTER.route}?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; + // static const SEARCH = _Paths.SEARCH; } // Keeping this as Get_Cli will require it. Any addition can later be added to Screen @@ -51,4 +53,5 @@ abstract class _Paths { // static const USERS = '/users'; // static const USER_PROFILE = '/:uId'; // static const MY_PRODUCTS = '/my-products'; + // static const SEARCH = '/search'; } diff --git a/lib/app/routes/screen_extension.dart b/lib/app/routes/screen_extension.dart index aaf138b0..23877c7d 100644 --- a/lib/app/routes/screen_extension.dart +++ b/lib/app/routes/screen_extension.dart @@ -110,11 +110,14 @@ extension ScreenExtension on Screen { extension RoleExtension on Role { int getCurrentIndexFromRoute(GetNavConfig? currentRoute) { - final String? currentLocation = currentRoute?.location; + final String? currentLocation = currentRoute?.uri.path; int currentIndex = 0; if (currentLocation != null) { - currentIndex = - tabs.indexWhere((tab) => currentLocation.startsWith(tab.path)); + currentIndex = tabs.indexWhere((tab) { + String parentPath = tab.parent?.path ?? ''; + String fullPath = '$parentPath${tab.path}'; + return currentLocation.startsWith(fullPath); + }); } return (currentIndex > 0) ? currentIndex : 0; } diff --git a/lib/app/utils/icon_constants.dart b/lib/app/utils/icon_constants.dart new file mode 100644 index 00000000..b3351a95 --- /dev/null +++ b/lib/app/utils/icon_constants.dart @@ -0,0 +1,5 @@ +abstract class IconConstants { + static const _assetsIcon = 'assets/icons'; + + static const logo = '$_assetsIcon/logo.png'; +} diff --git a/lib/app/utils/img_constants.dart b/lib/app/utils/img_constants.dart new file mode 100644 index 00000000..83a5c0c6 --- /dev/null +++ b/lib/app/utils/img_constants.dart @@ -0,0 +1,6 @@ +abstract class ImgConstants { + static const _assetsImg = 'assets/images'; + + static const dash = '$_assetsImg/dash.png'; + static const flutterfire = '$_assetsImg/flutterfire_300x.png'; +} diff --git a/lib/app/widgets/image_picker_button.dart b/lib/app/widgets/image_picker_button.dart index d6e87ff4..dbd504ef 100644 --- a/lib/app/widgets/image_picker_button.dart +++ b/lib/app/widgets/image_picker_button.dart @@ -44,16 +44,38 @@ enum ImageSources implements ActionEnum { } static Future getFile() async { - FilePickerResult? result = await FilePicker.platform - .pickFiles(type: FileType.image, allowMultiple: false); + if (GetPlatform.isWeb) { + // Web-specific file picking logic + return await getWebFile(); + } else { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.image, + allowMultiple: false, + ); + + if (result != null && result.files.isNotEmpty) { + final fileBytes = result.files.first.bytes; + final fileName = result.files.first.name; + GetStorage().write(fileName, fileBytes); + return fileName; + } else { + Get.snackbar('Error', 'Image Not Selected'); + return null; + } + } + } + + static Future getWebFile() async { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.image, + allowMultiple: false, + ); if (result != null && result.files.isNotEmpty) { final fileBytes = result.files.first.bytes; final fileName = result.files.first.name; GetStorage().write(fileName, fileBytes); - return fileName; - //result.files.single.path;//is causing issues for Web, see https://github.com/miguelpruivo/flutter_file_picker/wiki/FAQ } else { Get.snackbar('Error', 'Image Not Selected'); return null; @@ -64,11 +86,12 @@ enum ImageSources implements ActionEnum { class ImagePickerButton extends MenuSheetButton { final ValueSetter? callback; - const ImagePickerButton( - {super.key, - super.icon = const Icon(Icons.image), - super.label = 'Pick an Image', - this.callback}); + const ImagePickerButton({ + super.key, + super.icon = const Icon(Icons.image), + super.label = 'Pick an Image', + this.callback, + }); @override Iterable get values => ImageSources.values; @@ -80,12 +103,10 @@ class ImagePickerButton extends MenuSheetButton { @override Widget build(BuildContext context) { - return !(GetPlatform.isAndroid || GetPlatform.isIOS) - ? TextButton.icon( - onPressed: () async => callbackFunc(await ImageSources.getFile()), - icon: icon, - label: const Text('Pick an Image'), - ) - : builder(context); + return TextButton.icon( + onPressed: () async => callbackFunc(await ImageSources.getFile()), + icon: icon, + label: const Text('Pick an Image'), + ); } -} +} \ No newline at end of file diff --git a/lib/app/widgets/login_widgets.dart b/lib/app/widgets/login_widgets.dart index b8f2d8c1..79ac3ad6 100644 --- a/lib/app/widgets/login_widgets.dart +++ b/lib/app/widgets/login_widgets.dart @@ -6,7 +6,9 @@ import 'package:get/get.dart'; import '../../services/auth_service.dart'; import '../../models/screens.dart'; import '../../services/remote_config.dart'; +import '../utils/img_constants.dart'; import 'menu_sheet_button.dart'; +import 'remotely_config_obx.dart'; class LoginWidgets { static Widget headerBuilder(context, constraints, shrinkOffset) { @@ -14,7 +16,7 @@ class LoginWidgets { padding: const EdgeInsets.all(20), child: AspectRatio( aspectRatio: 1, - child: Image.asset('assets/images/flutterfire_300x.png'), + child: Image.asset('assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png'), ), ); } @@ -24,7 +26,7 @@ class LoginWidgets { children: [ myWidget, const Padding( - padding: EdgeInsets.only(top: 16), + padding: EdgeInsets.only(top: 20), child: Text( 'By signing in, you agree to our terms and conditions.', style: TextStyle(color: Colors.grey), @@ -38,7 +40,7 @@ class LoginWidgets { padding: const EdgeInsets.all(20), child: AspectRatio( aspectRatio: 1, - child: Image.asset('assets/images/flutterfire_300x.png'), + child: Image.asset(ImgConstants.flutterfire), ), ); } @@ -56,31 +58,36 @@ class LoginBottomSheetToggle extends MenuSheetButton { @override Icon? get icon => (AuthService.to.isLoggedInValue) - ? values.length == 1 + ? values.length <= 1 ? const Icon(Icons.logout) : const Icon(Icons.menu) : const Icon(Icons.login); @override String? get label => (AuthService.to.isLoggedInValue) - ? values.length == 1 + ? values.length <= 1 ? 'Logout' : 'Click for Options' : 'Login'; + @override + void buttonPressed(Iterable values) async => values.isEmpty + ? callbackFunc(await Screen.LOGOUT.doAction()) + : super.buttonPressed(values); + @override Widget build(BuildContext context) { - MenuItemsController controller = Get.put( - MenuItemsController([]), - permanent: true); //must make true else gives error - Screen.sheet(null).then((val) { - controller.values.value = val; - }); - RemoteConfig.instance.then((ins) => - ins.addUseBottomSheetForProfileOptionsListener((val) async => - {controller.values.value = await Screen.sheet(null)})); + MenuItemsController controller = + MenuItemsController(const Iterable.empty()); return Obx(() => (AuthService.to.isLoggedInValue) - ? builder(context, vals: controller.values.value) + ? RemotelyConfigObx( + () => builder(context, vals: controller.values.value), + controller, + Screen.sheet, + Screen.NONE, + "useBottomSheetForProfileOptions", + Typer.boolean, + ) : !(current.currentPage!.name == Screen.LOGIN.path) ? IconButton( onPressed: () async { diff --git a/lib/app/widgets/menu_sheet_button.dart b/lib/app/widgets/menu_sheet_button.dart index abd3873e..31649e4d 100644 --- a/lib/app/widgets/menu_sheet_button.dart +++ b/lib/app/widgets/menu_sheet_button.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../models/action_enum.dart'; +import 'remotely_config_obx.dart'; -class MenuItemsController extends GetxController { - MenuItemsController(Iterable iter) : values = Rx>(iter); - - final Rx> values; +class MenuItemsController + extends RemoteConfigController> { + MenuItemsController(super.iter); } class MenuSheetButton extends StatelessWidget { @@ -65,7 +65,7 @@ class MenuSheetButton extends StatelessWidget { //This should be a modal bottom sheet if on Mobile (See https://mercyjemosop.medium.com/select-and-upload-images-to-firebase-storage-flutter-6fac855970a9) Widget builder(BuildContext context, {Iterable? vals}) { Iterable values = vals ?? values_!; - return values.length == 1 || + return values.length <= 1 || Get.mediaQuery.orientation == Orientation.portrait // : Get.context!.isPortrait ? (icon != null diff --git a/lib/app/widgets/remotely_config_obx.dart b/lib/app/widgets/remotely_config_obx.dart new file mode 100644 index 00000000..684503c0 --- /dev/null +++ b/lib/app/widgets/remotely_config_obx.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../services/remote_config.dart'; + +class RemoteConfigController extends GetxController { + RemoteConfigController(L iter) : values = Rx(iter); + final Rx values; +} + +class RemotelyConfigObx>> + extends ObxWidget { + final Widget Function() builder; + final X param; + final C controller; + final String config; + final Typer configType; + final Future Function(X) func; + + RemotelyConfigObx(this.builder, this.controller, this.func, this.param, + this.config, this.configType, + {super.key}) { + Get.put(controller, permanent: true); //must make true else gives error + func(param).then((v) { + controller.values.value = v; + }); + RemoteConfig.instance.then((ins) => ins.addListener(config, configType, + (val) async => {controller.values.value = await func(param)})); + } + + @override + Widget build() => builder(); +} + +class RemotelyConfigObxVal extends ObxWidget { + final Widget Function(T) builder; + final T data; + final String config; + final Typer configType; + + RemotelyConfigObxVal(this.builder, this.data, this.config, this.configType, + {super.key, required Future Function(X) func, required X param}) { + func(param).then((v) => {data.value = v}); + RemoteConfig.instance.then((ins) => ins.addListener( + config, configType, (val) async => {data.value = await func(param)})); + } + + RemotelyConfigObxVal.noparam( + this.builder, this.data, this.config, this.configType, + {super.key, required Future Function() func}) { + func().then((v) => {data.value = v}); + RemoteConfig.instance.then((ins) => ins.addListener( + config, configType, (val) async => {data.value = await func()})); + } + + @override + Widget build() => builder(data); +} diff --git a/lib/app/widgets/screen_widget.dart b/lib/app/widgets/screen_widget.dart index d80c9275..438f91e5 100644 --- a/lib/app/widgets/screen_widget.dart +++ b/lib/app/widgets/screen_widget.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import '../routes/app_pages.dart'; import '../../models/role.dart'; import '../../models/screens.dart'; +import 'login_widgets.dart'; class ScreenWidget extends StatelessWidget { final Widget body; @@ -73,3 +74,31 @@ class ScreenWidget extends StatelessWidget { return null; //TODO multi fab button on press } } + +extension ScreenWidgetExtension on Screen { + Widget? widget(GetNavConfig current) { + //those with accessor == widget must be handled here + switch (this) { + case Screen.SEARCH: + return IconButton(onPressed: () => {}, icon: Icon(icon)); + case Screen.LOGIN: + return LoginBottomSheetToggle(current); + case Screen.LOGOUT: + return LoginBottomSheetToggle(current); + default: + } + return null; + } + +//This could be used to add icon buttons in expanded web view instead of the context menu + static List topRightMenuButtons(GetNavConfig current) { + List widgets = []; + for (var screen in Screen.topRightMenu()) { + widgets.add(Container( + margin: const EdgeInsets.only(right: 15), + child: screen.widget(current))); + } + + return widgets; //This will return empty. We need a Obx + } +} diff --git a/lib/app/widgets/search_bar_button.dart b/lib/app/widgets/search_bar_button.dart new file mode 100644 index 00000000..14f60d37 --- /dev/null +++ b/lib/app/widgets/search_bar_button.dart @@ -0,0 +1,3 @@ +// This uses Remote Config to know where to locate the search button +// If on top, then it expands to title area on press in mobiles and is already expanded in web +// If on Nav Bar, the it initiates a SearchAnchor as a bottomsheet in mobile and like a drawer/nav column in web \ No newline at end of file diff --git a/lib/models/access_level.dart b/lib/models/access_level.dart index a7b89742..20b79252 100644 --- a/lib/models/access_level.dart +++ b/lib/models/access_level.dart @@ -1,7 +1,7 @@ enum AccessLevel { + notAuthed, // used for login screens public, //available without any login guest, //available with guest login - notAuthed, // used for login screens authenticated, //available on login roleBased, //available on login and with allowed roles masked, //available in a partly masked manner based on role diff --git a/lib/models/cart_item.dart b/lib/models/cart_item.dart new file mode 100644 index 00000000..5d737ccc --- /dev/null +++ b/lib/models/cart_item.dart @@ -0,0 +1,29 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; + +class CartItem { + String id; + String name; + int quantity; + + CartItem({ + required this.id, + required this.name, + required this.quantity, + }); + + factory CartItem.fromDocument(DocumentSnapshot doc) { + final data = doc.data() as Map; + return CartItem( + id: doc.id, + name: data['name'] ?? '', + quantity: data['quantity'] ?? 0, + ); + } + + Map toMap() { + return { + 'name': name, + 'quantity': quantity, + }; + } +} diff --git a/lib/models/category.dart b/lib/models/category.dart new file mode 100644 index 00000000..e8a10903 --- /dev/null +++ b/lib/models/category.dart @@ -0,0 +1,26 @@ +class Category { + String id; + String name; + List subCategories; + + Category( + {required this.id, required this.name, this.subCategories = const []}); + + factory Category.fromMap(Map data) { + return Category( + id: data['id'], + name: data['name'], + subCategories: (data['subCategories'] as List) + .map((subCat) => Category.fromMap(subCat)) + .toList(), + ); + } + + Map toMap() { + return { + 'id': id, + 'name': name, + 'subCategories': subCategories.map((subCat) => subCat.toMap()).toList(), + }; + } +} diff --git a/lib/models/role_requests.dart b/lib/models/role_requests.dart new file mode 100644 index 00000000..56bb9efe --- /dev/null +++ b/lib/models/role_requests.dart @@ -0,0 +1,23 @@ +class RoleRequest { + String userId; + String requestedRole; + bool approved; + + RoleRequest({required this.userId, required this.requestedRole, this.approved = false}); + + factory RoleRequest.fromMap(Map data) { + return RoleRequest( + userId: data['userId'], + requestedRole: data['requestedRole'], + approved: data['approved'] ?? false, + ); + } + + Map toMap() { + return { + 'userId': userId, + 'requestedRole': requestedRole, + 'approved': approved, + }; + } +} diff --git a/lib/models/screens.dart b/lib/models/screens.dart index 24dee39f..700eb7b1 100644 --- a/lib/models/screens.dart +++ b/lib/models/screens.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../app/widgets/login_widgets.dart'; import '../services/remote_config.dart'; import 'action_enum.dart'; import 'access_level.dart'; @@ -8,9 +7,13 @@ import '../../services/auth_service.dart'; enum AccessedVia { auto, - widget, //example: top right button - navigator, //bottom nav. can be linked to drawer items //handled in ScreenWidget - drawer, //creates nav tree //handled in RootView + widget, + topRight, + topCenter, + topLeft, + topBar, //bar below the main top menu bar + navigator, //bottom nav. can be linked to drawer items. left strip in expanded web //handled in ScreenWidget + drawer, //creates nav tree. persistant in expanded web and linked with nav icons //handled in RootView bottomSheet, //context menu for web handled via the Button that calls the sheet fab, //handled in ScreenWidget singleTap, //when an item of a list is clicked @@ -18,6 +21,7 @@ enum AccessedVia { } enum Screen implements ActionEnum { + NONE.none(), //null HOME('/home', icon: Icons.home, label: "Home", @@ -37,22 +41,18 @@ enum Screen implements ActionEnum { parent: HOME), PRODUCT_DETAILS('/:productId', accessLevel: AccessLevel.public, parent: PRODUCTS), - LOGIN('/login', - icon: Icons.login, - accessor_: AccessedVia.widget, - accessLevel: AccessLevel.notAuthed), PROFILE('/profile', icon: Icons.account_box_rounded, label: "Profile", accessor_: AccessedVia.drawer, accessLevel: AccessLevel.authenticated, - remoteConfig: true), + remoteConfig: "useBottomSheetForProfileOptions"), SETTINGS('/settings', icon: Icons.settings, label: "Settings", accessor_: AccessedVia.drawer, accessLevel: AccessLevel.authenticated, - remoteConfig: true), + remoteConfig: "useBottomSheetForProfileOptions"), CART('/cart', icon: Icons.trolley, label: "Cart", @@ -96,20 +96,45 @@ enum Screen implements ActionEnum { accessLevel: AccessLevel.roleBased), MY_PRODUCT_DETAILS('/:productId', parent: MY_PRODUCTS, accessLevel: AccessLevel.roleBased), + SEARCH('/search', + icon: Icons.search, + label: "Search", + accessor_: AccessedVia.topRight, + remoteConfig: "showSearchBarOnTop", + accessLevel: AccessLevel.public), LOGOUT('/login', icon: Icons.logout, label: "Logout", - accessor_: AccessedVia.bottomSheet, + accessor_: AccessedVia.topRight, + remoteConfig: "useBottomSheetForProfileOptions", accessLevel: AccessLevel.authenticated), + ROLE_REQUEST('/role-request', + icon: Icons.request_page, + label: "Role Request", + accessor_: AccessedVia.drawer, + accessLevel: AccessLevel.authenticated), + LOGIN('/login', + icon: Icons.login, + accessor_: AccessedVia.topRight, + accessLevel: AccessLevel.notAuthed), ; const Screen(this.path, {this.icon, this.label, - this.parent, + this.parent = Screen.NONE, this.accessor_ = AccessedVia.singleTap, this.accessLevel = AccessLevel.authenticated, - this.remoteConfig = false}); + this.remoteConfig}); + + const Screen.none() + : path = '', + icon = null, + label = null, + parent = null, + accessor_ = AccessedVia.singleTap, + accessLevel = AccessLevel.authenticated, + remoteConfig = null; @override final IconData? icon; @@ -121,10 +146,10 @@ enum Screen implements ActionEnum { final Screen? parent; final AccessLevel accessLevel; //if false it is role based. true means allowed for all - final bool remoteConfig; + final String? remoteConfig; Future get accessor async { - if (remoteConfig && + if (remoteConfig == "useBottomSheetForProfileOptions" && (await RemoteConfig.instance).useBottomSheetForProfileOptions()) { return AccessedVia.bottomSheet; } @@ -164,6 +189,12 @@ enum Screen implements ActionEnum { return list; } + static Iterable topRightMenu() { + return Screen.values.where((Screen screen) => + screen.accessor_ == AccessedVia.topRight && + AuthService.to.accessLevel.index >= screen.accessLevel.index); + } + @override Future doAction() async { if (this == LOGOUT) { @@ -171,7 +202,4 @@ enum Screen implements ActionEnum { } Get.rootDelegate.toNamed(route); } - - Widget? widget(GetNavConfig current) => - (this == LOGIN) ? LoginBottomSheetToggle(current) : null; } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 8bf72aaa..a6bef783 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -5,6 +5,7 @@ import 'package:firebase_ui_auth/firebase_ui_auth.dart' as fbui; import 'package:firebase_ui_localizations/firebase_ui_localizations.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get_flutter_fire/models/access_level.dart'; import '../models/screens.dart'; import '../constants.dart'; @@ -37,6 +38,14 @@ class AuthService extends GetxService { }); } + AccessLevel get accessLevel => user != null + ? user!.isAnonymous + ? _userRole.value.index > Role.buyer.index + ? AccessLevel.roleBased + : AccessLevel.authenticated + : AccessLevel.guest + : AccessLevel.public; + bool get isEmailVerified => user != null && (user!.email == null || user!.emailVerified); diff --git a/lib/services/remote_config.dart b/lib/services/remote_config.dart index 5d1145a5..cb2a8928 100644 --- a/lib/services/remote_config.dart +++ b/lib/services/remote_config.dart @@ -13,7 +13,6 @@ class RemoteConfig { } final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; - final List listeners = []; Future init() async { await _remoteConfig.setConfigSettings(RemoteConfigSettings( @@ -68,11 +67,4 @@ class RemoteConfig { bool showSearchBarOnTop() { return _remoteConfig.getBool("showSearchBarOnTop"); } - - void addUseBottomSheetForProfileOptionsListener(listener) { - addListener("useBottomSheetForProfileOptions", Typer.boolean, listener); - if (!listeners.contains(listener)) { - listeners.add(listener); - } - } } diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 00000000..887b9e76 --- /dev/null +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,11 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter +FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100644 index 00000000..abed0b45 --- /dev/null +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter" +export "FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..c795730d --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Runner/GoogleService-Info.plist b/macos/Runner/GoogleService-Info.plist new file mode 100644 index 00000000..78e3a245 --- /dev/null +++ b/macos/Runner/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo + API_KEY + AIzaSyD12e9qHOj8ulb6cxP0DIWHW2tIeYzgKtc + GCM_SENDER_ID + 56053565487 + PLIST_VERSION + 1 + BUNDLE_ID + com.example.getFlutterFire + PROJECT_ID + sharekhan-nemin + STORAGE_BUCKET + sharekhan-nemin.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:56053565487:ios:3515d315addadb07cb5eda + + \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 877fc75e..cc5b1abe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: a0f161b92610e078b4962d7e6ebeb66dc9cce0ada3514aeee442f68165d78185 + url: "https://pub.dev" + source: hosted + version: "4.17.5" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: "6a55b319f8d33c307396b9104512e8130a61904528ab7bd8b5402678fca54b81" + url: "https://pub.dev" + source: hosted + version: "6.2.5" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: "89dfa1304d3da48b3039abbb2865e3d30896ef858e569a16804a99f4362283a9" + url: "https://pub.dev" + source: hosted + version: "3.12.5" collection: dependency: transitive description: @@ -117,10 +141,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a" + sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258" url: "https://pub.dev" source: hosted - version: "8.0.3" + version: "8.0.6" file_selector_linux: dependency: transitive description: @@ -181,26 +205,26 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: f0a75f61992d036e4c46ad0e9febd364d98aa2c092690a5475cb1421a8243cfe + sha256: cfc2d970829202eca09e2896f0a5aa7c87302817ecc0bdfa954f026046bf10ba url: "https://pub.dev" source: hosted - version: "4.19.5" + version: "4.20.0" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: feb77258404309ffc7761c78e1c0ad2ed5e4fdc378e035619e2cc13be4397b62 + sha256: a0270e1db3b2098a14cb2a2342b3cd2e7e458e0c391b1f64f6f78b14296ec093 url: "https://pub.dev" source: hosted - version: "7.2.6" + version: "7.3.0" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "6d527f357da2bf93a67a42b423aa92943104a0c290d1d72ad9a42c779d501cd2" + sha256: "64e067e763c6378b7e774e872f0f59f6812885e43020e25cde08f42e9459837b" url: "https://pub.dev" source: hosted - version: "5.11.5" + version: "5.12.0" firebase_core: dependency: "direct main" description: @@ -213,34 +237,34 @@ packages: dependency: transitive description: name: firebase_core_platform_interface - sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9" + sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.17.3" firebase_dynamic_links: dependency: transitive description: name: firebase_dynamic_links - sha256: f704859abc17d99e74b47eaf47455b45a88ab7e2973f03e6130ff666b45fe11f + sha256: "47b8c8a8546d8a7f9000edb90848549f20b137d814ee7e0407b3d43b8445e282" url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.5.7" firebase_dynamic_links_platform_interface: dependency: transitive description: name: firebase_dynamic_links_platform_interface - sha256: f86992605b50e2f0ce6c24993430affc98021da8d8a74d5596b7a2c84196c110 + sha256: "72e7810635f908ce060c5803c7acb29116c5b6befc73e90446c52722bc9506a2" url: "https://pub.dev" source: hosted - version: "0.2.6+33" + version: "0.2.6+35" firebase_remote_config: dependency: "direct main" description: @@ -269,26 +293,26 @@ packages: dependency: "direct main" description: name: firebase_storage - sha256: da76ca9c11d795c4bae1bd13b31d54bb9eb9ccbee7eb5f6b86b8294370e9d488 + sha256: "2ae478ceec9f458c1bcbf0ee3e0100e4e909708979e83f16d5d9fba35a5b42c1" url: "https://pub.dev" source: hosted - version: "11.7.5" + version: "11.7.7" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: be17bfa9110a6429b40dd3760c755034079fd734aa1dd2476d5638ab780cc508 + sha256: "4e18662e6a66e2e0e181c06f94707de06d5097d70cfe2b5141bf64660c5b5da9" url: "https://pub.dev" source: hosted - version: "5.1.20" + version: "5.1.22" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: "5219c20c0768a8e2ffedf0a116b7bc80ab32fcc6e2cbd50cbde14f8c4575c3f4" + sha256: "3a44aacd38a372efb159f6fe36bb4a7d79823949383816457fd43d3d47602a53" url: "https://pub.dev" source: hosted - version: "3.9.5" + version: "3.9.7" firebase_ui_auth: dependency: "direct main" description: @@ -351,10 +375,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.0.20" flutter_svg: dependency: transitive description: @@ -417,10 +441,10 @@ packages: dependency: transitive description: name: google_sign_in_android - sha256: "7647893c65e6720973f0e579051c8f84b877b486614d9f70a404259c41a4632e" + sha256: d30fb34b659679ea74397e9748b4ab5d720720d57dcc79538f1b3c4a68654cb3 url: "https://pub.dev" source: hosted - version: "6.1.23" + version: "6.1.27" google_sign_in_ios: dependency: transitive description: @@ -441,10 +465,10 @@ packages: dependency: transitive description: name: google_sign_in_web - sha256: fc0f14ed45ea616a6cfb4d1c7534c2221b7092cc4f29a709f0c3053cc3e821bd + sha256: d606264c7a1a526a3aa79d938b85a601d8589731a478bd4a3dcbdeb14a572228 url: "https://pub.dev" source: hosted - version: "0.12.4" + version: "0.12.4+1" http: dependency: transitive description: @@ -465,18 +489,18 @@ packages: dependency: "direct main" description: name: image_picker - sha256: "33974eca2e87e8b4e3727f1b94fa3abcb25afe80b6bc2c4d449a0e150aedf720" + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: "79455f6cff4cbef583b2b524bbf0d4ec424e5959f4d464e36ef5323715b98370" + sha256: ff39a10ab4f48f4ac70776d0494a97bf073cd2570892cd46bc8a5cac162c25db url: "https://pub.dev" source: hosted - version: "0.8.12" + version: "0.8.12+4" image_picker_for_web: dependency: transitive description: @@ -489,10 +513,10 @@ packages: dependency: transitive description: name: image_picker_ios - sha256: cb0db0ec0d3e2cd49674f2e6053be25ccdb959832607c1cbd215dd6cf10fb0dd + sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" url: "https://pub.dev" source: hosted - version: "0.8.11" + version: "0.8.12" image_picker_linux: dependency: transitive description: @@ -625,10 +649,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.7" path_provider_foundation: dependency: transitive description: @@ -657,10 +681,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" petitparser: dependency: transitive description: @@ -673,10 +697,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -798,10 +822,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.1" xdg_directories: dependency: transitive description: @@ -819,5 +843,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.3.4 <4.0.0" - flutter: ">=3.19.2" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2909a374..3b85c0c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,43 +1,44 @@ name: get_flutter_fire version: 1.0.0+1 publish_to: none -description: Boilerplate for Flutter with GetX, showing sample utilization of Firebase capabilities -environment: +description: Boilerplate for Flutter with GetX, showing sample utilization of + Firebase capabilities +environment: sdk: '>=3.3.4 <4.0.0' -dependencies: +dependencies: + cloud_firestore: ^4.17.5 cupertino_icons: ^1.0.6 - get: 4.6.6 - flutter: - sdk: flutter + file_picker: ^8.0.3 + firebase_analytics: ^10.10.7 + firebase_auth: ^4.19.5 firebase_core: ^2.31.0 + firebase_remote_config: ^4.4.7 + firebase_storage: ^11.7.5 firebase_ui_auth: ^1.14.0 - firebase_auth: ^4.19.5 - google_sign_in: ^6.2.1 + firebase_ui_localizations: ^1.12.0 firebase_ui_oauth_google: ^1.3.2 + flutter: + sdk: flutter + get: 4.6.6 + get_storage: ^2.1.1 google_fonts: ^6.2.1 - firebase_storage: ^11.7.5 + google_sign_in: ^6.2.1 image_picker: ^1.1.1 - file_picker: ^8.0.3 path: ^1.9.0 - get_storage: ^2.1.1 - firebase_ui_localizations: ^1.12.0 - firebase_remote_config: ^4.4.7 - firebase_analytics: ^10.10.7 -dev_dependencies: +dev_dependencies: flutter_lints: 3.0.2 - flutter_test: + flutter_test: sdk: flutter -flutter: +flutter: fonts: - family: SocialIcons fonts: - asset: packages/firebase_ui_auth/fonts/SocialIcons.ttf assets: - - assets/images/flutterfire_300x.png - - assets/images/dash.png - - assets/icons/logo.png + - assets/ + - assets/images/ + - assets/icons/ uses-material-design: true -