├── .gitignore ├── Assets ├── Images │ ├── banner.jpg │ ├── daynight.png │ ├── fitmen.jpg │ ├── ic_launcher.png │ ├── icon.png │ ├── insurance.png │ ├── star.png │ ├── star_fill.png │ └── star_unfill.png └── fonts │ └── GoogleSansRegular.ttf ├── LICENSE ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ ├── com │ │ │ │ └── nividata │ │ │ │ │ └── bmi_calculator │ │ │ │ │ └── MainActivity.java │ │ │ └── io │ │ │ │ └── flutter │ │ │ │ └── plugins │ │ │ │ └── GeneratedPluginRegistrant.java │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── banner_new3.jpg ├── bmi_calculator.iml ├── ic_launcher.png ├── ios ├── .gitignore ├── BmiCalculator.swift ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner-Bridging-Header.h ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── nividata1.xcuserdatad │ │ │ ├── UserInterfaceState.xcuserstate │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── nividata1.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x-1.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x-1.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x-1.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x-1.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── ItunesArtwork@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── main.m └── ServiceDefinitions.json ├── lib ├── core │ ├── app_theme.dart │ ├── color_scheme.dart │ └── constants.dart ├── data │ ├── model │ │ └── bmi.dart │ ├── repository │ │ └── local_repository.dart │ └── sharedpref │ │ ├── preferences.dart │ │ └── shared_preference_helper.dart ├── extension │ └── datetime_extension.dart ├── main.dart ├── utility │ ├── app_util.dart │ └── bmi_util.dart └── view │ ├── about │ └── about.dart │ ├── app │ ├── app_view.dart │ ├── theme_controller.dart │ └── theme_provider.dart │ ├── common │ ├── animate_button.dart │ ├── common_alert_dialog.dart │ ├── rate_dialog.dart │ ├── scale_transition.dart │ └── size_transition.dart │ ├── dashboard │ ├── bmi_controller.dart │ ├── bmi_provider.dart │ ├── dashboard_view.dart │ ├── drawer_footer_view.dart │ ├── drawer_header_view.dart │ └── theme_icon_button.dart │ ├── drawer │ ├── drawer_scaffold.dart │ ├── menu_screen.dart │ └── utils.dart │ ├── home │ ├── age_card_view.dart │ ├── calculate_button_view.dart │ ├── gender_card_view.dart │ ├── height_card_view.dart │ ├── height_unit_switch.dart │ ├── home_view.dart │ └── weight_card_view.dart │ ├── result │ └── result_view.dart │ ├── setting │ ├── setting_view.dart │ └── theme_change_switch.dart │ └── splash │ └── splash.dart ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Miscellaneous 3 | *.class 4 | *.lock 5 | *.log 6 | *.pyc 7 | *.swp 8 | .DS_Store 9 | .atom/ 10 | .buildlog/ 11 | .history 12 | .svn/ 13 | 14 | # IntelliJ related 15 | *.iml 16 | *.ipr 17 | *.iws 18 | .idea/ 19 | 20 | # Visual Studio Code related 21 | .classpath 22 | .project 23 | .settings/ 24 | .vscode/ 25 | 26 | # Flutter repo-specific 27 | /bin/cache/ 28 | /bin/mingit/ 29 | /dev/benchmarks/mega_gallery/ 30 | /dev/bots/.recipe_deps 31 | /dev/bots/android_tools/ 32 | /dev/docs/doc/ 33 | /dev/docs/flutter.docs.zip 34 | /dev/docs/lib/ 35 | /dev/docs/pubspec.yaml 36 | /dev/integration_tests/**/xcuserdata 37 | /dev/integration_tests/**/Pods 38 | /packages/flutter/coverage/ 39 | version 40 | 41 | # packages file containing multi-root paths 42 | .packages.generated 43 | 44 | # Flutter/Dart/Pub related 45 | **/doc/api/ 46 | .dart_tool/ 47 | .flutter-plugins 48 | .flutter-plugins-dependencies 49 | .packages 50 | .pub-cache/ 51 | .pub/ 52 | build/ 53 | flutter_*.png 54 | linked_*.ds 55 | unlinked.ds 56 | unlinked_spec.ds 57 | 58 | # Android related 59 | **/android/**/gradle-wrapper.jar 60 | **/android/.gradle 61 | **/android/captures/ 62 | **/android/gradlew 63 | **/android/gradlew.bat 64 | **/android/local.properties 65 | **/android/**/GeneratedPluginRegistrant.java 66 | **/android/key.properties 67 | *.jks 68 | 69 | # iOS/XCode related 70 | **/ios/**/*.mode1v3 71 | **/ios/**/*.mode2v3 72 | **/ios/**/*.moved-aside 73 | **/ios/**/*.pbxuser 74 | **/ios/**/*.perspectivev3 75 | **/ios/**/*sync/ 76 | **/ios/**/.sconsign.dblite 77 | **/ios/**/.tags* 78 | **/ios/**/.vagrant/ 79 | **/ios/**/DerivedData/ 80 | **/ios/**/Icon? 81 | **/ios/**/Pods/ 82 | **/ios/**/.symlinks/ 83 | **/ios/**/profile 84 | **/ios/**/xcuserdata 85 | **/ios/.generated/ 86 | **/ios/Flutter/App.framework 87 | **/ios/Flutter/Flutter.framework 88 | **/ios/Flutter/Flutter.podspec 89 | **/ios/Flutter/Generated.xcconfig 90 | **/ios/Flutter/app.flx 91 | **/ios/Flutter/app.zip 92 | **/ios/Flutter/flutter_assets/ 93 | **/ios/Flutter/flutter_export_environment.sh 94 | **/ios/ServiceDefinitions.json 95 | **/ios/Runner/GeneratedPluginRegistrant.* 96 | 97 | # macOS 98 | **/macos/Flutter/GeneratedPluginRegistrant.swift 99 | **/macos/Flutter/Flutter-Debug.xcconfig 100 | **/macos/Flutter/Flutter-Release.xcconfig 101 | **/macos/Flutter/Flutter-Profile.xcconfig 102 | 103 | # Coverage 104 | coverage/ 105 | 106 | # Symbols 107 | app.*.symbols 108 | 109 | # Exceptions to above rules. 110 | !**/ios/**/default.mode1v3 111 | !**/ios/**/default.mode2v3 112 | !**/ios/**/default.pbxuser 113 | !**/ios/**/default.perspectivev3 114 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 115 | !/dev/ci/**/Gemfile.lock 116 | 117 | 118 | 119 | # Flutter repo-specific 120 | /bin/internal/bootstrap.bat 121 | /bin/internal/bootstrap.sh 122 | /dev/devicelab/ABresults*.json 123 | analysis_benchmark.json 124 | 125 | 126 | # Flutter/Dart/Pub related 127 | **/generated_plugin_registrant.dart 128 | 129 | # Android related 130 | .gradle/ 131 | # iOS/XCode related 132 | **/ios/Flutter/.last_build_id 133 | **/ios/Flutter/ephemeral 134 | 135 | # macOS 136 | **/Flutter/ephemeral/ 137 | **/Pods/ 138 | **/macos/Flutter/ephemeral 139 | **/xcuserdata/ 140 | 141 | /android/.gradle/ 142 | /lib/firebase_options.dart 143 | /android/app/google-services.json 144 | /ios/firebase_app_id_file.json 145 | /ios/Runner/GoogleService-Info.plist 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /Assets/Images/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/banner.jpg -------------------------------------------------------------------------------- /Assets/Images/daynight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/daynight.png -------------------------------------------------------------------------------- /Assets/Images/fitmen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/fitmen.jpg -------------------------------------------------------------------------------- /Assets/Images/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/ic_launcher.png -------------------------------------------------------------------------------- /Assets/Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/icon.png -------------------------------------------------------------------------------- /Assets/Images/insurance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/insurance.png -------------------------------------------------------------------------------- /Assets/Images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/star.png -------------------------------------------------------------------------------- /Assets/Images/star_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/star_fill.png -------------------------------------------------------------------------------- /Assets/Images/star_unfill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/Images/star_unfill.png -------------------------------------------------------------------------------- /Assets/fonts/GoogleSansRegular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/Assets/fonts/GoogleSansRegular.ttf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nividata Consultancy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BMI Calculator 2 | 3 | 10 | [![Contributors][contributors-shield]][contributors-url] 11 | [![Forks][forks-shield]][forks-url] 12 | [![Stargazers][stars-shield]][stars-url] 13 | [![Issues][issues-shield]][issues-url] 14 | [![MIT License][license-shield]][license-url] 15 | [![LinkedIn][linkedin-shield]][linkedin-url] 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | 24 | 25 | 26 |

BMI Calculator

27 | 28 |

29 |
30 | View Demo 31 | · 32 | Report Bug 33 | · 34 | Request Feature 35 |

36 |

