Repository: perpetio/fitness Branch: master Commit: abfe5d79e91d Files: 146 Total size: 245.3 KB Directory structure: gitextract_x3j_na3e/ ├── .firebase/ │ └── hosting.YnVpbGQvd2Vi.cache ├── .firebaserc ├── .gitignore ├── .metadata ├── .vscode/ │ └── launch.json ├── README.md ├── android/ │ ├── .gitignore │ ├── app/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── google-services.json │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin/ │ │ │ │ └── com/ │ │ │ │ └── example/ │ │ │ │ └── fitness_flutter/ │ │ │ │ └── MainActivity.kt │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21/ │ │ │ │ └── launch_background.xml │ │ │ ├── values/ │ │ │ │ └── styles.xml │ │ │ └── values-night/ │ │ │ └── styles.xml │ │ └── profile/ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ └── settings.gradle ├── assets/ │ └── fonts/ │ └── NotoSansKR/ │ ├── NotoSansKR-Black.otf │ ├── NotoSansKR-Bold.otf │ ├── NotoSansKR-Light.otf │ ├── NotoSansKR-Medium.otf │ ├── NotoSansKR-Regular.otf │ ├── NotoSansKR-Thin.otf │ └── OFL.txt ├── firebase.json ├── ios/ │ ├── .gitignore │ ├── Flutter/ │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.imageset/ │ │ │ ├── Contents.json │ │ │ └── README.md │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── GoogleService-Info.plist │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ ├── Runner.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Runner.xcscheme │ └── Runner.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings ├── lib/ │ ├── core/ │ │ ├── const/ │ │ │ ├── color_constants.dart │ │ │ ├── data_constants.dart │ │ │ ├── path_constants.dart │ │ │ └── text_constants.dart │ │ ├── extensions/ │ │ │ ├── exceptions.dart │ │ │ └── list_extension.dart │ │ └── service/ │ │ ├── auth_service.dart │ │ ├── date_service.dart │ │ ├── firebase_storage_service.dart │ │ ├── notification_service.dart │ │ ├── user_service.dart │ │ └── validation_service.dart │ ├── data/ │ │ ├── exercise_data.dart │ │ └── workout_data.dart │ ├── main.dart │ └── screens/ │ ├── change_password/ │ │ ├── bloc/ │ │ │ ├── change_password_bloc.dart │ │ │ ├── change_password_event.dart │ │ │ └── change_password_state.dart │ │ └── change_password_page.dart │ ├── common_widgets/ │ │ ├── fitness_button.dart │ │ ├── fitness_loading.dart │ │ ├── fitness_text_field.dart │ │ ├── settings_container.dart │ │ └── settings_textfield.dart │ ├── edit_account/ │ │ ├── bloc/ │ │ │ ├── edit_account_bloc.dart │ │ │ ├── edit_account_event.dart │ │ │ └── edit_account_state.dart │ │ └── edit_account_screen.dart │ ├── forgot_password/ │ │ ├── bloc/ │ │ │ ├── forgot_password_bloc.dart │ │ │ ├── forgot_password_event.dart │ │ │ └── forgot_password_state.dart │ │ ├── page/ │ │ │ └── forgot_password_page.dart │ │ └── widget/ │ │ └── forgot_password_content.dart │ ├── home/ │ │ ├── bloc/ │ │ │ ├── home_bloc.dart │ │ │ ├── home_event.dart │ │ │ └── home_state.dart │ │ ├── page/ │ │ │ └── home_page.dart │ │ └── widget/ │ │ ├── home_content.dart │ │ ├── home_exercises_card.dart │ │ └── home_statistics.dart │ ├── onboarding/ │ │ ├── bloc/ │ │ │ ├── onboarding_bloc.dart │ │ │ ├── onboarding_event.dart │ │ │ └── onboarding_state.dart │ │ ├── page/ │ │ │ └── onboarding_page.dart │ │ └── widget/ │ │ ├── onboarding_content.dart │ │ └── onboarding_tile.dart │ ├── reminder/ │ │ ├── bloc/ │ │ │ ├── reminder_bloc.dart │ │ │ ├── reminder_event.dart │ │ │ └── reminder_state.dart │ │ ├── page/ │ │ │ └── reminder_page.dart │ │ └── widget/ │ │ └── reminder_content.dart │ ├── settings/ │ │ ├── bloc/ │ │ │ └── bloc/ │ │ │ ├── settings_bloc.dart │ │ │ ├── settings_event.dart │ │ │ └── settings_state.dart │ │ └── settings_screen.dart │ ├── sign_in/ │ │ ├── bloc/ │ │ │ ├── sign_in_bloc.dart │ │ │ ├── sign_in_event.dart │ │ │ └── sign_in_state.dart │ │ ├── page/ │ │ │ └── sign_in_page.dart │ │ └── widget/ │ │ └── sign_in_content.dart │ ├── sign_up/ │ │ ├── bloc/ │ │ │ ├── signup_bloc.dart │ │ │ ├── signup_event.dart │ │ │ └── signup_state.dart │ │ ├── page/ │ │ │ └── sign_up_page.dart │ │ └── widget/ │ │ └── sign_up_content.dart │ ├── start_workout/ │ │ ├── bloc/ │ │ │ ├── start_workout_bloc.dart │ │ │ ├── start_workout_event.dart │ │ │ └── start_workout_state.dart │ │ ├── page/ │ │ │ └── start_workout_page.dart │ │ └── widget/ │ │ ├── start_workout_content.dart │ │ ├── start_workout_timer.dart │ │ └── start_workout_video.dart │ ├── tab_bar/ │ │ ├── bloc/ │ │ │ ├── tab_bar_bloc.dart │ │ │ ├── tab_bar_event.dart │ │ │ └── tab_bar_state.dart │ │ └── page/ │ │ └── tab_bar_page.dart │ ├── workout_details_screen/ │ │ ├── bloc/ │ │ │ ├── workoutdetails_bloc.dart │ │ │ ├── workoutdetails_event.dart │ │ │ └── workoutdetails_state.dart │ │ ├── page/ │ │ │ └── workout_details_page.dart │ │ └── widget/ │ │ ├── panel/ │ │ │ ├── exercises_list.dart │ │ │ ├── workout_details_panel.dart │ │ │ └── workout_tag.dart │ │ ├── workout_details_body.dart │ │ └── workout_details_content.dart │ └── workouts/ │ ├── bloc/ │ │ ├── workouts_bloc.dart │ │ ├── workouts_event.dart │ │ └── workouts_state.dart │ ├── page/ │ │ └── workouts_page.dart │ └── widget/ │ ├── workout_card.dart │ └── workout_content.dart ├── pubspec.yaml ├── test/ │ └── widget_test.dart └── web/ ├── index.html └── manifest.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .firebase/hosting.YnVpbGQvd2Vi.cache ================================================ favicon.png,1627476717084,fcc7c4545d5b62ad01682589e6fdc7ea03d0a3b42069963c815c344b632eb5cf index.html,1628684383634,1051083f81afd85530dc6f625a66db192802643a052eb50cea7f7d2bd20ebba0 manifest.json,1627476717084,d0d6379e93f986cee36758f174b4efa8979682b207c918dec2bc3740eff1040f version.json,1628684383462,5dcd469bc11f713a36c92003695dd1da4b0006774f1c3b42e7ff831a3a4ceb2d assets/FontManifest.json,1628684383600,aa361fc98a839bc49ddc2604f202ec33abb0f0ab27e2be59b5247a1fcdb2ead0 assets/AssetManifest.json,1628684383599,17fe0fa630c7f4f330c4c6bf8dad31e4157cea4c1dc21a07a98c035a8d2d34b0 flutter_service_worker.js,1628684385015,1d7f5adedddd3647f2c4c901f9a0ff69d748dd8147c8adc4f3a1d7dae63f8129 assets/assets/icons/home/inProgress.png,1627541391853,7bc0474de46c967f873d2bcabbdbdf9adc50a46fe93977e7070568f7e3bee0c2 assets/assets/icons/home/home_icon.png,1627476717071,d5992b9449c135ebca5abbcfcc9aa93c96180c07c239734f7129fd01a08ab16a assets/assets/icons/home/settings_icon.png,1627476717071,2052e06624c6ba4f838811d64b0bca16387162557e669686f1b6d7a5f432061b assets/assets/icons/home/workouts_icon.png,1627476717071,d4e9fc9ff51b525840a61bf11af289b5524c65b348d7a318d606fa112a11ef18 assets/assets/icons/home/2.0x/home_icon.png,1627476717071,22ddd6939a80f0d07e67121a7f71e7b570e9e5016e15e9989f0a3e027236ebcf assets/assets/icons/home/2.0x/inProgress.png,1627541391853,42fe3cc05ccb67299adbfccc94f67c04fd8eec223daf3f01eed673b31bdb6159 assets/assets/icons/home/2.0x/progress.png,1627541391853,11bbe318fd9ac5dcd149b451aee42085c80bbc12ef02636fae609f85e3cbf3b4 assets/assets/icons/home/time.png,1627541391854,05c98dec62b51ad6c4e6295e7d5ec3daa632374625d2c9842ed97c4dac4d6914 assets/assets/icons/home/2.0x/settings_icon.png,1627476717071,405ba6b024bc84ceb1698a221f1184090af38e2452b06bed338c503bfa0f3679 assets/assets/icons/home/3.0x/home_icon.png,1627476717071,ecd934e9678d5b0661069482aa5764c15ab8e668d9c689ed09751633503cbdbd assets/assets/icons/home/progress.png,1627541391853,5eeb25d8e93e1cf6f6548cafeb353835e8688ef8733b9403b7b450827966e340 assets/assets/icons/home/2.0x/workouts_icon.png,1627476717071,94a3b56db05fa2e6b1a0567d93879ec94179cad50b14d3c5080eec87b9354c4a assets/assets/icons/home/3.0x/progress.png,1627541391853,51e983851d9c59a107db8f256b54986b5819ebe840885cd168adfe03fc5b1970 assets/assets/icons/home/3.0x/inProgress.png,1627541391853,0d9bad09c7980d4873cd75ef43f62ec65dd92ba41351131d60904dbebf60d8d1 assets/assets/icons/home/3.0x/settings_icon.png,1627476717071,1e534e7685939ae5ca88b42da8fb27a1a4ef3356d55dda33bb09c5e2798cd072 assets/assets/icons/home/2.0x/time.png,1627541391853,c27b6fbfc2ceeb0007ccf69420f9859b67040733667d4a5cf1fddf28f272d233 assets/assets/icons/home/3.0x/workouts_icon.png,1627476717071,6a466c0256a614273cd71747e6419dc7ff39bc3afaf69e9e6b28279b37f55ef6 assets/assets/icons/home/3.0x/time.png,1627541391853,ca298549f348f66ee596fef39269539276ba3105980fde377d8a1861f51561e4 assets/assets/icons/social_networks/twitter.png,1627974636761,17a2ec2ea903ef50ce037b71945d7b39515cf7c2412e4c17d6210ac841e2b695 assets/assets/icons/social_networks/facebook.png,1627974636761,037453a4f24c6dbeed1ae71a2dd87e9cfe5e75b458a103e8605baa2d1949ed4c assets/assets/icons/social_networks/instagram.png,1627974636761,659f6da2518edbfc82b81d45719b6c783f402184688fd9dd7a69cc00e7b98985 assets/assets/icons/social_networks/2.0x/facebook.png,1628600072787,da27b8dad17c284ccc2e96143cf3beb434fb071b26b6dd7ccc8052d9d59742ed assets/assets/icons/social_networks/2.0x/instagram.png,1628600072787,74298e13e3d208ad8ff4ad2ff170eb2c39f414cb01c4917ddf967ccb9dd254b7 assets/assets/icons/social_networks/2.0x/twitter.png,1628600072788,6c5843fbb7b838b7824ca5e1fb10cba120f052aa335b629ec54ce4f22fc3d553 assets/assets/icons/social_networks/3.0x/instagram.png,1628600072788,2210f89c02c9ef355e01d9998de73d9e3a6f75f7d68a0ebfb426ecd78128b502 assets/assets/icons/social_networks/3.0x/twitter.png,1628600072788,272e5cb4dd2c6562f6d31dc571a23f0d217b2a0a022f49e071c1bedc31dd3861 assets/assets/icons/workouts/exercise.png,1627974636767,bb33a19837c97c646aa357c533553c01fb5cbecc72c4731f0b3c535b86046f18 assets/assets/icons/social_networks/3.0x/facebook.png,1628600072788,9332b336848b4ab7b9a668c5e8cb89dba5ea1a39f2eed7003b3e464a317ee455 assets/assets/icons/workouts/rectangle.png,1627974636769,d553d3f50a5f8b73641a1c117f76e47ac58c3f9d3cf66c81e7f608e9265e92cb assets/assets/icons/workouts/back.png,1627974636767,aabf91ffe8f8776ef8475d6e6eab4c0603c4fa4a804d87a62e4ed6292d535161 assets/assets/icons/workouts/time.png,1628600028291,d16b5cae5487b260e850385e1ee4b7c355734d2c37eba3bd4b46f75dce537c14 assets/assets/icons/workouts/2.0x/exercise.png,1627974636762,620e82b3ccdc8f0d201e832b19258204f93e6b853cab8d7710dffc1988fb54c3 assets/assets/icons/workouts/2.0x/back.png,1627974636762,f033d9517b2d9bde6c64be19df67892c106e612dba94c469c0328f30fb5268ec assets/assets/icons/workouts/pilates.png,1627974636769,23048f33edbdd08f90871a83eba430d2a7db2ccd1073295e7eb1a4406b6900a8 assets/assets/icons/workouts/full_body.png,1627974636768,55795cff8624b69d1827d894f9153bed116865ec730f562dedf661e021866bf4 assets/assets/icons/workouts/yoga.png,1627974636769,0afc17a3ac8a9fd54d534b454edb179611c46e70f6a3cd145f1cf4f03db3932d assets/assets/icons/workouts/stretching.png,1627974636769,f828a67e18874f5b0f91f08bc8db4815dc6367401bd0e404bfcd19d21c93fa1a assets/assets/icons/workouts/2.0x/rectangle.png,1627974636763,b8236891b6108204238c5f8bed1a41b4c4129657f57495d6564a4af11440ba07 assets/assets/icons/workouts/3.0x/back.png,1627974636764,ff31bd2df51646df3f523bc72d80d57aec848bc4955137bab47deeb20e724012 assets/assets/icons/workouts/3.0x/exercise.png,1627974636764,e69bf158bc27e2c7914512b89e9498c7dd1e5a4b1d9699f8ff6c7306f89e8066 assets/assets/icons/workouts/3.0x/rectangle.png,1627974636766,710fb1a6d76696a83ef8904085346f61b2a69e43728a350f00d0e0252bff2939 assets/assets/images/auth/eye_icon.png,1627476717071,50714d4635e94e6b72a8fbe3f818de12f4083e50e1d0dbefe141c889a2f1b0c5 assets/assets/images/auth/2.0x/eye_icon.png,1627476717071,e67eb0e58981ea5449ebb2ecf143837573fba5eb24715c64e67dac7fb556ab53 assets/assets/images/auth/3.0x/eye_icon.png,1627476717071,e0669e0918d978fc87a68931e374d3259752b36d19d2b33c6370dc902f885d3d assets/assets/images/exercises/recicling.png,1627974636770,10dae13fcb21a288a8fda5064377a9f0ba57e0aa57c586ea5f5401d767d8e7ef assets/assets/images/home/finished.png,1627541391857,45bf0f45d0d96704d617fecbd031c6498e299e5103bae29f5c2850b3013192dc assets/assets/images/home/profile.png,1627541391857,6efa0744af74087d0a19e589272f13688293091ce2669358a9c4d54a43671c7d assets/assets/images/home/2.0x/finished.png,1627541391855,45439308580254d9ff8a22d6beb619c558d26329f6afc829e77063e3a03e985e assets/assets/images/home/3.0x/finished.png,1627541391857,9a99e9ccdb8673317afbaccbd9d1dbe69e5b47f2d31d0cb1c9b93ac58f9f5596 assets/assets/images/exercises/2.0x/recicling.png,1627974636770,b03fd6617bb63714ec8f5698a60448de3e0a9b43474706d0dedfd2c98b49ffc9 assets/assets/images/home/cardio.png,1627541391857,83160409601703b1094dc9fa4c0d8bb3e766d747b84f1267f80c882249c90a7d assets/assets/images/home/2.0x/profile.png,1627541391855,9ebb583c083b5ed05a1e576eb0afeb8b1f7a84e06c1d47cbed26712ac68c89e0 assets/assets/images/home/arms.png,1627541391857,fd4b57b3938f547a4a190659dbce510b971d206249c80858a157edc5642decd1 assets/assets/icons/workouts/2.0x/stretching.png,1627974636764,32865e2e2991959be425679d2a9adf95bde704953f5a60ba014019676ed799cc assets/assets/images/exercises/3.0x/recicling.png,1627974636770,c43ec3c35dade35e999540d48efef69dd2d879afadfac7c31856af64d7c5da00 assets/assets/images/home/2.0x/cardio.png,1627541391855,87f389c80fa1cdd7b5806ba2366f1d6d60813c9cf2b70ab9ee03585b7b975718 assets/assets/images/home/3.0x/profile.png,1627541391857,893bd1deae6afd3501b1fb64e29e9a5bb205003370dfdb1d75d67ca3aedc17c3 icons/Icon-192.png,1627476717084,d2e0131bb7851eb9d98f7885edb5ae4b4d6b7a6c7addf8a25b9b712b39274c0f icons/Icon-512.png,1627476717084,7a31ce91e554f1941158ca46f31c7f3f2b7c8c129229ea74a8fae1affe335033 assets/packages/wakelock_web/assets/no_sleep.js,1628124038000,f21ecad86108032c97fe6d07e50f6d35bc4969aa3b2a005efae256ffe62f47e9 assets/assets/icons/workouts/2.0x/full_body.png,1627974636762,2c4f84089eb438faf7454176d1e4a63a4fe347d1dc2653ab6e3c333eda558818 assets/assets/icons/workouts/2.0x/pilates.png,1627974636763,f66772f601df58f2695b7bbb1270bbbc4bfaadcca97ff62ed119da8036556051 assets/assets/icons/workouts/2.0x/yoga.png,1627974636764,bbb0d97a68dd003eef88afd51fabf753d0d58099fa6e9338770241f297e6672d assets/assets/images/onboarding/onboarding.png,1627476717078,f4cfaf552bbf4b0879f756e186a66fa87fb5e17fd536ad101f0e44872d5705a7 assets/assets/images/onboarding/onboarding_2.png,1627476717078,f4cc70e7e552e4da9f4c41fb28b955f4d65bc4c7d330e1ba5d62960196fc08f0 assets/assets/images/onboarding/onboarding_3.png,1627476717078,53343c02627acf4d378e07e471763e72736b3efd447a514e7409d5c0e8939d93 assets/assets/images/home/3.0x/cardio.png,1627541391857,077b9cee95437bce72c44a34c1065c8b5fd99c6b91f0a79edb8a69ec35a6c091 assets/assets/icons/workouts/3.0x/stretching.png,1627974636766,0811239b2403d6e070169d10ca808955d9b5ec2fd93d161ece285d215ae91c2e assets/assets/images/home/2.0x/arms.png,1627541391854,27781c5555ae28644a9e9efe958f742753b1d7cee3209b09c2d68e5b49339d8e assets/assets/icons/workouts/3.0x/full_body.png,1627974636765,2a3d7e98fe2e0f15decda6407b80abcc65d5e7ff095981bc3e02791541e0b71e assets/assets/icons/workouts/3.0x/pilates.png,1627974636766,a780d1e23eb46c7378383d169e97cada1eede86288d180da25f12ce8a1e4863d assets/assets/icons/workouts/3.0x/yoga.png,1627974636767,1c72a662c2b51a77e957ab072cec3498ba153c9c676c6ea539b8ca5b0bde7666 assets/assets/images/home/4.0x/cardio.png,1628683736558,355c988a629774683f1aac71528ce43dfada71f3d6b642e704dd997d8f05ca5d assets/NOTICES,1628684383600,3b48445974753dfd5712fcb092bd7207f584b468408cd3e552faef4de9ae440d assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1620072074000,3064af137aeffc9011ba060601a01177b279963822310a778aeafa74c209732c assets/assets/images/home/3.0x/arms.png,1627541391856,6cc1efdf929f4d2cd59b3f049ad5d33d25026ad22baac7a23853a973ec46c71d assets/assets/images/onboarding/2.0x/onboarding_3.png,1627476717073,86bd038648f8ce048eaf3c48b7b3c2066ec2a43df2a3c0bcedcdf700e3532b1b assets/assets/images/onboarding/2.0x/onboarding.png,1627476717072,26b551a45e87a9cf8babb5addbf9ede5fad3d442b687a7e6243b4d05f8156474 assets/assets/images/onboarding/2.0x/onboarding_2.png,1627476717072,0eda63106507ebf1a0b93a117b9fca53c0644504e843ce41a749bf4b1a5cdf43 assets/assets/images/onboarding/3.0x/onboarding.png,1627476717074,0221495dd68432c49e07e15e205b7599fda50ac16471a7a7e7390bc498c39512 assets/assets/images/onboarding/3.0x/onboarding_3.png,1627476717078,c6c3498775f88fdf25eb4b1812c1cbd3095812cec8a3367a97772b5fe3cc68d0 assets/assets/images/onboarding/3.0x/onboarding_2.png,1627476717076,a3b63e486fefbfad680fc70c34074e3e9484d70f352ea3da6d7d5e306b351b4c assets/fonts/MaterialIcons-Regular.otf,1615596762000,5f71a8843e4edc9656c39061c2232458a6fc77e1603305960e4efa9c77f8b7a2 main.dart.js.map,1628674203768,531ff586ff58cbfe23a63229b5628751af705a96cb075de69bdc8ff5ff160cc2 main.dart.js,1628684383054,4e4b3485395a76f874b5d6f902f15227931035b4b12242f80e1361effb413c60 assets/assets/fonts/NotoSansKR/NotoSansKR-Regular.otf,1627476717049,2c26f3d820132e71301a7ce1d1e1097d86d644452e7c80554572dba87f3a7118 assets/assets/fonts/NotoSansKR/NotoSansKR-Medium.otf,1627476717027,d6ce5d5436052d0d766580a1a4abc0b1337af2a5313e1acf755a582a3a61f632 assets/assets/videos/workouts/cow.mp4,1628079771714,5691a0fce029f9dd827f94234f67f6ee3525b58b9fb5de5bb0547e73ad4a45d1 assets/assets/fonts/NotoSansKR/NotoSansKR-Bold.otf,1627476716983,e28b1a09897c998992d4843d4717b8a783e9da76b4b3e66b605443ea7593fd6a assets/assets/videos/workouts/warriorII.mp4,1628079771842,8a269d6317bf254963acfbc028edb9e5ed8e9973bc715bb09fb00c30bb1cf071 assets/assets/videos/workouts/reclining.mp4,1628079771809,44f49a3ef19265ad6e0e7a7a65681e7a3a56f93dcea535b316f99508fdcaa0ac ================================================ FILE: .firebaserc ================================================ { "projects": { "default": "fitness-1a139" } } ================================================ FILE: .gitignore ================================================ # Miscellaneous *.class *.log *.pyc *.swp .DS_Store .atom/ .buildlog/ .history .svn/ # IntelliJ related *.iml *.ipr *.iws .idea/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. #.vscode/ # Flutter/Dart/Pub related **/doc/api/ **/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies .packages .pub-cache/ .pub/ /build/ # Web related lib/generated_plugin_registrant.dart # Symbolication related app.*.symbols # Obfuscation related app.*.map.json # Android Studio will place build artifacts here /android/app/debug /android/app/profile /android/app/release ================================================ FILE: .metadata ================================================ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # # This file should be version controlled and should not be manually edited. version: revision: 1d9032c7e1d867f071f2277eb1673e8f9b0274e3 channel: stable project_type: app ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "fitness", "request": "launch", "type": "dart", "flutterMode": "debug" } ] } ================================================ FILE: README.md ================================================ # Fitness app Example of Fitness app done using Flutter for iOS and Android platforms. ![Home](https://github.com/perpetio/fitness/blob/master/examples/Home.png?raw=true) ![Workout](https://github.com/perpetio/fitness/blob/master/examples/Workout_1.png?raw=true) # See Behance UI/UX case: https://www.behance.net/gallery/132549709/Selfit-Fitness-app?tracking_source=for_you_feed_user_published ## See our tutorial posts: Part 1 - [How to Make a Fitness App With Flutter: A Tutorial by Perpetio. Part I](https://perpet.io/blog/how-to-build-a-clubhouse-clone-app-with-flutter-a-tutorial-by-perpetio-part-1/) Part 2 - [How to Make a Fitness App With Flutter: A Tutorial by Perpetio. Part II](https://perpet.io/blog/how-to-make-a-fitness-app-with-flutter-a-tutorial-by-perpetio-part-ii/) Part 3 - [How to Make a Fitness App With Flutter: A Tutorial by Perpetio. Part III](https://perpet.io/blog/how-to-make-a-fitness-app-with-flutter-a-tutorial-by-perpetio-part-iii/) ## Getting Started This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project: - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) For help getting started with Flutter, view our [online documentation](https://flutter.dev/docs), which offers tutorials, samples, guidance on mobile development, and a full API reference. ================================================ FILE: android/.gitignore ================================================ gradle-wrapper.jar /.gradle /captures/ /gradlew /gradlew.bat /local.properties GeneratedPluginRegistrant.java # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app key.properties ================================================ FILE: android/app/build.gradle ================================================ def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { flutterVersionName = '1.0' } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply plugin: 'com.google.gms.google-services' android { compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.fitness_flutter" minSdkVersion 16 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName multiDexEnabled true } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } } flutter { source '../..' } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation platform('com.google.firebase:firebase-bom:28.3.0') implementation 'com.android.support:multidex:1.0.3' } ================================================ FILE: android/app/src/debug/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/google-services.json ================================================ { "project_info": { "project_number": "422986340486", "project_id": "fitness-1a139", "storage_bucket": "fitness-1a139.appspot.com" }, "client": [ { "client_info": { "mobilesdk_app_id": "1:422986340486:android:45ba422650bd65bab51907", "android_client_info": { "package_name": "com.example.fitness_flutter" } }, "oauth_client": [ { "client_id": "422986340486-ctl45jld32bdhtdh4uvrid4p76sv8obr.apps.googleusercontent.com", "client_type": 3 } ], "api_key": [ { "current_key": "AIzaSyDzZoKOFTonkIBPz2oexzg9wOu0wITYa2M" } ], "services": { "appinvite_service": { "other_platform_oauth_client": [ { "client_id": "422986340486-ctl45jld32bdhtdh4uvrid4p76sv8obr.apps.googleusercontent.com", "client_type": 3 }, { "client_id": "422986340486-5u73b18na5os4i1mvs3pte0gn6kg6ijh.apps.googleusercontent.com", "client_type": 2, "ios_info": { "bundle_id": "io.flutter.flutter.app" } } ] } } } ], "configuration_version": "1" } ================================================ FILE: android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/kotlin/com/example/fitness_flutter/MainActivity.kt ================================================ package com.example.fitness_flutter import io.flutter.embedding.android.FlutterActivity class MainActivity: FlutterActivity() { } ================================================ FILE: android/app/src/main/res/drawable/launch_background.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable-v21/launch_background.xml ================================================ ================================================ FILE: android/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: android/app/src/main/res/values-night/styles.xml ================================================ ================================================ FILE: android/app/src/profile/AndroidManifest.xml ================================================ ================================================ FILE: android/build.gradle ================================================ buildscript { ext.kotlin_version = '1.3.50' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.8' } } allprojects { repositories { google() jcenter() } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { project.evaluationDependsOn(':app') } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: android/gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip ================================================ FILE: android/gradle.properties ================================================ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true ================================================ FILE: android/settings.gradle ================================================ include ':app' def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def properties = new Properties() assert localPropertiesFile.exists() localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" ================================================ FILE: assets/fonts/NotoSansKR/OFL.txt ================================================ This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: firebase.json ================================================ { "hosting": { "public": "build/web", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ], "rewrites": [ { "source": "**", "destination": "/index.html" } ] } } ================================================ FILE: ios/.gitignore ================================================ *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 **/*sync/ .sconsign.dblite .tags* **/.vagrant/ **/DerivedData/ Icon? **/Pods/ **/.symlinks/ profile xcuserdata **/.generated/ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* # Exceptions to above rules. !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 ================================================ FILE: ios/Flutter/AppFrameworkInfo.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable App CFBundleIdentifier io.flutter.flutter.app CFBundleInfoDictionaryVersion 6.0 CFBundleName App CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 MinimumOSVersion 8.0 ================================================ FILE: ios/Flutter/Debug.xcconfig ================================================ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" ================================================ FILE: ios/Flutter/Release.xcconfig ================================================ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" ================================================ FILE: ios/Podfile ================================================ # Uncomment this line to define a global platform for your project platform :ios, '12.1' # 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__)) end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end end ================================================ FILE: ios/Runner/AppDelegate.swift ================================================ import UIKit import Flutter import Firebase @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate } // FirebaseApp.configure() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } ================================================ FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "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" } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "LaunchImage.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "LaunchImage@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "LaunchImage@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md ================================================ # Launch Screen Assets You can customize the launch screen with your own desired assets by replacing the image files in this directory. You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. ================================================ FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: ios/Runner/Base.lproj/Main.storyboard ================================================ ================================================ FILE: ios/Runner/GoogleService-Info.plist ================================================ CLIENT_ID 422986340486-6ip6fb1b9o5ologha5r3h4i34o9djqtp.apps.googleusercontent.com REVERSED_CLIENT_ID com.googleusercontent.apps.422986340486-6ip6fb1b9o5ologha5r3h4i34o9djqtp API_KEY AIzaSyBmt3kA_NpOr5fmOj0QevpECGXHKSfUZaM GCM_SENDER_ID 422986340486 PLIST_VERSION 1 BUNDLE_ID com.example.fitnessFlutter PROJECT_ID fitness-1a139 STORAGE_BUCKET fitness-1a139.appspot.com IS_ADS_ENABLED IS_ANALYTICS_ENABLED IS_APPINVITE_ENABLED IS_GCM_ENABLED IS_SIGNIN_ENABLED GOOGLE_APP_ID 1:422986340486:ios:86a9572efdb76722b51907 ================================================ FILE: ios/Runner/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName fitness_flutter CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSApplicationQueriesSchemes https http LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads NSCameraUsageDescription App used photo for user data NSMicrophoneUsageDescription App used micro for choosing image NSPhotoLibraryUsageDescription App used photo for user data UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance ================================================ FILE: ios/Runner/Runner-Bridging-Header.h ================================================ #import "GeneratedPluginRegistrant.h" ================================================ FILE: ios/Runner.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1EB624816E59D8EA5A00964D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 569C6E9C77C6674A97AA1A92 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 57C0953A26C3F5A7005A4D27 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 57C0953926C3F5A7005A4D27 /* GoogleService-Info.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 = ""; }; 184EC59B0E1035731D467CDF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 569C6E9C77C6674A97AA1A92 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57C0953926C3F5A7005A4D27 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Downloads/GoogleService-Info.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 = ""; }; A8363722806A4336D53660BD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; ECA2DBB364BC87298B753327 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1EB624816E59D8EA5A00964D /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0C9026A7982AB459FCD34A31 /* Pods */ = { isa = PBXGroup; children = ( A8363722806A4336D53660BD /* Pods-Runner.debug.xcconfig */, ECA2DBB364BC87298B753327 /* Pods-Runner.release.xcconfig */, 184EC59B0E1035731D467CDF /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; 140B08CC25ACDA9584F56CDD /* Frameworks */ = { isa = PBXGroup; children = ( 569C6E9C77C6674A97AA1A92 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; }; 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 */, 0C9026A7982AB459FCD34A31 /* Pods */, 140B08CC25ACDA9584F56CDD /* Frameworks */, ); sourceTree = ""; }; 97C146EF1CF9000F007C117D /* Products */ = { isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, ); name = Products; sourceTree = ""; }; 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( 57C0953926C3F5A7005A4D27 /* GoogleService-Info.plist */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( B59C01CE5681BA981EDB94A6 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 653D0ABDFCB77C6BB1B470B4 /* [CP] Embed Pods Frameworks */, ); 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 = 1250; 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 */, 57C0953A26C3F5A7005A4D27 /* GoogleService-Info.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"; }; 653D0ABDFCB77C6BB1B470B4 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 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\n"; }; B59C01CE5681BA981EDB94A6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 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 = 12.0; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = 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)"; DEVELOPMENT_TEAM = C3S69D62C5; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.fitnessFlutter; 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 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; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; 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 = 12.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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 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 = 12.0; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; 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)"; DEVELOPMENT_TEAM = C3S69D62C5; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.fitnessFlutter; 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)"; DEVELOPMENT_TEAM = C3S69D62C5; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.fitnessFlutter; 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 */; } ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ PreviewsEnabled ================================================ FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme ================================================ ================================================ FILE: ios/Runner.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ PreviewsEnabled ================================================ FILE: lib/core/const/color_constants.dart ================================================ import 'package:flutter/material.dart'; class ColorConstants { static const textColor = Color(0xFF1F2022); static const primaryColor = Color(0xFF6358E1); static const textBlack = Color(0xFF1F2022); static const white = Color(0xFFFFFFFF); static const grey = Color(0xFFB6BDC6); static const loadingBlack = Color(0x80000000); static const textFieldBackground = Color(0xFFFBFCFF); static const textFieldBorder = Color(0xFFB9BBC5); static const disabledColor = Color(0xFFE1E1E5); static const errorColor = Color(0xFFF25252); static const homeBackgroundColor = Color.fromRGBO(252, 252, 252, 1); static const textGrey = Color(0xFF8F98A3); static const cardioColor = Color(0xFFFCB74F); static const armsColor = Color(0xFF5C9BA4); } ================================================ FILE: lib/core/const/data_constants.dart ================================================ import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/onboarding/widget/onboarding_tile.dart'; class DataConstants { // Onboarding static final onboardingTiles = [ OnboardingTile( title: TextConstants.onboarding1Title, mainText: TextConstants.onboarding1Description, imagePath: PathConstants.onboarding1, ), OnboardingTile( title: TextConstants.onboarding2Title, mainText: TextConstants.onboarding2Description, imagePath: PathConstants.onboarding2, ), OnboardingTile( title: TextConstants.onboarding3Title, mainText: TextConstants.onboarding3Description, imagePath: PathConstants.onboarding3, ), ]; // Workouts static final List workouts = [ WorkoutData( title: TextConstants.yogaTitle, exercices: TextConstants.yogaExercises, minutes: TextConstants.yogaMinutes, currentProgress: 10, progress: 16, image: PathConstants.yoga, exerciseDataList: [ ExerciseData( title: TextConstants.reclining, minutes: TextConstants.recliningMinutes, progress: 1, video: PathConstants.recliningVideo, description: TextConstants.warriorDescription, steps: [ TextConstants.warriorStep1, TextConstants.warriorStep2, TextConstants.warriorStep1, TextConstants.warriorStep2, TextConstants.warriorStep1, TextConstants.warriorStep2, ], ), ExerciseData( title: TextConstants.cowPose, minutes: TextConstants.cowPoseMinutes, progress: 0.3, video: PathConstants.cowPoseVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.warriorPose, minutes: TextConstants.warriorPoseMinutes, progress: 0.99, video: PathConstants.warriorIIVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ]), WorkoutData( title: TextConstants.pilatesTitle, exercices: TextConstants.pilatesExercises, minutes: TextConstants.pilatesMinutes, currentProgress: 1, progress: 20, image: PathConstants.pilates, exerciseDataList: [ ExerciseData( title: TextConstants.reclining, minutes: TextConstants.recliningMinutes, progress: 0.1, video: PathConstants.recliningVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.cowPose, minutes: TextConstants.cowPoseMinutes, progress: 0.1, video: PathConstants.cowPoseVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.warriorPose, minutes: TextConstants.warriorPoseMinutes, progress: 0.0, video: PathConstants.warriorIIVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ]), WorkoutData( title: TextConstants.fullBodyTitle, exercices: TextConstants.fullBodyExercises, minutes: TextConstants.fullBodyMinutes, currentProgress: 12, progress: 14, image: PathConstants.fullBody, exerciseDataList: [ ExerciseData( title: TextConstants.reclining, minutes: TextConstants.recliningMinutes, progress: 0.99, video: PathConstants.recliningVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.cowPose, minutes: TextConstants.cowPoseMinutes, progress: 0.6, video: PathConstants.cowPoseVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.warriorPose, minutes: TextConstants.warriorPoseMinutes, progress: 0.8, video: PathConstants.warriorIIVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ]), WorkoutData( title: TextConstants.stretchingTitle, exercices: TextConstants.stretchingExercises, minutes: TextConstants.stretchingMinutes, currentProgress: 0, progress: 8, image: PathConstants.stretching, exerciseDataList: [ ExerciseData( title: TextConstants.reclining, minutes: TextConstants.recliningMinutes, progress: 0.0, video: PathConstants.recliningVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.cowPose, minutes: TextConstants.cowPoseMinutes, progress: 0.0, video: PathConstants.cowPoseVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.warriorPose, minutes: TextConstants.warriorPoseMinutes, progress: 0.0, video: PathConstants.warriorIIVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ], ), ]; static final List homeWorkouts = [ WorkoutData( title: TextConstants.cardioTitle, exercices: TextConstants.cardioExercises, minutes: TextConstants.cardioMinutes, currentProgress: 10, progress: 16, image: PathConstants.cardio, exerciseDataList: [ ExerciseData( title: TextConstants.reclining, minutes: TextConstants.recliningMinutes, progress: 1, video: PathConstants.recliningVideo, description: TextConstants.warriorDescription, steps: [ TextConstants.warriorStep1, TextConstants.warriorStep2, TextConstants.warriorStep1, TextConstants.warriorStep2, TextConstants.warriorStep1, TextConstants.warriorStep2, ], ), ExerciseData( title: TextConstants.cowPose, minutes: TextConstants.cowPoseMinutes, progress: 0.3, video: PathConstants.cowPoseVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.warriorPose, minutes: TextConstants.warriorPoseMinutes, progress: 0.99, video: PathConstants.warriorIIVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ]), WorkoutData( title: TextConstants.armsTitle, exercices: TextConstants.armsExercises, minutes: TextConstants.armsMinutes, currentProgress: 1, progress: 20, image: PathConstants.cardio, exerciseDataList: [ ExerciseData( title: TextConstants.reclining, minutes: TextConstants.recliningMinutes, progress: 0.1, video: PathConstants.recliningVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.cowPose, minutes: TextConstants.cowPoseMinutes, progress: 0.1, video: PathConstants.cowPoseVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ExerciseData( title: TextConstants.warriorPose, minutes: TextConstants.warriorPoseMinutes, progress: 0.0, video: PathConstants.warriorIIVideo, description: TextConstants.warriorDescription, steps: [TextConstants.warriorStep1, TextConstants.warriorStep2], ), ]), ]; // Reminder static List reminderDays = [ TextConstants.everyday, TextConstants.monday_friday, TextConstants.weekends, TextConstants.monday, TextConstants.tuesday, TextConstants.wednesday, TextConstants.thursday, TextConstants.friday, TextConstants.saturday, TextConstants.sunday, ]; } ================================================ FILE: lib/core/const/path_constants.dart ================================================ class PathConstants { // Onboarding static const String onboarding1 = 'assets/images/onboarding/onboarding.png'; static const String onboarding2 = 'assets/images/onboarding/onboarding_2.png'; static const String onboarding3 = 'assets/images/onboarding/onboarding_3.png'; // Auth static const String eye = 'assets/images/auth/eye_icon.png'; // Tabbar static const String home = 'assets/icons/home/home_icon.png'; static const String workouts = 'assets/icons/home/workouts_icon.png'; static const String settings = 'assets/icons/home/settings_icon.png'; // Home static const String profile = 'assets/images/home/profile.png'; static const String finished = 'assets/images/home/finished.png'; static const String inProgress = 'assets/icons/home/inProgress.png'; static const String timeSent = 'assets/icons/home/time.png'; static const String cardio = 'assets/images/home/cardio.png'; static const String arms = 'assets/images/home/arms.png'; static const String progress = 'assets/icons/home/progress.png'; // Workout Details static const String yogaWorkout = 'assets/images/workouts/yoga_workout.png'; static const String back = 'assets/icons/workouts/back.png'; static const String rectangle = 'assets/icons/workouts/rectangle.png'; static const String timeTracker = 'assets/icons/workouts/time.png'; static const String exerciseTracker = 'assets/icons/workouts/exercise.png'; static const String recicling = 'assets/images/exercises/recicling.png'; //Workouts static const String yoga = 'assets/icons/workouts/yoga.png'; static const String pilates = 'assets/icons/workouts/pilates.png'; static const String fullBody = 'assets/icons/workouts/full_body.png'; static const String stretching = 'assets/icons/workouts/stretching.png'; //social networks static const String facebook = 'assets/icons/social_networks/facebook.png'; static const String instagram = 'assets/icons/social_networks/instagram.png'; static const String twitter = 'assets/icons/social_networks/twitter.png'; // Start workout -> Yoga static const String recliningVideo = 'assets/videos/workouts/reclining.mp4'; static const String cowPoseVideo = 'assets/videos/workouts/cow.mp4'; static const String warriorIIVideo = 'assets/videos/workouts/warriorII.mp4'; } ================================================ FILE: lib/core/const/text_constants.dart ================================================ class TextConstants { // Common static const String start = "Start"; // Onboarding static const String onboarding1Title = "Workout anywhere"; static const String onboarding2Title = "Learn techniques"; static const String onboarding3Title = "Stay strong & healthy"; static const String onboarding1Description = "You can do your workout at home without any equipment, outside or at the gym."; static const String onboarding2Description = "Our workout programs are made by professionals."; static const String onboarding3Description = "We want you to fully enjoy the program and stay healthy and positive."; // Sign Up static const String signUp = "Sign up"; static const String signIn = "Sign In"; static const String signOut = "Sign Out"; static const String username = "Username"; static const String userNamePlaceholder = "Your name"; static const String usernameErrorText = 'Text is required'; static const String email = "Email"; static const String emailPlaceholder = "example@mail.com"; static const String emailErrorText = 'Email is unvalid, please enter email properly'; static const String password = "Password"; static const String passwordPlaceholder = "Must be at least 6 symbols"; static const String passwordErrorText = 'Password should contain at least 6 characters'; static const String confirmPassword = "Confirm password"; static const String confirmPasswordPlaceholder = "Re-enter password"; static const String confirmPasswordErrorText = 'Password is not the same'; static const String alreadyHaveAccount = "Already have an account?"; // Sing In static const String passwordPlaceholderSignIn = "Enter your password"; static const String forgotPassword = "Forgot password?"; static const String doNotHaveAnAccount = "Do not have an account?"; // Tab bar static const String homeIcon = "Home"; static const String workoutsIcon = "Workouts"; static const String settingsIcon = "Settings"; // Home screen static const String checkActivity = "Let's check your activity"; static const String finished = "Finished"; static const String completedWorkouts = "Completed workouts"; static const String inProgress = "In progress"; static const String workouts = "Workouts"; static const String timeSent = "Time sent"; static const String minutes = "Minutes"; static const String discoverWorkouts = "Discover new workouts"; static const String keepProgress = "Keep the progress!"; static const String profileSuccessful = "You are more successful than 88% users."; // Home screen -> cardio card static const String cardioTitle = "Cardio"; static const String cardioExercises = "10"; static const String cardioMinutes = "50"; // Home screen -> arms card static const String armsTitle = "Arms"; static const String armsExercises = "6"; static const String armsMinutes = "35"; //Workout screen -> yoga card static const String yogaTitle = "Yoga"; static const String yogaExercises = "16"; static const String yogaMinutes = "52"; //Workout screen -> pilates card static const String pilatesTitle = "Pilates"; static const String pilatesExercises = "20"; static const String pilatesMinutes = "60"; //Workout screen -> fullBody card static const String fullBodyTitle = "Full body"; static const String fullBodyExercises = "14"; static const String fullBodyMinutes = "48"; //Workout screen -> stretching card static const String stretchingTitle = "Stretching"; static const String stretchingExercises = "8"; static const String stretchingMinutes = "35"; //Settings screen static const String joinUs = 'Join us in social media'; static const String calendar = "Calendar"; static const String reminder = "Reminder"; static const String rateUsOn = "Rate us on "; static const String terms = "Terms & Conditions"; // Workout Details screen common static const String workout = "Workout"; static const String exercisesLowercase = "exercises"; static const String exercisesUppercase = "Exercises"; // Workout Details screen -> card1 static const String reclining = "Reclining to big toe"; static const int recliningMinutes = 12; // Workout Details screen -> card2 static const String cowPose = "Cow Pose"; static const int cowPoseMinutes = 8; // Workout Details screen -> card3 static const String warriorPose = "Warrior II Pose"; static const int warriorPoseMinutes = 12; // Start Workout screen static const String back = "Back"; static const String next = "Next"; static const String nextExercise = "Next Exercise:"; // Start Workout screen -> Warrior II Pose static const String warriorDescription = "Named for a fierce warrior, an incarnation of Shiva, this version of Warrior Pose increases stamina."; static const String warriorStep1 = "Stand in Tadasana (Mountain Pose). Raise your arms parallel to the floor and reach them actively out to the sides, shoulder blades wide, palms down."; static const String warriorStep2 = "Turn your right foot slightly to the right and your left foot out to the left 90 degrees. Align the left heel with the right heel. Firm your thighs and turn your left thigh outward so that the center of the left knee cap is in line with the center of the left ankle."; // Reminder screen static const String selectTime = "Please select reminder time"; static const String save = "Save"; static const String repeating = "How often repeat"; // Reminder day of the week static const String everyday = "Everyday"; static const String monday_friday = "Mon - Fri"; static const String weekends = "Weekends"; static const String monday = "Monday"; static const String tuesday = "Tue"; static const String wednesday = "Wed"; static const String thursday = "Thu"; static const String friday = "Fri"; static const String saturday = "Sat"; static const String sunday = "Sun"; // Edit account screen static const String editAccount = 'Edit account'; static const String editPhoto = 'Edit photo'; static const String fullName = 'Full name'; static const String nameShouldContain2Char = 'Name should contain at least 2 characters'; static const String changePassword = 'Change Password'; // Edit account screen -> show settings static const String cameraPermission = 'Camera permisson'; static const String cameAccess = 'This app needs camera access to take pictures for upload user profile photo'; static const String deny = 'Deny'; static const String settings = 'Settings'; static const String fullNamePlaceholder = 'Enter your full name'; //Change password screen static const String newPassword = 'New password'; static const String passwordUpdated = 'Password successfully updated!'; //Forgot password screen static const passwordReset = 'Password Reset'; static const sendActivationBuild = 'Send Activation Link'; static const resetPasswordLinkWasSent = 'Reset password link was sent on your email.'; } ================================================ FILE: lib/core/extensions/exceptions.dart ================================================ class CustomFirebaseException implements Exception { final String message; CustomFirebaseException(this.message); @override String toString() { return message; } } ================================================ FILE: lib/core/extensions/list_extension.dart ================================================ extension FirstWhereOrNullExtension on Iterable { E? firstWhereOrNull(bool Function(E) test) { for (E element in this) { if (test(element)) return element; } return null; } } ================================================ FILE: lib/core/service/auth_service.dart ================================================ import 'package:firebase_auth/firebase_auth.dart'; import 'package:fitness_flutter/core/extensions/exceptions.dart'; import 'package:flutter/services.dart'; class AuthService { static final FirebaseAuth auth = FirebaseAuth.instance; static Future signUp(String email, String password, String name) async { UserCredential result = await auth.createUserWithEmailAndPassword(email: email.trim(), password: password.trim()); final User user = result.user!; await user.updateDisplayName(name); return user; } static Future resetPassword(String email) async { try { await auth.sendPasswordResetEmail(email: email); return true; } on FirebaseAuthException catch (e) { throw CustomFirebaseException(getExceptionMessage(e)); } catch (e) { throw Exception(e); } } static Future signIn(String email, String password) async { try { final UserCredential result = await auth.signInWithEmailAndPassword( email: email.trim(), password: password.trim(), ); final User? user = result.user; if (user == null) { throw Exception("User not found"); } return user; } on FirebaseAuthException catch (e) { throw CustomFirebaseException(getExceptionMessage(e)); } catch (e) { throw Exception(e); } } static Future signOut() async { await auth.signOut(); } } String getExceptionMessage(FirebaseAuthException e) { print(e.code); switch (e.code) { case 'user-not-found': return 'User not found'; case 'wrong-password': return 'Password is incorrect'; case 'requires-recent-login': return 'Log in again before retrying this request'; default: return e.message ?? 'Error'; } } ================================================ FILE: lib/core/service/date_service.dart ================================================ class MinutesSeconds { final int minutes; final int seconds; MinutesSeconds({ required this.minutes, required this.seconds, }); } class DateService { static MinutesSeconds convertIntoSeconds(int second) { final int minutes = second ~/ 60; final int seconds = second % 60; return MinutesSeconds(minutes: minutes, seconds: seconds); } } ================================================ FILE: lib/core/service/firebase_storage_service.dart ================================================ import 'dart:io'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:fitness_flutter/core/service/user_service.dart'; class FirebaseStorageService { FirebaseStorage storage = FirebaseStorage.instance; static Future listExample() async { ListResult result = await FirebaseStorage.instance.ref().listAll(); result.items.forEach((element) { print(element.name); }); } static Future uploadImage({required String filePath}) async { File file = File(filePath); try { final User? user = FirebaseAuth.instance.currentUser; if (user != null) { TaskSnapshot upload = await FirebaseStorage.instance.ref('user_logos/${user.uid}.png').putFile(file); String downloadUrl = await upload.ref.getDownloadURL(); await UserService.editPhoto(downloadUrl); return true; } return false; } catch (e) { print(e); return false; } } } ================================================ FILE: lib/core/service/notification_service.dart ================================================ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class NotificationService { static final NotificationService notificationService = NotificationService._internal(); static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); factory NotificationService() { return notificationService; } NotificationService._internal(); init() {} } ================================================ FILE: lib/core/service/user_service.dart ================================================ import 'package:firebase_auth/firebase_auth.dart'; import 'package:fitness_flutter/core/extensions/exceptions.dart'; import 'package:fitness_flutter/core/service/auth_service.dart'; class UserService { static final FirebaseAuth firebase = FirebaseAuth.instance; static Future editPhoto(String photoUrl) async { try { await firebase.currentUser?.updatePhotoURL(photoUrl); return true; } catch (e) { print(e); return false; } } static Future changeUserData( {required String displayName, required String email}) async { try { await firebase.currentUser?.updateDisplayName(displayName); await firebase.currentUser?.updateEmail(email); return true; } catch (e) { print(e); throw Exception(e); } } static Future changePassword({required String newPass}) async { try { await firebase.currentUser?.updatePassword(newPass); return true; } on FirebaseAuthException catch (e) { throw CustomFirebaseException(getExceptionMessage(e)); } catch (e) { throw Exception(e); } } static Future signOut() async { await firebase.signOut(); } } ================================================ FILE: lib/core/service/validation_service.dart ================================================ class ValidationService { static bool username(String text) { return text.length > 1; } static bool email(String text) { bool emailValid = RegExp( r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+") .hasMatch(text); return emailValid; } static bool password(String text) { return text.length >= 6; } static bool confirmPassword(String password, String confirmPassword) { return password == confirmPassword; } } ================================================ FILE: lib/data/exercise_data.dart ================================================ class ExerciseData { final String title; final int minutes; final double progress; final String video; final String description; final List steps; ExerciseData({ required this.title, required this.minutes, required this.progress, required this.video, required this.description, required this.steps, }); @override String toString() { return 'ExerciseData(title: $title, minutes: $minutes, progress: $progress, video: $video)'; } } ================================================ FILE: lib/data/workout_data.dart ================================================ import 'package:fitness_flutter/data/exercise_data.dart'; class WorkoutData { final String title; final String exercices; final String minutes; final int currentProgress; final int progress; final String image; final List exerciseDataList; WorkoutData({ required this.title, required this.exercices, required this.minutes, required this.currentProgress, required this.progress, required this.image, required this.exerciseDataList, }); @override String toString() { return 'WorkoutData(title: $title, exercices: $exercices, minutes: $minutes, currentProgress: $currentProgress, progress: $progress, image: $image, exerciseDataList: $exerciseDataList)'; } } ================================================ FILE: lib/main.dart ================================================ import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/service/notification_service.dart'; import 'package:fitness_flutter/screens/onboarding/page/onboarding_page.dart'; import 'package:fitness_flutter/screens/tab_bar/page/tab_bar_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/data/latest.dart' as tz; void main() async { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); await Firebase.initializeApp(); runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => new _MyAppState(); } class _MyAppState extends State { static late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = NotificationService.flutterLocalNotificationsPlugin; @override initState() { super.initState(); const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('app_icon'); final IOSInitializationSettings initializationSettingsIOS = IOSInitializationSettings(); final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS); tz.initializeTimeZones(); flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: selectNotification); } @override Widget build(BuildContext context) { final isLoggedIn = FirebaseAuth.instance.currentUser != null; return MaterialApp( debugShowCheckedModeBanner: false, title: 'Fitness', theme: ThemeData( textTheme: TextTheme(bodyText1: TextStyle(color: ColorConstants.textColor)), fontFamily: 'NotoSansKR', scaffoldBackgroundColor: Colors.white, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: isLoggedIn ? TabBarPage() : OnboardingPage(), ); } Future selectNotification(String? payload) async { showDialog( context: context, builder: (_) { return new AlertDialog( title: Text("PayLoad"), content: Text("Payload : $payload"), ); }, ); } } ================================================ FILE: lib/screens/change_password/bloc/change_password_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/user_service.dart'; import 'package:meta/meta.dart'; part 'change_password_event.dart'; part 'change_password_state.dart'; class ChangePasswordBloc extends Bloc { ChangePasswordBloc() : super(ChangePasswordInitial()); @override Stream mapEventToState( ChangePasswordEvent event, ) async* { if (event is ChangePassword) { yield ChangePasswordProgress(); try { await UserService.changePassword(newPass: event.newPass); yield ChangePasswordSuccess(message: TextConstants.passwordUpdated); await Future.delayed(Duration(seconds: 1)); yield ChangePasswordInitial(); } catch (e) { yield ChangePasswordError(e.toString()); await Future.delayed(Duration(seconds: 1)); yield ChangePasswordInitial(); } } } } ================================================ FILE: lib/screens/change_password/bloc/change_password_event.dart ================================================ part of 'change_password_bloc.dart'; @immutable abstract class ChangePasswordEvent {} class ChangePassword extends ChangePasswordEvent { final String newPass; ChangePassword({required this.newPass}); } ================================================ FILE: lib/screens/change_password/bloc/change_password_state.dart ================================================ part of 'change_password_bloc.dart'; @immutable abstract class ChangePasswordState {} class ChangePasswordInitial extends ChangePasswordState {} class ChangePasswordProgress extends ChangePasswordState {} class ChangePasswordError extends ChangePasswordState { final String error; ChangePasswordError(this.error); } class ChangePasswordSuccess extends ChangePasswordState { final String message; ChangePasswordSuccess({required this.message}); } ================================================ FILE: lib/screens/change_password/change_password_page.dart ================================================ import 'package:firebase_auth/firebase_auth.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:fitness_flutter/screens/change_password/bloc/change_password_bloc.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_loading.dart'; import 'package:fitness_flutter/screens/common_widgets/settings_container.dart'; import 'package:fitness_flutter/screens/common_widgets/settings_textfield.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ChangePasswordScreen extends StatefulWidget { ChangePasswordScreen({Key? key}) : super(key: key); @override _ChangePasswordScreenState createState() => _ChangePasswordScreenState(); } class _ChangePasswordScreenState extends State { final TextEditingController _newPassController = TextEditingController(); final TextEditingController _confirmPassController = TextEditingController(); final User? user = FirebaseAuth.instance.currentUser; bool isNewPassInvalid = false; bool isConfirmPassInvalid = false; late String userName; @override void initState() { userName = user?.displayName ?? "No Username"; super.initState(); } @override Widget build(BuildContext context) { return Scaffold( body: _buildContext(context), appBar: AppBar( title: Text(TextConstants.changePassword, style: TextStyle(color: Colors.black, fontSize: 18)), backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.of(context).pop(), ), iconTheme: IconThemeData( color: ColorConstants.primaryColor, ))); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (context) => ChangePasswordBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is ChangePasswordInitial || currState is ChangePasswordError || currState is ChangePasswordProgress || currState is ChangePasswordSuccess, builder: (context, state) { if (state is ChangePasswordProgress) return Stack(children: [_editAccountContent(context), FitnessLoading()]); if (state is ChangePasswordError) { WidgetsBinding.instance!.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(state.error))); }); } if (state is ChangePasswordSuccess) { WidgetsBinding.instance!.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(state.message))); }); } return _editAccountContent(context); }, listenWhen: (_, currState) => true, listener: (context, state) {}, ), ); } Widget _editAccountContent(BuildContext context) { ChangePasswordBloc _bloc = BlocProvider.of(context); double height = MediaQuery.of(context).size.height; return SafeArea( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.only(top: 20.0, left: 20.0, right: 20.0), child: SizedBox( height: height - 140 - MediaQuery.of(context).padding.bottom, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 15), Text(TextConstants.newPassword, style: TextStyle(fontWeight: FontWeight.w600)), SettingsContainer( child: SettingsTextField( controller: _newPassController, obscureText: true, placeHolder: TextConstants.passwordPlaceholder, ), ), if (isNewPassInvalid) Text(TextConstants.passwordErrorText, style: TextStyle(color: ColorConstants.errorColor)), SizedBox(height: 10), Text(TextConstants.confirmPassword, style: TextStyle(fontWeight: FontWeight.w600)), SettingsContainer( child: SettingsTextField( controller: _confirmPassController, obscureText: true, placeHolder: TextConstants.confirmPasswordPlaceholder, ), ), if (isConfirmPassInvalid) Text(TextConstants.confirmPasswordErrorText, style: TextStyle(color: ColorConstants.errorColor)), Spacer(), FitnessButton( title: TextConstants.save, isEnabled: true, onTap: () { FocusScope.of(context).unfocus(); setState(() { isNewPassInvalid = !ValidationService.password(_newPassController.text); isConfirmPassInvalid = _newPassController.text != _confirmPassController.text; }); if (!(isNewPassInvalid || isConfirmPassInvalid)) { _bloc.add(ChangePassword(newPass: _newPassController.text)); } }, ), ]), ), ), ), ); } } ================================================ FILE: lib/screens/common_widgets/fitness_button.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:flutter/material.dart'; class FitnessButton extends StatelessWidget { final String title; final bool isEnabled; final Function() onTap; FitnessButton({required this.title, this.isEnabled = true, required this.onTap}); @override Widget build(BuildContext context) { return Container( width: double.infinity, height: 55, decoration: BoxDecoration( color: isEnabled ? ColorConstants.primaryColor : ColorConstants.disabledColor, borderRadius: BorderRadius.circular(100), ), child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(100), onTap: onTap, child: Center( child: Text( title, style: TextStyle( color: ColorConstants.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ), ), ), ); } } ================================================ FILE: lib/screens/common_widgets/fitness_loading.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class FitnessLoading extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: double.infinity, height: double.infinity, color: ColorConstants.loadingBlack, child: Center( child: Theme( data: ThemeData( cupertinoOverrideTheme: CupertinoThemeData(brightness: Brightness.dark), ), child: CupertinoActivityIndicator( radius: 17, ), ), ), ); } } ================================================ FILE: lib/screens/common_widgets/fitness_text_field.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class FitnessTextField extends StatefulWidget { final String title; final String placeholder; final String errorText; final bool obscureText; final bool isError; final TextEditingController controller; final VoidCallback onTextChanged; final TextInputAction textInputAction; final TextInputType? keyboardType; const FitnessTextField({ required this.title, required this.placeholder, this.obscureText = false, this.isError = false, required this.controller, required this.onTextChanged, required this.errorText, this.textInputAction = TextInputAction.done, this.keyboardType, Key? key, }) : super(key: key); @override _FitnessTextFieldState createState() => _FitnessTextFieldState(); } class _FitnessTextFieldState extends State { final focusNode = FocusNode(); bool stateObscureText = false; bool stateIsError = false; @override void initState() { super.initState(); focusNode.addListener( () { setState(() { if (focusNode.hasFocus) { stateIsError = false; } }); }, ); stateObscureText = widget.obscureText; stateIsError = widget.isError; } @override void didUpdateWidget(covariant FitnessTextField oldWidget) { super.didUpdateWidget(oldWidget); stateObscureText = widget.obscureText; stateIsError = focusNode.hasFocus ? false : widget.isError; } @override Widget build(BuildContext context) { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _createHeader(), const SizedBox(height: 5), _createTextFieldStack(), if (stateIsError) ...[ _createError(), ], ], ), ); } Widget _createHeader() { return Text( widget.title, style: TextStyle( color: _getUserNameColor(), fontSize: 14, fontWeight: FontWeight.w500, ), ); } Color _getUserNameColor() { if (focusNode.hasFocus) { return ColorConstants.primaryColor; } else if (stateIsError) { return ColorConstants.errorColor; } else if (widget.controller.text.isNotEmpty) { return ColorConstants.textBlack; } return ColorConstants.grey; } Widget _createTextFieldStack() { return Stack( children: [ _createTextField(), if (widget.obscureText) ...[ Positioned( right: 0, bottom: 0, top: 0, child: _createShowEye(), ), ], ], ); } Widget _createTextField() { return TextField( focusNode: focusNode, controller: widget.controller, obscureText: stateObscureText, textInputAction: widget.textInputAction, keyboardType: widget.keyboardType, style: TextStyle( color: ColorConstants.textBlack, fontSize: 16, ), decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide( color: stateIsError ? ColorConstants.errorColor : ColorConstants.textFieldBorder.withOpacity(0.4), ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide( color: ColorConstants.primaryColor, ), ), hintText: widget.placeholder, hintStyle: TextStyle( color: ColorConstants.grey, fontSize: 16, ), filled: true, fillColor: ColorConstants.textFieldBackground, ), onChanged: (text) { setState(() {}); widget.onTextChanged(); }, ); } Widget _createShowEye() { return GestureDetector( onTap: () { setState(() { stateObscureText = !stateObscureText; }); }, child: Image( image: AssetImage( PathConstants.eye, ), color: widget.controller.text.isNotEmpty ? ColorConstants.primaryColor : ColorConstants.grey, ), ); } Widget _createError() { return Container( padding: const EdgeInsets.only(top: 2), child: Text( widget.errorText, style: TextStyle( fontSize: 14, color: ColorConstants.errorColor, ), ), ); } } ================================================ FILE: lib/screens/common_widgets/settings_container.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:flutter/material.dart'; class SettingsContainer extends StatelessWidget { final bool withArrow; final Widget child; final Function()? onTap; SettingsContainer({Key? key, this.withArrow = false, required this.child, this.onTap}) : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.symmetric(vertical: 10.0), child: Container( child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(40), onTap: onTap, child: Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: Row( children: [Expanded(child: child), if (withArrow) Icon(Icons.arrow_forward_ios, color: ColorConstants.primaryColor, size: 20)], ), ), ), ), width: double.infinity, height: 50, decoration: BoxDecoration( borderRadius: BorderRadius.circular(40), color: ColorConstants.white, boxShadow: [BoxShadow(color: ColorConstants.textBlack.withOpacity(0.12), blurRadius: 5.0, spreadRadius: 1.1)], ), ), ); } } ================================================ FILE: lib/screens/common_widgets/settings_textfield.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:flutter/material.dart'; class SettingsTextField extends StatefulWidget { final TextEditingController controller; final bool obscureText; final String placeHolder; const SettingsTextField({ Key? key, required this.controller, this.obscureText = false, required this.placeHolder, }) : super(key: key); @override _SettingsTextFieldState createState() => _SettingsTextFieldState(); } class _SettingsTextFieldState extends State { final focusNode = FocusNode(); bool stateObscureText = false; @override void initState() { super.initState(); stateObscureText = widget.obscureText; } @override void didUpdateWidget(covariant SettingsTextField oldWidget) { super.didUpdateWidget(oldWidget); stateObscureText = widget.obscureText; } @override Widget build(BuildContext context) { return Container( width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ _createSettingsTextField(), if (widget.obscureText) ...[ Positioned( right: 0, bottom: 0, top: 0, child: _createShowEye(), ), ], ], ), ], ), ); } Widget _createSettingsTextField() { return TextField( focusNode: focusNode, controller: widget.controller, obscureText: stateObscureText, style: TextStyle(fontWeight: FontWeight.w600), decoration: InputDecoration( hintText: widget.placeHolder, hintStyle: TextStyle(color: ColorConstants.grey, fontSize: 16), border: InputBorder.none, focusedBorder: InputBorder.none, enabledBorder: InputBorder.none, errorBorder: InputBorder.none, disabledBorder: InputBorder.none, ), ); } Widget _createShowEye() { return GestureDetector( onTap: () { setState(() { stateObscureText = !stateObscureText; }); }, child: Image( image: AssetImage( PathConstants.eye, ), color: widget.controller.text.isNotEmpty ? ColorConstants.primaryColor : ColorConstants.grey, ), ); } } ================================================ FILE: lib/screens/edit_account/bloc/edit_account_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/core/service/firebase_storage_service.dart'; import 'package:fitness_flutter/core/service/user_service.dart'; import 'package:image_picker/image_picker.dart'; import 'package:meta/meta.dart'; part 'edit_account_event.dart'; part 'edit_account_state.dart'; class EditAccountBloc extends Bloc { EditAccountBloc() : super(EditAccountInitial()); @override Stream mapEventToState( EditAccountEvent event, ) async* { if (event is UploadImage) { try { final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery); if (image != null) { yield EditAccountProgress(); await FirebaseStorageService.uploadImage(filePath: image.path); yield EditPhotoSuccess(image); } } catch (e) { yield EditAccountError(e.toString()); await Future.delayed(Duration(seconds: 1)); yield EditAccountInitial(); } } if (event is ChangeUserData) { yield EditAccountProgress(); try { await UserService.changeUserData( displayName: event.displayName, email: event.email); yield EditAccountInitial(); } catch (e) { yield EditAccountError(e.toString()); await Future.delayed(Duration(seconds: 1)); yield EditAccountInitial(); } } } } ================================================ FILE: lib/screens/edit_account/bloc/edit_account_event.dart ================================================ part of 'edit_account_bloc.dart'; @immutable abstract class EditAccountEvent {} class UploadImage extends EditAccountEvent {} class ChangeUserData extends EditAccountEvent { final String displayName; final String email; ChangeUserData({required this.displayName, required this.email}); } ================================================ FILE: lib/screens/edit_account/bloc/edit_account_state.dart ================================================ part of 'edit_account_bloc.dart'; @immutable abstract class EditAccountState {} class EditAccountInitial extends EditAccountState {} class EditAccountProgress extends EditAccountState {} class EditAccountError extends EditAccountState { final String error; EditAccountError(this.error); } class EditPhotoSuccess extends EditAccountState { final XFile image; EditPhotoSuccess(this.image); } ================================================ FILE: lib/screens/edit_account/edit_account_screen.dart ================================================ import 'dart:io'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:fitness_flutter/screens/change_password/change_password_page.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_loading.dart'; import 'package:fitness_flutter/screens/common_widgets/settings_container.dart'; import 'package:fitness_flutter/screens/common_widgets/settings_textfield.dart'; import 'package:fitness_flutter/screens/edit_account/bloc/edit_account_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:permission_handler/permission_handler.dart'; class EditAccountScreen extends StatefulWidget { EditAccountScreen({Key? key}) : super(key: key); @override _EditAccountScreenState createState() => _EditAccountScreenState(); } class _EditAccountScreenState extends State { final TextEditingController _nameController = TextEditingController(); final TextEditingController _emailController = TextEditingController(); final User? user = FirebaseAuth.instance.currentUser; String? photoUrl; bool isNameInvalid = false; bool isEmailInvalid = false; late String userName; late String userEmail; @override void initState() { userName = user?.displayName ?? "No Username"; userEmail = user?.email ?? 'No email'; photoUrl = user?.photoURL ?? null; _nameController.text = userName; _emailController.text = userEmail; super.initState(); } @override Widget build(BuildContext context) { return Scaffold( body: _buildContext(context), appBar: AppBar( title: Text(TextConstants.editAccount, style: TextStyle(color: Colors.black, fontSize: 18)), backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.of(context).pop(), ), iconTheme: IconThemeData( color: ColorConstants.primaryColor, ))); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (context) => EditAccountBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is EditAccountInitial || currState is EditAccountProgress || currState is EditAccountError || currState is EditPhotoSuccess, builder: (context, state) { if (state is EditAccountProgress) return Stack( children: [_editAccountContent(context), FitnessLoading()], ); if (state is EditAccountError) { WidgetsBinding.instance!.addPostFrameCallback((_) async { _showOpenSettingsPopUp(); }); } if (state is EditPhotoSuccess) photoUrl = state.image.path; return _editAccountContent(context); }, listenWhen: (_, currState) => true, listener: (context, state) {}, ), ); } Widget _editAccountContent(BuildContext context) { EditAccountBloc _bloc = BlocProvider.of(context); double height = MediaQuery.of(context).size.height; return SafeArea( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.only(top: 20.0, left: 20.0, right: 20.0), child: SizedBox( height: height - 140 - MediaQuery.of(context).padding.bottom, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Center(child: _getImageWidget()), SizedBox(height: 15), Center( child: TextButton( onPressed: () { _bloc.add(UploadImage()); }, child: Text( TextConstants.editPhoto, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: ColorConstants.primaryColor, ), ), ), ), SizedBox(height: 15), Text( TextConstants.fullName, style: TextStyle(fontWeight: FontWeight.w600), ), SettingsContainer( child: SettingsTextField( controller: _nameController, placeHolder: TextConstants.fullNamePlaceholder, )), if (isNameInvalid) Text(TextConstants.nameShouldContain2Char, style: TextStyle(color: ColorConstants.errorColor)), Text(TextConstants.email, style: TextStyle(fontWeight: FontWeight.w600)), SettingsContainer( child: SettingsTextField( controller: _emailController, placeHolder: TextConstants.emailPlaceholder, )), if (isEmailInvalid) Text(TextConstants.emailErrorText, style: TextStyle(color: ColorConstants.errorColor)), SizedBox(height: 15), InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ChangePasswordScreen())); }, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(TextConstants.changePassword, style: TextStyle( fontWeight: FontWeight.w600, color: ColorConstants.primaryColor, fontSize: 18)), SizedBox(width: 10), Icon(Icons.arrow_forward_ios, color: ColorConstants.primaryColor) ], ), ), Spacer(), FitnessButton( title: TextConstants.save, isEnabled: true, onTap: () { FocusScope.of(context).unfocus(); setState(() { isNameInvalid = !(_nameController.text.length > 1); isEmailInvalid = !ValidationService.email(_emailController.text); }); if (!(isNameInvalid || isEmailInvalid)) { if (userName != _nameController.text || userEmail != _emailController.text) { _bloc.add(ChangeUserData( displayName: _nameController.text, email: _emailController.text)); userName = _nameController.text; userEmail = _emailController.text; } } Navigator.pop(context, true); }, ), ]), ), ), ), ); } Widget _getImageWidget() { if (photoUrl != null) { if (photoUrl!.startsWith('https://')) { return CircleAvatar( child: ClipOval( child: FadeInImage.assetNetwork( placeholder: PathConstants.profile, image: photoUrl!, fit: BoxFit.cover, width: 200, height: 120)), radius: 60); } else { return CircleAvatar( backgroundImage: FileImage(File(photoUrl!)), radius: 60); } } else return CircleAvatar( backgroundImage: AssetImage(PathConstants.profile), radius: 60); } void _showOpenSettingsPopUp() { showDialog( context: context, builder: (BuildContext context) => CupertinoAlertDialog( title: Text(TextConstants.cameraPermission), content: Text(TextConstants.cameAccess), actions: [ CupertinoDialogAction( child: Text(TextConstants.deny), onPressed: () => Navigator.of(context).pop(), ), CupertinoDialogAction( child: Text(TextConstants.settings), onPressed: () => openAppSettings(), ), ], ), ); } } ================================================ FILE: lib/screens/forgot_password/bloc/forgot_password_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/core/service/auth_service.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; part 'forgot_password_event.dart'; part 'forgot_password_state.dart'; class ForgotPasswordBloc extends Bloc { ForgotPasswordBloc() : super(ForgotPasswordInitial()); final emailController = TextEditingController(); bool isError = false; @override Stream mapEventToState( ForgotPasswordEvent event, ) async* { if (event is ForgotPasswordTappedEvent) { try { yield ForgotPasswordLoading(); await AuthService.resetPassword(emailController.text); yield ForgotPasswordSuccess(); } catch (e) { print('Error: ' + e.toString()); yield ForgotPasswordError(message: e.toString()); } } } } ================================================ FILE: lib/screens/forgot_password/bloc/forgot_password_event.dart ================================================ part of 'forgot_password_bloc.dart'; @immutable abstract class ForgotPasswordEvent {} class ForgotPasswordTappedEvent extends ForgotPasswordEvent {} ================================================ FILE: lib/screens/forgot_password/bloc/forgot_password_state.dart ================================================ part of 'forgot_password_bloc.dart'; @immutable abstract class ForgotPasswordState {} class ForgotPasswordInitial extends ForgotPasswordState {} class ForgotPasswordError extends ForgotPasswordState { final String message; ForgotPasswordError({required this.message}); } class ForgotPasswordLoading extends ForgotPasswordState {} class ForgotPasswordSuccess extends ForgotPasswordState {} ================================================ FILE: lib/screens/forgot_password/page/forgot_password_page.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/screens/forgot_password/bloc/forgot_password_bloc.dart'; import 'package:fitness_flutter/screens/forgot_password/widget/forgot_password_content.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ForgotPasswordPage extends StatelessWidget { const ForgotPasswordPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(TextConstants.passwordReset, style: TextStyle(color: Colors.black, fontSize: 18)), backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.of(context).pop(), ), iconTheme: IconThemeData( color: ColorConstants.primaryColor, )), body: _buildContext(context), ); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (BuildContext context) => ForgotPasswordBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is ForgotPasswordInitial, builder: (context, state) { return ForgotPasswordContent(); }, listenWhen: (_, currState) => currState is ForgotPasswordError || currState is ForgotPasswordSuccess, listener: (context, state) { if (state is ForgotPasswordSuccess) { forgotPasswordSuccessfullySended(context); } if (state is ForgotPasswordError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.message)), ); } }, ), ); } Future forgotPasswordSuccessfullySended(BuildContext context) async { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(TextConstants.resetPasswordLinkWasSent), duration: Duration(seconds: 2))); await Future.delayed(Duration(seconds: 2)); Navigator.pop(context); } } ================================================ FILE: lib/screens/forgot_password/widget/forgot_password_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_loading.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_text_field.dart'; import 'package:fitness_flutter/screens/forgot_password/bloc/forgot_password_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ForgotPasswordContent extends StatefulWidget { const ForgotPasswordContent({Key? key}) : super(key: key); @override _ForgotPasswordContentState createState() => _ForgotPasswordContentState(); } class _ForgotPasswordContentState extends State { bool _isButtonEnabled = false; bool _isTextFieldError = false; @override Widget build(BuildContext context) { return Container( height: double.infinity, width: double.infinity, color: ColorConstants.white, child: Stack( children: [ _createMainData(context), BlocBuilder( buildWhen: (_, currState) => currState is ForgotPasswordLoading || currState is ForgotPasswordError || currState is ForgotPasswordSuccess, builder: (context, state) { if (state is ForgotPasswordLoading) { return _createLoading(); } else if (state is ForgotPasswordSuccess) { return SizedBox(); } else if (state is ForgotPasswordError) { return SizedBox(); } return SizedBox(); }, ), ], ), ); } Widget _createLoading() { return FitnessLoading(); } Widget _createMainData(BuildContext context) { double height = MediaQuery.of(context).size.height; return SafeArea( child: SingleChildScrollView( child: SizedBox( height: height - 30 - MediaQuery.of(context).padding.bottom - kToolbarHeight, child: Column( children: [ Spacer(flex: 2), _createForm(context), Spacer(flex: 3), _createResetPasswordButton(context), const SizedBox(height: 30), ], ), ), ), ); } Widget _createForm(BuildContext context) { final bloc = BlocProvider.of(context); return BlocBuilder( builder: (context, state) { return FitnessTextField( title: TextConstants.email, keyboardType: TextInputType.emailAddress, placeholder: TextConstants.emailPlaceholder, controller: bloc.emailController, errorText: TextConstants.emailErrorText, isError: _isTextFieldError, onTextChanged: () { setState(() { _isButtonEnabled = bloc.emailController.text.length > 0; }); }, ); }, ); } Widget _createResetPasswordButton(BuildContext context) { final bloc = BlocProvider.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: BlocBuilder( builder: (context, state) { return FitnessButton( title: TextConstants.sendActivationBuild, isEnabled: _isButtonEnabled, onTap: () { FocusScope.of(context).unfocus(); if (_isButtonEnabled) { setState(() { _isTextFieldError = !ValidationService.email(bloc.emailController.text); }); if (!_isTextFieldError) { bloc.add(ForgotPasswordTappedEvent()); } } }, ); }, ), ); } } ================================================ FILE: lib/screens/home/bloc/home_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; part 'home_event.dart'; part 'home_state.dart'; class HomeBloc extends Bloc { HomeBloc() : super(HomeInitial()); @override Stream mapEventToState( HomeEvent event, ) async* { if (event is ReloadImageEvent) { yield ReloadImageState(); } } } ================================================ FILE: lib/screens/home/bloc/home_event.dart ================================================ part of 'home_bloc.dart'; @immutable abstract class HomeEvent {} class ReloadImageEvent extends HomeEvent {} ================================================ FILE: lib/screens/home/bloc/home_state.dart ================================================ part of 'home_bloc.dart'; @immutable abstract class HomeState {} class HomeInitial extends HomeState {} class ReloadImageState extends HomeState {} ================================================ FILE: lib/screens/home/page/home_page.dart ================================================ import 'package:fitness_flutter/screens/home/bloc/home_bloc.dart'; import 'package:fitness_flutter/screens/home/widget/home_content.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: _buildContext(context), ); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (BuildContext context) => HomeBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is HomeInitial, builder: (context, state) { return HomeContent(); }, listenWhen: (_, currState) => true, listener: (context, state) {}, ), ); } } ================================================ FILE: lib/screens/home/widget/home_content.dart ================================================ import 'package:firebase_auth/firebase_auth.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/data_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/screens/edit_account/edit_account_screen.dart'; import 'package:fitness_flutter/screens/home/bloc/home_bloc.dart'; import 'package:fitness_flutter/screens/home/widget/home_statistics.dart'; import 'package:fitness_flutter/screens/workout_details_screen/page/workout_details_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'home_exercises_card.dart'; class HomeContent extends StatelessWidget { const HomeContent({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return Container( color: ColorConstants.homeBackgroundColor, height: double.infinity, width: double.infinity, child: _createHomeBody(context), ); } Widget _createHomeBody(BuildContext context) { return SafeArea( child: ListView( padding: const EdgeInsets.symmetric(vertical: 20), children: [ _createProfileData(context), const SizedBox(height: 35), HomeStatistics(), const SizedBox(height: 30), _createExercisesList(context), const SizedBox(height: 25), _createProgress(), ], ), ); } Widget _createExercisesList(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Text( TextConstants.discoverWorkouts, style: TextStyle( color: ColorConstants.textBlack, fontSize: 18, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 15), Container( height: 160, child: ListView( scrollDirection: Axis.horizontal, children: [ const SizedBox(width: 20), WorkoutCard( color: ColorConstants.cardioColor, workout: DataConstants.homeWorkouts[0], onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (_) => WorkoutDetailsPage( workout: DataConstants.workouts[0], )))), const SizedBox(width: 15), WorkoutCard( color: ColorConstants.armsColor, workout: DataConstants.homeWorkouts[1], onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (_) => WorkoutDetailsPage( workout: DataConstants.workouts[2], )))), const SizedBox(width: 20), ], ), ), ], ); } Widget _createProfileData(BuildContext context) { final User? user = FirebaseAuth.instance.currentUser; final displayName = user?.displayName ?? "No Username"; return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Hi, $displayName', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 2), Text( TextConstants.checkActivity, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, ), ), ], ), BlocBuilder( buildWhen: (_, currState) => currState is ReloadImageState, builder: (context, state) { final photoUrl = FirebaseAuth.instance.currentUser?.photoURL ?? null; return GestureDetector( child: photoUrl == null ? CircleAvatar( backgroundImage: AssetImage(PathConstants.profile), radius: 60) : CircleAvatar( child: ClipOval( child: FadeInImage.assetNetwork( placeholder: PathConstants.profile, image: photoUrl, fit: BoxFit.cover, width: 200, height: 120)), radius: 25), onTap: () async { await Navigator.of(context).push( MaterialPageRoute(builder: (_) => EditAccountScreen())); BlocProvider.of(context).add(ReloadImageEvent()); }, ); }, ), ], ), ); } Widget _createProgress() { return Container( width: double.infinity, margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: ColorConstants.white, boxShadow: [ BoxShadow( color: ColorConstants.textBlack.withOpacity(0.12), blurRadius: 5.0, spreadRadius: 1.1, ), ], ), child: Row( children: [ Image( image: AssetImage( PathConstants.progress, ), ), SizedBox(width: 20), Flexible( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( TextConstants.keepProgress, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 3), Text( TextConstants.profileSuccessful, style: TextStyle( fontSize: 16, ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ], ), ), ], ), ); } } ================================================ FILE: lib/screens/home/widget/home_exercises_card.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:flutter/material.dart'; class WorkoutCard extends StatelessWidget { final Color color; final WorkoutData workout; final Function() onTap; WorkoutCard({ required this.color, required this.workout, required this.onTap, }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.only( left: 20, top: 10, right: 12, ), height: 160, width: screenWidth * 0.6, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: color, ), child: Stack( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 5), Text( workout.title, style: TextStyle( color: ColorConstants.white, fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10), Text( "${workout.exercices} exercises", style: TextStyle( color: ColorConstants.white, fontSize: 16, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 5), Text( "${workout.minutes} minutes", style: TextStyle( color: ColorConstants.white, fontSize: 16, fontWeight: FontWeight.w500, ), ), ], ), ], ), Positioned( right: 0, bottom: 0, child: Image( image: AssetImage(workout.image), ), ), ], ), ), ); } } ================================================ FILE: lib/screens/home/widget/home_statistics.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:flutter/material.dart'; class HomeStatistics extends StatelessWidget { const HomeStatistics({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(horizontal: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _createComletedWorkouts(context), _createColumnStatistics(), ], ), ); } Widget _createComletedWorkouts(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; return Container( padding: const EdgeInsets.all(15), height: 200, width: screenWidth * 0.35, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: ColorConstants.white, boxShadow: [ BoxShadow( color: ColorConstants.textBlack.withOpacity(0.12), blurRadius: 5.0, spreadRadius: 1.1, ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ Image( image: AssetImage( PathConstants.finished, ), ), const SizedBox(width: 10), Expanded( child: Text( TextConstants.finished, style: TextStyle( color: ColorConstants.textBlack, fontSize: 18, fontWeight: FontWeight.w500, ), overflow: TextOverflow.fade, softWrap: false, ), ), ], ), Text( '12', style: TextStyle( fontSize: 48, fontWeight: FontWeight.w700, color: ColorConstants.textBlack, ), ), Text( TextConstants.completedWorkouts, textAlign: TextAlign.center, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: ColorConstants.textGrey, ), ), ], ), ); } Widget _createColumnStatistics() { return Column( children: [ DataWorkouts( icon: PathConstants.inProgress, title: TextConstants.inProgress, count: 2, text: TextConstants.workouts, ), const SizedBox(height: 20), DataWorkouts( icon: PathConstants.timeSent, title: TextConstants.timeSent, count: 62, text: TextConstants.minutes, ), ], ); } } class DataWorkouts extends StatelessWidget { final String icon; final String title; final int count; final String text; DataWorkouts({ required this.icon, required this.title, required this.count, required this.text, }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; return Container( padding: const EdgeInsets.symmetric(horizontal: 15), height: 90, width: screenWidth * 0.5, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: ColorConstants.white, boxShadow: [ BoxShadow( color: ColorConstants.textBlack.withOpacity(0.12), blurRadius: 5.0, spreadRadius: 1.1, ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ Image(image: AssetImage(icon)), const SizedBox(width: 10), Text( title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, color: ColorConstants.textBlack, ), ), ], ), Row( children: [ Text( count.toString(), style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, color: ColorConstants.textBlack, ), ), const SizedBox(width: 10), Text( text, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: ColorConstants.grey, ), ), ], ), ], ), ); } } ================================================ FILE: lib/screens/onboarding/bloc/onboarding_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; part 'onboarding_event.dart'; part 'onboarding_state.dart'; class OnboardingBloc extends Bloc { OnboardingBloc() : super(OnboardingInitial()); int pageIndex = 0; final pageController = PageController(initialPage: 0); @override Stream mapEventToState( OnboardingEvent event, ) async* { if (event is PageChangedEvent) { if (pageIndex == 2) { yield NextScreenState(); return; } pageIndex += 1; pageController.animateToPage( pageIndex, duration: Duration(milliseconds: 500), curve: Curves.ease, ); yield PageChangedState(counter: pageIndex); } else if (event is PageSwipedEvent) { pageIndex = event.index; yield PageChangedState(counter: pageIndex); } } } ================================================ FILE: lib/screens/onboarding/bloc/onboarding_event.dart ================================================ part of 'onboarding_bloc.dart'; @immutable abstract class OnboardingEvent {} class PageChangedEvent extends OnboardingEvent {} class PageSwipedEvent extends OnboardingEvent { final int index; PageSwipedEvent({required this.index}); } ================================================ FILE: lib/screens/onboarding/bloc/onboarding_state.dart ================================================ part of 'onboarding_bloc.dart'; @immutable abstract class OnboardingState {} class OnboardingInitial extends OnboardingState {} class PageChangedState extends OnboardingState { final int counter; PageChangedState({ required this.counter, }); } class NextScreenState extends OnboardingState {} ================================================ FILE: lib/screens/onboarding/page/onboarding_page.dart ================================================ import 'package:fitness_flutter/screens/onboarding/bloc/onboarding_bloc.dart'; import 'package:fitness_flutter/screens/onboarding/widget/onboarding_content.dart'; import 'package:fitness_flutter/screens/sign_up/page/sign_up_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class OnboardingPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: _buildBody(context), ); } BlocProvider _buildBody(BuildContext context) { return BlocProvider( create: (BuildContext context) => OnboardingBloc(), child: BlocConsumer( listenWhen: (_, currState) => currState is NextScreenState, listener: (context, state) { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) { return SignUpPage(); }, ), ); }, buildWhen: (_, currState) => currState is OnboardingInitial, builder: (context, state) { return OnboardingContent(); }, ), ); } } ================================================ FILE: lib/screens/onboarding/widget/onboarding_content.dart ================================================ import 'package:dots_indicator/dots_indicator.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/data_constants.dart'; import 'package:fitness_flutter/screens/onboarding/bloc/onboarding_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:percent_indicator/circular_percent_indicator.dart'; class OnboardingContent extends StatelessWidget { @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 4, child: _createPageView(bloc.pageController, bloc), ), Expanded( flex: 2, child: _createStatic(bloc), ), ], ), ); } Widget _createPageView(PageController controller, OnboardingBloc bloc) { return PageView( scrollDirection: Axis.horizontal, controller: controller, children: DataConstants.onboardingTiles, onPageChanged: (index) { bloc.add(PageSwipedEvent(index: index)); }, ); } Widget _createStatic(OnboardingBloc bloc) { return Column( children: [ SizedBox( height: 30, ), BlocBuilder( buildWhen: (_, currState) => currState is PageChangedState, builder: (context, state) { return DotsIndicator( dotsCount: 3, position: bloc.pageIndex.toDouble(), decorator: DotsDecorator( color: Colors.grey, activeColor: ColorConstants.primaryColor, ), ); }, ), Spacer(), BlocBuilder( buildWhen: (_, currState) => currState is PageChangedState, builder: (context, state) { final percent = _getPercent(bloc.pageIndex); return TweenAnimationBuilder( tween: Tween(begin: 0, end: percent), duration: Duration(seconds: 1), builder: (context, value, _) => CircularPercentIndicator( radius: 110, backgroundColor: ColorConstants.primaryColor, progressColor: Colors.white, percent: 1 - value, center: Material( shape: CircleBorder(), color: ColorConstants.primaryColor, child: RawMaterialButton( shape: CircleBorder(), onPressed: () { bloc.add(PageChangedEvent()); }, child: Padding( padding: const EdgeInsets.all(24.0), child: Icon( Icons.east_rounded, size: 38.0, color: Colors.white, ), ), ), ), )); }, ), SizedBox(height: 30), ], ); } double _getPercent(int pageIndex) { switch (pageIndex) { case 0: return 0.25; case 1: return 0.65; case 2: return 1; default: return 0; } } } ================================================ FILE: lib/screens/onboarding/widget/onboarding_tile.dart ================================================ import 'package:flutter/material.dart'; class OnboardingTile extends StatelessWidget { final title, imagePath, mainText; OnboardingTile({this.imagePath, this.mainText, this.title}); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; return Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( children: [ const SizedBox(height: 34), Expanded( child: Image.asset( imagePath, ), ), const SizedBox(height: 65), Text( title, style: TextStyle( fontWeight: FontWeight.w700, fontSize: 24.0, ), ), const SizedBox(height: 15), Padding( padding: EdgeInsets.symmetric( horizontal: screenWidth / 100, ), child: Text( mainText, style: TextStyle( fontSize: 16.0, ), textAlign: TextAlign.center, ), ), ], ), ); } } ================================================ FILE: lib/screens/reminder/bloc/reminder_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:meta/meta.dart'; import 'package:timezone/timezone.dart' as tz; part 'reminder_event.dart'; part 'reminder_state.dart'; class ReminderBloc extends Bloc { ReminderBloc() : super(ReminderInitial()); int? selectedRepeatDayIndex; late DateTime reminderTime; int? dayTime; @override Stream mapEventToState( ReminderEvent event, ) async* { if (event is RepeatDaySelectedEvent) { selectedRepeatDayIndex = event.index; dayTime = event.dayTime; yield RepeatDaySelectedState(index: selectedRepeatDayIndex); } else if (event is ReminderNotificationTimeEvent) { reminderTime = event.dateTime; yield ReminderNotificationState(); } else if (event is OnSaveTappedEvent) { _scheuleAtParticularTimeAndDate(reminderTime, dayTime); yield OnSaveTappedState(); } } Future _scheuleAtParticularTimeAndDate( DateTime dateTime, int? dayTime) async { final flutterNotificationsPlugin = FlutterLocalNotificationsPlugin(); final androidPlatformChannelSpecifics = AndroidNotificationDetails( 'your other channel id', 'your other channel name', 'your other channel description'); final iOSPlatformChannelSpecifics = IOSNotificationDetails(); NotificationDetails platformChannelSpecifics = NotificationDetails( android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics); await flutterNotificationsPlugin.zonedSchedule( 1, "Fitness", "Hey, it's time to start your exercises!", _scheduleWeekly(dateTime, days: _createNotificationDayOfTheWeek(dayTime)), platformChannelSpecifics, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, androidAllowWhileIdle: true, matchDateTimeComponents: DateTimeComponents.dayOfWeekAndTime, ); } tz.TZDateTime _scheduleDaily(DateTime dateTime) { final now = tz.TZDateTime.now(tz.local); var timezoneOffset = DateTime.now().timeZoneOffset; final scheduleDate = tz.TZDateTime.utc(now.year, now.month, now.day) .add(Duration(hours: dateTime.hour, minutes: dateTime.minute)) .subtract(Duration(hours: timezoneOffset.inHours)); return scheduleDate.isBefore(now) ? scheduleDate.add(Duration(days: 1)) : scheduleDate; } tz.TZDateTime _scheduleWeekly(DateTime dateTime, {required List? days}) { tz.TZDateTime scheduleDate = _scheduleDaily(dateTime); for (final int day in days ?? []) { scheduleDate = scheduleDate.add(Duration(days: day)); } return scheduleDate; } List _createNotificationDayOfTheWeek(int? dayTime) { switch (dayTime) { case 0: return [ DateTime.monday, DateTime.tuesday, DateTime.wednesday, DateTime.thursday, DateTime.friday, DateTime.saturday, DateTime.sunday ]; case 1: return [ DateTime.monday, DateTime.tuesday, DateTime.wednesday, DateTime.thursday, DateTime.friday ]; case 2: return [DateTime.saturday, DateTime.sunday]; case 3: return [DateTime.monday]; case 4: return [DateTime.tuesday]; case 5: return [DateTime.wednesday]; case 6: return [DateTime.thursday]; case 7: return [DateTime.friday]; case 8: return [DateTime.saturday]; case 9: return [DateTime.sunday]; default: return []; } } } ================================================ FILE: lib/screens/reminder/bloc/reminder_event.dart ================================================ part of 'reminder_bloc.dart'; @immutable abstract class ReminderEvent {} class RepeatDaySelectedEvent extends ReminderEvent { final int index; final int dayTime; RepeatDaySelectedEvent({required this.index, required this.dayTime}); } class ReminderNotificationTimeEvent extends ReminderEvent { final DateTime dateTime; ReminderNotificationTimeEvent({required this.dateTime}); } class OnSaveTappedEvent extends ReminderEvent {} ================================================ FILE: lib/screens/reminder/bloc/reminder_state.dart ================================================ part of 'reminder_bloc.dart'; @immutable abstract class ReminderState {} class ReminderInitial extends ReminderState {} class RepeatDaySelectedState extends ReminderState { final int? index; RepeatDaySelectedState({ required this.index, }); } class ReminderNotificationState extends ReminderState {} class OnSaveTappedState extends ReminderState {} ================================================ FILE: lib/screens/reminder/page/reminder_page.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/reminder/bloc/reminder_bloc.dart'; import 'package:fitness_flutter/screens/reminder/widget/reminder_content.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ReminderPage extends StatelessWidget { const ReminderPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => ReminderBloc(), child: BlocBuilder( buildWhen: (_, currState) => currState is ReminderInitial, builder: (context, state) { return Scaffold( body: _buildContext(context), appBar: AppBar( centerTitle: false, titleSpacing: 0, title: Text( TextConstants.reminder, style: TextStyle(color: Colors.black, fontSize: 18), ), backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon( Icons.arrow_back_ios_new, color: ColorConstants.primaryColor, ), onPressed: () => Navigator.of(context).pop(), ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: FitnessButton( title: TextConstants.save, onTap: () { final bloc = BlocProvider.of(context); bloc.add(OnSaveTappedEvent()); }, ), ), ); }, ), ); } BlocConsumer _buildContext( BuildContext context) { return BlocConsumer( buildWhen: (_, currState) => currState is ReminderInitial, builder: (context, state) { if (state is ReminderInitial) { BlocProvider.of(context).add( ReminderNotificationTimeEvent(dateTime: DateTime.now()), ); } return ReminderContent(); }, listenWhen: (_, currState) => currState is OnSaveTappedState, listener: (context, state) { Navigator.of(context).pop(); }, ); } } ================================================ FILE: lib/screens/reminder/widget/reminder_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/data_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/screens/reminder/bloc/reminder_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ReminderContent extends StatelessWidget { const ReminderContent({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( width: double.infinity, height: double.infinity, color: ColorConstants.white, child: _createDetailedReminder(context), ); } Widget _createDetailedReminder(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: ListView( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _createSelectTime(), const SizedBox(height: 20), _createTimePicker(context), const SizedBox(height: 20), _createRepeating(), const SizedBox(height: 15), _createDayRepeating(context), ], ), ], ), ); } Widget _createSelectTime() { return Text( TextConstants.selectTime, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, ), ); } Widget _createTimePicker(BuildContext context) { final bloc = BlocProvider.of(context); return Container( height: 250, child: CupertinoDatePicker( mode: CupertinoDatePickerMode.time, onDateTimeChanged: (DateTime value) { bloc.add(ReminderNotificationTimeEvent(dateTime: value)); }, ), ); } Widget _createRepeating() { return Text(TextConstants.repeating, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)); } Widget _createDayRepeating(BuildContext context) { final bloc = BlocProvider.of(context); return BlocBuilder( buildWhen: (_, currState) => currState is RepeatDaySelectedState, builder: (context, state) { return Wrap( spacing: 10, runSpacing: 15, children: [ for (int i = 0; i < DataConstants.reminderDays.length; i++) ...[ RepeatingDay( title: DataConstants.reminderDays[i], isSelected: bloc.selectedRepeatDayIndex == i, onTap: () { bloc.add(RepeatDaySelectedEvent( index: i, dayTime: bloc.dayTime = i)); }, ), ], ], ); }, ); } } class RepeatingDay extends StatelessWidget { final String title; final bool isSelected; final VoidCallback onTap; RepeatingDay({ required this.title, required this.isSelected, required this.onTap, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { onTap(); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 7), decoration: BoxDecoration( color: isSelected ? ColorConstants.primaryColor : ColorConstants.grey.withOpacity(0.18), borderRadius: BorderRadius.circular(20), ), child: Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: isSelected ? ColorConstants.white : ColorConstants.grey, ), ), ), ); } } ================================================ FILE: lib/screens/settings/bloc/bloc/settings_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; part 'settings_event.dart'; part 'settings_state.dart'; class SettingsBloc extends Bloc { SettingsBloc() : super(SettingsInitial()); @override Stream mapEventToState( SettingsEvent event, ) async* {} } ================================================ FILE: lib/screens/settings/bloc/bloc/settings_event.dart ================================================ part of 'settings_bloc.dart'; @immutable abstract class SettingsEvent {} ================================================ FILE: lib/screens/settings/bloc/bloc/settings_state.dart ================================================ part of 'settings_bloc.dart'; @immutable abstract class SettingsState {} class SettingsInitial extends SettingsState {} ================================================ FILE: lib/screens/settings/settings_screen.dart ================================================ import 'dart:io'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/auth_service.dart'; import 'package:fitness_flutter/screens/common_widgets/settings_container.dart'; import 'package:fitness_flutter/screens/edit_account/edit_account_screen.dart'; import 'package:fitness_flutter/screens/reminder/page/reminder_page.dart'; import 'package:fitness_flutter/screens/settings/bloc/bloc/settings_bloc.dart'; import 'package:fitness_flutter/screens/sign_in/page/sign_in_page.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:url_launcher/url_launcher.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({Key? key}) : super(key: key); @override _SettingsScreenState createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { String? photoUrl; @override Widget build(BuildContext context) { return Scaffold(body: _buildContext(context)); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (context) => SettingsBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is SettingsInitial, builder: (context, state) { return _settingsContent(context); }, listenWhen: (_, currState) => true, listener: (context, state) {}, ), ); } Widget _settingsContent(BuildContext context) { final User? user = FirebaseAuth.instance.currentUser; final displayName = user?.displayName ?? "No Username"; photoUrl = user?.photoURL ?? null; return SafeArea( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.only(top: 20.0, left: 20.0, right: 20.0), child: Column(children: [ Stack(alignment: Alignment.topRight, children: [ Center( child: photoUrl == null ? CircleAvatar(backgroundImage: AssetImage(PathConstants.profile), radius: 60) : CircleAvatar( child: ClipOval( child: FadeInImage.assetNetwork( placeholder: PathConstants.profile, image: photoUrl!, fit: BoxFit.cover, width: 200, height: 120, )), radius: 60, ), ), TextButton( onPressed: () async { await Navigator.push(context, MaterialPageRoute(builder: (context) => EditAccountScreen())); setState(() { photoUrl = user?.photoURL ?? null; }); }, style: TextButton.styleFrom(shape: CircleBorder(), backgroundColor: ColorConstants.primaryColor.withOpacity(0.16)), child: Icon(Icons.edit, color: ColorConstants.primaryColor)), ]), SizedBox(height: 15), Text(displayName, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), SizedBox(height: 15), SettingsContainer( child: Text(TextConstants.reminder, style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500)), withArrow: true, onTap: () { Navigator.push(context, MaterialPageRoute(builder: (_) => ReminderPage())); }, ), if (!kIsWeb) SettingsContainer( child: Text(TextConstants.rateUsOn + '${Platform.isIOS ? 'App store' : 'Play market'}', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500)), onTap: () { return launch(Platform.isIOS ? 'https://www.apple.com/app-store/' : 'https://play.google.com/store'); }, ), SettingsContainer( onTap: () => launch('https://perpet.io/'), child: Text(TextConstants.terms, style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500))), SettingsContainer( child: Text(TextConstants.signOut, style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500)), onTap: () { AuthService.signOut(); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => SignInPage()), ); }), SizedBox(height: 15), Text(TextConstants.joinUs, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)), SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () => launch('https://www.facebook.com/perpetio/'), style: TextButton.styleFrom(shape: CircleBorder(), backgroundColor: Colors.white, elevation: 1), child: Image.asset(PathConstants.facebook)), TextButton( onPressed: () => launch('https://www.instagram.com/perpetio/'), style: TextButton.styleFrom(shape: CircleBorder(), backgroundColor: Colors.white, elevation: 1), child: Image.asset(PathConstants.instagram)), TextButton( onPressed: () => launch('https://twitter.com/perpetio'), style: TextButton.styleFrom(shape: CircleBorder(), backgroundColor: Colors.white, elevation: 1), child: Image.asset(PathConstants.twitter)), ], ) ]), ), ), ); } } ================================================ FILE: lib/screens/sign_in/bloc/sign_in_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/core/service/auth_service.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:flutter/material.dart'; part 'sign_in_event.dart'; part 'sign_in_state.dart'; class SignInBloc extends Bloc { SignInBloc() : super(SignInInitial()); final emailController = TextEditingController(); final passwordController = TextEditingController(); bool isButtonEnabled = false; @override Stream mapEventToState( SignInEvent event, ) async* { if (event is OnTextChangeEvent) { if (isButtonEnabled != _checkIfSignInButtonEnabled()) { isButtonEnabled = _checkIfSignInButtonEnabled(); yield SignInButtonEnableChangedState(isEnabled: isButtonEnabled); } } else if (event is SignInTappedEvent) { if (_checkValidatorsOfTextField()) { try { yield LoadingState(); await AuthService.signIn(emailController.text, passwordController.text); yield NextTabBarPageState(); print("Go to the next page"); } catch (e) { print('E to tstrng: ' + e.toString()); yield ErrorState(message: e.toString()); } } else { yield ShowErrorState(); } } else if (event is ForgotPasswordTappedEvent) { yield NextForgotPasswordPageState(); } else if (event is SignUpTappedEvent) { yield NextSignUpPageState(); } } bool _checkIfSignInButtonEnabled() { return emailController.text.isNotEmpty && passwordController.text.isNotEmpty; } bool _checkValidatorsOfTextField() { return ValidationService.email(emailController.text) && ValidationService.password(passwordController.text); } } ================================================ FILE: lib/screens/sign_in/bloc/sign_in_event.dart ================================================ part of 'sign_in_bloc.dart'; abstract class SignInEvent {} class OnTextChangeEvent extends SignInEvent {} class SignInTappedEvent extends SignInEvent {} class SignUpTappedEvent extends SignInEvent {} class ForgotPasswordTappedEvent extends SignInEvent {} ================================================ FILE: lib/screens/sign_in/bloc/sign_in_state.dart ================================================ part of 'sign_in_bloc.dart'; abstract class SignInState { const SignInState(); } class SignInInitial extends SignInState {} class SignInButtonEnableChangedState extends SignInState { final bool isEnabled; SignInButtonEnableChangedState({ required this.isEnabled, }); } class ShowErrorState extends SignInState {} class NextForgotPasswordPageState extends SignInState {} class NextSignUpPageState extends SignInState {} class NextTabBarPageState extends SignInState {} class ErrorState extends SignInState { final String message; ErrorState({ required this.message, }); } class LoadingState extends SignInState {} ================================================ FILE: lib/screens/sign_in/page/sign_in_page.dart ================================================ import 'package:fitness_flutter/screens/forgot_password/page/forgot_password_page.dart'; import 'package:fitness_flutter/screens/sign_in/bloc/sign_in_bloc.dart'; import 'package:fitness_flutter/screens/sign_in/widget/sign_in_content.dart'; import 'package:fitness_flutter/screens/sign_up/page/sign_up_page.dart'; import 'package:fitness_flutter/screens/tab_bar/page/tab_bar_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class SignInPage extends StatelessWidget { const SignInPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: _buildContext(context), ); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (BuildContext context) => SignInBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is SignInInitial, builder: (context, state) { return SignInContent(); }, listenWhen: (_, currState) => currState is NextForgotPasswordPageState || currState is NextSignUpPageState || currState is NextTabBarPageState || currState is ErrorState, listener: (context, state) { if (state is NextForgotPasswordPageState) { Navigator.of(context).push(MaterialPageRoute(builder: (_) => ForgotPasswordPage())); } else if (state is NextSignUpPageState) { Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => SignUpPage())); } else if (state is NextTabBarPageState) { Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => TabBarPage())); } else if (state is ErrorState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.message)), ); } }, ), ); } } ================================================ FILE: lib/screens/sign_in/widget/sign_in_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_loading.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_text_field.dart'; import 'package:fitness_flutter/screens/sign_in/bloc/sign_in_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class SignInContent extends StatelessWidget { const SignInContent({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( height: double.infinity, width: double.infinity, color: ColorConstants.white, child: Stack( children: [ _createMainData(context), BlocBuilder( buildWhen: (_, currState) => currState is LoadingState || currState is ErrorState || currState is NextTabBarPageState, builder: (context, state) { if (state is LoadingState) { return _createLoading(); } else if (state is ErrorState || state is NextTabBarPageState) { return SizedBox(); } return SizedBox(); }, ), ], ), ); } Widget _createMainData(BuildContext context) { double height = MediaQuery.of(context).size.height; return SafeArea( child: SingleChildScrollView( child: SizedBox( height: height - 30 - MediaQuery.of(context).padding.bottom, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), _createHeader(), const SizedBox(height: 50), _createForm(context), const SizedBox(height: 20), _createForgotPasswordButton(context), const SizedBox(height: 40), _createSignInButton(context), Spacer(), _createDoNotHaveAccountText(context), const SizedBox(height: 30), ], ), ), ), ); } Widget _createLoading() { return FitnessLoading(); } Widget _createHeader() { return Center( child: Text( TextConstants.signIn, style: TextStyle( color: ColorConstants.textBlack, fontSize: 24, fontWeight: FontWeight.bold, ), ), ); } Widget _createForm(BuildContext context) { final bloc = BlocProvider.of(context); return BlocBuilder( buildWhen: (_, currState) => currState is ShowErrorState, builder: (context, state) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ FitnessTextField( title: TextConstants.email, textInputAction: TextInputAction.next, keyboardType: TextInputType.emailAddress, placeholder: TextConstants.emailPlaceholder, controller: bloc.emailController, errorText: TextConstants.emailErrorText, isError: state is ShowErrorState ? !ValidationService.email(bloc.emailController.text) : false, onTextChanged: () { bloc.add(OnTextChangeEvent()); }, ), const SizedBox(height: 20), FitnessTextField( title: TextConstants.password, placeholder: TextConstants.passwordPlaceholderSignIn, controller: bloc.passwordController, errorText: TextConstants.passwordErrorText, isError: state is ShowErrorState ? !ValidationService.password(bloc.passwordController.text) : false, obscureText: true, onTextChanged: () { bloc.add(OnTextChangeEvent()); }, ), ], ); }, ); } Widget _createForgotPasswordButton(BuildContext context) { final bloc = BlocProvider.of(context); return GestureDetector( child: Padding( padding: const EdgeInsets.only(left: 21), child: Text( TextConstants.forgotPassword, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: ColorConstants.primaryColor, ), ), ), onTap: () { FocusScope.of(context).unfocus(); bloc.add(ForgotPasswordTappedEvent()); }, ); } Widget _createSignInButton(BuildContext context) { final bloc = BlocProvider.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: BlocBuilder( buildWhen: (_, currState) => currState is SignInButtonEnableChangedState, builder: (context, state) { return FitnessButton( title: TextConstants.signIn, isEnabled: state is SignInButtonEnableChangedState ? state.isEnabled : false, onTap: () { FocusScope.of(context).unfocus(); bloc.add(SignInTappedEvent()); }, ); }, ), ); } Widget _createDoNotHaveAccountText(BuildContext context) { final bloc = BlocProvider.of(context); return Center( child: RichText( text: TextSpan( text: TextConstants.doNotHaveAnAccount, style: TextStyle( color: ColorConstants.textBlack, fontSize: 18, ), children: [ TextSpan( text: " ${TextConstants.signUp}", style: TextStyle( color: ColorConstants.primaryColor, fontSize: 18, fontWeight: FontWeight.bold, ), recognizer: TapGestureRecognizer() ..onTap = () { bloc.add(SignUpTappedEvent()); }, ), ], ), ), ); } } ================================================ FILE: lib/screens/sign_up/bloc/signup_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/core/service/auth_service.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; part 'signup_event.dart'; part 'signup_state.dart'; class SignUpBloc extends Bloc { SignUpBloc() : super(SignupInitial()); final userNameController = TextEditingController(); final emailController = TextEditingController(); final passwordController = TextEditingController(); final confirmPasswordController = TextEditingController(); bool isButtonEnabled = false; @override Stream mapEventToState( SignupEvent event, ) async* { if (event is OnTextChangedEvent) { if (isButtonEnabled != checkIfSignUpButtonEnabled()) { isButtonEnabled = checkIfSignUpButtonEnabled(); yield SignUpButtonEnableChangedState(isEnabled: isButtonEnabled); } } else if (event is SignUpTappedEvent) { if (checkValidatorsOfTextField()) { try { yield LoadingState(); await AuthService.signUp(emailController.text, passwordController.text, userNameController.text); yield NextTabBarPageState(); print("Go to the next page"); } catch (e) { yield ErrorState(message: e.toString()); } } else { yield ShowErrorState(); } } else if (event is SignInTappedEvent) { yield NextSignInPageState(); } } bool checkIfSignUpButtonEnabled() { return userNameController.text.isNotEmpty && emailController.text.isNotEmpty && passwordController.text.isNotEmpty && confirmPasswordController.text.isNotEmpty; } bool checkValidatorsOfTextField() { return ValidationService.username(userNameController.text) && ValidationService.email(emailController.text) && ValidationService.password(passwordController.text) && ValidationService.confirmPassword(passwordController.text, confirmPasswordController.text); } } ================================================ FILE: lib/screens/sign_up/bloc/signup_event.dart ================================================ part of 'signup_bloc.dart'; @immutable abstract class SignupEvent {} class OnTextChangedEvent extends SignupEvent {} class SignUpTappedEvent extends SignupEvent {} class SignInTappedEvent extends SignupEvent {} ================================================ FILE: lib/screens/sign_up/bloc/signup_state.dart ================================================ part of 'signup_bloc.dart'; @immutable abstract class SignUpState {} class SignupInitial extends SignUpState {} class SignUpButtonEnableChangedState extends SignUpState { final bool isEnabled; SignUpButtonEnableChangedState({ required this.isEnabled, }); } class ShowErrorState extends SignUpState {} class ErrorState extends SignUpState { final String message; ErrorState({required this.message}); } class NextTabBarPageState extends SignUpState {} class NextSignInPageState extends SignUpState {} class LoadingState extends SignUpState {} ================================================ FILE: lib/screens/sign_up/page/sign_up_page.dart ================================================ import 'package:fitness_flutter/screens/sign_in/page/sign_in_page.dart'; import 'package:fitness_flutter/screens/sign_up/bloc/signup_bloc.dart'; import 'package:fitness_flutter/screens/sign_up/widget/sign_up_content.dart'; import 'package:fitness_flutter/screens/tab_bar/page/tab_bar_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class SignUpPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold(body: _buildBody(context)); } BlocProvider _buildBody(BuildContext context) { return BlocProvider( create: (BuildContext context) => SignUpBloc(), child: BlocConsumer( listenWhen: (_, currState) => currState is NextTabBarPageState || currState is NextSignInPageState || currState is ErrorState, listener: (context, state) { if (state is NextTabBarPageState) { Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => TabBarPage())); } else if (state is NextSignInPageState) { Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => SignInPage())); } else if (state is ErrorState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.message)), ); } }, buildWhen: (_, currState) => currState is SignupInitial, builder: (context, state) { return SignUpContent(); }, ), ); } } ================================================ FILE: lib/screens/sign_up/widget/sign_up_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/core/service/validation_service.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_loading.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_text_field.dart'; import 'package:fitness_flutter/screens/sign_up/bloc/signup_bloc.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class SignUpContent extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, child: Container( width: double.infinity, height: double.infinity, color: ColorConstants.white, child: Stack( children: [ _createMainData(context), BlocBuilder( buildWhen: (_, currState) => currState is LoadingState || currState is NextTabBarPageState || currState is ErrorState, builder: (context, state) { if (state is LoadingState) { return _createLoading(); } else if (state is NextTabBarPageState || state is ErrorState) { return SizedBox(); } return SizedBox(); }, ), ], ), ), ); } Widget _createMainData(BuildContext context) { return SafeArea( child: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 20), _createTitle(), // const SizedBox(height: 50), _createForm(context), const SizedBox(height: 40), _createSignUpButton(context), // Spacer(), const SizedBox(height: 40), _createHaveAccountText(context), const SizedBox(height: 30), ], ), ), ); } Widget _createLoading() { return FitnessLoading(); } Widget _createTitle() { return Text( TextConstants.signUp, style: TextStyle( color: ColorConstants.textBlack, fontSize: 24, fontWeight: FontWeight.bold, ), ); } Widget _createForm(BuildContext context) { final bloc = BlocProvider.of(context); return BlocBuilder( buildWhen: (_, currState) => currState is ShowErrorState, builder: (context, state) { return Column( children: [ FitnessTextField( title: TextConstants.username, placeholder: TextConstants.userNamePlaceholder, controller: bloc.userNameController, textInputAction: TextInputAction.next, errorText: TextConstants.usernameErrorText, isError: state is ShowErrorState ? !ValidationService.username(bloc.userNameController.text) : false, onTextChanged: () { bloc.add(OnTextChangedEvent()); }, ), const SizedBox(height: 20), FitnessTextField( title: TextConstants.email, placeholder: TextConstants.emailPlaceholder, textInputAction: TextInputAction.next, keyboardType: TextInputType.emailAddress, controller: bloc.emailController, errorText: TextConstants.emailErrorText, isError: state is ShowErrorState ? !ValidationService.email(bloc.emailController.text) : false, onTextChanged: () { bloc.add(OnTextChangedEvent()); }, ), const SizedBox(height: 20), FitnessTextField( title: TextConstants.password, placeholder: TextConstants.passwordPlaceholder, obscureText: true, isError: state is ShowErrorState ? !ValidationService.password(bloc.passwordController.text) : false, textInputAction: TextInputAction.next, controller: bloc.passwordController, errorText: TextConstants.passwordErrorText, onTextChanged: () { bloc.add(OnTextChangedEvent()); }, ), const SizedBox(height: 20), FitnessTextField( title: TextConstants.confirmPassword, placeholder: TextConstants.confirmPasswordPlaceholder, obscureText: true, isError: state is ShowErrorState ? !ValidationService.confirmPassword(bloc.passwordController.text, bloc.confirmPasswordController.text) : false, controller: bloc.confirmPasswordController, errorText: TextConstants.confirmPasswordErrorText, onTextChanged: () { bloc.add(OnTextChangedEvent()); }, ), ], ); }, ); } Widget _createSignUpButton(BuildContext context) { final bloc = BlocProvider.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: BlocBuilder( buildWhen: (_, currState) => currState is SignUpButtonEnableChangedState, builder: (context, state) { return FitnessButton( title: TextConstants.signUp, isEnabled: state is SignUpButtonEnableChangedState ? state.isEnabled : false, onTap: () { FocusScope.of(context).unfocus(); bloc.add(SignUpTappedEvent()); }, ); }, ), ); } Widget _createHaveAccountText(BuildContext context) { final bloc = BlocProvider.of(context); return RichText( text: TextSpan( text: TextConstants.alreadyHaveAccount, style: TextStyle( color: ColorConstants.textBlack, fontSize: 18, ), children: [ TextSpan( text: " ${TextConstants.signIn}", style: TextStyle( color: ColorConstants.primaryColor, fontSize: 18, fontWeight: FontWeight.bold, ), recognizer: TapGestureRecognizer() ..onTap = () { bloc.add(SignInTappedEvent()); }, ), ], ), ); } } ================================================ FILE: lib/screens/start_workout/bloc/start_workout_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; part 'start_workout_event.dart'; part 'start_workout_state.dart'; class StartWorkoutBloc extends Bloc { StartWorkoutBloc() : super(StartWorkoutInitial()); int time = 0; @override Stream mapEventToState( StartWorkoutEvent event, ) async* { if (event is BackTappedEvent) { yield BackTappedState(); } else if (event is PlayTappedEvent) { time = event.time; yield PlayTimerState(time: event.time); } else if (event is PauseTappedEvent) { time = event.time; yield PauseTimerState(currentTime: time); } } } ================================================ FILE: lib/screens/start_workout/bloc/start_workout_event.dart ================================================ part of 'start_workout_bloc.dart'; @immutable abstract class StartWorkoutEvent {} class BackTappedEvent extends StartWorkoutEvent {} class PlayTappedEvent extends StartWorkoutEvent { final int time; PlayTappedEvent({ required this.time, }); } class PauseTappedEvent extends StartWorkoutEvent { final int time; PauseTappedEvent({ required this.time, }); } class ChangeTimerEvent extends StartWorkoutEvent {} ================================================ FILE: lib/screens/start_workout/bloc/start_workout_state.dart ================================================ part of 'start_workout_bloc.dart'; @immutable abstract class StartWorkoutState {} class StartWorkoutInitial extends StartWorkoutState {} class BackTappedState extends StartWorkoutState {} class PlayTimerState extends StartWorkoutState { final int time; PlayTimerState({ required this.time, }); } class PauseTimerState extends StartWorkoutState { final int currentTime; PauseTimerState({ required this.currentTime, }); } ================================================ FILE: lib/screens/start_workout/page/start_workout_page.dart ================================================ import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:fitness_flutter/screens/start_workout/bloc/start_workout_bloc.dart'; import 'package:fitness_flutter/screens/start_workout/widget/start_workout_content.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class StartWorkoutPage extends StatelessWidget { final ExerciseData exercise; final ExerciseData currentExercise; final ExerciseData? nextExercise; StartWorkoutPage( {required this.exercise, required this.currentExercise, required this.nextExercise}); @override Widget build(BuildContext context) { return Scaffold( body: _buildContext(context), ); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (context) => StartWorkoutBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is StartWorkoutInitial, builder: (context, state) { return StartWorkoutContent( exercise: exercise, nextExercise: nextExercise, ); }, listenWhen: (_, currState) => currState is BackTappedState, listener: (context, state) { if (state is BackTappedState) { Navigator.pop(context); } }, ), ); } } ================================================ FILE: lib/screens/start_workout/widget/start_workout_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/start_workout/bloc/start_workout_bloc.dart'; import 'package:fitness_flutter/screens/start_workout/page/start_workout_page.dart'; import 'package:fitness_flutter/screens/start_workout/widget/start_workout_video.dart'; import 'package:fitness_flutter/screens/workout_details_screen/bloc/workoutdetails_bloc.dart' as workout_bloc; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class StartWorkoutContent extends StatelessWidget { final ExerciseData exercise; final ExerciseData? nextExercise; StartWorkoutContent({required this.exercise, required this.nextExercise}); @override Widget build(BuildContext context) { return Container( height: double.infinity, width: double.infinity, color: ColorConstants.white, child: SafeArea( child: _createDetailedExercise(context), ), ); } Widget _createDetailedExercise(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _createBackButton(context), const SizedBox(height: 23), _createVideo(context), const SizedBox(height: 8), Expanded( child: ListView(children: [ _createTitle(), const SizedBox(height: 9), _createDescription(), const SizedBox(height: 30), _createSteps(), ]), ), _createTimeTracker(context), ], ), ); } Widget _createBackButton(BuildContext context) { final bloc = BlocProvider.of(context); return Padding( padding: const EdgeInsets.only(left: 10, top: 8), child: GestureDetector( child: BlocBuilder( builder: (context, state) { return Row( children: [ Image(image: AssetImage(PathConstants.back)), const SizedBox(width: 17), Text( TextConstants.back, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500), ), ], ); }, ), onTap: () { bloc.add(BackTappedEvent()); }, ), ); } Widget _createVideo(BuildContext context) { final bloc = BlocProvider.of(context); return Container( height: 264, width: double.infinity, decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: ColorConstants.white), child: StartWorkoutVideo( exercise: exercise, onPlayTapped: (time) { bloc.add(PlayTappedEvent(time: time)); }, onPauseTapped: (time) { bloc.add(PauseTappedEvent(time: time)); }, ), ); } Widget _createTitle() { return Text(exercise.title, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)); } Widget _createDescription() { return Text(exercise.description, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)); } Widget _createSteps() { return Column( children: [ for (int i = 0; i < exercise.steps.length; i++) ...[ Step(number: "${i + 1}", description: exercise.steps[i]), const SizedBox(height: 20), ], ], ); } Widget _createTimeTracker(BuildContext context) { // final bloc = BlocProvider.of(context); return Container( width: double.infinity, color: ColorConstants.white, child: Column( children: [ nextExercise != null ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( TextConstants.nextExercise, style: TextStyle( color: ColorConstants.grey, fontSize: 17, fontWeight: FontWeight.w600, ), ), const SizedBox(width: 5), Text( nextExercise?.title ?? "", style: TextStyle( color: ColorConstants.textBlack, fontSize: 17, fontWeight: FontWeight.w600, ), ), const SizedBox(width: 6.5), Icon(Icons.access_time, size: 20), const SizedBox(width: 6.5), Text('00:${nextExercise!.minutes > 10 ? nextExercise!.minutes : '0${nextExercise!.minutes}'}') // BlocBuilder( // buildWhen: (_, currState) => currState is PlayTimerState || currState is PauseTimerState, // builder: (context, state) { // return StartWorkoutTimer( // time: bloc.time, // isPaused: !(state is PlayTimerState), // ); // }, // ), ], ) : SizedBox.shrink(), const SizedBox(height: 18), _createButton(context), ], ), ); } Widget _createButton(BuildContext context) { return FitnessButton( title: nextExercise != null ? TextConstants.next : 'Finish', onTap: () { if (nextExercise != null) { List exercisesList = BlocProvider.of(context).workout.exerciseDataList; int currentExerciseIndex = exercisesList.indexOf(exercise); if (currentExerciseIndex < exercisesList.length - 1) { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => BlocProvider.value( value: BlocProvider.of(context), child: StartWorkoutPage( exercise: exercisesList[currentExerciseIndex + 1], currentExercise: exercisesList[currentExerciseIndex + 1], nextExercise: currentExerciseIndex + 2 < exercisesList.length ? exercisesList[currentExerciseIndex + 2] : null, ), )), ); } } else { Navigator.of(context).pop(); } }, ); } } class Step extends StatelessWidget { final String number; final String description; Step({required this.number, required this.description}); @override Widget build(BuildContext context) { return Row( children: [ Container( height: 25, width: 25, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: ColorConstants.primaryColor.withOpacity(0.12), ), child: Center(child: Text(number, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: ColorConstants.primaryColor))), ), const SizedBox(width: 10), Expanded(child: Text(description)), ], ); } } ================================================ FILE: lib/screens/start_workout/widget/start_workout_timer.dart ================================================ import 'package:custom_timer/custom_timer.dart'; import 'package:fitness_flutter/core/service/date_service.dart'; import 'package:flutter/material.dart'; class StartWorkoutTimer extends StatefulWidget { final int time; final bool isPaused; StartWorkoutTimer({ required this.time, required this.isPaused, }); @override _StartWorkoutTimerState createState() => _StartWorkoutTimerState(); } class _StartWorkoutTimerState extends State { @override Widget build(BuildContext context) { return widget.isPaused ? _createPauseText() : _createCountdownTimer(); } Widget _createCountdownTimer() { return CustomTimer( from: Duration(seconds: widget.time), to: Duration(seconds: 0), onBuildAction: CustomTimerAction.auto_start, builder: (CustomTimerRemainingTime remaining) { return Text( "${remaining.minutes}:${remaining.seconds}", style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600), ); }, ); } Widget _createPauseText() { final minutesSeconds = DateService.convertIntoSeconds(widget.time); return Text( "${minutesSeconds.minutes.toString().padLeft(2, '0')}:${minutesSeconds.seconds.toString().padLeft(2, '0')}", style: TextStyle( fontSize: 17, fontWeight: FontWeight.w600, ), ); } } ================================================ FILE: lib/screens/start_workout/widget/start_workout_video.dart ================================================ import 'dart:async'; import 'package:chewie/chewie.dart'; import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:video_player/video_player.dart'; import 'package:flutter/material.dart'; class StartWorkoutVideo extends StatefulWidget { final ExerciseData exercise; final Function(int) onPlayTapped; final Function(int) onPauseTapped; StartWorkoutVideo({ required this.exercise, required this.onPlayTapped, required this.onPauseTapped, }); @override _StartWorkoutVideoState createState() => _StartWorkoutVideoState(); } class _StartWorkoutVideoState extends State { late VideoPlayerController _controller; // late Future _initializeVideoPlayerFuture; late bool isPlayButtonHidden = false; late ChewieController _chewieController; Timer? timer; Timer? videoTimer; // bool _isVideoPlaying = false; @override void initState() { _controller = VideoPlayerController.asset(widget.exercise.video); _controller.initialize(); _chewieController = ChewieController( videoPlayerController: _controller, looping: true, autoPlay: false, deviceOrientationsAfterFullScreen: [DeviceOrientation.portraitUp], aspectRatio: 15 / 10, placeholder: Center(child: CupertinoActivityIndicator()), materialProgressColors: ChewieProgressColors(playedColor: ColorConstants.primaryColor)); super.initState(); } @override void dispose() { _controller.dispose(); _chewieController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AspectRatio(aspectRatio: _controller.value.aspectRatio, child: _createVideoContainer()); } Widget _createVideoContainer() { return ClipRRect( borderRadius: BorderRadius.circular(20), child: Theme(data: Theme.of(context).copyWith(platform: TargetPlatform.android), child: Chewie(controller: _chewieController)), ); } // Widget _createPlayButton() { // return Center( // child: GestureDetector( // onTap: () { // // timer?.cancel(); // _chewieController.isPlaying ? _chewieController.pause() : _chewieController.play(); // setState(() { // _isVideoPlaying = _chewieController.isPlaying; // }); // // setState(() { // // if (_controller.value.isPlaying) { // // _controller.pause(); // // widget.onPauseTapped(_getCurrentTime()); // // } else { // // _controller.play(); // // _playTimer(); // // widget.onPlayTapped(_getCurrentTime()); // // } // // }); // }, // child: Container( // height: 50, // width: 50, // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(30), // color: ColorConstants.white.withOpacity(0.8), // ), // child: Icon( // _isVideoPlaying ? Icons.pause : Icons.play_arrow, // color: ColorConstants.primaryColor, // ), // ), // ), // ); // } // void _playTimer() { // timer = Timer(Duration(seconds: 3), () { // setState(() { // isPlayButtonHidden = true; // }); // }); // } // int _getCurrentTime() { // int duration = _controller.value.duration.inSeconds; // int position = _controller.value.position.inSeconds; // return duration - position; // } } ================================================ FILE: lib/screens/tab_bar/bloc/tab_bar_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; part 'tab_bar_event.dart'; part 'tab_bar_state.dart'; class TabBarBloc extends Bloc { TabBarBloc() : super(TabBarInitial()); int currentIndex = 0; bool isSelected = false; @override Stream mapEventToState( TabBarEvent event, ) async* { if (event is TabBarItemTappedEvent) { currentIndex = event.index; yield TabBarItemSelectedState(index: currentIndex); } } } ================================================ FILE: lib/screens/tab_bar/bloc/tab_bar_event.dart ================================================ part of 'tab_bar_bloc.dart'; @immutable abstract class TabBarEvent {} class TabBarItemTappedEvent extends TabBarEvent { final int index; TabBarItemTappedEvent({ required this.index, }); } ================================================ FILE: lib/screens/tab_bar/bloc/tab_bar_state.dart ================================================ part of 'tab_bar_bloc.dart'; @immutable abstract class TabBarState {} class TabBarInitial extends TabBarState {} class TabBarItemSelectedState extends TabBarState { final int index; TabBarItemSelectedState({ required this.index, }); } ================================================ FILE: lib/screens/tab_bar/page/tab_bar_page.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/screens/home/page/home_page.dart'; import 'package:fitness_flutter/screens/settings/settings_screen.dart'; import 'package:fitness_flutter/screens/tab_bar/bloc/tab_bar_bloc.dart'; import 'package:fitness_flutter/screens/workouts/page/workouts_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class TabBarPage extends StatelessWidget { const TabBarPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => TabBarBloc(), child: BlocConsumer( listener: (context, state) {}, buildWhen: (_, currState) => currState is TabBarInitial || currState is TabBarItemSelectedState, builder: (context, state) { final bloc = BlocProvider.of(context); return Scaffold( body: _createBody(context, bloc.currentIndex), bottomNavigationBar: _createdBottomTabBar(context), ); }, ), ); } Widget _createdBottomTabBar(BuildContext context) { final bloc = BlocProvider.of(context); return BottomNavigationBar( currentIndex: bloc.currentIndex, fixedColor: ColorConstants.primaryColor, items: [ BottomNavigationBarItem( icon: Image( image: AssetImage(PathConstants.home), color: bloc.currentIndex == 0 ? ColorConstants.primaryColor : null, ), label: TextConstants.homeIcon, ), BottomNavigationBarItem( icon: Image( image: AssetImage(PathConstants.workouts), color: bloc.currentIndex == 1 ? ColorConstants.primaryColor : null, ), label: TextConstants.workoutsIcon, ), BottomNavigationBarItem( icon: Image( image: AssetImage(PathConstants.settings), color: bloc.currentIndex == 2 ? ColorConstants.primaryColor : null, ), label: TextConstants.settingsIcon, ), ], onTap: (index) { bloc.add(TabBarItemTappedEvent(index: index)); }, ); } Widget _createBody(BuildContext context, int index) { final children = [ HomePage(), WorkoutsPage(), SettingsScreen() // Scaffold( // body: Center( // child: RawMaterialButton( // fillColor: Colors.red, // child: Text( // TextConstants.signOut, // style: TextStyle( // color: ColorConstants.white, // ), // ), // onPressed: () { // AuthService.signOut(); // Navigator.pushReplacement( // context, // MaterialPageRoute(builder: (_) => SignInPage()), // ); // }, // ), // ), // ), ]; return children[index]; } } ================================================ FILE: lib/screens/workout_details_screen/bloc/workoutdetails_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:meta/meta.dart'; part 'workoutdetails_event.dart'; part 'workoutdetails_state.dart'; class WorkoutDetailsBloc extends Bloc { final WorkoutData workout; WorkoutDetailsBloc({required this.workout}) : super(WorkoutDetailsInitial()); @override Stream mapEventToState( WorkoutDetailsEvent event, ) async* { if (event is BackTappedEvent) { yield BackTappedState(); } else if (event is WorkoutExerciseCellTappedEvent) { yield WorkoutExerciseCellTappedState( currentExercise: event.currentExercise, nextExercise: event.nextExercise, ); } } } ================================================ FILE: lib/screens/workout_details_screen/bloc/workoutdetails_event.dart ================================================ part of 'workoutdetails_bloc.dart'; @immutable abstract class WorkoutDetailsEvent {} class BackTappedEvent extends WorkoutDetailsEvent {} class WorkoutExerciseCellTappedEvent extends WorkoutDetailsEvent { final ExerciseData currentExercise; final ExerciseData? nextExercise; WorkoutExerciseCellTappedEvent({ required this.currentExercise, required this.nextExercise, }); } ================================================ FILE: lib/screens/workout_details_screen/bloc/workoutdetails_state.dart ================================================ part of 'workoutdetails_bloc.dart'; @immutable abstract class WorkoutDetailsState {} class WorkoutDetailsInitial extends WorkoutDetailsState {} class BackTappedState extends WorkoutDetailsState {} class WorkoutExerciseCellTappedState extends WorkoutDetailsState { final ExerciseData currentExercise; final ExerciseData? nextExercise; WorkoutExerciseCellTappedState({ required this.currentExercise, required this.nextExercise, }); } ================================================ FILE: lib/screens/workout_details_screen/page/workout_details_page.dart ================================================ import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/common_widgets/fitness_button.dart'; import 'package:fitness_flutter/screens/start_workout/page/start_workout_page.dart'; import 'package:fitness_flutter/screens/workout_details_screen/bloc/workoutdetails_bloc.dart'; import 'package:fitness_flutter/screens/workout_details_screen/widget/workout_details_content.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fitness_flutter/core/extensions/list_extension.dart'; class WorkoutDetailsPage extends StatelessWidget { final WorkoutData workout; WorkoutDetailsPage({required this.workout}); @override Widget build(BuildContext context) { return _buildContext(context); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (context) => WorkoutDetailsBloc(workout: workout), child: BlocConsumer( buildWhen: (_, currState) => currState is WorkoutDetailsInitial, builder: (context, state) { return Scaffold( floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: FitnessButton( title: TextConstants.start, onTap: () { ExerciseData? exercise = workout.exerciseDataList.firstWhereOrNull((element) => element.progress < 1); if (exercise == null) exercise = workout.exerciseDataList.first; int exerciseIndex = workout.exerciseDataList.indexOf(exercise); Navigator.of(context).push( MaterialPageRoute( builder: (_) => BlocProvider.value( value: BlocProvider.of(context), child: StartWorkoutPage( exercise: exercise!, currentExercise: exercise, nextExercise: exerciseIndex + 1 < workout.exerciseDataList.length ? workout.exerciseDataList[exerciseIndex + 1] : null, ), )), ); }, ), ), body: WorkoutDetailsContent(workout: workout)); }, listenWhen: (_, currState) => currState is BackTappedState || currState is WorkoutExerciseCellTappedState, listener: (context, state) { if (state is BackTappedState) { Navigator.pop(context); } else if (state is WorkoutExerciseCellTappedState) { Navigator.of(context).push( MaterialPageRoute( builder: (_) => BlocProvider.value( value: BlocProvider.of(context), child: StartWorkoutPage( exercise: state.currentExercise, currentExercise: state.currentExercise, nextExercise: state.nextExercise, ), )), ); } }, ), ); } } ================================================ FILE: lib/screens/workout_details_screen/widget/panel/exercises_list.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/data/exercise_data.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/workout_details_screen/bloc/workoutdetails_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; class ExercisesList extends StatelessWidget { final WorkoutData workout; final List exercises; const ExercisesList({required this.exercises, required this.workout}); @override Widget build(BuildContext context) { return ListView.separated( padding: EdgeInsets.only(top: 10), itemCount: exercises.length, itemBuilder: (context, index) { return ExerciseCell( currentExercise: exercises[index], nextExercise: index == exercises.length - 1 ? null : exercises[index + 1], workout: workout, ); }, separatorBuilder: (context, index) { return const SizedBox(height: 15); }, ); } } class ExerciseCell extends StatelessWidget { final WorkoutData workout; final ExerciseData currentExercise; final ExerciseData? nextExercise; const ExerciseCell({ required this.currentExercise, required this.workout, required this.nextExercise, }); @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return BlocBuilder( buildWhen: (_, currState) => currState is WorkoutExerciseCellTappedState, builder: (context, state) { return InkWell( borderRadius: BorderRadius.circular(40), onTap: () { bloc.add( WorkoutExerciseCellTappedEvent( currentExercise: currentExercise, nextExercise: nextExercise, ), ); }, child: Container( width: double.infinity, padding: const EdgeInsets.only(left: 10, right: 25, top: 10, bottom: 10), decoration: BoxDecoration( color: ColorConstants.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: ColorConstants.textBlack.withOpacity(0.12), blurRadius: 5.0, spreadRadius: 1.1, ), ], ), child: Row( children: [ _createImage(), const SizedBox(width: 10), Expanded( child: _createExerciseTextInfo(), ), const SizedBox(width: 10), _createRightArrow(), ], ), ), ); }, ); } Widget _createImage() { return Container( width: 75, height: 70, decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), image: DecorationImage( image: AssetImage(workout.image), fit: BoxFit.contain, ), ), ); } Widget _createExerciseTextInfo() { final minutesStr = "${currentExercise.minutes} minutes"; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( currentExercise.title, style: TextStyle( color: ColorConstants.textColor, fontSize: 16, fontWeight: FontWeight.w700, ), ), Text( minutesStr, style: TextStyle( color: ColorConstants.textBlack, fontSize: 14, fontWeight: FontWeight.w400, ), ), const SizedBox(height: 11), Padding( padding: const EdgeInsets.only(right: 20), child: LinearPercentIndicator( percent: currentExercise.progress, progressColor: ColorConstants.primaryColor, backgroundColor: ColorConstants.primaryColor.withOpacity(0.12), lineHeight: 6, padding: EdgeInsets.zero, ), ), ], ); } Widget _createRightArrow() { return RotatedBox( quarterTurns: 2, child: Image( image: AssetImage(PathConstants.back), ), ); } } ================================================ FILE: lib/screens/workout_details_screen/widget/panel/workout_details_panel.dart ================================================ import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/workout_details_screen/widget/panel/exercises_list.dart'; import 'package:fitness_flutter/screens/workout_details_screen/widget/panel/workout_tag.dart'; import 'package:flutter/material.dart'; class WorkoutDetailsPanel extends StatelessWidget { final WorkoutData workout; WorkoutDetailsPanel({required this.workout}); @override Widget build(BuildContext context) { return _createPanelData(); } Widget _createPanelData() { return Column( children: [ const SizedBox(height: 15), _createRectangle(), const SizedBox(height: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _createHeader(), const SizedBox(height: 20), _createWorkoutData(), SizedBox(height: 20), _createExerciesList(), ], ), ), ], ); } Widget _createRectangle() { return Image(image: AssetImage(PathConstants.rectangle)); } Widget _createHeader() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Text( workout.title + " " + TextConstants.workout, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), ); } Widget _createWorkoutData() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Row( children: [ WorkoutTag( icon: PathConstants.timeTracker, content: "${workout.minutes}:00", ), const SizedBox(width: 15), WorkoutTag( icon: PathConstants.exerciseTracker, content: "${workout.exercices} ${TextConstants.exercisesLowercase}", ), ], ), ); } Widget _createExerciesList() { return Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: ExercisesList(exercises: workout.exerciseDataList, workout: workout), ), ); } } ================================================ FILE: lib/screens/workout_details_screen/widget/panel/workout_tag.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:flutter/material.dart'; class WorkoutTag extends StatelessWidget { final String icon; final String content; WorkoutTag({required this.icon, required this.content}); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 17, vertical: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: ColorConstants.primaryColor.withOpacity(0.12), ), child: Row( children: [ Image.asset(icon, height: 17, width: 17, fit: BoxFit.fill), const SizedBox(width: 7), Text(content, style: TextStyle(color: ColorConstants.primaryColor, fontSize: 14, fontWeight: FontWeight.w500)), ], ), ); } } ================================================ FILE: lib/screens/workout_details_screen/widget/workout_details_body.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/path_constants.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/workout_details_screen/bloc/workoutdetails_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class WorkoutDetailsBody extends StatelessWidget { final WorkoutData workout; WorkoutDetailsBody({required this.workout}); @override Widget build(BuildContext context) { return Container( height: double.infinity, width: double.infinity, color: ColorConstants.white, child: Stack( children: [ _createImage(), _createBackButton(context), ], ), ); } Widget _createBackButton(BuildContext context) { final bloc = BlocProvider.of(context); return Positioned( child: SafeArea( child: BlocBuilder( builder: (context, state) { return GestureDetector( child: Container( width: 30, height: 30, child: Image( image: AssetImage(PathConstants.back), ), ), onTap: () { bloc.add(BackTappedEvent()); }, ); }, ), ), left: 20, top: 14, ); } Widget _createImage() { return Container( width: double.infinity, child: Image( image: AssetImage(workout.image), fit: BoxFit.cover, ), ); } } ================================================ FILE: lib/screens/workout_details_screen/widget/workout_details_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/workout_details_screen/widget/panel/workout_details_panel.dart'; import 'package:fitness_flutter/screens/workout_details_screen/widget/workout_details_body.dart'; import 'package:flutter/material.dart'; import 'package:sliding_up_panel/sliding_up_panel.dart'; class WorkoutDetailsContent extends StatelessWidget { final WorkoutData workout; const WorkoutDetailsContent({required this.workout}); @override Widget build(BuildContext context) { return Container( height: double.infinity, width: double.infinity, color: ColorConstants.white, child: _createSlidingUpPanel(context), ); } Widget _createSlidingUpPanel(BuildContext context) { return SlidingUpPanel( panel: WorkoutDetailsPanel(workout: workout), body: WorkoutDetailsBody(workout: workout), minHeight: MediaQuery.of(context).size.height * 0.65, maxHeight: MediaQuery.of(context).size.height * 0.87, isDraggable: true, borderRadius: BorderRadius.only( topLeft: Radius.circular(50), topRight: Radius.circular(50), ), ); } } ================================================ FILE: lib/screens/workouts/bloc/workouts_bloc.dart ================================================ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:meta/meta.dart'; part 'workouts_event.dart'; part 'workouts_state.dart'; class WorkoutsBloc extends Bloc { WorkoutsBloc() : super(WorkoutsInitial()); @override Stream mapEventToState( WorkoutsEvent event, ) async* { if (event is CardTappedEvent) { yield CardTappedState(workout: event.workout); } } } ================================================ FILE: lib/screens/workouts/bloc/workouts_event.dart ================================================ part of 'workouts_bloc.dart'; @immutable abstract class WorkoutsEvent {} class CardTappedEvent extends WorkoutsEvent { final WorkoutData workout; CardTappedEvent({required this.workout}); } ================================================ FILE: lib/screens/workouts/bloc/workouts_state.dart ================================================ part of 'workouts_bloc.dart'; @immutable abstract class WorkoutsState {} class WorkoutsInitial extends WorkoutsState {} class CardTappedState extends WorkoutsState { final WorkoutData workout; CardTappedState({required this.workout}); } ================================================ FILE: lib/screens/workouts/page/workouts_page.dart ================================================ import 'package:fitness_flutter/screens/workout_details_screen/page/workout_details_page.dart'; import 'package:fitness_flutter/screens/workouts/bloc/workouts_bloc.dart'; import 'package:fitness_flutter/screens/workouts/widget/workout_content.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class WorkoutsPage extends StatelessWidget { const WorkoutsPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold(body: _buildContext(context)); } BlocProvider _buildContext(BuildContext context) { return BlocProvider( create: (context) => WorkoutsBloc(), child: BlocConsumer( buildWhen: (_, currState) => currState is WorkoutsInitial, builder: (context, state) { return WorkoutContent(); }, listenWhen: (_, currState) => currState is CardTappedState, listener: (context, state) { if (state is CardTappedState) { Navigator.of(context, rootNavigator: true).push( MaterialPageRoute( builder: (_) => WorkoutDetailsPage(workout: state.workout), ), ); } }, ), ); } } ================================================ FILE: lib/screens/workouts/widget/workout_card.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/text_constants.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/workouts/bloc/workouts_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:percent_indicator/percent_indicator.dart'; class WorkoutCard extends StatelessWidget { final WorkoutData workout; WorkoutCard({Key? key, required this.workout}) : super(key: key); @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return Container( width: double.infinity, height: 140, margin: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: ColorConstants.white, boxShadow: [BoxShadow(color: ColorConstants.textBlack.withOpacity(0.12), blurRadius: 5.0, spreadRadius: 1.1)], ), child: Material( color: Colors.transparent, child: BlocBuilder( buildWhen: (_, currState) => currState is CardTappedState, builder: (context, state) { return InkWell( borderRadius: BorderRadius.circular(10), onTap: () { bloc.add(CardTappedEvent(workout: workout)); }, child: Container( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(workout.title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 3), Text(workout.exercices + " " + TextConstants.exercisesUppercase, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: ColorConstants.grey), overflow: TextOverflow.ellipsis, maxLines: 2), const SizedBox(height: 3), Text(workout.minutes + " " + TextConstants.minutes, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: ColorConstants.grey), overflow: TextOverflow.ellipsis, maxLines: 2), Spacer(), Text('${workout.currentProgress}/${workout.progress}', style: TextStyle(fontSize: 10)), SizedBox(height: 3), Padding( padding: const EdgeInsets.only(right: 30.0, left: 2), child: LinearPercentIndicator( percent: workout.currentProgress / workout.progress, progressColor: ColorConstants.primaryColor, backgroundColor: ColorConstants.primaryColor.withOpacity(0.12), lineHeight: 6, padding: EdgeInsets.zero, ), ) ], ), ), SizedBox(width: 60), Expanded(child: ClipRRect(borderRadius: BorderRadius.circular(15), child: Image.asset(workout.image, fit: BoxFit.fill))), ], ), ), ); }, ), ), ); } } ================================================ FILE: lib/screens/workouts/widget/workout_content.dart ================================================ import 'package:fitness_flutter/core/const/color_constants.dart'; import 'package:fitness_flutter/core/const/data_constants.dart'; import 'package:fitness_flutter/data/workout_data.dart'; import 'package:fitness_flutter/screens/workouts/widget/workout_card.dart'; import 'package:flutter/material.dart'; class WorkoutContent extends StatelessWidget { WorkoutContent({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( color: ColorConstants.homeBackgroundColor, height: double.infinity, width: double.infinity, child: _createHomeBody(context), ); } Widget _createHomeBody(BuildContext context) { return Padding( padding: const EdgeInsets.only(top: 50), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Text('Workouts', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), ), const SizedBox(height: 5), Expanded( child: ListView( children: DataConstants.workouts .map( (e) => _createWorkoutCard(e), ) .toList(), ), ), ], ), ); } Widget _createWorkoutCard(WorkoutData workoutData) { return Padding( padding: const EdgeInsets.only(bottom: 20), child: WorkoutCard(workout: workoutData), ); } } ================================================ FILE: pubspec.yaml ================================================ name: fitness_flutter description: A new Flutter project. # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter #BloC flutter_bloc: ^7.0.1 # Firebase firebase_core: ^1.4.0 firebase_auth: ^3.0.1 firebase_storage: ^10.0.1 # IOS Icons cupertino_icons: ^1.0.2 # Page View dots indicator dots_indicator: ^2.0.0 # Percent circle percent_indicator: ^3.0.1 # URL launcher url_launcher: ^6.0.9 # Workout Details sliding up panel sliding_up_panel: ^2.0.0+1 # Video player video_player: ^2.1.12 chewie: git: url: git@github.com:v1nnk1/chewie.git # Countdown timer custom_timer: ^0.0.6 # Image library image_picker: ^0.8.2 # Reminder notification flutter_local_notifications: ^8.0.0 # Native timezone flutter_native_timezone: ^2.0.0 # Permission handler permission_handler: ^8.1.4+2 dev_dependencies: flutter_test: sdk: flutter # The following section is specific to Flutter. flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: assets: - assets/images/onboarding/ - assets/images/auth/ - assets/images/home/ - assets/images/exercises/ - assets/icons/home/ - assets/icons/workouts/ - assets/icons/social_networks/ - assets/videos/workouts/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages fonts: - family: NotoSansKR fonts: - asset: assets/fonts/NotoSansKR/NotoSansKR-Regular.otf - asset: assets/fonts/NotoSansKR/NotoSansKR-Medium.otf weight: 500 - asset: assets/fonts/NotoSansKR/NotoSansKR-Bold.otf weight: 700 # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # ================================================ FILE: test/widget_test.dart ================================================ // This is a basic Flutter widget test. // // To perform an interaction with a widget in your test, use the WidgetTester // utility that Flutter provides. For example, you can send tap and scroll // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fitness_flutter/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); } ================================================ FILE: web/index.html ================================================ fitness_flutter ================================================ FILE: web/manifest.json ================================================ { "name": "fitness_flutter", "short_name": "fitness_flutter", "start_url": ".", "display": "standalone", "background_color": "#0175C2", "theme_color": "#0175C2", "description": "A new Flutter project.", "orientation": "portrait-primary", "prefer_related_applications": false, "icons": [ { "src": "icons/Icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "icons/Icon-512.png", "sizes": "512x512", "type": "image/png" } ] }