37 | 38 | 39 | 40 | 41 | ## Table of Contents 42 | 43 | * [About the Project](#about-the-project) 44 | * [Roadmap](#roadmap) 45 | * [Contributing](#contributing) 46 | * [License](#license) 47 | * [Contact](#contact) 48 | * [Acknowledgements](#acknowledgements) 49 | 50 | 51 | 52 | ## About The Project 53 | 54 | [![Product Name Screen Shot][product-screenshot]](https://example.com) 55 | 56 | ## What is BMI? 57 | Body Mass Index(BMI) is value derived from person's weight and height. The result of BMI measurement can give an idea about weather a person has correct weight for 58 | their height. 59 | 60 | ## How to Calculate BMI? 61 | BMI calculation is based on simple formula using person's weight and height. 62 | The formula for BMI = kg/m2 where kg is person's weight in kilograms and m2 is their height in meters squared. In simplified format it would be 63 | BMI = (Weight in Kilograms)/(Height in meters * Height in meters) 64 | 65 | For example if person's weight is 68kg and height is 172cm then 66 | BMI = 68/(1.72*2) = 23 67 | 68 | BMI calculator indicate whether person falls under healthy weight, underweight or overweight. If person's BMI is out of healthy range, their health 69 | risk may significantly increases. 70 | 71 | BMI Range for adults 72 | BMI : weight status 73 | Below 18.5 : Underweight 74 | 18.5 – 24.9 : Normal or Healthy Weight 75 | 25.0 - 29.9 : Overweight 76 | 30.0 & above: Obese 77 | 78 | ## Doctors uses BMI too for 79 | evalution for diet and physical acitivty 80 | cadiovascular disease and other health related problems 81 | measure fat in body 82 | 83 | ## Health risks for extra weight 84 | raises blood pressure and cholesterol and triglyceride levels 85 | it can make diabetes and other health problems 86 | hypertension or high blood pressure 87 | type 2 diabetes 88 | coronary heart disease 89 | gallbladder disease 90 | osteoarthritis 91 | sleep apnea and respiratory problems 92 | 93 | ## Health risks for under weight 94 | malnutrition, anemia or vitamin deficiencies 95 | osteoporosis from too little vitamin D and calcium 96 | Decreased Immune System 97 | fertility issues caused by irregular menstrual cycles 98 | growth and development issues in children and teenagers 99 | 100 | ## Who shouldn't use a BMI calculator 101 | BMI should not be used for muscle builders, athletes, pregnant women, the elderly or young children. 102 | This is because BMI does not take into account whether the weight is carried as muscle or fat it's just the number. Those with a higher muscle mass, such as athletes, may have a high BMI but not be at greater health risk. Those with a lower muscle mass, such as children who have not completed their growth or the elderly who may be losing some muscle mass may have a lower BMI. 103 | 104 | ### Built With 105 | This application built [Flutter](https://flutter.dev/). Flutter is cross-platform open source mobile framework built by Google. Flutter use Dart as a primary language which is highly scalable and easy codebase. 106 | * [Getting started with Flutter](https://flutter.dev/docs). 107 | * [Dart](https://dart.dev/) 108 | 109 | 110 | ## Roadmap 111 | 112 | See the [open issues](https://github.com/Fenil-Nividata/FlutterBMI/issues) for a list of proposed features (and known issues). 113 | 114 | 115 | 116 | ## Contributing 117 | 118 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 119 | 120 | 1. Fork the Project 121 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 122 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 123 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 124 | 5. Open a Pull Request 125 | 126 | 127 | ## License 128 | 129 | Distributed under the MIT License. See [LICENSE](https://github.com/Fenil-Nividata/FlutterBMI/blob/master/LICENSE) for more information. 130 | 131 | 132 | ## Contact 133 | 134 | [Nividata Consultancy](https://www.nividata.com/contact/) 135 | 136 | ## Developers 137 | 138 | ### Fenil Patel 139 | Contact: [LinkedIn](https://www.linkedin.com/in/fenilpatel23/) 140 | [GitHub](https://github.com/Fenscode) 141 | 142 | 143 | ## This app using following awesome open source libraries 144 | 145 | * [Flutter Responsive Screen](https://pub.dev/packages/flutter_responsive_screen) 146 | * [Font Awesome Flutter](https://pub.dev/packages/font_awesome_flutter) 147 | * [Animated Text Kit](https://pub.dev/packages/animated_text_kit) 148 | * [Dynamic Theme](https://pub.dev/packages/dynamic_theme) 149 | * [Shared Preferences](https://pub.dev/packages/shared_preferences) 150 | * [Flutter Launcher Icons](https://pub.dev/packages/flutter_launcher_icons) 151 | 152 | 153 | 154 | 155 | [contributors-shield]: https://img.shields.io/github/contributors/othneildrew/Best-README-Template.svg?style=flat-square 156 | [contributors-url]: https://github.com/Fenil-Nividata/FlutterBMI/graphs/contributors 157 | [forks-shield]: https://img.shields.io/github/forks/othneildrew/Best-README-Template.svg?style=flat-square 158 | [forks-url]: https://github.com/Fenil-Nividata/FlutterBMI/network/members 159 | [stars-shield]: https://img.shields.io/github/stars/othneildrew/Best-README-Template.svg?style=flat-square 160 | [stars-url]: https://github.com/Fenil-Nividata/FlutterBMI/stargazers 161 | [issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=flat-square 162 | [issues-url]: https://github.com/Fenil-Nividata/FlutterBMI/issues 163 | [license-shield]: https://img.shields.io/github/license/othneildrew/Best-README-Template.svg?style=flat-square 164 | [license-url]: https://github.com/Fenil-Nividata/FlutterBMI/blob/master/LICENSE 165 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555 166 | [linkedin-url]: https://in.linkedin.com/company/nividataconsultancy 167 | [product-screenshot]: https://github.com/Fenil-Nividata/FlutterBMI/blob/master/banner_new3.jpg 168 | 169 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def keystoreProperties = new Properties() 10 | def keystorePropertiesFile = rootProject.file('key.properties') 11 | if (keystorePropertiesFile.exists()) { 12 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 13 | } 14 | 15 | 16 | def flutterRoot = localProperties.getProperty('flutter.sdk') 17 | if (flutterRoot == null) { 18 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 19 | } 20 | 21 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 22 | if (flutterVersionCode == null) { 23 | flutterVersionCode = '5' 24 | } 25 | 26 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 27 | if (flutterVersionName == null) { 28 | flutterVersionName = '1.0.2' 29 | } 30 | 31 | apply plugin: 'com.android.application' 32 | apply plugin: 'kotlin-android' 33 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 34 | 35 | android { 36 | compileSdkVersion flutter.compileSdkVersion 37 | ndkVersion flutter.ndkVersion 38 | 39 | compileOptions { 40 | sourceCompatibility JavaVersion.VERSION_1_8 41 | targetCompatibility JavaVersion.VERSION_1_8 42 | } 43 | 44 | kotlinOptions { 45 | jvmTarget = '1.8' 46 | } 47 | 48 | sourceSets { 49 | main.java.srcDirs += 'src/main/kotlin' 50 | } 51 | 52 | lintOptions { 53 | disable 'InvalidPackage' 54 | } 55 | 56 | defaultConfig { 57 | applicationId "com.nividata.bmi_calculator" 58 | minSdkVersion 21 59 | targetSdkVersion flutter.targetSdkVersion 60 | versionCode flutterVersionCode.toInteger() 61 | versionName flutterVersionName 62 | } 63 | 64 | signingConfigs { 65 | release { 66 | keyAlias keystoreProperties['keyAlias'] 67 | keyPassword keystoreProperties['keyPassword'] 68 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 69 | storePassword keystoreProperties['storePassword'] 70 | } 71 | } 72 | buildTypes { 73 | release { 74 | signingConfig signingConfigs.release 75 | } 76 | } 77 | } 78 | 79 | flutter { 80 | source '../..' 81 | } 82 | 83 | dependencies { 84 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 85 | } 86 | 87 | // START: FlutterFire Configuration 88 | apply plugin: 'com.google.gms.google-services' 89 | apply plugin: 'com.google.firebase.crashlytics' 90 | // END: FlutterFire Configuration -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 10 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/nividata/bmi_calculator/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.nividata.bmi_calculator; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | import io.flutter.Log; 6 | 7 | import io.flutter.embedding.engine.FlutterEngine; 8 | 9 | /** 10 | * Generated file. Do not edit. 11 | * This file is generated by the Flutter tool based on the 12 | * plugins that support the Android platform. 13 | */ 14 | @Keep 15 | public final class GeneratedPluginRegistrant { 16 | private static final String TAG = "GeneratedPluginRegistrant"; 17 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 18 | try { 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.analytics.FlutterFirebaseAnalyticsPlugin()); 20 | } catch(Exception e) { 21 | Log.e(TAG, "Error registering plugin firebase_analytics, io.flutter.plugins.firebase.analytics.FlutterFirebaseAnalyticsPlugin", e); 22 | } 23 | try { 24 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 25 | } catch(Exception e) { 26 | Log.e(TAG, "Error registering plugin firebase_core, io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin", e); 27 | } 28 | try { 29 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.crashlytics.FlutterFirebaseCrashlyticsPlugin()); 30 | } catch(Exception e) { 31 | Log.e(TAG, "Error registering plugin firebase_crashlytics, io.flutter.plugins.firebase.crashlytics.FlutterFirebaseCrashlyticsPlugin", e); 32 | } 33 | try { 34 | flutterEngine.getPlugins().add(new com.iyaffle.launchreview.LaunchReviewPlugin()); 35 | } catch(Exception e) { 36 | Log.e(TAG, "Error registering plugin launch_review, com.iyaffle.launchreview.LaunchReviewPlugin", e); 37 | } 38 | try { 39 | flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin()); 40 | } catch(Exception e) { 41 | Log.e(TAG, "Error registering plugin package_info_plus, dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin", e); 42 | } 43 | try { 44 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 45 | } catch(Exception e) { 46 | Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e); 47 | } 48 | try { 49 | flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.share.SharePlusPlugin()); 50 | } catch(Exception e) { 51 | Log.e(TAG, "Error registering plugin share_plus, dev.fluttercommunity.plus.share.SharePlusPlugin", e); 52 | } 53 | try { 54 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 55 | } catch(Exception e) { 56 | Log.e(TAG, "Error registering plugin shared_preferences_android, io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin", e); 57 | } 58 | try { 59 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 60 | } catch(Exception e) { 61 | Log.e(TAG, "Error registering plugin url_launcher_android, io.flutter.plugins.urllauncher.UrlLauncherPlugin", e); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | // START: FlutterFire Configuration 12 | classpath 'com.google.gms:google-services:4.3.8' 13 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' 14 | // END: FlutterFire Configuration 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | } 23 | } 24 | 25 | rootProject.buildDir = '../build' 26 | subprojects { 27 | project.buildDir = "${rootProject.buildDir}/${project.name}" 28 | } 29 | subprojects { 30 | project.evaluationDependsOn(':app') 31 | } 32 | 33 | tasks.register("clean", Delete) { 34 | delete rootProject.buildDir 35 | } 36 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /banner_new3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/banner_new3.jpg -------------------------------------------------------------------------------- /bmi_calculator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ic_launcher.png -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | /firebase_app_id_file.json 34 | /Runner/GoogleService-Info.plist 35 | -------------------------------------------------------------------------------- /ios/BmiCalculator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BmiCalculator.swift 3 | // Runner 4 | // 5 | // Created by Jay Savsani on 18/10/19. 6 | // Copyright © 2019 The Chromium Authors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - launch_review (0.0.1): 4 | - Flutter 5 | - package_info_plus (0.4.5): 6 | - Flutter 7 | - share_plus (0.0.1): 8 | - Flutter 9 | - shared_preferences_ios (0.0.1): 10 | - Flutter 11 | - url_launcher_ios (0.0.1): 12 | - Flutter 13 | 14 | DEPENDENCIES: 15 | - Flutter (from `Flutter`) 16 | - launch_review (from `.symlinks/plugins/launch_review/ios`) 17 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) 18 | - share_plus (from `.symlinks/plugins/share_plus/ios`) 19 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) 20 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) 21 | 22 | EXTERNAL SOURCES: 23 | Flutter: 24 | :path: Flutter 25 | launch_review: 26 | :path: ".symlinks/plugins/launch_review/ios" 27 | package_info_plus: 28 | :path: ".symlinks/plugins/package_info_plus/ios" 29 | share_plus: 30 | :path: ".symlinks/plugins/share_plus/ios" 31 | shared_preferences_ios: 32 | :path: ".symlinks/plugins/shared_preferences_ios/ios" 33 | url_launcher_ios: 34 | :path: ".symlinks/plugins/url_launcher_ios/ios" 35 | 36 | SPEC CHECKSUMS: 37 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 38 | launch_review: 75d5a956ba8eaa493e9c9d4bf4c05e505e8d5ed0 39 | package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e 40 | share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 41 | shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad 42 | url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de 43 | 44 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 45 | 46 | COCOAPODS: 1.11.2 47 | -------------------------------------------------------------------------------- /ios/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcuserdata/nividata1.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner.xcodeproj/project.xcworkspace/xcuserdata/nividata1.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcuserdata/nividata1.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseTargetSettings 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcuserdata/nividata1.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner.xcworkspace/xcuserdata/nividata1.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x-1.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x-1.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x-1.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "ItunesArtwork@2x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaysavsani07/FlutterBMI/e4e60a9b74e07cba6545eeb0be6267ec0e957c74/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | BMI 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ios/ServiceDefinitions.json: -------------------------------------------------------------------------------- 1 | {"services":[]} -------------------------------------------------------------------------------- /lib/core/app_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppTheme { 4 | static lightTheme() { 5 | final ThemeData base = ThemeData.light(); 6 | 7 | TextTheme _buildTextThemeLight(TextTheme textTheme) { 8 | return textTheme.copyWith( 9 | titleSmall: textTheme.titleSmall!.copyWith( 10 | color: Color.fromRGBO(86, 81, 104, 1), fontWeight: FontWeight.w900), 11 | bodySmall: textTheme.bodySmall!.copyWith(color: Color(0xFF8D8E98)), 12 | ); 13 | } 14 | 15 | return base.copyWith( 16 | primaryColor: Colors.white, 17 | primaryColorDark: Colors.white, 18 | brightness: Brightness.light, 19 | iconTheme: IconThemeData(color: Colors.deepPurple), 20 | textTheme: _buildTextThemeLight(base.textTheme), 21 | cardTheme: CardTheme( 22 | color: Colors.white, 23 | elevation: 2.0, 24 | ), 25 | appBarTheme: AppBarTheme( 26 | backgroundColor: Colors.white, 27 | iconTheme: IconThemeData( 28 | color: Colors.black38, 29 | ), 30 | actionsIconTheme: IconThemeData( 31 | color: Colors.black38, 32 | ), 33 | centerTitle: true, 34 | elevation: 1, 35 | titleTextStyle: TextStyle( 36 | color: Color.fromRGBO(86, 81, 104, 1), 37 | fontSize: 16.0, 38 | fontWeight: FontWeight.w900, 39 | ), 40 | ), 41 | dialogTheme: base.dialogTheme.copyWith( 42 | backgroundColor: Colors.white, 43 | ), 44 | chipTheme: ChipThemeData( 45 | selectedColor: Colors.deepPurple, 46 | backgroundColor: Color.fromRGBO(237, 231, 246, 1), 47 | secondaryLabelStyle: 48 | TextStyle(fontWeight: FontWeight.w900, color: Colors.white), 49 | brightness: Brightness.light, 50 | disabledColor: Colors.grey, 51 | labelPadding: EdgeInsets.symmetric(horizontal: 5), 52 | labelStyle: 53 | TextStyle(fontWeight: FontWeight.w600, color: Colors.black54), 54 | padding: EdgeInsets.all(10.0), 55 | secondarySelectedColor: Colors.deepPurple, 56 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), 57 | ), 58 | bottomSheetTheme: 59 | BottomSheetThemeData(backgroundColor: Colors.black.withOpacity(0)), 60 | ); 61 | } 62 | 63 | static ThemeData darkTheme() { 64 | final ThemeData base = ThemeData.dark(); 65 | 66 | TextTheme _buildTextThemeLight(TextTheme textTheme) { 67 | return textTheme.copyWith( 68 | titleSmall: textTheme.titleSmall! 69 | .copyWith(color: Colors.white60, fontWeight: FontWeight.w900), 70 | bodySmall: textTheme.bodySmall!.copyWith(color: Color(0xFF8D8E98)), 71 | ); 72 | } 73 | 74 | return base.copyWith( 75 | primaryColor: Colors.black, 76 | primaryColorDark: Colors.black87, 77 | brightness: Brightness.dark, 78 | cardColor: Colors.grey.shade800, 79 | iconTheme: IconThemeData(color: Colors.grey.shade300), 80 | cardTheme: CardTheme( 81 | color: Colors.grey.shade800, 82 | elevation: 2.0, 83 | ), 84 | textTheme: _buildTextThemeLight(base.textTheme), 85 | appBarTheme: AppBarTheme( 86 | backgroundColor: Colors.black87, 87 | iconTheme: IconThemeData( 88 | color: Colors.grey.shade300, 89 | ), 90 | actionsIconTheme: IconThemeData( 91 | color: Colors.grey.shade300, 92 | ), 93 | centerTitle: true, 94 | elevation: 1, 95 | titleTextStyle: TextStyle( 96 | color: Colors.white60, 97 | fontSize: 16.0, 98 | fontWeight: FontWeight.w900, 99 | ), 100 | ), 101 | dialogTheme: base.dialogTheme.copyWith( 102 | backgroundColor: Colors.black87, 103 | ), 104 | chipTheme: ChipThemeData( 105 | selectedColor: Colors.deepPurple, 106 | backgroundColor: Color.fromRGBO(237, 231, 246, 1), 107 | secondaryLabelStyle: 108 | TextStyle(fontWeight: FontWeight.w900, color: Colors.white), 109 | brightness: Brightness.dark, 110 | disabledColor: Colors.grey, 111 | labelPadding: EdgeInsets.symmetric(horizontal: 5), 112 | labelStyle: 113 | TextStyle(fontWeight: FontWeight.w600, color: Colors.black54), 114 | padding: EdgeInsets.all(10.0), 115 | secondarySelectedColor: Colors.deepPurple, 116 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), 117 | ), 118 | bottomSheetTheme: 119 | BottomSheetThemeData(backgroundColor: Colors.black.withOpacity(0)), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/core/color_scheme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension CustomColorScheme on ColorScheme { 4 | Color get buttonColor => 5 | brightness == Brightness.light ? Colors.grey.shade200 : Colors.grey.shade700; 6 | 7 | Color get accentColor => 8 | brightness == Brightness.light ? Color.fromRGBO(86, 81, 104, 1) : Colors.white60; 9 | } 10 | -------------------------------------------------------------------------------- /lib/core/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 3 | 4 | const double kBottomContainerHeight = 80.0; 5 | const Color kBottomContainerColor = Color(0xFFEB1555); 6 | const Color kActiveCardColor = Color(0xFF1D1E33); 7 | const Color kInactiveCardColor = Color(0xFF111328); 8 | String platformVersion = ""; 9 | 10 | enum Gender { 11 | Male, 12 | Female, 13 | } 14 | 15 | String aboutBMI = 16 | "Body Mass Index(BMI) is value derived from person's weight and height." + 17 | "The result of BMI measurement can give an idea about weather a person has correct weight for their height."; 18 | 19 | Icon themeIcon = Icon( 20 | FontAwesomeIcons.solidMoon, 21 | color: Colors.transparent, 22 | ); 23 | 24 | const kLabelTextStyle = TextStyle( 25 | fontSize: 20.0, 26 | color: Color(0xFF8D8E98), 27 | ); 28 | 29 | const listHeading = 30 | TextStyle(fontSize: 18.0, color: Colors.grey, fontWeight: FontWeight.w900); 31 | 32 | const listTitle = TextStyle( 33 | fontSize: 16.0, color: Colors.grey, fontWeight: FontWeight.normal); 34 | 35 | const listTrailing = 36 | TextStyle(fontSize: 14.0, color: Colors.grey, fontWeight: FontWeight.w900); 37 | 38 | const kNumberTextStyle = TextStyle( 39 | color: Color.fromRGBO(86, 81, 104, 1), 40 | fontSize: 50.0, 41 | fontWeight: FontWeight.w900); 42 | 43 | const kTitleTextStyle = TextStyle( 44 | fontSize: 50.0, 45 | fontWeight: FontWeight.bold, 46 | ); 47 | 48 | const kResultTextStyle = TextStyle( 49 | color: Color(0xFF24D876), 50 | fontSize: 22.0, 51 | fontWeight: FontWeight.bold, 52 | ); 53 | 54 | const kBMITextStyle = TextStyle( 55 | fontSize: 100.0, 56 | fontWeight: FontWeight.bold, 57 | ); 58 | 59 | const kResultBodyTextStyle = TextStyle( 60 | fontSize: 22.0, 61 | ); 62 | -------------------------------------------------------------------------------- /lib/data/model/bmi.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/constants.dart'; 2 | 3 | class Bmi { 4 | final int age; 5 | final Weight weight; 6 | final Height height; 7 | final Gender gender; 8 | 9 | Bmi({ 10 | required this.age, 11 | required this.weight, 12 | required this.height, 13 | required this.gender, 14 | }); 15 | 16 | factory Bmi.initial() { 17 | return Bmi( 18 | age: 18, 19 | weight: Weight.initial(), 20 | height: Height.initial(), 21 | gender: Gender.Female, 22 | ); 23 | } 24 | 25 | Bmi copyWith({ 26 | int? age, 27 | Weight? weight, 28 | Height? height, 29 | Gender? gender, 30 | }) { 31 | return Bmi( 32 | age: age ?? this.age, 33 | weight: weight ?? this.weight, 34 | height: height ?? this.height, 35 | gender: gender ?? this.gender, 36 | ); 37 | } 38 | } 39 | 40 | class Weight { 41 | final bool isKg; 42 | final int weight; 43 | 44 | Weight({ 45 | required this.isKg, 46 | required this.weight, 47 | }); 48 | 49 | factory Weight.initial() { 50 | return Weight( 51 | isKg: true, 52 | weight: 50, 53 | ); 54 | } 55 | 56 | Weight copyWith({ 57 | bool? isKg, 58 | int? weight, 59 | }) { 60 | return Weight( 61 | isKg: isKg ?? this.isKg, 62 | weight: weight ?? this.weight, 63 | ); 64 | } 65 | 66 | Map toJson() { 67 | return { 68 | "is_kg": isKg, 69 | "weight": weight, 70 | }; 71 | } 72 | 73 | factory Weight.fromJson({required Map json}) { 74 | return Weight( 75 | isKg: json["is_kg"], 76 | weight: json["weight"], 77 | ); 78 | } 79 | } 80 | 81 | class Height { 82 | final bool isCm; 83 | final int height; 84 | final int feet; 85 | final int inch; 86 | 87 | Height({ 88 | required this.isCm, 89 | required this.height, 90 | required this.feet, 91 | required this.inch, 92 | }); 93 | 94 | factory Height.initial() { 95 | return Height( 96 | isCm: true, 97 | height: 170, 98 | feet: 4, 99 | inch: 8, 100 | ); 101 | } 102 | 103 | Height copyWith({ 104 | bool? isCm, 105 | int? height, 106 | int? feet, 107 | int? inch, 108 | }) { 109 | return Height( 110 | isCm: isCm ?? this.isCm, 111 | height: height ?? this.height, 112 | feet: feet ?? this.feet, 113 | inch: inch ?? this.inch, 114 | ); 115 | } 116 | 117 | Map toJson() { 118 | return { 119 | "is_cm": isCm, 120 | "height": height, 121 | "feet": feet, 122 | "inch": inch, 123 | }; 124 | } 125 | 126 | factory Height.fromJson({required Map json}) { 127 | return Height( 128 | isCm: json["is_cm"], 129 | height: json["height"], 130 | feet: json["feet"], 131 | inch: json["inch"], 132 | ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/data/repository/local_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/data/sharedpref/preferences.dart'; 2 | import 'package:bmi_calculator/data/sharedpref/shared_preference_helper.dart'; 3 | 4 | class LocalRepository { 5 | final SharedPreferencesHelper preferencesHelper; 6 | 7 | LocalRepository({ 8 | required this.preferencesHelper, 9 | }); 10 | 11 | Future saveIsFirstTime() { 12 | return preferencesHelper.putBool(Preferences.isFirstTime, false); 13 | } 14 | 15 | Future rated() { 16 | return preferencesHelper.putBool(Preferences.rated, true); 17 | } 18 | 19 | Future remindMeLater() { 20 | return preferencesHelper.putBool(Preferences.remindMeLater, true); 21 | } 22 | 23 | bool isRemindOrRated() { 24 | return preferencesHelper.getBool(Preferences.remindMeLater, 25 | defValue: false) || 26 | preferencesHelper.getBool(Preferences.rated, defValue: false); 27 | } 28 | 29 | bool getFirstTime() { 30 | return preferencesHelper.getBool(Preferences.isFirstTime, defValue: true); 31 | } 32 | 33 | int getAppOpenCount() { 34 | return preferencesHelper.getInt(Preferences.appOpenCount, defValue: 0); 35 | } 36 | 37 | Future increaseAppOpenCount() { 38 | return preferencesHelper.putInt( 39 | Preferences.appOpenCount, getAppOpenCount() + 1); 40 | } 41 | 42 | Future setFirstTimeDate() { 43 | return preferencesHelper.putInt( 44 | Preferences.firstTimeDate, DateTime.now().millisecondsSinceEpoch); 45 | } 46 | 47 | DateTime getFirstTimeDate() { 48 | int dateTime = preferencesHelper.getInt(Preferences.firstTimeDate); 49 | if (dateTime == -1) { 50 | return DateTime.now(); 51 | } else { 52 | return DateTime.fromMillisecondsSinceEpoch(dateTime); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/data/sharedpref/preferences.dart: -------------------------------------------------------------------------------- 1 | class Preferences { 2 | static const String theme = "theme"; 3 | static const String isKg = "is_kg"; 4 | static const String isCm = "is_cm"; 5 | static const String isFirstTime = "isFirstTime"; 6 | static const String firstTimeDate = "firstTimeDate"; 7 | static const String appOpenCount = "appOpenCount"; 8 | static const String remindMeLater = "remindMeLater"; 9 | static const String rated = "rated"; 10 | } 11 | -------------------------------------------------------------------------------- /lib/data/sharedpref/shared_preference_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | class SharedPreferencesHelper { 5 | final SharedPreferences? _prefs; 6 | 7 | SharedPreferencesHelper(this._prefs); 8 | 9 | // get bool 10 | bool getBool(String key, {bool defValue = false}) { 11 | if (_prefs == null) return defValue; 12 | return _prefs!.getBool(key) ?? defValue; 13 | } 14 | 15 | // put bool 16 | Future putBool(String key, bool value) { 17 | if (_prefs == null) return Future.value(false); 18 | return _prefs!.setBool(key, value); 19 | } 20 | 21 | // get int 22 | int getInt(String key, {int defValue = -1}) { 23 | if (_prefs == null) return defValue; 24 | return _prefs!.getInt(key) ?? defValue; 25 | } 26 | 27 | // put int. 28 | Future putInt(String key, int value) { 29 | if (_prefs == null) return Future.value(false); 30 | return _prefs!.setInt(key, value); 31 | } 32 | 33 | // get double 34 | double getDouble(String key, {double defValue = -1}) { 35 | if (_prefs == null) return defValue; 36 | return _prefs!.getDouble(key) ?? defValue; 37 | } 38 | 39 | // put double 40 | Future putDouble(String key, double value) { 41 | if (_prefs == null) return Future.value(false); 42 | return _prefs!.setDouble(key, value); 43 | } 44 | 45 | // get string 46 | String getString(String key, {String defValue = ''}) { 47 | if (_prefs == null) return defValue; 48 | return _prefs!.getString(key) ?? defValue; 49 | } 50 | 51 | // put string 52 | Future putString(String key, String value) { 53 | if (_prefs == null) return Future.value(false); 54 | return _prefs!.setString(key, value); 55 | } 56 | 57 | // get string list 58 | List getStringList(String key, {List defValue = const []}) { 59 | if (_prefs == null) return defValue; 60 | return _prefs!.getStringList(key) ?? defValue; 61 | } 62 | 63 | // put string list 64 | Future putStringList(String key, List value) { 65 | if (_prefs == null) return Future.value(false); 66 | return _prefs!.setStringList(key, value); 67 | } 68 | 69 | // get object 70 | Map? getObj(String key) { 71 | if (_prefs == null) return null; 72 | String? data = _prefs!.getString(key); 73 | return (data == null || data.isEmpty) 74 | ? null 75 | : json.decode(data) as Map; 76 | } 77 | 78 | // put object 79 | Future putObj(String key, Map? value) { 80 | if (_prefs == null) return Future.value(false); 81 | return _prefs!.setString(key, value == null ? "" : json.encode(value)); 82 | } 83 | 84 | // get object with mapper fun 85 | T? getObject(String key, T Function(Map v) f, 86 | {T? defValue}) { 87 | Map? map = getObj(key); 88 | return map == null ? defValue : f(map); 89 | } 90 | 91 | // put object with mapper fun 92 | Future putObject( 93 | String key, Map Function() toJson) { 94 | if (_prefs == null) return Future.value(false); 95 | return _prefs!.setString(key, json.encode(toJson())); 96 | } 97 | 98 | // get object list 99 | List? getObjList(String key) { 100 | if (_prefs == null) return null; 101 | List? dataLis = _prefs!.getStringList(key); 102 | return dataLis?.map((value) { 103 | Map dataMap = json.decode(value); 104 | return dataMap; 105 | }).toList(); 106 | } 107 | 108 | // get obj list with mapper fun 109 | List getObjectList(String key, T Function(Map v) f, 110 | {List defValue = const []}) { 111 | List? dataList = getObjList(key); 112 | List? list = dataList?.map((value) { 113 | return f(value); 114 | }).toList(); 115 | return list ?? defValue; 116 | } 117 | 118 | // put object list 119 | Future putObjectList( 120 | String key, List list, Map Function(T) toJson) { 121 | if (_prefs == null) return Future.value(false); 122 | 123 | List? dataList = list.map((value) { 124 | return json.encode(toJson(value)); 125 | }).toList(); 126 | return _prefs!.setStringList(key, dataList); 127 | } 128 | 129 | // get dynamic 130 | dynamic getDynamic(String key, {dynamic defValue}) { 131 | if (_prefs == null) return defValue; 132 | return _prefs!.get(key) ?? defValue; 133 | } 134 | 135 | // have key 136 | bool haveKey(String key) { 137 | if (_prefs == null) return false; 138 | return _prefs!.getKeys().contains(key); 139 | } 140 | 141 | // get keys 142 | Set? getKeys() { 143 | if (_prefs == null) return null; 144 | return _prefs!.getKeys(); 145 | } 146 | 147 | // remove 148 | Future remove(String key) { 149 | if (_prefs == null) return Future.value(false); 150 | return _prefs!.remove(key); 151 | } 152 | 153 | // clear 154 | Future clear() { 155 | if (_prefs == null) return Future.value(false); 156 | return _prefs!.clear(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /lib/extension/datetime_extension.dart: -------------------------------------------------------------------------------- 1 | extension DateExtension on DateTime { 2 | DateTime getDateOnly() { 3 | return DateTime(year, month, day); 4 | } 5 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/data/sharedpref/shared_preference_helper.dart'; 2 | import 'package:bmi_calculator/firebase_options.dart'; 3 | import 'package:bmi_calculator/view/app/app_view.dart'; 4 | import 'package:bmi_calculator/view/app/theme_provider.dart'; 5 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 6 | import 'package:firebase_core/firebase_core.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/services.dart'; 9 | import 'package:package_info_plus/package_info_plus.dart'; 10 | import 'package:provider/provider.dart'; 11 | import 'package:shared_preferences/shared_preferences.dart'; 12 | 13 | import 'core/constants.dart'; 14 | import 'data/repository/local_repository.dart'; 15 | 16 | Future main() async { 17 | WidgetsFlutterBinding.ensureInitialized(); 18 | await Firebase.initializeApp( 19 | options: DefaultFirebaseOptions.currentPlatform, 20 | ); 21 | 22 | getAppVersionName(); 23 | 24 | SystemChrome.setPreferredOrientations([ 25 | DeviceOrientation.portraitUp, 26 | DeviceOrientation.portraitDown, 27 | ]); 28 | 29 | SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); 30 | 31 | runApp( 32 | Provider( 33 | create: (context) => SharedPreferencesHelper(sharedPreferences), 34 | child: Provider( 35 | create: (context) => LocalRepository( 36 | preferencesHelper: context.read()), 37 | child: ChangeNotifierProvider( 38 | create: (context) => BmiProvider( 39 | sharedPreferencesHelper: context.read()), 40 | child: ChangeNotifierProvider( 41 | create: (context) => ThemeProvider( 42 | sharedPreferencesHelper: 43 | context.read()), 44 | child: MyApp(), 45 | ), 46 | ), 47 | ), 48 | ), 49 | ); 50 | } 51 | 52 | void getAppVersionName() async { 53 | try { 54 | PackageInfo info = await PackageInfo.fromPlatform(); 55 | platformVersion = info.version; 56 | } on PlatformException { 57 | platformVersion = 'Failed to get platform version.'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/utility/app_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:share_plus/share_plus.dart'; 3 | 4 | class AppUtil { 5 | static void onShareTap() { 6 | String share = ""; 7 | 8 | if (Platform.isAndroid) { 9 | share = 10 | "https://play.google.com/store/apps/details?id=com.nividata.bmi_calculator"; 11 | } else if (Platform.isIOS) { 12 | share = 13 | "https://apps.apple.com/us/app/bmi-calculate-body-mass-index/id1488893444?ls=1"; 14 | } 15 | if (share.isNotEmpty) Share.share(share); 16 | } 17 | 18 | static List measurementUnitList = [ 19 | "Centimetre", 20 | "Feet-Inch", 21 | ]; 22 | 23 | static List weightUnitList = [ 24 | "Kilogram", 25 | "Pound", 26 | ]; 27 | } 28 | -------------------------------------------------------------------------------- /lib/utility/bmi_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class BmiUtil { 6 | final int height; 7 | final double weight; 8 | final bool isKg; 9 | 10 | late double _bmi; 11 | 12 | BmiUtil({ 13 | required this.height, 14 | required this.weight, 15 | required this.isKg, 16 | }); 17 | 18 | static int feetInchToCM(int feet, int inch) { 19 | int totalInch = ((feet * 12) + inch); 20 | return (totalInch * 2.54).round(); 21 | } 22 | 23 | String calculateBMI() { 24 | if (isKg) { 25 | _bmi = weight / pow(height / 100, 2); 26 | } else { 27 | _bmi = (weight * 0.45359237) / pow(height / 100, 2); 28 | } 29 | return _bmi.toStringAsFixed(1); 30 | } 31 | 32 | String getResult() { 33 | if (_bmi <= 16) { 34 | return 'VERY SEVERELY UNDERWEIGHT'; 35 | } else if (_bmi > 16.0 && _bmi <= 16.9) { 36 | return 'SEVERELY UNDERWEIGHT'; 37 | } else if (_bmi > 17.0 && _bmi <= 18.4) { 38 | return 'UNDERWEIGHT'; 39 | } else if (_bmi > 18.5 && _bmi <= 24.9) { 40 | return 'NORMAL'; 41 | } else if (_bmi > 25.0 && _bmi <= 29.9) { 42 | return 'OVERWEIGHT'; 43 | } else if (_bmi > 30.0 && _bmi <= 34.9) { 44 | return 'OBESE Class 1 \n(Moderately obese)'; 45 | } else if (_bmi > 35.0 && _bmi <= 39.9) { 46 | return 'OBESE Class 2 \n(Severely obese)'; 47 | } else if (_bmi >= 40.0) { 48 | return 'OBESE Class 3 \n(Very Severely obese)'; 49 | } else { 50 | return 'NORMAL'; 51 | } 52 | } 53 | 54 | TextStyle resultTextStyle(String result) { 55 | switch (result) { 56 | case "VERY SEVERELY UNDERWEIGHT": 57 | return TextStyle( 58 | color: Color.fromRGBO(241, 198, 231, 1), 59 | fontWeight: FontWeight.w700, 60 | letterSpacing: 2.0, 61 | fontSize: 22.0, 62 | ); 63 | case "SEVERELY UNDERWEIGHT": 64 | return TextStyle( 65 | color: Color.fromRGBO(229, 176, 234, 1), 66 | fontWeight: FontWeight.w700, 67 | letterSpacing: 2.0, 68 | fontSize: 22.0, 69 | ); 70 | case "UNDERWEIGHT": 71 | return TextStyle( 72 | color: Color.fromRGBO(189, 131, 206, 1), 73 | fontWeight: FontWeight.w700, 74 | letterSpacing: 2.0, 75 | fontSize: 22.0, 76 | ); 77 | case "NORMAL": 78 | return TextStyle( 79 | color: Color.fromRGBO(82, 222, 151, 1), 80 | fontWeight: FontWeight.w700, 81 | letterSpacing: 2.0, 82 | fontSize: 22.0, 83 | ); 84 | case "OVERWEIGHT": 85 | return TextStyle( 86 | color: Color.fromRGBO(241, 188, 49, 1), 87 | fontWeight: FontWeight.w700, 88 | letterSpacing: 2.0, 89 | fontSize: 22.0, 90 | ); 91 | case "OBESE Class 1 \n(Moderately obese)": 92 | return TextStyle( 93 | color: Color.fromRGBO(226, 88, 34, 1), 94 | fontWeight: FontWeight.w700, 95 | letterSpacing: 2.0, 96 | fontSize: 22.0, 97 | ); 98 | case "OBESE Class 2 \n(Severely obese)": 99 | return TextStyle( 100 | color: Color.fromRGBO(178, 34, 34, 1), 101 | fontWeight: FontWeight.w700, 102 | letterSpacing: 2.0, 103 | fontSize: 22.0, 104 | ); 105 | case "OBESE Class 3 \n(Very Severely obese)": 106 | return TextStyle( 107 | color: Color.fromRGBO(124, 10, 2, 1), 108 | fontWeight: FontWeight.w700, 109 | letterSpacing: 2.0, 110 | fontSize: 22.0, 111 | ); 112 | default: 113 | return TextStyle( 114 | color: Color.fromRGBO(0, 251, 182, 1), 115 | fontWeight: FontWeight.w700, 116 | letterSpacing: 2.0, 117 | fontSize: 22.0, 118 | ); 119 | } 120 | } 121 | 122 | String getInterpretation() { 123 | if (_bmi >= 25) { 124 | return 'You have a higher than normal body weight. Try to exercise more!'; 125 | } else if (_bmi > 18.5) { 126 | return 'You have a normal body weight. Good job!'; 127 | } else { 128 | return 'You have a lower than normal body weight. You should eat more!'; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/view/about/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:launch_review/launch_review.dart'; 4 | 5 | class AboutUsView extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | appBar: AppBar( 10 | elevation: 0.0, 11 | centerTitle: false, 12 | title: Text( 13 | 'About App', 14 | textScaleFactor: 1.2, 15 | textDirection: TextDirection.ltr, 16 | textAlign: TextAlign.start, 17 | style: Theme.of(context).appBarTheme.titleTextStyle!.copyWith( 18 | fontSize: 20, 19 | fontWeight: FontWeight.bold, 20 | ), 21 | ), 22 | ), 23 | body: Container( 24 | color: Theme.of(context).primaryColor, 25 | width: MediaQuery.of(context).size.width, 26 | height: MediaQuery.of(context).size.height, 27 | child: Stack( 28 | fit: StackFit.loose, 29 | children: [ 30 | Container( 31 | // height: 190.0, 32 | child: Image( 33 | image: AssetImage("Assets/Images/banner.jpg"), 34 | // height: 200.0, 35 | ), 36 | ), 37 | Container( 38 | margin: EdgeInsets.fromLTRB(20.0, 210.0, 0.0, 0.0), 39 | child: Column( 40 | children: [ 41 | Row( 42 | crossAxisAlignment: CrossAxisAlignment.center, 43 | mainAxisAlignment: MainAxisAlignment.center, 44 | children: [ 45 | Image( 46 | image: AssetImage("Assets/Images/ic_launcher.png"), 47 | height: 30.0, 48 | width: 30.0, 49 | ), 50 | Text( 51 | " BMI.", 52 | style: TextStyle( 53 | fontWeight: FontWeight.bold, 54 | fontSize: 18.0, 55 | ), 56 | ), 57 | Text( 58 | " Calculator".toUpperCase(), 59 | style: TextStyle( 60 | fontWeight: FontWeight.normal, 61 | fontSize: 16.0, 62 | color: Colors.grey.shade600), 63 | ), 64 | ], 65 | ), 66 | Divider( 67 | color: Colors.grey, 68 | indent: 5.0, 69 | endIndent: 20.0, 70 | ), 71 | Padding( 72 | padding: EdgeInsets.fromLTRB(5.0, 10.0, 20.0, 0.0), 73 | child: Text( 74 | aboutBMI, 75 | style: TextStyle(fontSize: 14.0), 76 | textAlign: TextAlign.justify, 77 | ), 78 | ), 79 | Padding( 80 | padding: const EdgeInsets.all(8.0), 81 | child: MaterialButton( 82 | shape: RoundedRectangleBorder( 83 | borderRadius: new BorderRadius.circular(30.0)), 84 | color: Colors.deepPurple, 85 | child: Text("Read More..."), 86 | textColor: Theme.of(context).primaryColor, 87 | onPressed: () { 88 | LaunchReview.launch( 89 | androidAppId: "com.nividata.bmi_calculator", 90 | iOSAppId: "id1488893444"); 91 | }, 92 | ), 93 | ), 94 | Padding( 95 | padding: const EdgeInsets.all(8.0), 96 | child: Row( 97 | crossAxisAlignment: CrossAxisAlignment.center, 98 | mainAxisAlignment: MainAxisAlignment.center, 99 | children: [ 100 | Text( 101 | "Send us your feedback on ", 102 | style: TextStyle( 103 | fontSize: 11.0, fontWeight: FontWeight.w500), 104 | ), 105 | SelectableText( 106 | "info@nividata.com", 107 | style: TextStyle( 108 | color: Colors.blue, 109 | fontSize: 11.0, 110 | fontWeight: FontWeight.w500), 111 | ), 112 | ], 113 | ), 114 | ), 115 | Padding( 116 | padding: const EdgeInsets.fromLTRB(0.0, 80.0, 0.0, 5.0), 117 | child: Text( 118 | "V" + platformVersion, 119 | style: TextStyle(fontWeight: FontWeight.w900), 120 | ), 121 | ), 122 | Text( 123 | "App is up to date", 124 | style: TextStyle( 125 | fontWeight: FontWeight.normal, 126 | fontSize: 11.0, 127 | color: Colors.grey), 128 | ), 129 | ], 130 | ), 131 | ) 132 | ], 133 | ), 134 | ), 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/view/app/app_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/view/app/theme_provider.dart'; 2 | import 'package:bmi_calculator/view/splash/splash.dart'; 3 | import 'package:bmi_calculator/core/app_theme.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class MyApp extends StatelessWidget { 8 | const MyApp({ 9 | Key? key, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Consumer( 15 | builder: (context, data, child) { 16 | return MaterialApp( 17 | title: 'BMI', 18 | debugShowCheckedModeBanner: false, 19 | themeMode: data.themeMode, 20 | theme: AppTheme.lightTheme(), 21 | darkTheme: AppTheme.darkTheme(), 22 | home: Splash(), 23 | ); 24 | }, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/view/app/theme_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/data/sharedpref/preferences.dart'; 2 | import 'package:bmi_calculator/data/sharedpref/shared_preference_helper.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class InheritedThemeWrapper extends StatefulWidget { 6 | final SharedPreferencesHelper sharedPreferencesHelper; 7 | final Widget child; 8 | 9 | InheritedThemeWrapper({ 10 | Key? key, 11 | required this.child, 12 | required this.sharedPreferencesHelper, 13 | }) : super(key: key); 14 | 15 | static InheritedThemeWrapperState of(BuildContext context) { 16 | return (context.dependOnInheritedWidgetOfExactType())!.data; 17 | } 18 | 19 | @override 20 | InheritedThemeWrapperState createState() => InheritedThemeWrapperState(); 21 | } 22 | 23 | class InheritedThemeWrapperState extends State { 24 | ThemeMode themeMode = ThemeMode.light; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | themeMode = ThemeMode.values[ 30 | widget.sharedPreferencesHelper.getInt(Preferences.theme, defValue: 1)]; 31 | setState(() {}); 32 | } 33 | 34 | Future changeTheme(ThemeMode themeMode) async { 35 | setState(() { 36 | this.themeMode = themeMode; 37 | }); 38 | await widget.sharedPreferencesHelper 39 | .putInt(Preferences.theme, themeMode.index); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return InheritedTheme( 45 | child: this.widget.child, 46 | data: this, 47 | themeMode: themeMode, 48 | ); 49 | } 50 | } 51 | 52 | class InheritedTheme extends InheritedWidget { 53 | final Widget child; 54 | final ThemeMode themeMode; 55 | final InheritedThemeWrapperState data; 56 | 57 | InheritedTheme({ 58 | Key? key, 59 | required this.child, 60 | required this.data, 61 | required this.themeMode, 62 | }) : super(key: key, child: child); 63 | 64 | @override 65 | bool updateShouldNotify(InheritedTheme oldWidget) { 66 | return themeMode != oldWidget.themeMode; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/view/app/theme_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/data/sharedpref/preferences.dart'; 2 | import 'package:bmi_calculator/data/sharedpref/shared_preference_helper.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class ThemeProvider with ChangeNotifier { 6 | final SharedPreferencesHelper sharedPreferencesHelper; 7 | ThemeMode themeMode = ThemeMode.light; 8 | 9 | ThemeProvider({ 10 | required this.sharedPreferencesHelper, 11 | }) { 12 | themeMode = ThemeMode 13 | .values[sharedPreferencesHelper.getInt(Preferences.theme, defValue: 1)]; 14 | notifyListeners(); 15 | } 16 | 17 | Future changeTheme(ThemeMode themeMode) async { 18 | this.themeMode = themeMode; 19 | notifyListeners(); 20 | await sharedPreferencesHelper.putInt(Preferences.theme, themeMode.index); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/view/common/animate_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class AnimatedLoader extends AnimatedWidget { 4 | static final _opacityTween = new Tween(begin: 0.0, end: 1.0); 5 | 6 | AnimatedLoader({ 7 | Key? key, 8 | this.alignment = FractionalOffset.center, 9 | required this.animation, 10 | required this.child, 11 | }) : super(key: key, listenable: animation); 12 | 13 | final FractionalOffset alignment; 14 | final Animation animation; 15 | final Widget child; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Opacity( 20 | opacity: _opacityTween.evaluate(animation), 21 | child: child, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/view/common/common_alert_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CommonAlertDialog extends AlertDialog { 4 | final Widget child; 5 | 6 | const CommonAlertDialog({required this.child, Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return AlertDialog( 11 | shape: const RoundedRectangleBorder( 12 | borderRadius: BorderRadius.all(Radius.circular(12))), 13 | contentPadding: const EdgeInsets.all(0.1), 14 | content: ClipRRect( 15 | borderRadius: const BorderRadius.all(Radius.circular(12)), 16 | child: child, 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/view/common/rate_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:launch_review/launch_review.dart'; 3 | 4 | class RateDialog extends StatelessWidget { 5 | const RateDialog({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Padding( 10 | padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), 11 | child: Column( 12 | mainAxisSize: MainAxisSize.min, 13 | crossAxisAlignment: CrossAxisAlignment.center, 14 | children: [ 15 | Image( 16 | image: AssetImage("Assets/Images/star.png"), 17 | filterQuality: FilterQuality.high, 18 | width: 100.0, 19 | height: 100.0, 20 | ), 21 | const SizedBox(height: 18), 22 | Text( 23 | "Rate BMI App", 24 | textAlign: TextAlign.center, 25 | style: 26 | Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 20), 27 | ), 28 | const SizedBox(height: 8), 29 | Text( 30 | "Tap a star to give your rating.", 31 | textAlign: TextAlign.justify, 32 | style: Theme.of(context).textTheme.titleSmall!.copyWith( 33 | fontWeight: FontWeight.normal, 34 | ), 35 | ), 36 | const SizedBox(height: 18), 37 | Row( 38 | mainAxisAlignment: MainAxisAlignment.center, 39 | children: [ 40 | Image( 41 | image: AssetImage("Assets/Images/star_fill.png"), 42 | filterQuality: FilterQuality.high, 43 | width: 26, 44 | height: 26, 45 | ), 46 | SizedBox(width: 8), 47 | Image( 48 | image: AssetImage("Assets/Images/star_fill.png"), 49 | filterQuality: FilterQuality.high, 50 | width: 26, 51 | height: 26, 52 | ), 53 | SizedBox(width: 8), 54 | Image( 55 | image: AssetImage("Assets/Images/star_fill.png"), 56 | filterQuality: FilterQuality.high, 57 | width: 26, 58 | height: 26, 59 | ), 60 | SizedBox(width: 8), 61 | Image( 62 | image: AssetImage("Assets/Images/star_fill.png"), 63 | filterQuality: FilterQuality.high, 64 | width: 26, 65 | height: 26, 66 | ), 67 | SizedBox(width: 8), 68 | Image( 69 | image: AssetImage("Assets/Images/star_unfill.png"), 70 | filterQuality: FilterQuality.high, 71 | color: Theme.of(context).textTheme.titleSmall!.color, 72 | width: 26, 73 | height: 26, 74 | ), 75 | ], 76 | ), 77 | const SizedBox(height: 24), 78 | Row( 79 | mainAxisAlignment: MainAxisAlignment.center, 80 | children: [ 81 | InkWell( 82 | onTap: () { 83 | Navigator.of(context).pop(false); 84 | }, 85 | borderRadius: BorderRadius.circular(16), 86 | child: Container( 87 | height: 36, 88 | width: 90, 89 | decoration: BoxDecoration( 90 | borderRadius: BorderRadius.circular(16), 91 | border: Border.all( 92 | color: Theme.of(context).textTheme.titleSmall!.color!, 93 | ), 94 | ), 95 | child: Row( 96 | mainAxisAlignment: MainAxisAlignment.center, 97 | children: [ 98 | Text( 99 | "Not Now", 100 | style: Theme.of(context).textTheme.titleSmall!.copyWith( 101 | fontWeight: FontWeight.normal, 102 | fontSize: 14, 103 | color: 104 | Theme.of(context).textTheme.titleSmall!.color, 105 | ), 106 | ), 107 | ], 108 | ), 109 | ), 110 | ), 111 | const SizedBox(width: 8), 112 | InkWell( 113 | onTap: () { 114 | LaunchReview.launch( 115 | androidAppId: "com.nividata.bmi_calculator", 116 | iOSAppId: "id1488893444", 117 | ); 118 | Navigator.of(context).pop(false); 119 | }, 120 | borderRadius: BorderRadius.circular(16), 121 | child: Container( 122 | height: 36, 123 | width: 90, 124 | decoration: BoxDecoration( 125 | borderRadius: BorderRadius.circular(16), 126 | gradient: LinearGradient( 127 | colors: [ 128 | Color(0xffe44e7a), 129 | Color(0xffe56665), 130 | Color(0xffe47e50), 131 | ], 132 | )), 133 | child: Row( 134 | mainAxisAlignment: MainAxisAlignment.center, 135 | children: [ 136 | Text( 137 | "Rate", 138 | style: Theme.of(context).textTheme.titleSmall!.copyWith( 139 | fontWeight: FontWeight.w500, 140 | fontSize: 14, 141 | color: Colors.white, 142 | ), 143 | ), 144 | ], 145 | ), 146 | ), 147 | ), 148 | ], 149 | ), 150 | ], 151 | ), 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/view/common/scale_transition.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ScaleRoute extends PageRouteBuilder { 4 | final Widget page; 5 | 6 | ScaleRoute({required this.page}) 7 | : super( 8 | pageBuilder: ( 9 | BuildContext context, 10 | Animation animation, 11 | Animation secondaryAnimation, 12 | ) => 13 | page, 14 | transitionsBuilder: ( 15 | BuildContext context, 16 | Animation animation, 17 | Animation secondaryAnimation, 18 | Widget child, 19 | ) => 20 | ScaleTransition( 21 | scale: Tween( 22 | begin: 0.0, 23 | end: 1.0, 24 | ).animate( 25 | CurvedAnimation( 26 | parent: animation, 27 | curve: Curves.fastOutSlowIn, 28 | ), 29 | ), 30 | child: child, 31 | ), 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /lib/view/common/size_transition.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SizeRoute extends PageRouteBuilder { 4 | final Widget page; 5 | SizeRoute({required this.page}) 6 | : super( 7 | pageBuilder: ( 8 | BuildContext context, 9 | Animation animation, 10 | Animation secondaryAnimation, 11 | ) => 12 | page, 13 | transitionsBuilder: ( 14 | BuildContext context, 15 | Animation animation, 16 | Animation secondaryAnimation, 17 | Widget child, 18 | ) => 19 | Align( 20 | child: SizeTransition( 21 | sizeFactor: animation, 22 | child: child, 23 | ), 24 | ), 25 | ); 26 | } -------------------------------------------------------------------------------- /lib/view/dashboard/bmi_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/constants.dart'; 2 | import 'package:bmi_calculator/data/model/bmi.dart'; 3 | import 'package:bmi_calculator/data/sharedpref/preferences.dart'; 4 | import 'package:bmi_calculator/data/sharedpref/shared_preference_helper.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class InheritedBmiWrapper extends StatefulWidget { 8 | final SharedPreferencesHelper sharedPreferencesHelper; 9 | final Widget child; 10 | 11 | InheritedBmiWrapper({ 12 | Key? key, 13 | required this.child, 14 | required this.sharedPreferencesHelper, 15 | }) : super(key: key); 16 | 17 | static InheritedBmiWrapperState of(BuildContext context) { 18 | return (context.dependOnInheritedWidgetOfExactType())!.data; 19 | } 20 | 21 | @override 22 | InheritedBmiWrapperState createState() => InheritedBmiWrapperState(); 23 | } 24 | 25 | class InheritedBmiWrapperState extends State { 26 | Bmi bmi = Bmi.initial(); 27 | bool buttonPressed = false; 28 | bool loopActive = false; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | bmi = bmi.copyWith( 34 | height: bmi.height.copyWith( 35 | isCm: widget.sharedPreferencesHelper 36 | .getBool(Preferences.isKg, defValue: true)), 37 | weight: bmi.weight.copyWith( 38 | isKg: widget.sharedPreferencesHelper 39 | .getBool(Preferences.isCm, defValue: true)), 40 | ); 41 | setState(() {}); 42 | } 43 | 44 | void setButtonPress(bool value) { 45 | buttonPressed = value; 46 | } 47 | 48 | void changeAgeValue(int change) { 49 | if (bmi.age + change >= 1) { 50 | setState(() { 51 | bmi = bmi.copyWith(age: bmi.age + change); 52 | }); 53 | } 54 | } 55 | 56 | void decreaseAge() async { 57 | if (loopActive) return; 58 | 59 | loopActive = true; 60 | 61 | while (buttonPressed) { 62 | if (bmi.age - 1 >= 1) { 63 | setState(() { 64 | bmi = bmi.copyWith(age: bmi.age - 1); 65 | }); 66 | } 67 | await Future.delayed(Duration(milliseconds: 100)); 68 | decreaseAge(); 69 | } 70 | loopActive = false; 71 | } 72 | 73 | void increaseAge() async { 74 | if (loopActive) return; 75 | 76 | loopActive = true; 77 | 78 | while (buttonPressed) { 79 | setState(() { 80 | bmi = bmi.copyWith(age: bmi.age + 1); 81 | }); 82 | await Future.delayed(Duration(milliseconds: 100)); 83 | increaseAge(); 84 | } 85 | loopActive = false; 86 | } 87 | 88 | void changeWeightValue(int change) { 89 | if (bmi.weight.weight + change >= 5) { 90 | setState(() { 91 | bmi = bmi.copyWith( 92 | weight: bmi.weight.copyWith(weight: bmi.weight.weight + change)); 93 | }); 94 | } 95 | } 96 | 97 | void decreaseWeight() async { 98 | if (loopActive) return; 99 | 100 | loopActive = true; 101 | 102 | while (buttonPressed) { 103 | if ((bmi.weight.weight - 1 >= 5)) { 104 | setState(() { 105 | bmi = bmi.copyWith( 106 | weight: bmi.weight.copyWith(weight: bmi.weight.weight - 1)); 107 | }); 108 | } 109 | 110 | await Future.delayed(Duration(milliseconds: 100)); 111 | decreaseWeight(); 112 | } 113 | loopActive = false; 114 | } 115 | 116 | void increaseWeight() async { 117 | if (loopActive) return; 118 | 119 | loopActive = true; 120 | 121 | while (buttonPressed) { 122 | setState(() { 123 | bmi = bmi.copyWith( 124 | weight: bmi.weight.copyWith(weight: bmi.weight.weight + 1)); 125 | }); 126 | await Future.delayed(Duration(milliseconds: 100)); 127 | increaseWeight(); 128 | } 129 | loopActive = false; 130 | } 131 | 132 | Future changeHeightUnit(bool isCm) async { 133 | setState(() { 134 | bmi = bmi.copyWith(height: bmi.height.copyWith(isCm: isCm)); 135 | }); 136 | await widget.sharedPreferencesHelper.putBool(Preferences.isCm, isCm); 137 | } 138 | 139 | void changeHeightValue(int height) { 140 | setState(() { 141 | bmi = bmi.copyWith(height: bmi.height.copyWith(height: height)); 142 | }); 143 | } 144 | 145 | void changeHeightFeet(int? feet) { 146 | setState(() { 147 | bmi = bmi.copyWith(height: bmi.height.copyWith(feet: feet)); 148 | }); 149 | } 150 | 151 | void changeHeightInch(int? inch) { 152 | setState(() { 153 | bmi = bmi.copyWith(height: bmi.height.copyWith(inch: inch)); 154 | }); 155 | } 156 | 157 | void changeGender(Gender gender) { 158 | setState(() { 159 | bmi = bmi.copyWith(gender: gender); 160 | }); 161 | } 162 | 163 | @override 164 | Widget build(BuildContext context) { 165 | return InheritedBmi( 166 | child: this.widget.child, 167 | data: this, 168 | bmi: bmi, 169 | ); 170 | } 171 | } 172 | 173 | class InheritedBmi extends InheritedWidget { 174 | final Widget child; 175 | final Bmi bmi; 176 | final InheritedBmiWrapperState data; 177 | 178 | InheritedBmi({ 179 | Key? key, 180 | required this.child, 181 | required this.data, 182 | required this.bmi, 183 | }) : super(key: key, child: child); 184 | 185 | @override 186 | bool updateShouldNotify(InheritedBmi oldWidget) { 187 | return bmi != oldWidget.bmi; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /lib/view/dashboard/bmi_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/constants.dart'; 2 | import 'package:bmi_calculator/data/model/bmi.dart'; 3 | import 'package:bmi_calculator/data/sharedpref/preferences.dart'; 4 | import 'package:bmi_calculator/data/sharedpref/shared_preference_helper.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class BmiProvider with ChangeNotifier { 8 | final SharedPreferencesHelper sharedPreferencesHelper; 9 | 10 | Bmi bmi = Bmi.initial(); 11 | bool buttonPressed = false; 12 | bool loopActive = false; 13 | 14 | BmiProvider({ 15 | required this.sharedPreferencesHelper, 16 | }) { 17 | bmi = bmi.copyWith( 18 | height: bmi.height.copyWith( 19 | isCm: sharedPreferencesHelper.getBool(Preferences.isCm, 20 | defValue: true)), 21 | weight: bmi.weight.copyWith( 22 | isKg: sharedPreferencesHelper.getBool(Preferences.isKg, 23 | defValue: true)), 24 | ); 25 | notifyListeners(); 26 | } 27 | 28 | void setButtonPress(bool value) { 29 | buttonPressed = value; 30 | } 31 | 32 | void changeAgeValue(int change) { 33 | if (bmi.age + change >= 1) { 34 | bmi = bmi.copyWith(age: bmi.age + change); 35 | notifyListeners(); 36 | } 37 | } 38 | 39 | void decreaseAge() async { 40 | if (loopActive) return; 41 | 42 | loopActive = true; 43 | 44 | while (buttonPressed) { 45 | if (bmi.age - 1 >= 1) { 46 | bmi = bmi.copyWith(age: bmi.age - 1); 47 | notifyListeners(); 48 | } 49 | await Future.delayed(Duration(milliseconds: 100)); 50 | decreaseAge(); 51 | } 52 | loopActive = false; 53 | } 54 | 55 | void increaseAge() async { 56 | if (loopActive) return; 57 | 58 | loopActive = true; 59 | 60 | while (buttonPressed) { 61 | bmi = bmi.copyWith(age: bmi.age + 1); 62 | notifyListeners(); 63 | 64 | await Future.delayed(Duration(milliseconds: 100)); 65 | increaseAge(); 66 | } 67 | loopActive = false; 68 | } 69 | 70 | void changeWeightValue(int change) { 71 | if (bmi.weight.weight + change >= 5) { 72 | bmi = bmi.copyWith( 73 | weight: bmi.weight.copyWith(weight: bmi.weight.weight + change)); 74 | notifyListeners(); 75 | } 76 | } 77 | 78 | void decreaseWeight() async { 79 | if (loopActive) return; 80 | 81 | loopActive = true; 82 | 83 | while (buttonPressed) { 84 | if ((bmi.weight.weight - 1 >= 5)) { 85 | bmi = bmi.copyWith( 86 | weight: bmi.weight.copyWith(weight: bmi.weight.weight - 1)); 87 | notifyListeners(); 88 | } 89 | 90 | await Future.delayed(Duration(milliseconds: 100)); 91 | decreaseWeight(); 92 | } 93 | loopActive = false; 94 | } 95 | 96 | void increaseWeight() async { 97 | if (loopActive) return; 98 | 99 | loopActive = true; 100 | 101 | while (buttonPressed) { 102 | bmi = bmi.copyWith( 103 | weight: bmi.weight.copyWith(weight: bmi.weight.weight + 1)); 104 | notifyListeners(); 105 | 106 | await Future.delayed(Duration(milliseconds: 100)); 107 | increaseWeight(); 108 | } 109 | loopActive = false; 110 | } 111 | 112 | Future changeWeightUnit(bool isKg) async { 113 | bmi = bmi.copyWith(weight: bmi.weight.copyWith(isKg: isKg)); 114 | notifyListeners(); 115 | await sharedPreferencesHelper.putBool(Preferences.isKg, isKg); 116 | } 117 | 118 | Future changeHeightUnit(bool isCm) async { 119 | bmi = bmi.copyWith(height: bmi.height.copyWith(isCm: isCm)); 120 | notifyListeners(); 121 | await sharedPreferencesHelper.putBool(Preferences.isCm, isCm); 122 | } 123 | 124 | void changeHeightValue(int height) { 125 | bmi = bmi.copyWith(height: bmi.height.copyWith(height: height)); 126 | notifyListeners(); 127 | } 128 | 129 | void changeHeightFeet(int? feet) { 130 | bmi = bmi.copyWith(height: bmi.height.copyWith(feet: feet)); 131 | notifyListeners(); 132 | } 133 | 134 | void changeHeightInch(int? inch) { 135 | bmi = bmi.copyWith(height: bmi.height.copyWith(inch: inch)); 136 | notifyListeners(); 137 | } 138 | 139 | void changeGender(Gender gender) { 140 | bmi = bmi.copyWith(gender: gender); 141 | notifyListeners(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /lib/view/dashboard/dashboard_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/data/repository/local_repository.dart'; 2 | import 'package:bmi_calculator/extension/datetime_extension.dart'; 3 | import 'package:bmi_calculator/utility/app_util.dart'; 4 | import 'package:bmi_calculator/view/about/about.dart'; 5 | import 'package:bmi_calculator/view/common/size_transition.dart'; 6 | import 'package:bmi_calculator/view/dashboard/drawer_footer_view.dart'; 7 | import 'package:bmi_calculator/view/dashboard/drawer_header_view.dart'; 8 | import 'package:bmi_calculator/view/dashboard/theme_icon_button.dart'; 9 | import 'package:bmi_calculator/view/drawer/drawer_scaffold.dart'; 10 | import 'package:bmi_calculator/view/drawer/menu_screen.dart' as m; 11 | import 'package:bmi_calculator/view/home/home_view.dart'; 12 | import 'package:bmi_calculator/view/setting/setting_view.dart'; 13 | import 'package:flutter/material.dart'; 14 | import 'package:launch_review/launch_review.dart'; 15 | import 'package:provider/provider.dart'; 16 | import 'package:url_launcher/url_launcher.dart'; 17 | 18 | import '../common/common_alert_dialog.dart'; 19 | import '../common/rate_dialog.dart'; 20 | 21 | class DashboardView extends StatefulWidget { 22 | const DashboardView({Key? key}) : super(key: key); 23 | 24 | @override 25 | State createState() => _DashboardViewState(); 26 | } 27 | 28 | class _DashboardViewState extends State { 29 | @override 30 | void initState() { 31 | super.initState(); 32 | WidgetsBinding.instance.addPostFrameCallback((_) async { 33 | LocalRepository localRepository = context.read(); 34 | 35 | await localRepository.increaseAppOpenCount(); 36 | if (localRepository.getFirstTime()) { 37 | await localRepository.saveIsFirstTime(); 38 | await localRepository.setFirstTimeDate(); 39 | } else if (!localRepository.isRemindOrRated()) { 40 | DateTime dateTime = localRepository.getFirstTimeDate(); 41 | if (DateTime.now() 42 | .getDateOnly() 43 | .difference(dateTime.getDateOnly()) 44 | .inDays > 45 | 1 && 46 | localRepository.getAppOpenCount() > 2) { 47 | await showDialog( 48 | context: context, 49 | barrierDismissible: false, 50 | builder: (context) => const CommonAlertDialog(child: RateDialog()), 51 | ).then((value) async { 52 | if (value != null) { 53 | if (value) { 54 | await localRepository.rated(); 55 | } else { 56 | await localRepository.remindMeLater(); 57 | } 58 | } 59 | }); 60 | } 61 | } 62 | }); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return DrawerScaffold( 68 | appBar: AppBar( 69 | automaticallyImplyLeading: true, 70 | primary: true, 71 | title: Text( 72 | 'BMI CALCULATOR', 73 | textDirection: TextDirection.ltr, 74 | ), 75 | actions: [ 76 | Container( 77 | padding: EdgeInsets.fromLTRB(0.0, 0.0, 15.0, 0.0), 78 | height: MediaQuery.of(context).size.height, 79 | child: ThemeIconButton(), 80 | ) 81 | ], 82 | ), 83 | drawers: [ 84 | m.SideDrawer( 85 | percentage: 0.8, 86 | menu: m.Menu( 87 | items: [ 88 | m.MenuItem(id: 'home', title: 'Home'), 89 | m.MenuItem(id: 'setting', title: 'Settings'), 90 | m.MenuItem(id: 'aboutUs', title: 'About App'), 91 | m.MenuItem(id: 'share', title: 'Share App'), 92 | m.MenuItem(id: 'rateUs', title: 'Rate App'), 93 | m.MenuItem(id: 'feedback', title: 'Send Feedback') 94 | ], 95 | ), 96 | headerView: DrawerHeaderView(), 97 | footerView: DrawerFooterView(), 98 | animation: true, 99 | padding: EdgeInsets.fromLTRB(40.0, 16.0, 0.0, 10.0), 100 | color: Colors.black87, 101 | cornerRadius: 20, 102 | background: DecorationImage( 103 | image: AssetImage("Assets/Images/fitmen.jpg"), 104 | colorFilter: ColorFilter.mode(Colors.white54, BlendMode.dstOut), 105 | fit: BoxFit.cover, 106 | ), 107 | selectorColor: Color.fromRGBO(67, 193, 152, 1), 108 | textStyle: TextStyle( 109 | fontWeight: FontWeight.bold, 110 | fontSize: 17.0, 111 | color: Colors.white70, 112 | ), 113 | selectedItemId: "home", 114 | onMenuItemSelected: (itemId) async { 115 | switch (itemId) { 116 | case 'home': 117 | break; 118 | case 'setting': 119 | Navigator.push(context, SizeRoute(page: SettingView())); 120 | break; 121 | case 'aboutUs': 122 | Navigator.push(context, SizeRoute(page: AboutUsView())); 123 | break; 124 | case 'share': 125 | AppUtil.onShareTap(); 126 | break; 127 | case 'rateUs': 128 | LaunchReview.launch( 129 | androidAppId: "com.nividata.bmi_calculator", 130 | iOSAppId: "id1488893444", 131 | ); 132 | break; 133 | case 'feedback': 134 | final Uri email = Uri( 135 | scheme: "mailto", 136 | path: "info@nividata.com", 137 | query: 138 | "${Uri.encodeComponent("subject")}=${Uri.encodeComponent("Feedback for BMI App")}"); 139 | if (await canLaunchUrl(email)) { 140 | await launchUrl(email); 141 | } 142 | break; 143 | default: 144 | break; 145 | } 146 | }, 147 | ), 148 | ], 149 | builder: (context, id) => HomeView(), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/view/dashboard/drawer_footer_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 3 | import 'package:url_launcher/url_launcher_string.dart'; 4 | 5 | class DrawerFooterView extends StatelessWidget { 6 | const DrawerFooterView({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | margin: EdgeInsets.fromLTRB(30.0, 0.0, 0.0, 20.0), 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.center, 14 | mainAxisAlignment: MainAxisAlignment.spaceAround, 15 | children: [ 16 | Row( 17 | mainAxisSize: MainAxisSize.max, 18 | children: [ 19 | Text( 20 | "Built with ", 21 | style: TextStyle( 22 | color: Colors.white, 23 | fontSize: 10.0, 24 | fontWeight: FontWeight.w600, 25 | ), 26 | ), 27 | Icon( 28 | FontAwesomeIcons.solidHeart, 29 | color: Colors.red, 30 | size: 10, 31 | ), 32 | Text( 33 | " By", 34 | style: TextStyle( 35 | color: Colors.white, 36 | fontSize: 10.0, 37 | fontWeight: FontWeight.w600, 38 | ), 39 | ) 40 | ], 41 | ), 42 | Text(""), 43 | GestureDetector( 44 | child: Row( 45 | children: [ 46 | CircleAvatar( 47 | radius: 20.0, 48 | backgroundImage: AssetImage("Assets/Images/icon.png"), 49 | ), 50 | Text( 51 | " NiviData\n Consultancy", 52 | style: TextStyle( 53 | color: Colors.white, 54 | fontSize: 16.0, 55 | fontWeight: FontWeight.bold, 56 | ), 57 | ), 58 | ], 59 | ), 60 | onTap: () async { 61 | const url = 'https://nividata.com'; 62 | if (await canLaunchUrlString(url)) { 63 | await launchUrlString( 64 | url, 65 | mode: LaunchMode.externalApplication, 66 | ); 67 | } 68 | }, 69 | ), 70 | ], 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/view/dashboard/drawer_header_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DrawerHeaderView extends StatelessWidget { 4 | const DrawerHeaderView({Key? key}) : super(key: key); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Container( 9 | margin: EdgeInsets.fromLTRB(20.0, 0.0, 0.0, 0.0), 10 | child: Column( 11 | crossAxisAlignment: CrossAxisAlignment.start, 12 | children: [ 13 | SizedBox(height: 20), 14 | Image( 15 | image: AssetImage("Assets/Images/ic_launcher.png"), 16 | filterQuality: FilterQuality.high, 17 | width: 100.0, 18 | height: 100.0, 19 | ), 20 | Text( 21 | " BMI.", 22 | style: TextStyle( 23 | color: Colors.white, 24 | fontSize: 34.0, 25 | fontWeight: FontWeight.bold, 26 | ), 27 | ) 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/view/dashboard/theme_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/view/app/theme_provider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class ThemeIconButton extends StatelessWidget { 7 | const ThemeIconButton({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Consumer(builder: (context, data, child) { 12 | return IconButton( 13 | icon: Icon(data.themeMode == ThemeMode.light 14 | ? FontAwesomeIcons.solidMoon 15 | : FontAwesomeIcons.solidSun), 16 | onPressed: () { 17 | context.read().changeTheme( 18 | data.themeMode == ThemeMode.light 19 | ? ThemeMode.dark 20 | : ThemeMode.light); 21 | }, 22 | ); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/view/drawer/menu_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:bmi_calculator/view/drawer/drawer_scaffold.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // final menuScreenKey = GlobalKey(debugLabel: 'MenuScreen'); 7 | 8 | enum Direction { 9 | left, 10 | right, 11 | } 12 | 13 | typedef SideDrawerItemBuilder = Function(BuildContext, MenuItem, bool); 14 | 15 | class SideDrawer extends StatefulWidget { 16 | SideDrawer({ 17 | this.menu, 18 | this.headerView, 19 | this.footerView, 20 | this.selectedItemId, 21 | this.slide = false, 22 | double? percentage, 23 | double? degree, 24 | this.onMenuItemSelected, 25 | this.child, 26 | this.color = Colors.white, 27 | this.background, 28 | this.animation = false, 29 | this.direction = Direction.left, 30 | this.selectorColor, 31 | this.drawerWidth = 300, 32 | this.duration, 33 | this.curve, 34 | this.textStyle, 35 | this.padding = const EdgeInsets.only(left: 40.0, top: 15.0, bottom: 15.0), 36 | this.alignment = Alignment.centerLeft, 37 | this.itemBuilder, 38 | this.elevation = 16, 39 | this.cornerRadius, 40 | this.withSafeAre = true, 41 | Key? key, 42 | }) : assert((child != null && menu == null && itemBuilder == null) || 43 | (child == null && menu != null)), 44 | this.percentage = percentage ?? 0.8, 45 | this.degree = degree == null ? null : max(min(45, degree), 15), 46 | this.scaleDownCurve = 47 | new Interval(0.0, 0.3, curve: curve ?? Curves.easeOut), 48 | this.scaleUpCurve = 49 | new Interval(0.0, 1.0, curve: curve ?? Curves.easeOut), 50 | this.slideOutCurve = 51 | new Interval(0.0, 1.0, curve: curve ?? Curves.easeOut), 52 | this.slideInCurve = 53 | new Interval(0.0, 1.0, curve: curve ?? Curves.easeOut), 54 | super(key: key); 55 | 56 | /// Scaling Percentage base on width and height 57 | final double percentage; 58 | 59 | /// Card's elevation 60 | /// Default : 16 61 | final double elevation; 62 | 63 | /// Card's corner radius 64 | final double? cornerRadius; 65 | 66 | /// Degree of rotation : 15->45 degree 67 | final double? degree; 68 | 69 | /// Drawer's width in Pixel, 70 | /// Default : 300px 71 | final double drawerWidth; 72 | 73 | /// Direction the drawer will appear ([Direction.left] or [Direction.right]) 74 | /// Default: [Direction.left] 75 | final Direction direction; 76 | 77 | /// Transition's [Curve], 78 | /// Default : [Curves.easeOut] 79 | final Curve? curve; 80 | 81 | /// Transition's [Duration], 82 | /// Defalut: 250ms 83 | final Duration? duration; 84 | 85 | /// [Menu] for drawer 86 | final Menu? menu; 87 | 88 | /// Current selected ID 89 | final T? selectedItemId; 90 | 91 | /// Flag for animation on menu item 92 | final bool animation; 93 | 94 | /// Flag for drawer slide with main container 95 | final bool slide; 96 | 97 | /// listen to menu selected 98 | final Function(T)? onMenuItemSelected; 99 | 100 | /// [Widget] for header on drawer 101 | final Widget? headerView; 102 | 103 | /// [Widget] for footer on drawer 104 | final Widget? footerView; 105 | 106 | /// Custom builder for menu item 107 | final SideDrawerItemBuilder? itemBuilder; 108 | 109 | /// Widget for side drawer 110 | final Widget? child; 111 | 112 | /// Background for drawer 113 | final DecorationImage? background; 114 | 115 | /// Background [Color] for drawer 116 | final Color color; 117 | 118 | /// [Color] for selected menu item 119 | final Color? selectorColor; 120 | 121 | /// Menu item [TextStyle] 122 | final TextStyle? textStyle; 123 | 124 | /// Menu [Alignment] in drawer 125 | final Alignment alignment; 126 | 127 | /// Menu [Padding] in drawer 128 | final EdgeInsets padding; 129 | 130 | /// Easing [Curve] for scale down 131 | final Curve scaleDownCurve; 132 | 133 | /// Easing [Curve] for scale up 134 | final Curve scaleUpCurve; 135 | 136 | /// Easing [Curve] for slide out 137 | final Curve slideOutCurve; 138 | 139 | /// Easing [Curve] for slide in 140 | final Curve slideInCurve; 141 | 142 | /// to enable/disable [SafeArea] for headerView & footerView, default = true 143 | final bool withSafeAre; 144 | 145 | double maxSlideAmount(context) => 146 | drawerWidth; // ?? MediaQuery.of(context).size.width * percentage; 147 | 148 | @override 149 | _SideDrawerState createState() => _SideDrawerState(); 150 | } 151 | 152 | class _SideDrawerState extends State with TickerProviderStateMixin { 153 | double? selectorYTop; 154 | double? selectorYBottom; 155 | 156 | Color? selectorColor; 157 | TextStyle? textStyle; 158 | 159 | double get maxSlideAmount => widget.maxSlideAmount(context); 160 | 161 | setSelectedRenderBox(RenderBox newRenderBox, bool useState) async { 162 | final renderBox = context.findRenderObject() as RenderBox?; 163 | 164 | final newYTop = 165 | newRenderBox.localToGlobal(Offset(0.0, 0.0), ancestor: renderBox).dy; 166 | 167 | final newYBottom = newYTop + newRenderBox.size.height; 168 | if (newYTop != selectorYTop) { 169 | selectorYTop = newYTop; 170 | selectorYBottom = newYBottom; 171 | } 172 | } 173 | 174 | @override 175 | void initState() { 176 | super.initState(); 177 | } 178 | 179 | @override 180 | void dispose() { 181 | super.dispose(); 182 | } 183 | 184 | Widget createMenuItems(DrawerMenuController menuController) { 185 | final List listItems = []; 186 | 187 | if (widget.child != null) { 188 | listItems.add(widget.child ?? SizedBox()); 189 | } else { 190 | final animationIntervalDuration = 0.5; 191 | final perListItemDelay = 192 | menuController.state != MenuState.closing ? 0.15 : 0.0; 193 | final millis = menuController.state != MenuState.closing 194 | ? 150 * widget.menu!.items.length 195 | : 600; 196 | 197 | final maxDuration = (widget.menu!.items.length - 1) * perListItemDelay + 198 | animationIntervalDuration; 199 | 200 | for (var i = 0; i < widget.menu!.items.length; ++i) { 201 | final animationIntervalStart = i * perListItemDelay; 202 | final animationIntervalEnd = 203 | animationIntervalStart + animationIntervalDuration; 204 | MenuItem item = widget.menu!.items[i]; 205 | listItems.add(buildListItem(menuController, item, 206 | animationIntervalStart, animationIntervalEnd, millis, maxDuration)); 207 | } 208 | } 209 | 210 | return Container( 211 | alignment: widget.alignment, 212 | margin: EdgeInsets.only( 213 | left: widget.direction == Direction.left 214 | ? 0 215 | : MediaQuery.of(context).size.width - maxSlideAmount), 216 | child: SingleChildScrollView( 217 | child: Container( 218 | child: Column( 219 | mainAxisSize: MainAxisSize.max, 220 | crossAxisAlignment: CrossAxisAlignment.start, 221 | children: listItems, 222 | ), 223 | ), 224 | ), 225 | ); 226 | } 227 | 228 | buildListItem( 229 | DrawerMenuController menuController, 230 | MenuItem item, 231 | double animationIntervalStart, 232 | double animationIntervalEnd, 233 | int millis, 234 | double maxDuration, 235 | ) { 236 | final isSelected = item.id == widget.selectedItemId; 237 | 238 | Function onTap = () { 239 | widget.onMenuItemSelected!(item.id); 240 | menuController.close(); 241 | }; 242 | Widget listItem = widget.itemBuilder == null 243 | ? _MenuListItem( 244 | padding: const EdgeInsets.only(left: 32.0), 245 | direction: widget.direction, 246 | title: item.title, 247 | isSelected: isSelected, 248 | selectorColor: selectorColor, 249 | textStyle: item.textStyle ?? textStyle, 250 | menuView: widget, 251 | width: maxSlideAmount, 252 | icon: item.icon == null ? item.prefix : Icon(item.icon), 253 | suffix: item.suffix, 254 | onTap: onTap as dynamic Function()?, 255 | drawBorder: !widget.animation, 256 | ) 257 | : InkWell( 258 | child: Container( 259 | alignment: Alignment.centerLeft, 260 | child: Container( 261 | child: widget.itemBuilder!(context, item, isSelected), 262 | width: maxSlideAmount, 263 | ), 264 | ), 265 | onTap: onTap as void Function()?, 266 | ); 267 | 268 | if (widget.animation) 269 | return AnimatedMenuListItem( 270 | menuState: menuController.state, 271 | isSelected: isSelected, 272 | duration: Duration(milliseconds: millis), 273 | curve: Interval(animationIntervalStart / maxDuration, 274 | animationIntervalEnd / maxDuration, 275 | curve: Curves.easeOut), 276 | menuListItem: listItem, 277 | ); 278 | else { 279 | return listItem; 280 | } 281 | } 282 | 283 | Widget createDrawer(DrawerMenuController menuController) { 284 | List widgets = []; 285 | if (widget.headerView != null) { 286 | widgets.add(Container( 287 | alignment: widget.alignment, 288 | margin: EdgeInsets.only( 289 | left: widget.direction == Direction.left 290 | ? 0 291 | : MediaQuery.of(context).size.width - maxSlideAmount), 292 | child: Container(width: maxSlideAmount, child: widget.headerView), 293 | )); 294 | } else {} 295 | widgets.add(Expanded( 296 | child: createMenuItems(menuController), 297 | flex: 1, 298 | )); 299 | 300 | if (widget.footerView != null) { 301 | widgets.add(Container( 302 | alignment: widget.alignment, 303 | margin: EdgeInsets.only( 304 | left: widget.direction == Direction.left 305 | ? 0 306 | : MediaQuery.of(context).size.width - maxSlideAmount), 307 | child: Container( 308 | width: maxSlideAmount, 309 | child: widget.footerView, 310 | margin: 311 | EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), 312 | ))); 313 | } 314 | DrawerMenuController controller = DrawerScaffold.currentController(context); 315 | return Transform( 316 | transform: Matrix4.translationValues( 317 | widget.slide 318 | ? (widget.direction == Direction.left ? 1 : -1) * 319 | (widget.maxSlideAmount(context)) * 320 | (controller.slidePercent - 1) 321 | : 0, 322 | 0, 323 | 0.0, 324 | ), 325 | child: SafeArea( 326 | top: widget.withSafeAre || widget.headerView == null, 327 | bottom: widget.withSafeAre || widget.footerView == null, 328 | child: Container( 329 | height: MediaQuery.of(context).size.height, 330 | child: Column( 331 | children: widgets, 332 | ), 333 | ), 334 | ), 335 | ); 336 | } 337 | 338 | @override 339 | Widget build(BuildContext context) { 340 | selectorColor = widget.selectorColor ?? Theme.of(context).indicatorColor; 341 | textStyle = widget.textStyle ?? 342 | Theme.of(context).textTheme.titleMedium?.copyWith( 343 | color: widget.color.computeLuminance() < 0.5 344 | ? Colors.white 345 | : Colors.black); 346 | return DrawerScaffoldMenuController( 347 | direction: widget.direction, 348 | builder: (BuildContext context, DrawerMenuController menuController) { 349 | var shouldRenderSelector = true; 350 | var actualSelectorYTop = selectorYTop; 351 | var actualSelectorYBottom = selectorYBottom; 352 | var selectorOpacity = 1.0; 353 | 354 | if (menuController.state == MenuState.closed || 355 | menuController.state == MenuState.closing || 356 | selectorYTop == null) { 357 | final RenderBox? menuScreenRenderBox = 358 | context.findRenderObject() as RenderBox?; 359 | 360 | if (menuScreenRenderBox != null) { 361 | final menuScreenHeight = menuScreenRenderBox.size.height; 362 | actualSelectorYTop = menuScreenHeight - 50.0; 363 | actualSelectorYBottom = menuScreenHeight; 364 | selectorOpacity = 0.0; 365 | } else { 366 | shouldRenderSelector = false; 367 | } 368 | } 369 | 370 | return Container( 371 | // padding: widget.direction == Direction.right 372 | // ? const EdgeInsets.only(left: 24) 373 | // : const EdgeInsets.only(right: 24), 374 | width: double.infinity, 375 | height: double.infinity, 376 | decoration: BoxDecoration( 377 | image: widget.background, 378 | color: widget.color, 379 | ), 380 | child: Center( 381 | child: Material( 382 | color: Colors.transparent, 383 | child: Stack( 384 | children: [ 385 | createDrawer(menuController), 386 | widget.animation && shouldRenderSelector 387 | ? ItemSelector( 388 | left: widget.direction == Direction.right 389 | ? MediaQuery.of(context).size.width - 390 | maxSlideAmount 391 | : 0, 392 | selectorColor: selectorColor, 393 | top: actualSelectorYTop, 394 | bottom: actualSelectorYBottom, 395 | opacity: selectorOpacity) 396 | : Container(), 397 | ], 398 | ), 399 | ), 400 | ), 401 | ); 402 | }); 403 | } 404 | 405 | static _SideDrawerState? of(BuildContext context, {bool nullOk = true}) { 406 | final _SideDrawerState? result = 407 | context.findAncestorStateOfType<_SideDrawerState>(); 408 | if (nullOk || result != null) return result; 409 | throw FlutterError.fromParts([ 410 | ErrorSummary( 411 | '_SideDrawerState.of() called with a context that does not contain a _SideDrawerState.'), 412 | context.describeElement('The context used was') 413 | ]); 414 | } 415 | } 416 | 417 | class ItemSelector extends ImplicitlyAnimatedWidget { 418 | final double? top; 419 | final double? bottom; 420 | final double? left; 421 | final double? opacity; 422 | 423 | final Color? selectorColor; 424 | 425 | ItemSelector({ 426 | this.left, 427 | this.top, 428 | this.bottom, 429 | this.opacity, 430 | this.selectorColor, 431 | }) : super(duration: const Duration(milliseconds: 250)); 432 | 433 | @override 434 | _ItemSelectorState createState() => _ItemSelectorState(); 435 | } 436 | 437 | class _ItemSelectorState extends AnimatedWidgetBaseState { 438 | Tween? _topY; 439 | Tween? _bottomY; 440 | Tween? _opacity; 441 | 442 | @override 443 | void forEachTween(visitor) { 444 | _topY = visitor( 445 | _topY, 446 | widget.top!, 447 | (dynamic value) => Tween(begin: value), 448 | ) as Tween?; 449 | _bottomY = visitor( 450 | _bottomY, 451 | widget.bottom!, 452 | (dynamic value) => Tween(begin: value), 453 | ) as Tween?; 454 | _opacity = visitor( 455 | _opacity, 456 | widget.opacity!, 457 | (dynamic value) => Tween(begin: value), 458 | ) as Tween?; 459 | } 460 | 461 | @override 462 | Widget build(BuildContext context) { 463 | return Positioned( 464 | top: _topY!.evaluate(animation), 465 | left: widget.left, 466 | child: Opacity( 467 | opacity: _opacity!.evaluate(animation)!, 468 | child: Container( 469 | width: 5.0, 470 | height: _bottomY!.evaluate(animation)! - _topY!.evaluate(animation)!, 471 | color: widget.selectorColor, 472 | ), 473 | ), 474 | ); 475 | } 476 | } 477 | 478 | class AnimatedMenuListItem extends ImplicitlyAnimatedWidget { 479 | final Widget? menuListItem; 480 | final MenuState? menuState; 481 | final bool? isSelected; 482 | final Duration duration; 483 | 484 | AnimatedMenuListItem({ 485 | this.menuListItem, 486 | this.menuState, 487 | this.isSelected, 488 | required this.duration, 489 | required Curve curve, 490 | Key? key, 491 | }) : super(key: key, duration: duration, curve: curve); 492 | 493 | @override 494 | _AnimatedMenuListItemState createState() => _AnimatedMenuListItemState(); 495 | } 496 | 497 | class _AnimatedMenuListItemState 498 | extends AnimatedWidgetBaseState { 499 | final double closedSlidePosition = 200.0; 500 | final double openSlidePosition = 0.0; 501 | 502 | _SideDrawerState? get _sideDrawerState => _SideDrawerState.of(context); 503 | 504 | Tween? _translation; 505 | Tween? _opacity; 506 | 507 | updateSelectedRenderBox(bool useState) { 508 | final renderBox = context.findRenderObject() as RenderBox?; 509 | if (renderBox != null && widget.isSelected!) { 510 | _sideDrawerState?.setSelectedRenderBox.call(renderBox, useState); 511 | } 512 | } 513 | 514 | @override 515 | void forEachTween(visitor) { 516 | var slide, opacity; 517 | 518 | switch (widget.menuState) { 519 | case MenuState.closed: 520 | case MenuState.closing: 521 | slide = closedSlidePosition; 522 | opacity = 0.0; 523 | break; 524 | case MenuState.open: 525 | case MenuState.opening: 526 | slide = openSlidePosition; 527 | opacity = 1.0; 528 | break; 529 | 530 | default: 531 | break; 532 | } 533 | 534 | _translation = visitor( 535 | _translation, 536 | slide, 537 | (dynamic value) => Tween(begin: value), 538 | ) as Tween?; 539 | 540 | _opacity = visitor( 541 | _opacity, 542 | opacity, 543 | (dynamic value) => Tween(begin: value), 544 | ) as Tween?; 545 | } 546 | 547 | @override 548 | Widget build(BuildContext context) { 549 | updateSelectedRenderBox(false); 550 | 551 | return Opacity( 552 | opacity: _opacity!.evaluate(animation)!, 553 | child: Transform( 554 | transform: Matrix4.translationValues( 555 | 0.0, 556 | _translation!.evaluate(animation)!, 557 | 0.0, 558 | ), 559 | child: widget.menuListItem, 560 | ), 561 | ); 562 | } 563 | } 564 | 565 | class _MenuListItem extends StatelessWidget { 566 | final String title; 567 | final bool? isSelected; 568 | final bool? drawBorder; 569 | final Function()? onTap; 570 | final Color? selectorColor; 571 | final TextStyle? textStyle; 572 | final SideDrawer? menuView; 573 | final Widget? icon; 574 | final Widget? suffix; 575 | final Direction direction; 576 | final double? width; 577 | final EdgeInsets? padding; 578 | 579 | _MenuListItem({ 580 | required this.title, 581 | this.isSelected, 582 | this.onTap, 583 | this.menuView, 584 | required this.textStyle, 585 | required this.selectorColor, 586 | this.icon, 587 | this.drawBorder, 588 | this.direction = Direction.right, 589 | this.width, 590 | this.padding, 591 | this.suffix, 592 | }); 593 | 594 | @override 595 | Widget build(BuildContext context) { 596 | TextStyle _textStyle = textStyle! 597 | .copyWith(color: isSelected! ? selectorColor : textStyle!.color); 598 | 599 | List children = []; 600 | if (icon != null) 601 | children.add(Padding( 602 | padding: EdgeInsets.only(right: 12), 603 | child: IconTheme( 604 | data: IconThemeData(color: _textStyle.color), child: icon!), 605 | )); 606 | children.add( 607 | Expanded( 608 | child: Container( 609 | child: Text( 610 | title, 611 | style: _textStyle, 612 | ), 613 | ), 614 | flex: 1, 615 | ), 616 | ); 617 | if (suffix != null) 618 | children.add(Padding( 619 | padding: EdgeInsets.only(right: 12), 620 | child: IconTheme( 621 | data: IconThemeData(color: _textStyle.color), child: suffix!), 622 | )); 623 | return InkWell( 624 | splashColor: const Color(0x44000000), 625 | onTap: isSelected! ? null : onTap, 626 | child: Container( 627 | width: width, 628 | alignment: Alignment.centerRight, 629 | decoration: drawBorder! 630 | ? ShapeDecoration( 631 | shape: Border( 632 | left: BorderSide( 633 | color: isSelected == true 634 | ? selectorColor! 635 | : Colors.transparent, 636 | width: 5.0), 637 | ), 638 | ) 639 | : null, 640 | child: Padding( 641 | padding: menuView!.padding, 642 | child: Row( 643 | mainAxisAlignment: MainAxisAlignment.start, 644 | children: children, 645 | ), 646 | ), 647 | ), 648 | ); 649 | } 650 | } 651 | 652 | class Menu { 653 | final List items; 654 | 655 | const Menu({ 656 | required this.items, 657 | }); 658 | } 659 | 660 | class MenuItem { 661 | final T? id; 662 | final String title; 663 | 664 | /// set icon from [MenuItem], if the icon is not null, the prefix must be null 665 | final IconData? icon; 666 | 667 | /// set prefix widget from [MenuItem], if the prefix is not null, the icon must be null 668 | final Widget? prefix; 669 | 670 | /// set prefix widget from [MenuItem] 671 | final Widget? suffix; 672 | 673 | /// set independent text style for title 674 | final TextStyle? textStyle; 675 | 676 | /// append data with [MenuItem], then can be use on itemBuilder 677 | final dynamic data; 678 | 679 | MenuItem({ 680 | this.id, 681 | required this.title, 682 | this.icon, 683 | this.prefix, 684 | this.suffix, 685 | this.textStyle, 686 | this.data, 687 | }) : assert(prefix == null || icon == null); 688 | 689 | MenuItem copyWith({ 690 | T? id, 691 | String? title, 692 | IconData? icon, 693 | Widget? prefix, 694 | Widget? suffix, 695 | TextStyle? textStyle, 696 | dynamic data, 697 | }) { 698 | return MenuItem( 699 | id: id ?? this.id, 700 | title: title ?? this.title, 701 | icon: icon, 702 | prefix: prefix, 703 | suffix: suffix, 704 | textStyle: textStyle ?? this.textStyle, 705 | data: data ?? this.data, 706 | ); 707 | } 708 | } 709 | -------------------------------------------------------------------------------- /lib/view/drawer/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | class Utils { 4 | static double fixed(double value, int decimal) { 5 | int fac = pow(10, decimal) as int; 6 | return (value * fac).round() / fac; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/view/home/age_card_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/color_scheme.dart'; 2 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class AgeCardView extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Card( 11 | elevation: 2.0, 12 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 13 | child: Padding( 14 | padding: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 10.0), 15 | child: Column( 16 | crossAxisAlignment: CrossAxisAlignment.center, 17 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 18 | children: [ 19 | Text( 20 | 'Age (In Year)', 21 | style: TextStyle( 22 | fontWeight: FontWeight.w900, 23 | color: Theme.of(context).colorScheme.accentColor), 24 | ), 25 | Selector( 26 | selector: (p0, p1) => p1.bmi.age, 27 | shouldRebuild: (p, c) => p != c, 28 | builder: (context, data, _) { 29 | return Text( 30 | data.toString(), 31 | style: TextStyle( 32 | fontSize: 60.0, 33 | fontWeight: FontWeight.w900, 34 | color: Theme.of(context).colorScheme.accentColor), 35 | ); 36 | }), 37 | Row( 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | mainAxisSize: MainAxisSize.max, 40 | children: [ 41 | GestureDetector( 42 | child: CircleAvatar( 43 | radius: 20.0, 44 | backgroundColor: Theme.of(context).colorScheme.buttonColor, 45 | child: IconButton( 46 | icon: Icon( 47 | FontAwesomeIcons.minus, 48 | color: Theme.of(context).iconTheme.color, 49 | ), 50 | onPressed: () { 51 | context.read().changeAgeValue(-1); 52 | }, 53 | ), 54 | ), 55 | onLongPressStart: (details) { 56 | context.read().setButtonPress(true); 57 | context.read().decreaseAge(); 58 | }, 59 | onLongPressUp: () { 60 | context.read().setButtonPress(false); 61 | }, 62 | ), 63 | GestureDetector( 64 | child: CircleAvatar( 65 | radius: 20.0, 66 | backgroundColor: Theme.of(context).colorScheme.buttonColor, 67 | child: IconButton( 68 | icon: Icon( 69 | FontAwesomeIcons.plus, 70 | color: Theme.of(context).iconTheme.color, 71 | ), 72 | onPressed: () { 73 | context.read().changeAgeValue(1); 74 | }, 75 | ), 76 | ), 77 | onLongPressStart: (details) { 78 | context.read().setButtonPress(true); 79 | context.read().increaseAge(); 80 | }, 81 | onLongPressUp: () { 82 | context.read().setButtonPress(false); 83 | }, 84 | ) 85 | ], 86 | ), 87 | ], 88 | ), 89 | ), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/view/home/calculate_button_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/view/common/animate_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class CalculateButtonView extends StatefulWidget { 5 | final Function() onTab; 6 | 7 | const CalculateButtonView({ 8 | required this.onTab, 9 | Key? key, 10 | }) : super(key: key); 11 | 12 | @override 13 | State createState() => _CalculateButtonViewState(); 14 | } 15 | 16 | class _CalculateButtonViewState extends State 17 | with SingleTickerProviderStateMixin { 18 | late final AnimationController _controller; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _controller = new AnimationController( 24 | duration: const Duration(milliseconds: 1000), 25 | vsync: this, 26 | )..repeat(reverse: true); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Stack( 32 | alignment: Alignment.center, 33 | children: [ 34 | AnimatedLoader( 35 | animation: _controller, 36 | // alignment: FractionalOffset.center, 37 | child: Container( 38 | alignment: Alignment.bottomCenter, 39 | width: 215.0, 40 | height: 62.0, 41 | decoration: BoxDecoration( 42 | color: Color.fromRGBO(179, 157, 219, 0.4), 43 | borderRadius: BorderRadius.circular(30.0), 44 | ), 45 | ), 46 | ), 47 | Container( 48 | alignment: Alignment.bottomCenter, 49 | padding: EdgeInsets.all(11.0), 50 | child: MaterialButton( 51 | child: Text( 52 | 'Calculate'.toUpperCase(), 53 | style: TextStyle( 54 | color: Colors.white, fontSize: 16.0, letterSpacing: 1), 55 | ), 56 | color: Colors.deepPurple, 57 | elevation: 2.0, 58 | clipBehavior: Clip.antiAliasWithSaveLayer, 59 | minWidth: 200.0, 60 | height: 50.0, 61 | shape: RoundedRectangleBorder( 62 | borderRadius: new BorderRadius.circular(30.0)), 63 | onPressed: widget.onTab, 64 | ), 65 | ), 66 | ], 67 | ); 68 | } 69 | 70 | @override 71 | void dispose() { 72 | _controller.dispose(); 73 | super.dispose(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/view/home/gender_card_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/color_scheme.dart'; 2 | import 'package:bmi_calculator/core/constants.dart'; 3 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class GenderCardView extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Padding( 11 | padding: EdgeInsets.fromLTRB(6.0, 5.0, 6.0, 0.0), 12 | child: Card( 13 | elevation: 2.0, 14 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 15 | child: Container( 16 | padding: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 10.0), 17 | // width: MediaQuery.of(context).size.height, 18 | // height: MediaQuery.of(context).size.height/8, 19 | child: Column( 20 | crossAxisAlignment: CrossAxisAlignment.center, 21 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 22 | children: [ 23 | Text( 24 | 'Gender', 25 | style: TextStyle( 26 | fontWeight: FontWeight.w900, 27 | color: Theme.of(context).colorScheme.accentColor), 28 | ), 29 | Row( 30 | mainAxisAlignment: MainAxisAlignment.spaceAround, 31 | children: [ 32 | Text( 33 | 'I\'m', 34 | style: TextStyle( 35 | fontSize: 60.0, 36 | fontWeight: FontWeight.w900, 37 | color: Theme.of(context).colorScheme.accentColor, 38 | ), 39 | ), 40 | Text( 41 | 'Female', 42 | style: TextStyle( 43 | fontWeight: FontWeight.w900, 44 | color: Theme.of(context).colorScheme.accentColor, 45 | ), 46 | ), 47 | GenderSwitch(), 48 | Text( 49 | 'Male', 50 | style: TextStyle( 51 | fontWeight: FontWeight.w900, 52 | color: Theme.of(context).colorScheme.accentColor, 53 | ), 54 | ), 55 | ], 56 | ) 57 | ], 58 | ), 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | 65 | class GenderSwitch extends StatelessWidget { 66 | const GenderSwitch({Key? key}) : super(key: key); 67 | 68 | @override 69 | Widget build(BuildContext context) { 70 | return Selector( 71 | selector: (p0, p1) => p1.bmi.gender, 72 | shouldRebuild: (p, c) => p != c, 73 | builder: (context, data, _) { 74 | return Switch( 75 | value: data == Gender.Male, 76 | onChanged: (value) { 77 | context 78 | .read() 79 | .changeGender(value ? Gender.Male : Gender.Female); 80 | }, 81 | inactiveTrackColor: Colors.grey.shade300, 82 | inactiveThumbColor: Colors.deepPurple, 83 | activeTrackColor: Colors.grey.shade300, 84 | activeColor: Colors.deepPurple, 85 | ); 86 | }); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/view/home/height_card_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/color_scheme.dart'; 2 | import 'package:bmi_calculator/core/constants.dart'; 3 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 4 | import 'package:bmi_calculator/view/home/height_unit_switch.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class HeightCardView extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | margin: EdgeInsets.fromLTRB(6.0, 5.0, 6.0, 0.0), 13 | child: Card( 14 | elevation: 2.0, 15 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 16 | child: Container( 17 | child: Column( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | Container( 21 | margin: EdgeInsets.fromLTRB(200.0, 10.0, 0.0, 0.0), 22 | height: 30.0, 23 | padding: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0), 24 | decoration: BoxDecoration( 25 | color: Color.fromRGBO(237, 231, 246, 1), 26 | borderRadius: BorderRadius.circular(40.0), 27 | ), 28 | child: Row( 29 | mainAxisSize: MainAxisSize.min, 30 | crossAxisAlignment: CrossAxisAlignment.center, 31 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 32 | children: [ 33 | Text( 34 | 'cm', 35 | style: TextStyle( 36 | fontSize: 12.0, 37 | fontStyle: FontStyle.italic, 38 | fontWeight: FontWeight.w900, 39 | color: Colors.grey.shade700), 40 | ), 41 | HeightUnitSwitch(), 42 | Text( 43 | 'ft', 44 | style: TextStyle( 45 | fontSize: 12.0, 46 | fontStyle: FontStyle.italic, 47 | fontWeight: FontWeight.w900, 48 | color: Colors.grey.shade700), 49 | ), 50 | ], 51 | ), 52 | ), 53 | HeightView(), 54 | ], 55 | )), 56 | ), 57 | ); 58 | } 59 | } 60 | 61 | class HeightView extends StatelessWidget { 62 | const HeightView({Key? key}) : super(key: key); 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | bool data = 67 | context.select((value) => value.bmi.height.isCm); 68 | return AnimatedSwitcher( 69 | child: data 70 | ? Container( 71 | height: 140.0, 72 | padding: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0), 73 | child: Column( 74 | children: [ 75 | Text( 76 | 'Height', 77 | style: TextStyle( 78 | fontWeight: FontWeight.w900, 79 | color: Theme.of(context).colorScheme.accentColor), 80 | ), 81 | Row( 82 | mainAxisAlignment: MainAxisAlignment.center, 83 | crossAxisAlignment: CrossAxisAlignment.baseline, 84 | textBaseline: TextBaseline.alphabetic, 85 | children: [ 86 | Selector( 87 | selector: (p0, p1) => p1.bmi.height.height, 88 | shouldRebuild: (p, c) => p != c, 89 | builder: (context, data, _) { 90 | return Text( 91 | data.toString(), 92 | style: Theme.of(context) 93 | .textTheme 94 | .titleSmall! 95 | .copyWith( 96 | fontSize: 50, 97 | ), 98 | // style: TextStyle( 99 | // color: Theme.of(context).colorScheme.accentColor, 100 | // fontSize: 50.0, 101 | // fontWeight: FontWeight.w900, 102 | // ), 103 | ); 104 | }), 105 | Text( 106 | 'cm', 107 | style: kLabelTextStyle, 108 | ), 109 | ], 110 | ), 111 | SliderTheme( 112 | data: SliderTheme.of(context).copyWith( 113 | activeTrackColor: Colors.grey.shade300, 114 | inactiveTrackColor: Colors.grey.shade300, 115 | thumbColor: Colors.deepPurple, 116 | overlayColor: Color(0x29EB1555), 117 | thumbShape: 118 | RoundSliderThumbShape(enabledThumbRadius: 15.0), 119 | overlayShape: 120 | RoundSliderOverlayShape(overlayRadius: 30.0), 121 | ), 122 | child: Selector( 123 | selector: (p0, p1) => p1.bmi.height.height, 124 | shouldRebuild: (p, c) => p != c, 125 | builder: (context, data, _) { 126 | return Slider( 127 | value: data.toDouble(), 128 | min: 120, 129 | max: 220, 130 | onChanged: (double newValue) { 131 | context 132 | .read() 133 | .changeHeightValue(newValue.toInt()); 134 | }, 135 | ); 136 | }), 137 | ) 138 | ], 139 | ), 140 | ) 141 | : Container( 142 | height: 140.0, 143 | padding: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0), 144 | child: Column( 145 | children: [ 146 | Text( 147 | 'Height\n', 148 | style: TextStyle( 149 | fontWeight: FontWeight.w900, 150 | color: Theme.of(context).colorScheme.accentColor), 151 | ), 152 | Row( 153 | crossAxisAlignment: CrossAxisAlignment.center, 154 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 155 | children: [ 156 | Container( 157 | height: 90.0, 158 | padding: EdgeInsets.all(24.0), 159 | decoration: BoxDecoration( 160 | color: Theme.of(context).colorScheme.buttonColor, 161 | borderRadius: BorderRadius.circular(20.0), 162 | ), 163 | child: Selector( 164 | selector: (p0, p1) => p1.bmi.height.feet, 165 | shouldRebuild: (p, c) => p != c, 166 | builder: (context, data, _) { 167 | return DropdownButton( 168 | isDense: true, 169 | value: data, 170 | hint: Text('ft'), 171 | icon: Icon( 172 | Icons.arrow_downward, 173 | color: Colors.grey, 174 | ), 175 | iconSize: 24, 176 | elevation: 16, 177 | // isExpanded: true, 178 | style: TextStyle( 179 | fontSize: 40.0, 180 | fontWeight: FontWeight.w900, 181 | color: Theme.of(context).colorScheme.accentColor), 182 | underline: Container( 183 | color: Colors.transparent, 184 | ), 185 | onChanged: (int? newValue) { 186 | context 187 | .read() 188 | .changeHeightFeet(newValue); 189 | }, 190 | items: [ 191 | 1, 192 | 2, 193 | 3, 194 | 4, 195 | 5, 196 | 6, 197 | 7, 198 | 8, 199 | 9, 200 | 10 201 | ].map>((int value) { 202 | return DropdownMenuItem( 203 | value: value, 204 | child: Text( 205 | value.toString(), 206 | style: TextStyle(fontSize: 40.0), 207 | ), 208 | ); 209 | }).toList(), 210 | ); 211 | }), 212 | ), 213 | Container( 214 | height: 90.0, 215 | padding: EdgeInsets.all(24.0), 216 | decoration: BoxDecoration( 217 | color: Theme.of(context).colorScheme.buttonColor, 218 | borderRadius: BorderRadius.circular(20.0), 219 | ), 220 | child: Selector( 221 | selector: (p0, p1) => p1.bmi.height.inch, 222 | shouldRebuild: (p, c) => p != c, 223 | builder: (context, data, _) { 224 | return DropdownButton( 225 | isDense: true, 226 | value: data, 227 | hint: Text('in'), 228 | icon: Icon( 229 | Icons.arrow_downward, 230 | color: Colors.grey, 231 | ), 232 | iconSize: 24, 233 | elevation: 16, 234 | style: TextStyle( 235 | fontSize: 45.0, 236 | fontWeight: FontWeight.w900, 237 | color: Theme.of(context).colorScheme.accentColor), 238 | underline: Container( 239 | color: Colors.transparent, 240 | ), 241 | onChanged: (int? newValue) { 242 | context 243 | .read() 244 | .changeHeightInch(newValue); 245 | }, 246 | items: [ 247 | 0, 248 | 1, 249 | 2, 250 | 3, 251 | 4, 252 | 5, 253 | 6, 254 | 7, 255 | 8, 256 | 9, 257 | 10, 258 | 11, 259 | 12 260 | ].map>((int value) { 261 | return DropdownMenuItem( 262 | value: value, 263 | child: Text( 264 | "$value\"", 265 | style: TextStyle(fontSize: 40.0), 266 | ), 267 | ); 268 | }).toList(), 269 | ); 270 | }), 271 | ), 272 | ], 273 | ), 274 | ], 275 | ), 276 | ), 277 | duration: Duration(milliseconds: 600), 278 | ); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /lib/view/home/height_unit_switch.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class HeightUnitSwitch extends StatelessWidget { 6 | const HeightUnitSwitch({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | 11 | return Selector( 12 | selector: (p0, p1) => p1.bmi.height.isCm, 13 | shouldRebuild: (p, c) => p != c, 14 | builder: (context, data, _) { 15 | return Switch( 16 | value: !data, 17 | onChanged: (value) { 18 | context.read().changeHeightUnit(!value); 19 | }, 20 | inactiveTrackColor: Color.fromRGBO(209, 196, 233, 1), 21 | inactiveThumbColor: Colors.deepPurple, 22 | activeTrackColor: Color.fromRGBO(209, 196, 233, 1), 23 | activeColor: Colors.deepPurple, 24 | materialTapTargetSize: MaterialTapTargetSize.padded, 25 | ); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/view/home/home_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/data/model/bmi.dart'; 2 | import 'package:bmi_calculator/utility/bmi_util.dart'; 3 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 4 | import 'package:bmi_calculator/view/home/age_card_view.dart'; 5 | import 'package:bmi_calculator/view/home/calculate_button_view.dart'; 6 | import 'package:bmi_calculator/view/home/gender_card_view.dart'; 7 | import 'package:bmi_calculator/view/home/height_card_view.dart'; 8 | import 'package:bmi_calculator/view/home/weight_card_view.dart'; 9 | import 'package:bmi_calculator/view/result/result_view.dart'; 10 | import 'package:bmi_calculator/view/common/size_transition.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:provider/provider.dart'; 13 | 14 | class HomeView extends StatelessWidget { 15 | const HomeView({Key? key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return SafeArea( 20 | top: false, 21 | child: Column( 22 | children: [ 23 | Expanded( 24 | child: SingleChildScrollView( 25 | padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 74.0), 26 | child: Column( 27 | children: [ 28 | Padding( 29 | padding: const EdgeInsets.symmetric(horizontal: 6), 30 | child: Row( 31 | crossAxisAlignment: CrossAxisAlignment.center, 32 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 33 | children: [ 34 | Expanded(child: AgeCardView()), 35 | SizedBox(width: 5,), 36 | Expanded(child: WeightCardView()), 37 | ], 38 | ), 39 | ), 40 | HeightCardView(), 41 | GenderCardView(), 42 | ], 43 | ), 44 | ), 45 | ), 46 | CalculateButtonView( 47 | onTab: () { 48 | Bmi bmi = context.read().bmi; 49 | BmiUtil calc; 50 | if (bmi.height.isCm) { 51 | calc = BmiUtil( 52 | height: bmi.height.height, 53 | weight: bmi.weight.weight.toDouble(), 54 | isKg: bmi.weight.isKg, 55 | ); 56 | } else { 57 | calc = BmiUtil( 58 | height: BmiUtil.feetInchToCM(bmi.height.feet, bmi.height.inch), 59 | weight: bmi.weight.weight.toDouble(), 60 | isKg: bmi.weight.isKg, 61 | ); 62 | } 63 | Navigator.push( 64 | context, 65 | SizeRoute( 66 | page: ResultView( 67 | bmiResult: calc.calculateBMI(), 68 | resultText: calc.getResult(), 69 | resultTextStyle: calc.resultTextStyle(calc.getResult()), 70 | interpretation: calc.getInterpretation(), 71 | ), 72 | ), 73 | ); 74 | }, 75 | ) 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/view/home/weight_card_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/color_scheme.dart'; 2 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class WeightCardView extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Card( 11 | elevation: 2.0, 12 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 13 | child: Padding( 14 | padding: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 10.0), 15 | child: Column( 16 | crossAxisAlignment: CrossAxisAlignment.center, 17 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 18 | children: [ 19 | Selector( 20 | selector: (p0, p1) => p1.bmi.weight.isKg, 21 | shouldRebuild: (p, c) => p != c, 22 | builder: (context, data, _) { 23 | return Text( 24 | 'Weight (${data?"kg":"lbs"})', 25 | style: TextStyle( 26 | fontWeight: FontWeight.w900, 27 | color: Theme.of(context).colorScheme.accentColor), 28 | ); 29 | }), 30 | Selector( 31 | selector: (p0, p1) => p1.bmi.weight.weight, 32 | shouldRebuild: (p, c) => p != c, 33 | builder: (context, data, _) { 34 | return Text( 35 | data.toString(), 36 | style: TextStyle( 37 | fontSize: 60.0, 38 | fontWeight: FontWeight.w900, 39 | color: Theme.of(context).colorScheme.accentColor), 40 | ); 41 | }), 42 | Row( 43 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 44 | children: [ 45 | GestureDetector( 46 | child: CircleAvatar( 47 | radius: 20.0, 48 | backgroundColor: Theme.of(context).colorScheme.buttonColor, 49 | child: IconButton( 50 | icon: Icon( 51 | FontAwesomeIcons.minus, 52 | color: Theme.of(context).iconTheme.color, 53 | ), 54 | onPressed: () { 55 | context.read().changeWeightValue(-1); 56 | }, 57 | ), 58 | ), 59 | onLongPressStart: (details) { 60 | context.read().setButtonPress(true); 61 | context.read().decreaseWeight(); 62 | }, 63 | onLongPressUp: () { 64 | context.read().setButtonPress(false); 65 | }, 66 | ), 67 | GestureDetector( 68 | child: CircleAvatar( 69 | radius: 20.0, 70 | backgroundColor: Theme.of(context).colorScheme.buttonColor, 71 | child: IconButton( 72 | icon: Icon( 73 | FontAwesomeIcons.plus, 74 | color: Theme.of(context).iconTheme.color, 75 | ), 76 | onPressed: () { 77 | context.read().changeWeightValue(1); 78 | }, 79 | ), 80 | ), 81 | onLongPressStart: (details) { 82 | context.read().setButtonPress(true); 83 | context.read().increaseWeight(); 84 | }, 85 | onLongPressUp: () { 86 | context.read().setButtonPress(false); 87 | }, 88 | ) 89 | ], 90 | ) 91 | ], 92 | ), 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/view/result/result_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/color_scheme.dart'; 2 | import 'package:bmi_calculator/core/constants.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | 6 | class ResultView extends StatelessWidget { 7 | final String bmiResult; 8 | final String resultText; 9 | final String interpretation; 10 | final TextStyle resultTextStyle; 11 | 12 | ResultView({ 13 | required this.bmiResult, 14 | required this.resultText, 15 | required this.resultTextStyle, 16 | required this.interpretation, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scaffold( 22 | appBar: AppBar( 23 | elevation: 0.0, 24 | title: Text( 25 | 'YOUR HEALTH STATUS', 26 | textDirection: TextDirection.ltr, 27 | ), 28 | ), 29 | body: SafeArea( 30 | top: false, 31 | child: Column( 32 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 33 | crossAxisAlignment: CrossAxisAlignment.stretch, 34 | children: [ 35 | Expanded( 36 | flex: 2, 37 | child: Card( 38 | color: Theme.of(context).cardTheme.color!, 39 | shape: RoundedRectangleBorder( 40 | borderRadius: BorderRadius.circular(16)), 41 | elevation: 15.0, 42 | margin: EdgeInsets.all(15.0), 43 | child: Column( 44 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 45 | crossAxisAlignment: CrossAxisAlignment.center, 46 | children: [ 47 | Flex( 48 | direction: Axis.vertical, 49 | children: [ 50 | RichText( 51 | text: TextSpan( 52 | text: '', 53 | style: TextStyle(fontSize: 24, color: Colors.black), 54 | children: [ 55 | TextSpan( 56 | text: bmiResult.split(".")[0].toString(), 57 | style: TextStyle( 58 | color: Theme.of(context).colorScheme.accentColor, 59 | fontSize: 100.0, 60 | fontWeight: FontWeight.bold, 61 | )), 62 | TextSpan( 63 | text: 64 | '.' + bmiResult.split(".")[1].toString(), 65 | style: TextStyle( 66 | fontSize: 36.0, 67 | color: Colors.grey.shade600)), 68 | ]), 69 | ), 70 | Text( 71 | 'Your BMI index', 72 | style: TextStyle( 73 | fontSize: 16.0, 74 | color: Color.fromRGBO(218, 218, 222, 1), 75 | fontWeight: FontWeight.w600, 76 | ), 77 | ), 78 | ], 79 | ), 80 | Text( 81 | resultText, 82 | textAlign: TextAlign.center, 83 | style: resultTextStyle, 84 | ), 85 | Padding( 86 | padding: EdgeInsets.symmetric(horizontal: 30), 87 | child: Text( 88 | interpretation, 89 | textAlign: TextAlign.center, 90 | style: TextStyle( 91 | color: Theme.of(context).colorScheme.accentColor, 92 | fontWeight: FontWeight.w700), 93 | ), 94 | ), 95 | Column( 96 | // crossAxisAlignment: CrossAxisAlignment.center, 97 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 98 | verticalDirection: VerticalDirection.up, 99 | children: [ 100 | Padding( 101 | padding: const EdgeInsets.all(10.0), 102 | child: MaterialButton( 103 | height: 50, 104 | minWidth: 150, 105 | shape: RoundedRectangleBorder( 106 | borderRadius: BorderRadius.circular(30)), 107 | color: Colors.deepPurple, 108 | child: Text( 109 | "Re-Calculate".toUpperCase(), 110 | style: TextStyle(color: Colors.white), 111 | ), 112 | onPressed: () { 113 | Navigator.pop(context); 114 | }, 115 | ), 116 | ), 117 | MaterialButton( 118 | height: 50, 119 | minWidth: 150, 120 | shape: RoundedRectangleBorder( 121 | borderRadius: BorderRadius.circular(30)), 122 | color: Colors.deepPurple.shade200, 123 | child: Text( 124 | "BMI Chart".toUpperCase(), 125 | style: TextStyle(color: Colors.white), 126 | ), 127 | onPressed: () { 128 | bmiChartBottomSheet(context); 129 | }, 130 | ), 131 | ], 132 | ), 133 | ], 134 | ), 135 | ), 136 | ), 137 | ], 138 | ), 139 | ), 140 | ); 141 | } 142 | 143 | void bmiChartBottomSheet(context) { 144 | showModalBottomSheet( 145 | context: context, 146 | builder: (BuildContext bc) { 147 | return new Container( 148 | height: 350.0, 149 | margin: EdgeInsets.all(10), 150 | color: Colors.transparent, //could change this to Color(0xFF737373), 151 | //so you don't have to change MaterialApp canvasColor 152 | child: new Container( 153 | decoration: new BoxDecoration( 154 | color: Theme.of(context).cardTheme.color, 155 | borderRadius: new BorderRadius.only( 156 | topLeft: const Radius.circular(10.0), 157 | topRight: const Radius.circular(10.0), 158 | bottomLeft: const Radius.circular(10.0), 159 | bottomRight: const Radius.circular(10.0))), 160 | child: new Center( 161 | child: new ListView( 162 | children: [ 163 | ListTile( 164 | leading: Icon( 165 | FontAwesomeIcons.solidCircle, 166 | color: Colors.transparent, 167 | ), 168 | title: Text('Weight Categories', style: listTrailing), 169 | trailing: Text("Index", style: listTrailing), 170 | ), 171 | Divider( 172 | color: Colors.grey, 173 | ), 174 | ListTile( 175 | selected: true, 176 | leading: Icon( 177 | FontAwesomeIcons.solidCircle, 178 | color: Color.fromRGBO(241, 198, 231, 1), 179 | ), 180 | title: 181 | Text('Very severely underweight', style: listTitle), 182 | trailing: Text("<= 16", style: listTrailing), 183 | ), 184 | ListTile( 185 | leading: Icon( 186 | FontAwesomeIcons.solidCircle, 187 | color: Color.fromRGBO(229, 176, 234, 1), 188 | ), 189 | title: Text('Severely underweight', style: listTitle), 190 | trailing: Text("16 - 16.9", style: listTrailing), 191 | ), 192 | ListTile( 193 | leading: Icon( 194 | FontAwesomeIcons.solidCircle, 195 | color: Color.fromRGBO(189, 131, 206, 1), 196 | ), 197 | title: Text('Underweight', style: listTitle), 198 | trailing: Text("17 - 18.4", style: listTrailing), 199 | ), 200 | ListTile( 201 | leading: Icon( 202 | FontAwesomeIcons.solidCircle, 203 | color: Color.fromRGBO(82, 222, 151, 1), 204 | ), 205 | title: Text('Healthy weight', style: listTitle), 206 | trailing: Text("18.5 - 24.9", style: listTrailing), 207 | ), 208 | ListTile( 209 | leading: Icon( 210 | FontAwesomeIcons.solidCircle, 211 | color: Color.fromRGBO(241, 188, 49, 1), 212 | ), 213 | title: Text('Overweight', style: listTitle), 214 | trailing: Text("25 - 29.9", style: listTrailing), 215 | ), 216 | ListTile( 217 | leading: Icon( 218 | FontAwesomeIcons.solidCircle, 219 | color: Color.fromRGBO(226, 88, 34, 1), 220 | ), 221 | title: Text('Obese class I', style: listTitle), 222 | trailing: Text("30 - 34.9", style: listTrailing), 223 | ), 224 | ListTile( 225 | leading: Icon( 226 | FontAwesomeIcons.solidCircle, 227 | color: Color.fromRGBO(178, 34, 34, 1), 228 | ), 229 | title: Text('Obese class II', style: listTitle), 230 | trailing: Text("35 - 39.9", style: listTrailing), 231 | ), 232 | ListTile( 233 | leading: Icon( 234 | FontAwesomeIcons.solidCircle, 235 | color: Color.fromRGBO(124, 10, 2, 1), 236 | ), 237 | title: Text('Obese class III', style: listTitle), 238 | trailing: Text(">= 40", style: listTrailing), 239 | ), 240 | ], 241 | ), 242 | )), 243 | ); 244 | }); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /lib/view/setting/setting_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/core/color_scheme.dart'; 2 | import 'package:bmi_calculator/utility/app_util.dart'; 3 | import 'package:bmi_calculator/view/dashboard/bmi_provider.dart'; 4 | import 'package:bmi_calculator/view/setting/theme_change_switch.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class SettingView extends StatelessWidget { 9 | const SettingView({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: AppBar( 15 | elevation: 0.0, 16 | centerTitle: false, 17 | title: Text( 18 | 'Settings', 19 | textScaleFactor: 1.2, 20 | textDirection: TextDirection.ltr, 21 | textAlign: TextAlign.start, 22 | style: Theme.of(context).appBarTheme.titleTextStyle!.copyWith( 23 | fontSize: 20, 24 | fontWeight: FontWeight.bold, 25 | ), 26 | ), 27 | ), 28 | body: Container( 29 | color: Theme.of(context).primaryColor, 30 | width: MediaQuery.of(context).size.width, 31 | child: Column( 32 | children: [ 33 | Card( 34 | margin: EdgeInsets.all(10.0), 35 | elevation: 2.0, 36 | shape: RoundedRectangleBorder( 37 | borderRadius: BorderRadius.circular(12)), 38 | child: Container( 39 | padding: EdgeInsets.fromLTRB(20.0, 25.0, 20.0, 30.0), 40 | decoration: BoxDecoration( 41 | color: Theme.of(context).cardColor, 42 | borderRadius: BorderRadius.circular(12.0), 43 | image: DecorationImage( 44 | image: AssetImage("Assets/Images/daynight.png"), 45 | fit: BoxFit.scaleDown)), 46 | child: Row( 47 | crossAxisAlignment: CrossAxisAlignment.center, 48 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 49 | children: [ 50 | Text( 51 | "Dark Mode", 52 | style: TextStyle( 53 | color: Theme.of(context).colorScheme.accentColor, 54 | fontSize: 16.0, 55 | fontWeight: FontWeight.w800), 56 | ), 57 | ThemeChangeSwitch(), 58 | ], 59 | ), 60 | ), 61 | ), 62 | Card( 63 | margin: EdgeInsets.all(10.0), 64 | elevation: 2.0, 65 | shape: RoundedRectangleBorder( 66 | borderRadius: BorderRadius.circular(12)), 67 | child: Container( 68 | padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 0.0), 69 | decoration: BoxDecoration( 70 | color: Theme.of(context).cardColor, 71 | borderRadius: BorderRadius.circular(12.0), 72 | ), 73 | child: Column( 74 | children: [ 75 | Text( 76 | "Unit of Measurement", 77 | style: TextStyle( 78 | color: Theme.of(context).colorScheme.accentColor, 79 | fontSize: 16.0, 80 | fontWeight: FontWeight.w800), 81 | ), 82 | Selector( 83 | selector: (p0, p1) => p1.bmi.height.isCm, 84 | shouldRebuild: (p, c) => p != c, 85 | builder: (context, data, _) { 86 | return Row( 87 | crossAxisAlignment: CrossAxisAlignment.center, 88 | mainAxisAlignment: MainAxisAlignment.center, 89 | children: [ 90 | Container( 91 | padding: EdgeInsets.all(10.0), 92 | child: ChoiceChip( 93 | selectedColor: 94 | Theme.of(context).chipTheme.selectedColor, 95 | backgroundColor: Theme.of(context) 96 | .chipTheme 97 | .backgroundColor, 98 | label: Text(AppUtil.measurementUnitList[0]), 99 | selected: data, 100 | onSelected: (selected) { 101 | context 102 | .read() 103 | .changeHeightUnit(true); 104 | }, 105 | ), 106 | ), 107 | Container( 108 | padding: EdgeInsets.all(10.0), 109 | child: ChoiceChip( 110 | selectedColor: 111 | Theme.of(context).chipTheme.selectedColor, 112 | backgroundColor: Theme.of(context) 113 | .chipTheme 114 | .backgroundColor, 115 | label: Text(AppUtil.measurementUnitList[1]), 116 | selected: !data, 117 | onSelected: (selected) { 118 | context 119 | .read() 120 | .changeHeightUnit(false); 121 | }, 122 | ), 123 | ) 124 | ], 125 | ); 126 | }), 127 | ], 128 | ), 129 | ), 130 | ), 131 | Card( 132 | margin: EdgeInsets.all(10.0), 133 | elevation: 2.0, 134 | shape: RoundedRectangleBorder( 135 | borderRadius: BorderRadius.circular(12)), 136 | child: Container( 137 | padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 0.0), 138 | decoration: BoxDecoration( 139 | color: Theme.of(context).cardColor, 140 | borderRadius: BorderRadius.circular(12.0), 141 | ), 142 | child: Column( 143 | children: [ 144 | Text( 145 | "Unit of Weight", 146 | style: TextStyle( 147 | color: Theme.of(context).colorScheme.accentColor, 148 | fontSize: 16.0, 149 | fontWeight: FontWeight.w800), 150 | ), 151 | Selector( 152 | selector: (p0, p1) => p1.bmi.weight.isKg, 153 | shouldRebuild: (p, c) => p != c, 154 | builder: (context, data, _) { 155 | return Row( 156 | crossAxisAlignment: CrossAxisAlignment.center, 157 | mainAxisAlignment: MainAxisAlignment.center, 158 | children: [ 159 | Container( 160 | padding: EdgeInsets.all(10.0), 161 | child: ChoiceChip( 162 | selectedColor: 163 | Theme.of(context).chipTheme.selectedColor, 164 | backgroundColor: Theme.of(context) 165 | .chipTheme 166 | .backgroundColor, 167 | label: Text(AppUtil.weightUnitList[0]), 168 | selected: data, 169 | onSelected: (selected) { 170 | context 171 | .read() 172 | .changeWeightUnit(true); 173 | }, 174 | ), 175 | ), 176 | Container( 177 | padding: EdgeInsets.all(10.0), 178 | child: ChoiceChip( 179 | selectedColor: 180 | Theme.of(context).chipTheme.selectedColor, 181 | backgroundColor: Theme.of(context) 182 | .chipTheme 183 | .backgroundColor, 184 | label: Text(AppUtil.weightUnitList[1]), 185 | selected: !data, 186 | onSelected: (selected) { 187 | context 188 | .read() 189 | .changeWeightUnit(false); 190 | }, 191 | ), 192 | ) 193 | ], 194 | ); 195 | }), 196 | ], 197 | ), 198 | ), 199 | ), 200 | ], 201 | ), 202 | ), 203 | ); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /lib/view/setting/theme_change_switch.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/view/app/theme_provider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class ThemeChangeSwitch extends StatelessWidget { 6 | const ThemeChangeSwitch({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Consumer(builder: (context, data, child) { 11 | return Switch( 12 | value: data.themeMode == ThemeMode.dark, 13 | onChanged: (value) { 14 | context.read().changeTheme( 15 | data.themeMode == ThemeMode.light 16 | ? ThemeMode.dark 17 | : ThemeMode.light); 18 | }, 19 | inactiveTrackColor: Colors.grey.shade300, 20 | inactiveThumbColor: Colors.grey, 21 | activeTrackColor: Color.fromRGBO(209, 196, 233, 1), 22 | activeColor: Colors.deepPurple, 23 | materialTapTargetSize: MaterialTapTargetSize.padded, 24 | ); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/view/splash/splash.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bmi_calculator/view/common/size_transition.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:animated_text_kit/animated_text_kit.dart'; 5 | 6 | import '../dashboard/dashboard_view.dart'; 7 | 8 | class Splash extends StatefulWidget { 9 | @override 10 | _SplashState createState() => _SplashState(); 11 | } 12 | 13 | class _SplashState extends State { 14 | startTime() async { 15 | var _duration = new Duration(seconds: 3); 16 | return Timer(_duration, navigationPage); 17 | } 18 | 19 | void navigationPage() { 20 | Navigator.of(context).pushReplacement(SizeRoute(page: DashboardView())); 21 | } 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | startTime(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | extendBodyBehindAppBar: true, 33 | extendBody: true, 34 | body: Stack( 35 | children: [ 36 | Container( 37 | child: Image.asset( 38 | "Assets/Images/fitmen.jpg", 39 | height: double.infinity, 40 | width: double.infinity, 41 | alignment: Alignment.center, 42 | fit: BoxFit.cover, 43 | excludeFromSemantics: true, 44 | scale: 50.0, 45 | ), 46 | ), 47 | Container( 48 | color: Colors.black54, 49 | ), 50 | Container( 51 | alignment: Alignment.centerLeft, 52 | padding: EdgeInsets.all(40.0), 53 | child: Column( 54 | mainAxisAlignment: MainAxisAlignment.center, 55 | crossAxisAlignment: CrossAxisAlignment.start, 56 | children: [ 57 | Text( 58 | "Calculate your", 59 | style: TextStyle( 60 | color: Colors.white, 61 | fontSize: 24.0, 62 | fontWeight: FontWeight.w500), 63 | ), 64 | AnimatedTextKit( 65 | animatedTexts: [ 66 | TyperAnimatedText( 67 | "BMI.", 68 | speed: Duration(milliseconds: 400), 69 | textStyle: TextStyle( 70 | color: Colors.green.shade200, 71 | fontSize: 100.0, 72 | fontWeight: FontWeight.w900, 73 | ), 74 | ), 75 | ], 76 | pause: Duration(seconds: 2), 77 | isRepeatingAnimation: false, 78 | ) 79 | ], 80 | ), 81 | ), 82 | Opacity( 83 | opacity: 0.0, 84 | child: Container( 85 | alignment: Alignment.bottomCenter, 86 | margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 60.0), 87 | child: MaterialButton( 88 | shape: RoundedRectangleBorder( 89 | borderRadius: BorderRadius.circular(22.0)), 90 | minWidth: 200.0, 91 | height: 45, 92 | elevation: 10.0, 93 | color: Colors.white, 94 | child: new Text('Let\'s get started', 95 | style: new TextStyle( 96 | fontSize: 16.0, 97 | color: Colors.deepPurple, 98 | fontWeight: FontWeight.w800)), 99 | onPressed: () {}, 100 | ), 101 | ), 102 | ), 103 | ], 104 | ), 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: bmi_calculator 2 | description: A new Flutter project. 3 | 4 | version: 1.0.5+9 5 | 6 | environment: 7 | sdk: '>=3.0.1 <4.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | font_awesome_flutter: ^10.4.0 14 | animated_text_kit: ^4.2.2 15 | shared_preferences: ^2.1.1 16 | share_plus: ^7.0.2 17 | launch_review: ^3.0.1 18 | url_launcher: ^6.1.11 19 | package_info_plus: ^4.0.2 20 | cupertino_icons: ^1.0.5 21 | provider: ^6.0.5 22 | 23 | firebase_core: ^2.4.1 24 | firebase_crashlytics: ^3.0.11 25 | firebase_analytics: ^10.1.0 26 | 27 | dev_dependencies: 28 | flutter_launcher_icons: ^0.13.1 29 | 30 | flutter_icons: 31 | image_path: "Assets/Images/ic_launcher.png" 32 | android: true 33 | ios: true 34 | min_sdk_android: 21 35 | 36 | flutter: 37 | uses-material-design: true 38 | assets: 39 | - Assets/Images/ 40 | 41 | fonts: 42 | - family: GoogleSans 43 | fonts: 44 | - asset: Assets/fonts/GoogleSansRegular.ttf 45 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | void main() { 9 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async { 10 | // // Build our app and trigger a frame. 11 | // await tester.pumpWidget(MyApp()); 12 | // 13 | // // Verify that our counter starts at 0. 14 | // expect(find.text('0'), findsOneWidget); 15 | // expect(find.text('1'), findsNothing); 16 | // 17 | // // Tap the '+' icon and trigger a frame. 18 | // await tester.tap(find.byIcon(Icons.add)); 19 | // await tester.pump(); 20 | // 21 | // // Verify that our counter has incremented. 22 | // expect(find.text('0'), findsNothing); 23 | // expect(find.text('1'), findsOneWidget); 24 | // }); 25 | } 26 | --------------------------------------------------------------------------------