├── .gitignore ├── .metadata ├── .travis.yml ├── LICENSE ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── bmicalculator │ │ │ └── MainActivity.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 │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── final_result.gif ├── fonts ├── SF-Pro-Display-Black.otf ├── SF-Pro-Display-Bold.otf ├── SF-Pro-Display-Heavy.otf ├── SF-Pro-Display-Light.otf ├── SF-Pro-Display-Medium.otf ├── SF-Pro-Display-Regular.otf ├── SF-Pro-Display-Semibold.otf ├── SF-Pro-Display-Thin.otf └── SF-Pro-Display-Ultralight.otf ├── images ├── gender_arrow.svg ├── gender_female.svg ├── gender_male.svg ├── gender_other.svg ├── pacman.svg ├── person.svg ├── user.svg └── weight_arrow.svg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.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 │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── main.m ├── lib ├── app_bar.dart ├── calculator.dart ├── fade_route.dart ├── input_page │ ├── card_title.dart │ ├── gender │ │ ├── gender_arrow.dart │ │ ├── gender_card.dart │ │ ├── gender_circle.dart │ │ ├── gender_icon.dart │ │ └── gender_styles.dart │ ├── height │ │ ├── height_card.dart │ │ ├── height_picker.dart │ │ ├── height_slider.dart │ │ └── height_styles.dart │ ├── input_page.dart │ ├── input_page_styles.dart │ ├── pacman_slider.dart │ ├── transition_dot.dart │ └── weight │ │ ├── weight_card.dart │ │ └── weight_slider.dart ├── main.dart ├── model │ └── gender.dart ├── result_page │ └── result_page.dart ├── styles.dart └── widget_utils.dart ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | 11 | *.iml 12 | .idea/ 13 | res/ 14 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: e5432a2843bd6c5eefc3d6c0d48ec3155bd53969 8 | channel: master 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | sudo: false 4 | addons: 5 | apt: 6 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18 7 | sources: 8 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version 9 | packages: 10 | - libstdc++6 11 | - fonts-droid 12 | before_script: 13 | - git clone https://github.com/flutter/flutter.git -b beta --depth 1 14 | - ./flutter/bin/flutter doctor 15 | - gem install coveralls-lcov 16 | script: 17 | - ./flutter/bin/flutter test --coverage 18 | after_success: 19 | - coveralls-lcov coverage/lcov.info 20 | cache: 21 | directories: 22 | - $HOME/.pub-cache 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Marcin Szałek 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 [![Build Status](https://travis-ci.org/MarcinusX/bmi_calculator.svg?branch=master)](https://travis-ci.org/MarcinusX/bmi_calculator) 2 | 3 | Implementation of [Johny Vino](https://dribbble.com/johnyvino)'s [design](https://dribbble.com/shots/3558444-BMI-Calculator). 4 | ## Design(s): 5 | ![Design](https://marcinszalek.pl/wp-content/uploads/2018/11/ezgif.com-resize.gif)![Design2](https://cdn.dribbble.com/users/997070/screenshots/5033686/johnyvino_upload_4x.png) 6 | ## Implementation: 7 | ![Implementation](final_result.gif) 8 | 9 | ## [Related blog posts](https://marcinszalek.pl/tag/bmi-calculator/) 10 | * [GenderCard](https://marcinszalek.pl/flutter/bmi-calculator-gender/) 11 | * [WeightCard](https://marcinszalek.pl/flutter/bmi-calculator-weight/) 12 | * [HeightCard](https://marcinszalek.pl/flutter/bmi-calculator-height/) 13 | * [Static layouts](https://marcinszalek.pl/flutter/bmi-calculator-layouts/) 14 | * [Animated slider hint](https://marcinszalek.pl/flutter/bmi-calculator-animated-slider-hint/) 15 | * [Custom slider](https://marcinszalek.pl/flutter/bmi-calculator-custom-slider/) 16 | * [Animated transition](https://marcinszalek.pl/flutter/bmi-calculator-in-flutter-part-7-animated-transition/) 17 | 18 | ## Available also on Medium! 19 | [See all related Medium posts](https://medium.com/flutter-community/marcin-sza%C5%82ek-bmi/home) 20 | 21 | ## Questions? 22 | Contact me at marcin@fidev.io 23 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /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 flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.bmicalculator" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/bmicalculator/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.bmicalculator; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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-4.4-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /final_result.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/final_result.gif -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Black.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Bold.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Heavy.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Heavy.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Light.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Medium.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Regular.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Semibold.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Thin.otf -------------------------------------------------------------------------------- /fonts/SF-Pro-Display-Ultralight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/fonts/SF-Pro-Display-Ultralight.otf -------------------------------------------------------------------------------- /images/gender_arrow.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/gender_female.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /images/gender_male.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /images/gender_other.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/pacman.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /images/person.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /images/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | noun_User_1904703_000000 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /images/weight_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 8.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 | def parse_KV_file(file, separator='=') 8 | file_abs_path = File.expand_path(file) 9 | if !File.exists? file_abs_path 10 | return []; 11 | end 12 | pods_ary = [] 13 | skip_line_start_symbols = ["#", "/"] 14 | File.foreach(file_abs_path) { |line| 15 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 16 | plugin = line.split(pattern=separator) 17 | if plugin.length == 2 18 | podname = plugin[0].strip() 19 | path = plugin[1].strip() 20 | podpath = File.expand_path("#{path}", file_abs_path) 21 | pods_ary.push({:name => podname, :path => podpath}); 22 | else 23 | puts "Invalid plugin specification: #{line}" 24 | end 25 | } 26 | return pods_ary 27 | end 28 | 29 | target 'Runner' do 30 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 31 | # referring to absolute paths on developers' machines. 32 | system('rm -rf .symlinks') 33 | system('mkdir -p .symlinks/plugins') 34 | 35 | # Flutter Pods 36 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 37 | if generated_xcode_build_settings.empty? 38 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 39 | end 40 | generated_xcode_build_settings.map { |p| 41 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 42 | symlink = File.join('.symlinks', 'flutter') 43 | File.symlink(File.dirname(p[:path]), symlink) 44 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 45 | end 46 | } 47 | 48 | # Plugin Pods 49 | plugin_pods = parse_KV_file('../.flutter-plugins') 50 | plugin_pods.map { |p| 51 | symlink = File.join('.symlinks', 'plugins', p[:name]) 52 | File.symlink(p[:path], symlink) 53 | pod p[:name], :path => File.join(symlink, 'ios') 54 | } 55 | end 56 | 57 | post_install do |installer| 58 | installer.pods_project.targets.each do |target| 59 | target.build_configurations.each do |config| 60 | config.build_settings['ENABLE_BITCODE'] = 'NO' 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | FBE56AA7ACED25F6B37D5402 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A80E9C884B45738C21C0DCB /* libPods-Runner.a */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXCopyFilesBuildPhase section */ 27 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 28 | isa = PBXCopyFilesBuildPhase; 29 | buildActionMask = 2147483647; 30 | dstPath = ""; 31 | dstSubfolderSpec = 10; 32 | files = ( 33 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 34 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 35 | ); 36 | name = "Embed Frameworks"; 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXCopyFilesBuildPhase section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 43 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 44 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 45 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 46 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 47 | 6A80E9C884B45738C21C0DCB /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 49 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 50 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 53 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 54 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 56 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 57 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 58 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 59 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 68 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 69 | FBE56AA7ACED25F6B37D5402 /* libPods-Runner.a in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | 283C19A5CDDCE6F30D6C9C44 /* Frameworks */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 6A80E9C884B45738C21C0DCB /* libPods-Runner.a */, 80 | ); 81 | name = Frameworks; 82 | sourceTree = ""; 83 | }; 84 | 5D976117F0C4C19808A99EF2 /* Pods */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | ); 88 | name = Pods; 89 | sourceTree = ""; 90 | }; 91 | 9740EEB11CF90186004384FC /* Flutter */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 95 | 3B80C3931E831B6300D905FE /* App.framework */, 96 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 97 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 98 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 99 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 100 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 101 | ); 102 | name = Flutter; 103 | sourceTree = ""; 104 | }; 105 | 97C146E51CF9000F007C117D = { 106 | isa = PBXGroup; 107 | children = ( 108 | 9740EEB11CF90186004384FC /* Flutter */, 109 | 97C146F01CF9000F007C117D /* Runner */, 110 | 97C146EF1CF9000F007C117D /* Products */, 111 | 5D976117F0C4C19808A99EF2 /* Pods */, 112 | 283C19A5CDDCE6F30D6C9C44 /* Frameworks */, 113 | ); 114 | sourceTree = ""; 115 | }; 116 | 97C146EF1CF9000F007C117D /* Products */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 97C146EE1CF9000F007C117D /* Runner.app */, 120 | ); 121 | name = Products; 122 | sourceTree = ""; 123 | }; 124 | 97C146F01CF9000F007C117D /* Runner */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 128 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 129 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 130 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 131 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 132 | 97C147021CF9000F007C117D /* Info.plist */, 133 | 97C146F11CF9000F007C117D /* Supporting Files */, 134 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 135 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 136 | ); 137 | path = Runner; 138 | sourceTree = ""; 139 | }; 140 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 97C146F21CF9000F007C117D /* main.m */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXNativeTarget section */ 151 | 97C146ED1CF9000F007C117D /* Runner */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 154 | buildPhases = ( 155 | 7DD57A06824F65403CCA5096 /* [CP] Check Pods Manifest.lock */, 156 | 9740EEB61CF901F6004384FC /* Run Script */, 157 | 97C146EA1CF9000F007C117D /* Sources */, 158 | 97C146EB1CF9000F007C117D /* Frameworks */, 159 | 97C146EC1CF9000F007C117D /* Resources */, 160 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 161 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 162 | BBC8D98D0EB55059DC07E0E5 /* [CP] Embed Pods Frameworks */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | ); 168 | name = Runner; 169 | productName = Runner; 170 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 171 | productType = "com.apple.product-type.application"; 172 | }; 173 | /* End PBXNativeTarget section */ 174 | 175 | /* Begin PBXProject section */ 176 | 97C146E61CF9000F007C117D /* Project object */ = { 177 | isa = PBXProject; 178 | attributes = { 179 | LastUpgradeCheck = 0910; 180 | ORGANIZATIONNAME = "The Chromium Authors"; 181 | TargetAttributes = { 182 | 97C146ED1CF9000F007C117D = { 183 | CreatedOnToolsVersion = 7.3.1; 184 | DevelopmentTeam = 3RRHFRV4Q4; 185 | }; 186 | }; 187 | }; 188 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 189 | compatibilityVersion = "Xcode 3.2"; 190 | developmentRegion = English; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | Base, 195 | ); 196 | mainGroup = 97C146E51CF9000F007C117D; 197 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 198 | projectDirPath = ""; 199 | projectRoot = ""; 200 | targets = ( 201 | 97C146ED1CF9000F007C117D /* Runner */, 202 | ); 203 | }; 204 | /* End PBXProject section */ 205 | 206 | /* Begin PBXResourcesBuildPhase section */ 207 | 97C146EC1CF9000F007C117D /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 212 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 213 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 214 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 215 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 216 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXResourcesBuildPhase section */ 221 | 222 | /* Begin PBXShellScriptBuildPhase section */ 223 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 224 | isa = PBXShellScriptBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | ); 228 | inputPaths = ( 229 | ); 230 | name = "Thin Binary"; 231 | outputPaths = ( 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | shellPath = /bin/sh; 235 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 236 | }; 237 | 7DD57A06824F65403CCA5096 /* [CP] Check Pods Manifest.lock */ = { 238 | isa = PBXShellScriptBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | ); 242 | inputPaths = ( 243 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 244 | "${PODS_ROOT}/Manifest.lock", 245 | ); 246 | name = "[CP] Check Pods Manifest.lock"; 247 | outputPaths = ( 248 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | shellPath = /bin/sh; 252 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 253 | showEnvVarsInLog = 0; 254 | }; 255 | 9740EEB61CF901F6004384FC /* Run Script */ = { 256 | isa = PBXShellScriptBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ); 260 | inputPaths = ( 261 | ); 262 | name = "Run Script"; 263 | outputPaths = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | shellPath = /bin/sh; 267 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 268 | }; 269 | BBC8D98D0EB55059DC07E0E5 /* [CP] Embed Pods Frameworks */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 276 | "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", 277 | ); 278 | name = "[CP] Embed Pods Frameworks"; 279 | outputPaths = ( 280 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | shellPath = /bin/sh; 284 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 285 | showEnvVarsInLog = 0; 286 | }; 287 | /* End PBXShellScriptBuildPhase section */ 288 | 289 | /* Begin PBXSourcesBuildPhase section */ 290 | 97C146EA1CF9000F007C117D /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 295 | 97C146F31CF9000F007C117D /* main.m in Sources */, 296 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXVariantGroup section */ 303 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | 97C146FB1CF9000F007C117D /* Base */, 307 | ); 308 | name = Main.storyboard; 309 | sourceTree = ""; 310 | }; 311 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 97C147001CF9000F007C117D /* Base */, 315 | ); 316 | name = LaunchScreen.storyboard; 317 | sourceTree = ""; 318 | }; 319 | /* End PBXVariantGroup section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | 97C147031CF9000F007C117D /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_CXX_LIBRARY = "libc++"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = YES; 332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_COMMA = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 337 | CLANG_WARN_EMPTY_BODY = YES; 338 | CLANG_WARN_ENUM_CONVERSION = YES; 339 | CLANG_WARN_INFINITE_RECURSION = YES; 340 | CLANG_WARN_INT_CONVERSION = YES; 341 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 342 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 345 | CLANG_WARN_STRICT_PROTOTYPES = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_UNREACHABLE_CODE = YES; 348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 349 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 350 | COPY_PHASE_STRIP = NO; 351 | DEBUG_INFORMATION_FORMAT = dwarf; 352 | ENABLE_STRICT_OBJC_MSGSEND = YES; 353 | ENABLE_TESTABILITY = YES; 354 | GCC_C_LANGUAGE_STANDARD = gnu99; 355 | GCC_DYNAMIC_NO_PIC = NO; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_OPTIMIZATION_LEVEL = 0; 358 | GCC_PREPROCESSOR_DEFINITIONS = ( 359 | "DEBUG=1", 360 | "$(inherited)", 361 | ); 362 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 363 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 364 | GCC_WARN_UNDECLARED_SELECTOR = YES; 365 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 366 | GCC_WARN_UNUSED_FUNCTION = YES; 367 | GCC_WARN_UNUSED_VARIABLE = YES; 368 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 369 | MTL_ENABLE_DEBUG_INFO = YES; 370 | ONLY_ACTIVE_ARCH = YES; 371 | SDKROOT = iphoneos; 372 | TARGETED_DEVICE_FAMILY = "1,2"; 373 | }; 374 | name = Debug; 375 | }; 376 | 97C147041CF9000F007C117D /* Release */ = { 377 | isa = XCBuildConfiguration; 378 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_NONNULL = YES; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_COMMA = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 397 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 398 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 399 | CLANG_WARN_STRICT_PROTOTYPES = YES; 400 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 401 | CLANG_WARN_UNREACHABLE_CODE = YES; 402 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 403 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 404 | COPY_PHASE_STRIP = NO; 405 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 406 | ENABLE_NS_ASSERTIONS = NO; 407 | ENABLE_STRICT_OBJC_MSGSEND = YES; 408 | GCC_C_LANGUAGE_STANDARD = gnu99; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 417 | MTL_ENABLE_DEBUG_INFO = NO; 418 | SDKROOT = iphoneos; 419 | TARGETED_DEVICE_FAMILY = "1,2"; 420 | VALIDATE_PRODUCT = YES; 421 | }; 422 | name = Release; 423 | }; 424 | 97C147061CF9000F007C117D /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 427 | buildSettings = { 428 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 429 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 430 | DEVELOPMENT_TEAM = 3RRHFRV4Q4; 431 | ENABLE_BITCODE = NO; 432 | FRAMEWORK_SEARCH_PATHS = ( 433 | "$(inherited)", 434 | "$(PROJECT_DIR)/Flutter", 435 | ); 436 | INFOPLIST_FILE = Runner/Info.plist; 437 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 438 | LIBRARY_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "$(PROJECT_DIR)/Flutter", 441 | ); 442 | PRODUCT_BUNDLE_IDENTIFIER = com.mszalek.bmiCalculator; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | VERSIONING_SYSTEM = "apple-generic"; 445 | }; 446 | name = Debug; 447 | }; 448 | 97C147071CF9000F007C117D /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 451 | buildSettings = { 452 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 453 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 454 | DEVELOPMENT_TEAM = 3RRHFRV4Q4; 455 | ENABLE_BITCODE = NO; 456 | FRAMEWORK_SEARCH_PATHS = ( 457 | "$(inherited)", 458 | "$(PROJECT_DIR)/Flutter", 459 | ); 460 | INFOPLIST_FILE = Runner/Info.plist; 461 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 462 | LIBRARY_SEARCH_PATHS = ( 463 | "$(inherited)", 464 | "$(PROJECT_DIR)/Flutter", 465 | ); 466 | PRODUCT_BUNDLE_IDENTIFIER = com.mszalek.bmiCalculator; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | VERSIONING_SYSTEM = "apple-generic"; 469 | }; 470 | name = Release; 471 | }; 472 | /* End XCBuildConfiguration section */ 473 | 474 | /* Begin XCConfigurationList section */ 475 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 476 | isa = XCConfigurationList; 477 | buildConfigurations = ( 478 | 97C147031CF9000F007C117D /* Debug */, 479 | 97C147041CF9000F007C117D /* Release */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 485 | isa = XCConfigurationList; 486 | buildConfigurations = ( 487 | 97C147061CF9000F007C117D /* Debug */, 488 | 97C147071CF9000F007C117D /* Release */, 489 | ); 490 | defaultConfigurationIsVisible = 0; 491 | defaultConfigurationName = Release; 492 | }; 493 | /* End XCConfigurationList section */ 494 | }; 495 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 496 | } 497 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /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/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /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.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.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.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.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" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@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/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinusX/bmi_calculator/7ecb126cacffc94b3866b6e2cff1219382241fea/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 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | bmi_calculator 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 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 | 45 | 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/input_page_styles.dart'; 2 | import 'package:bmi_calculator/widget_utils.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_svg/flutter_svg.dart'; 5 | 6 | class BmiAppBar extends StatelessWidget { 7 | final bool isInputPage; 8 | static const String wavingHandEmoji = "\uD83D\uDC4B"; 9 | static const String whiteSkinTone = "\uD83C\uDFFB"; 10 | 11 | const BmiAppBar({Key key, this.isInputPage = true}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Material( 16 | elevation: 1.0, 17 | child: Container( 18 | height: appBarHeight(context), 19 | color: Colors.white, 20 | child: Padding( 21 | padding: EdgeInsets.all(screenAwareSize(16.0, context)), 22 | child: Row( 23 | crossAxisAlignment: CrossAxisAlignment.end, 24 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 25 | children: [ 26 | _buildLabel(context), 27 | _buildIcon(context), 28 | ], 29 | ), 30 | ), 31 | ), 32 | ); 33 | } 34 | 35 | Padding _buildIcon(BuildContext context) { 36 | return Padding( 37 | padding: EdgeInsets.only(bottom: screenAwareSize(11.0, context)), 38 | child: SvgPicture.asset( 39 | 'images/user.svg', 40 | height: screenAwareSize(20.0, context), 41 | width: screenAwareSize(20.0, context), 42 | ), 43 | ); 44 | } 45 | 46 | RichText _buildLabel(BuildContext context) { 47 | return RichText( 48 | text: TextSpan( 49 | style: DefaultTextStyle.of(context).style.copyWith(fontSize: 34.0), 50 | children: [ 51 | TextSpan( 52 | text: isInputPage ? "Hi Johny " : "Your BMI", 53 | style: TextStyle(fontWeight: FontWeight.bold), 54 | ), 55 | TextSpan(text: isInputPage ? getEmoji(context) : ""), 56 | ], 57 | ), 58 | ); 59 | } 60 | 61 | // https://github.com/flutter/flutter/issues/9652 62 | String getEmoji(BuildContext context) { 63 | return Theme.of(context).platform == TargetPlatform.iOS 64 | ? wavingHandEmoji 65 | : wavingHandEmoji + whiteSkinTone; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/calculator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | double calculateBMI({int height, int weight}) => 4 | weight / _heightSquared(height); 5 | 6 | double calculateMinNormalWeight({int height}) => 18.5 * _heightSquared(height); 7 | 8 | double calculateMaxNormalWeight({int height}) => 25 * _heightSquared(height); 9 | 10 | double _heightSquared(int height) => math.pow(height / 100, 2); 11 | -------------------------------------------------------------------------------- /lib/fade_route.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FadeRoute extends MaterialPageRoute { 4 | FadeRoute({WidgetBuilder builder, RouteSettings settings}) 5 | : super(builder: builder, settings: settings); 6 | 7 | @override 8 | Duration get transitionDuration => const Duration(milliseconds: 500); 9 | 10 | @override 11 | Widget buildTransitions(BuildContext context, Animation animation, 12 | Animation secondaryAnimation, Widget child) { 13 | return FadeTransition(opacity: animation, child: child); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/input_page/card_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/widget_utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | const TextStyle _titleStyle = TextStyle( 5 | fontSize: 16.0, 6 | fontWeight: FontWeight.w600, 7 | color: Color.fromRGBO(14, 24, 35, 1.0), 8 | ); 9 | 10 | const TextStyle _subtitleStyle = TextStyle( 11 | fontSize: 11.0, 12 | color: Color.fromRGBO(143, 144, 156, 1.0), 13 | ); 14 | 15 | class CardTitle extends StatelessWidget { 16 | final String title; 17 | final String subtitle; 18 | 19 | const CardTitle(this.title, {Key key, this.subtitle}) : super(key: key); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Column( 24 | children: [ 25 | Padding( 26 | padding: EdgeInsets.only( 27 | top: screenAwareSize(8.0, context), 28 | left: screenAwareSize(13.0, context), 29 | right: screenAwareSize(11.0, context), 30 | bottom: screenAwareSize(8.0, context), 31 | ), 32 | child: Row( 33 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 34 | children: [ 35 | Text( 36 | title, 37 | style: _titleStyle, 38 | ), 39 | Text( 40 | subtitle ?? "", 41 | style: _subtitleStyle, 42 | ) 43 | ], 44 | ), 45 | ), 46 | Divider( 47 | height: 1.0, 48 | color: Color.fromRGBO(143, 144, 156, 0.22), 49 | ), 50 | ], 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/input_page/gender/gender_arrow.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/gender/gender_styles.dart'; 2 | import 'package:bmi_calculator/widget_utils.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_svg/flutter_svg.dart'; 5 | 6 | class GenderArrow extends AnimatedWidget { 7 | const GenderArrow({Key key, Listenable listenable}) 8 | : super(key: key, listenable: listenable); 9 | 10 | double _arrowLength(BuildContext context) => screenAwareSize(32.0, context); 11 | 12 | double _translationOffset(BuildContext context) => 13 | _arrowLength(context) * -0.4; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | Animation animation = listenable; 18 | return Transform.rotate( 19 | angle: animation.value, 20 | child: Transform.translate( 21 | offset: Offset(0.0, _translationOffset(context)), 22 | child: Transform.rotate( 23 | angle: -defaultGenderAngle, 24 | child: SvgPicture.asset( 25 | "images/gender_arrow.svg", 26 | height: _arrowLength(context), 27 | width: _arrowLength(context), 28 | ), 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/input_page/gender/gender_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/card_title.dart'; 2 | import 'package:bmi_calculator/input_page/gender/gender_arrow.dart'; 3 | import 'package:bmi_calculator/input_page/gender/gender_circle.dart'; 4 | import 'package:bmi_calculator/input_page/gender/gender_icon.dart'; 5 | import 'package:bmi_calculator/input_page/gender/gender_styles.dart'; 6 | import 'package:bmi_calculator/model/gender.dart'; 7 | import 'package:bmi_calculator/widget_utils.dart' show screenAwareSize; 8 | import 'package:flutter/material.dart'; 9 | 10 | class GenderCard extends StatefulWidget { 11 | final Gender gender; 12 | final ValueChanged onChanged; 13 | 14 | const GenderCard({ 15 | Key key, 16 | this.gender = Gender.other, 17 | this.onChanged, 18 | }) : super(key: key); 19 | 20 | @override 21 | _GenderCardState createState() => _GenderCardState(); 22 | } 23 | 24 | class _GenderCardState extends State 25 | with SingleTickerProviderStateMixin { 26 | AnimationController _arrowAnimationController; 27 | 28 | @override 29 | void initState() { 30 | _arrowAnimationController = new AnimationController( 31 | vsync: this, 32 | lowerBound: -defaultGenderAngle, 33 | upperBound: defaultGenderAngle, 34 | value: genderAngles[widget.gender], 35 | ); 36 | super.initState(); 37 | } 38 | 39 | @override 40 | void dispose() { 41 | _arrowAnimationController.dispose(); 42 | super.dispose(); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return Card( 48 | margin: EdgeInsets.only( 49 | left: screenAwareSize(16.0, context), 50 | right: screenAwareSize(4.0, context), 51 | bottom: screenAwareSize(4.0, context), 52 | ), 53 | child: Column( 54 | crossAxisAlignment: CrossAxisAlignment.center, 55 | children: [ 56 | CardTitle("GENDER"), 57 | Padding( 58 | padding: EdgeInsets.only(top: screenAwareSize(16.0, context)), 59 | child: _drawMainStack(), 60 | ), 61 | ], 62 | ), 63 | ); 64 | } 65 | 66 | Widget _drawMainStack() { 67 | return Container( 68 | width: double.infinity, 69 | child: Stack( 70 | alignment: Alignment.bottomCenter, 71 | children: [ 72 | _drawCircleIndicator(), 73 | GenderIconTranslated( 74 | gender: Gender.female, 75 | isSelected: widget.gender == Gender.female, 76 | ), 77 | GenderIconTranslated( 78 | gender: Gender.other, 79 | isSelected: widget.gender == Gender.other, 80 | ), 81 | GenderIconTranslated( 82 | gender: Gender.male, 83 | isSelected: widget.gender == Gender.male, 84 | ), 85 | _drawGestureDetector(), 86 | ], 87 | ), 88 | ); 89 | } 90 | 91 | Widget _drawCircleIndicator() { 92 | return Stack( 93 | alignment: Alignment.center, 94 | children: [ 95 | GenderCircle(), 96 | GenderArrow(listenable: _arrowAnimationController), 97 | ], 98 | ); 99 | } 100 | 101 | _drawGestureDetector() { 102 | return Positioned.fill( 103 | child: TapHandler( 104 | onGenderTapped: _setSelectedGender, 105 | ), 106 | ); 107 | } 108 | 109 | void _setSelectedGender(Gender gender) { 110 | widget.onChanged(gender); 111 | _arrowAnimationController.animateTo( 112 | genderAngles[gender], 113 | duration: Duration(milliseconds: 150), 114 | ); 115 | } 116 | } 117 | 118 | class TapHandler extends StatelessWidget { 119 | final Function(Gender) onGenderTapped; 120 | 121 | const TapHandler({Key key, this.onGenderTapped}) : super(key: key); 122 | 123 | @override 124 | Widget build(BuildContext context) { 125 | return Row( 126 | mainAxisSize: MainAxisSize.max, 127 | children: [ 128 | Expanded( 129 | child: GestureDetector(onTap: () => onGenderTapped(Gender.female))), 130 | Expanded( 131 | child: GestureDetector(onTap: () => onGenderTapped(Gender.other))), 132 | Expanded( 133 | child: GestureDetector(onTap: () => onGenderTapped(Gender.male))), 134 | ], 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/input_page/gender/gender_circle.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/gender/gender_styles.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class GenderCircle extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Container( 8 | width: circleSize(context), 9 | height: circleSize(context), 10 | decoration: BoxDecoration( 11 | shape: BoxShape.circle, 12 | color: Color.fromRGBO(244, 244, 244, 1.0), 13 | ), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/input_page/gender/gender_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/gender/gender_styles.dart'; 2 | import 'package:bmi_calculator/model/gender.dart'; 3 | import 'package:bmi_calculator/widget_utils.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_svg/flutter_svg.dart'; 6 | 7 | class GenderIconTranslated extends StatelessWidget { 8 | final Gender gender; 9 | final bool isSelected; 10 | 11 | const GenderIconTranslated({Key key, this.gender, this.isSelected = false}) : super(key: key); 12 | 13 | static final Map _genderImages = { 14 | Gender.female: "images/gender_female.svg", 15 | Gender.other: "images/gender_other.svg", 16 | Gender.male: "images/gender_male.svg", 17 | }; 18 | 19 | bool get _isOtherGender => gender == Gender.other; 20 | 21 | String get _assetName => _genderImages[gender]; 22 | 23 | double _iconSize(BuildContext context) { 24 | return screenAwareSize(_isOtherGender ? 22.0 : 16.0, context); 25 | } 26 | 27 | double _genderLeftPadding(BuildContext context) { 28 | return screenAwareSize(_isOtherGender ? 8.0 : 0.0, context); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | Widget icon = Padding( 34 | padding: EdgeInsets.only(left: _genderLeftPadding(context)), 35 | child: SvgPicture.asset( 36 | _assetName, 37 | height: _iconSize(context), 38 | width: _iconSize(context), 39 | color: isSelected ? null : Color.fromRGBO(143, 144, 156, 1.0), 40 | ), 41 | ); 42 | 43 | Widget rotatedIcon = Transform.rotate( 44 | angle: -genderAngles[gender], 45 | child: icon, 46 | ); 47 | 48 | Widget iconWithALine = Padding( 49 | padding: EdgeInsets.only(bottom: circleSize(context) / 2), 50 | child: Column( 51 | crossAxisAlignment: CrossAxisAlignment.center, 52 | children: [ 53 | rotatedIcon, 54 | GenderLine(), 55 | ], 56 | ), 57 | ); 58 | 59 | Widget rotatedIconWithALine = Transform.rotate( 60 | alignment: Alignment.bottomCenter, 61 | angle: genderAngles[gender], 62 | child: iconWithALine, 63 | ); 64 | 65 | Widget centeredIconWithALine = Padding( 66 | padding: EdgeInsets.only(bottom: circleSize(context) / 2), 67 | child: rotatedIconWithALine, 68 | ); 69 | 70 | return centeredIconWithALine; 71 | } 72 | } 73 | 74 | class GenderLine extends StatelessWidget { 75 | @override 76 | Widget build(BuildContext context) { 77 | return Padding( 78 | padding: EdgeInsets.only( 79 | bottom: screenAwareSize(6.0, context), 80 | top: screenAwareSize(8.0, context), 81 | ), 82 | child: Container( 83 | height: screenAwareSize(8.0, context), 84 | width: 1.0, 85 | color: Color.fromRGBO(216, 217, 223, 0.54), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/input_page/gender/gender_styles.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:bmi_calculator/model/gender.dart'; 4 | import 'package:bmi_calculator/widget_utils.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | const double defaultGenderAngle = math.pi / 4; 8 | const Map genderAngles = { 9 | Gender.female: -defaultGenderAngle, 10 | Gender.other: 0.0, 11 | Gender.male: defaultGenderAngle, 12 | }; 13 | 14 | double circleSize(BuildContext context) => screenAwareSize(80.0, context); 15 | -------------------------------------------------------------------------------- /lib/input_page/height/height_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/card_title.dart'; 2 | import 'package:bmi_calculator/input_page/height/height_picker.dart'; 3 | import 'package:bmi_calculator/widget_utils.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class HeightCard extends StatelessWidget { 7 | final int height; 8 | final ValueChanged onChanged; 9 | 10 | const HeightCard({Key key, this.height = 170, this.onChanged}) 11 | : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Card( 16 | margin: EdgeInsets.only( 17 | right: screenAwareSize(16.0, context), 18 | left: screenAwareSize(4.0, context), 19 | ), 20 | child: Column( 21 | crossAxisAlignment: CrossAxisAlignment.center, 22 | children: [ 23 | CardTitle("HEIGHT", subtitle: "(cm)"), 24 | Expanded( 25 | child: Padding( 26 | padding: EdgeInsets.only(bottom: screenAwareSize(8.0, context)), 27 | child: LayoutBuilder(builder: (context, constraints) { 28 | return HeightPicker( 29 | widgetHeight: constraints.maxHeight, 30 | height: height, 31 | onChange: (val) => onChanged(val), 32 | ); 33 | }), 34 | ), 35 | ), 36 | ], 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/input_page/height/height_picker.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:bmi_calculator/input_page/height/height_slider.dart'; 4 | import 'package:bmi_calculator/input_page/height/height_styles.dart'; 5 | import 'package:bmi_calculator/widget_utils.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_svg/flutter_svg.dart'; 8 | 9 | class HeightPicker extends StatefulWidget { 10 | final int maxHeight; 11 | final int minHeight; 12 | final int height; 13 | final double widgetHeight; 14 | final ValueChanged onChange; 15 | 16 | const HeightPicker( 17 | {Key key, 18 | this.height, 19 | this.widgetHeight, 20 | this.onChange, 21 | this.maxHeight = 190, 22 | this.minHeight = 145}) 23 | : super(key: key); 24 | 25 | int get totalUnits => maxHeight - minHeight; 26 | 27 | @override 28 | _HeightPickerState createState() => _HeightPickerState(); 29 | } 30 | 31 | class _HeightPickerState extends State { 32 | double startDragYOffset; 33 | int startDragHeight; 34 | 35 | double get _pixelsPerUnit { 36 | return _drawingHeight / widget.totalUnits; 37 | } 38 | 39 | double get _sliderPosition { 40 | double halfOfBottomLabel = labelsFontSize / 2; 41 | int unitsFromBottom = widget.height - widget.minHeight; 42 | return halfOfBottomLabel + unitsFromBottom * _pixelsPerUnit; 43 | } 44 | 45 | ///returns actual input_page.height of slider to be able to slide 46 | double get _drawingHeight { 47 | double totalHeight = widget.widgetHeight; 48 | double marginBottom = marginBottomAdapted(context); 49 | double marginTop = marginTopAdapted(context); 50 | return totalHeight - (marginBottom + marginTop + labelsFontSize); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return GestureDetector( 56 | behavior: HitTestBehavior.translucent, 57 | onTapDown: _onTapDown, 58 | onVerticalDragStart: _onDragStart, 59 | onVerticalDragUpdate: _onDragUpdate, 60 | child: Stack( 61 | children: [ 62 | _drawPersonImage(), 63 | _drawSlider(), 64 | _drawLabels(), 65 | ], 66 | ), 67 | ); 68 | } 69 | 70 | _onTapDown(TapDownDetails tapDownDetails) { 71 | int height = _globalOffsetToHeight(tapDownDetails.globalPosition); 72 | widget.onChange(_normalizeHeight(height)); 73 | } 74 | 75 | int _normalizeHeight(int height) { 76 | return math.max(widget.minHeight, math.min(widget.maxHeight, height)); 77 | } 78 | 79 | int _globalOffsetToHeight(Offset globalOffset) { 80 | RenderBox getBox = context.findRenderObject(); 81 | Offset localPosition = getBox.globalToLocal(globalOffset); 82 | double dy = localPosition.dy; 83 | dy = dy - marginTopAdapted(context) - labelsFontSize / 2; 84 | int height = widget.maxHeight - (dy ~/ _pixelsPerUnit); 85 | return height; 86 | } 87 | 88 | _onDragStart(DragStartDetails dragStartDetails) { 89 | int newHeight = _globalOffsetToHeight(dragStartDetails.globalPosition); 90 | widget.onChange(newHeight); 91 | setState(() { 92 | startDragYOffset = dragStartDetails.globalPosition.dy; 93 | startDragHeight = newHeight; 94 | }); 95 | } 96 | 97 | _onDragUpdate(DragUpdateDetails dragUpdateDetails) { 98 | double currentYOffset = dragUpdateDetails.globalPosition.dy; 99 | double verticalDifference = startDragYOffset - currentYOffset; 100 | int diffHeight = verticalDifference ~/ _pixelsPerUnit; 101 | int height = _normalizeHeight(startDragHeight + diffHeight); 102 | setState(() => widget.onChange(height)); 103 | } 104 | 105 | Widget _drawSlider() { 106 | return Positioned( 107 | child: HeightSlider(height: widget.height), 108 | left: 0.0, 109 | right: 0.0, 110 | bottom: _sliderPosition, 111 | ); 112 | } 113 | 114 | Widget _drawLabels() { 115 | int labelsToDisplay = widget.totalUnits ~/ 5 + 1; 116 | List labels = List.generate( 117 | labelsToDisplay, 118 | (idx) { 119 | return Text( 120 | "${widget.maxHeight - 5 * idx}", 121 | style: labelsTextStyle, 122 | ); 123 | }, 124 | ); 125 | 126 | return Align( 127 | alignment: Alignment.centerRight, 128 | child: IgnorePointer( 129 | child: Padding( 130 | padding: EdgeInsets.only( 131 | right: screenAwareSize(12.0, context), 132 | bottom: marginBottomAdapted(context), 133 | top: marginTopAdapted(context), 134 | ), 135 | child: Column( 136 | children: labels, 137 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 138 | ), 139 | ), 140 | ), 141 | ); 142 | } 143 | 144 | Widget _drawPersonImage() { 145 | double personImageHeight = _sliderPosition + marginBottomAdapted(context); 146 | return Align( 147 | alignment: Alignment.bottomCenter, 148 | child: SvgPicture.asset( 149 | "images/person.svg", 150 | height: personImageHeight, 151 | width: personImageHeight / 3, 152 | ), 153 | ); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/input_page/height/height_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/height/height_styles.dart'; 2 | import 'package:bmi_calculator/widget_utils.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class HeightSlider extends StatelessWidget { 6 | final int height; 7 | 8 | const HeightSlider({Key key, this.height}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return IgnorePointer( 13 | child: Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | SliderLabel(height: height), 17 | Row( 18 | children: [ 19 | SliderCircle(), 20 | Expanded(child: SliderLine()), 21 | ], 22 | ), 23 | ], 24 | ), 25 | ); 26 | } 27 | } 28 | 29 | class SliderLabel extends StatelessWidget { 30 | final int height; 31 | 32 | const SliderLabel({Key key, this.height}) : super(key: key); 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Padding( 37 | padding: EdgeInsets.only( 38 | left: screenAwareSize(4.0, context), 39 | bottom: screenAwareSize(2.0, context), 40 | ), 41 | child: Text( 42 | "$height", 43 | style: TextStyle( 44 | fontSize: selectedLabelFontSize, 45 | color: Theme.of(context).primaryColor, 46 | fontWeight: FontWeight.w600, 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | 53 | class SliderLine extends StatelessWidget { 54 | @override 55 | Widget build(BuildContext context) { 56 | return Row( 57 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 58 | mainAxisSize: MainAxisSize.max, 59 | children: List.generate( 60 | 40, 61 | (i) => Expanded( 62 | child: Container( 63 | height: 2.0, 64 | decoration: BoxDecoration( 65 | color: i.isEven 66 | ? Theme.of(context).primaryColor 67 | : Colors.white), 68 | ), 69 | )), 70 | ); 71 | } 72 | } 73 | 74 | class SliderCircle extends StatelessWidget { 75 | @override 76 | Widget build(BuildContext context) { 77 | return Container( 78 | width: circleSizeAdapted(context), 79 | height: circleSizeAdapted(context), 80 | decoration: BoxDecoration( 81 | color: Theme.of(context).primaryColor, 82 | shape: BoxShape.circle, 83 | ), 84 | child: Icon( 85 | Icons.unfold_more, 86 | color: Colors.white, 87 | size: 0.6 * circleSizeAdapted(context), 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/input_page/height/height_styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/widget_utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | export 'package:bmi_calculator/styles.dart'; 5 | 6 | double marginBottomAdapted(BuildContext context) => 7 | screenAwareSize(marginBottom, context); 8 | 9 | double marginTopAdapted(BuildContext context) => 10 | screenAwareSize(marginTop, context); 11 | 12 | double circleSizeAdapted(BuildContext context) => 13 | screenAwareSize(circleSize, context); 14 | 15 | const TextStyle labelsTextStyle = const TextStyle( 16 | color: labelsGrey, 17 | fontSize: labelsFontSize, 18 | ); 19 | 20 | const double circleSize = 32.0; 21 | const double marginBottom = circleSize / 2; 22 | const double marginTop = 26.0; 23 | const double selectedLabelFontSize = 14.0; 24 | const double labelsFontSize = 13.0; 25 | const Color labelsGrey = const Color.fromRGBO(216, 217, 223, 1.0); 26 | -------------------------------------------------------------------------------- /lib/input_page/input_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/app_bar.dart'; 2 | import 'package:bmi_calculator/fade_route.dart'; 3 | import 'package:bmi_calculator/input_page/gender/gender_card.dart'; 4 | import 'package:bmi_calculator/input_page/height/height_card.dart'; 5 | import 'package:bmi_calculator/input_page/input_page_styles.dart'; 6 | import 'package:bmi_calculator/input_page/pacman_slider.dart'; 7 | import 'package:bmi_calculator/input_page/transition_dot.dart'; 8 | import 'package:bmi_calculator/input_page/weight/weight_card.dart'; 9 | import 'package:bmi_calculator/model/gender.dart'; 10 | import 'package:bmi_calculator/result_page/result_page.dart'; 11 | import 'package:bmi_calculator/widget_utils.dart'; 12 | import 'package:flutter/material.dart'; 13 | 14 | class InputPage extends StatefulWidget { 15 | @override 16 | InputPageState createState() { 17 | return new InputPageState(); 18 | } 19 | } 20 | 21 | class InputPageState extends State with TickerProviderStateMixin { 22 | AnimationController _submitAnimationController; 23 | Gender gender = Gender.other; 24 | int height = 180; 25 | int weight = 70; 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | _submitAnimationController = AnimationController( 31 | vsync: this, 32 | duration: Duration(seconds: 2), 33 | ); 34 | _submitAnimationController.addStatusListener((status) { 35 | if (status == AnimationStatus.completed) { 36 | _goToResultPage().then((_) => _submitAnimationController.reset()); 37 | } 38 | }); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | _submitAnimationController.dispose(); 44 | super.dispose(); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return Stack( 50 | children: [ 51 | Scaffold( 52 | appBar: PreferredSize( 53 | child: BmiAppBar(), 54 | preferredSize: Size.fromHeight(appBarHeight(context)), 55 | ), 56 | body: Column( 57 | crossAxisAlignment: CrossAxisAlignment.start, 58 | children: [ 59 | InputSummaryCard( 60 | gender: gender, 61 | weight: weight, 62 | height: height, 63 | ), 64 | Expanded(child: _buildCards(context)), 65 | _buildBottom(context), 66 | ], 67 | ), 68 | ), 69 | TransitionDot(animation: _submitAnimationController), 70 | ], 71 | ); 72 | } 73 | 74 | Widget _buildCards(BuildContext context) { 75 | return Row( 76 | children: [ 77 | Expanded( 78 | child: Column( 79 | children: [ 80 | Expanded( 81 | child: GenderCard( 82 | gender: gender, 83 | onChanged: (val) => setState(() => gender = val), 84 | ), 85 | ), 86 | Expanded( 87 | child: WeightCard( 88 | weight: weight, 89 | onChanged: (val) => setState(() => weight = val), 90 | ), 91 | ), 92 | ], 93 | ), 94 | ), 95 | Expanded( 96 | child: HeightCard( 97 | height: height, 98 | onChanged: (val) => setState(() => height = val), 99 | ), 100 | ) 101 | ], 102 | ); 103 | } 104 | 105 | Widget _buildBottom(BuildContext context) { 106 | return Padding( 107 | padding: EdgeInsets.only( 108 | left: screenAwareSize(16.0, context), 109 | right: screenAwareSize(16.0, context), 110 | bottom: screenAwareSize(22.0, context), 111 | top: screenAwareSize(14.0, context), 112 | ), 113 | child: PacmanSlider( 114 | submitAnimationController: _submitAnimationController, 115 | onSubmit: onPacmanSubmit, 116 | ), 117 | ); 118 | } 119 | 120 | void onPacmanSubmit() { 121 | _submitAnimationController.forward(); 122 | } 123 | 124 | _goToResultPage() async { 125 | return Navigator.of(context).push(FadeRoute( 126 | builder: (context) => ResultPage( 127 | weight: weight, 128 | height: height, 129 | gender: gender, 130 | ), 131 | )); 132 | } 133 | } 134 | 135 | class InputSummaryCard extends StatelessWidget { 136 | final Gender gender; 137 | final int height; 138 | final int weight; 139 | 140 | const InputSummaryCard({Key key, this.gender, this.height, this.weight}) 141 | : super(key: key); 142 | 143 | @override 144 | Widget build(BuildContext context) { 145 | return Card( 146 | margin: EdgeInsets.all(screenAwareSize(16.0, context)), 147 | child: SizedBox( 148 | height: screenAwareSize(32.0, context), 149 | child: Row( 150 | children: [ 151 | Expanded(child: _genderText()), 152 | _divider(), 153 | Expanded(child: _text("${weight}kg")), 154 | _divider(), 155 | Expanded(child: _text("${height}cm")), 156 | ], 157 | ), 158 | ), 159 | ); 160 | } 161 | 162 | Widget _genderText() { 163 | String genderText = gender == Gender.other 164 | ? '-' 165 | : (gender == Gender.male ? 'Male' : 'Female'); 166 | return _text(genderText); 167 | } 168 | 169 | Widget _text(String text) { 170 | return Text( 171 | text, 172 | style: TextStyle( 173 | color: Color.fromRGBO(143, 144, 156, 1.0), 174 | fontSize: 15.0, 175 | ), 176 | textAlign: TextAlign.center, 177 | ); 178 | } 179 | 180 | Widget _divider() { 181 | return Container( 182 | width: 1.0, 183 | color: Color.fromRGBO(151, 151, 151, 0.1), 184 | ); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /lib/input_page/input_page_styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/widget_utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | double appBarHeight(BuildContext context) { 5 | return screenAwareSize(80.0, context) + MediaQuery.of(context).padding.top; 6 | } -------------------------------------------------------------------------------- /lib/input_page/pacman_slider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:bmi_calculator/widget_utils.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_svg/flutter_svg.dart'; 6 | 7 | const double _pacmanWidth = 21.0; 8 | const double _sliderHorizontalMargin = 24.0; 9 | const double _dotsLeftMargin = 8.0; 10 | 11 | class PacmanSlider extends StatefulWidget { 12 | final VoidCallback onSubmit; 13 | final AnimationController submitAnimationController; 14 | 15 | const PacmanSlider({Key key, this.onSubmit, this.submitAnimationController}) 16 | : super(key: key); 17 | 18 | @override 19 | _PacmanSliderState createState() => _PacmanSliderState(); 20 | } 21 | 22 | class _PacmanSliderState extends State 23 | with TickerProviderStateMixin { 24 | double _pacmanPosition = 24.0; 25 | Animation _bordersAnimation; 26 | Animation _submitWidthAnimation; 27 | AnimationController pacmanMovementController; 28 | Animation pacmanAnimation; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | pacmanMovementController = 34 | AnimationController(vsync: this, duration: Duration(milliseconds: 400)); 35 | _bordersAnimation = BorderRadiusTween( 36 | begin: BorderRadius.circular(8.0), 37 | end: BorderRadius.circular(50.0), 38 | ).animate(CurvedAnimation( 39 | parent: widget.submitAnimationController, 40 | curve: Interval(0.0, 0.07), 41 | )); 42 | } 43 | 44 | @override 45 | void dispose() { 46 | pacmanMovementController.dispose(); 47 | super.dispose(); 48 | } 49 | 50 | double get width => _submitWidthAnimation?.value ?? 0.0; 51 | 52 | @override 53 | Widget build(BuildContext context) { 54 | return LayoutBuilder( 55 | builder: (context, constraints) { 56 | _submitWidthAnimation = Tween( 57 | begin: constraints.maxWidth, 58 | end: screenAwareSize(52.0, context), 59 | ).animate(CurvedAnimation( 60 | parent: widget.submitAnimationController, 61 | curve: Interval(0.05, 0.15), 62 | )); 63 | return AnimatedBuilder( 64 | animation: widget.submitAnimationController, 65 | builder: (context, child) { 66 | Decoration decoration = BoxDecoration( 67 | borderRadius: _bordersAnimation.value, 68 | color: Theme.of(context).primaryColor, 69 | ); 70 | 71 | return Center( 72 | child: Container( 73 | height: screenAwareSize(52.0, context), 74 | width: width, 75 | decoration: decoration, 76 | child: _submitWidthAnimation.isDismissed 77 | ? GestureDetector( 78 | behavior: HitTestBehavior.translucent, 79 | onTap: () => _animatePacmanToEnd(), 80 | child: Stack( 81 | alignment: Alignment.centerRight, 82 | children: [ 83 | AnimatedDots(), 84 | _drawDotCurtain(decoration), 85 | _drawPacman(), 86 | ], 87 | ), 88 | ) 89 | : Container(), 90 | ), 91 | ); 92 | }, 93 | ); 94 | }, 95 | ); 96 | } 97 | 98 | Widget _drawDotCurtain(Decoration decoration) { 99 | if (width == 0.0) { 100 | return Container(); 101 | } 102 | double marginRight = 103 | width - _pacmanPosition - screenAwareSize(_pacmanWidth / 2, context); 104 | return Positioned.fill( 105 | right: marginRight, 106 | child: Container(decoration: decoration), 107 | ); 108 | } 109 | 110 | Widget _drawPacman() { 111 | pacmanAnimation = _initPacmanAnimation(); 112 | return Positioned( 113 | left: _pacmanPosition, 114 | child: GestureDetector( 115 | onHorizontalDragUpdate: (details) => _onPacmanDrag(width, details), 116 | onHorizontalDragEnd: (details) => _onPacmanEnd(width, details), 117 | child: PacmanIcon(), 118 | ), 119 | ); 120 | } 121 | 122 | Animation _initPacmanAnimation() { 123 | Animation animation = Tween( 124 | begin: _pacmanMinPosition(), 125 | end: _pacmanMaxPosition(width), 126 | ).animate(pacmanMovementController); 127 | 128 | animation.addListener(() { 129 | setState(() { 130 | _pacmanPosition = animation.value; 131 | }); 132 | if (animation.status == AnimationStatus.completed) { 133 | _onPacmanSubmited(); 134 | } 135 | }); 136 | return animation; 137 | } 138 | 139 | _onPacmanSubmited() { 140 | widget?.onSubmit(); 141 | Future.delayed(Duration(seconds: 1), () => _resetPacman()); 142 | } 143 | 144 | _onPacmanDrag(double width, DragUpdateDetails details) { 145 | setState(() { 146 | _pacmanPosition += details.delta.dx; 147 | _pacmanPosition = math.max(_pacmanMinPosition(), 148 | math.min(_pacmanMaxPosition(width), _pacmanPosition)); 149 | }); 150 | } 151 | 152 | _onPacmanEnd(double width, DragEndDetails details) { 153 | bool isOverHalf = 154 | _pacmanPosition + screenAwareSize(_pacmanWidth / 2, context) > 155 | 0.5 * width; 156 | if (isOverHalf) { 157 | _animatePacmanToEnd(); 158 | } else { 159 | _resetPacman(); 160 | } 161 | } 162 | 163 | _animatePacmanToEnd() { 164 | pacmanMovementController.forward( 165 | from: _pacmanPosition / _pacmanMaxPosition(width)); 166 | } 167 | 168 | _resetPacman() { 169 | if (this.mounted) { 170 | setState(() => _pacmanPosition = _pacmanMinPosition()); 171 | } 172 | } 173 | 174 | double _pacmanMinPosition() => 175 | screenAwareSize(_sliderHorizontalMargin, context); 176 | 177 | double _pacmanMaxPosition(double sliderWidth) => 178 | sliderWidth - 179 | screenAwareSize(_sliderHorizontalMargin / 2 + _pacmanWidth, context); 180 | } 181 | 182 | class AnimatedDots extends StatefulWidget { 183 | @override 184 | _AnimatedDotsState createState() => _AnimatedDotsState(); 185 | } 186 | 187 | class _AnimatedDotsState extends State 188 | with TickerProviderStateMixin { 189 | final int numberOfDots = 10; 190 | final double minOpacity = 0.1; 191 | final double maxOpacity = 0.5; 192 | AnimationController hintAnimationController; 193 | 194 | @override 195 | void initState() { 196 | super.initState(); 197 | _initHintAnimationController(); 198 | hintAnimationController.forward(); 199 | } 200 | 201 | @override 202 | void dispose() { 203 | hintAnimationController.dispose(); 204 | super.dispose(); 205 | } 206 | 207 | void _initHintAnimationController() { 208 | hintAnimationController = AnimationController( 209 | vsync: this, 210 | duration: Duration(milliseconds: 800), 211 | ); 212 | hintAnimationController.addStatusListener((status) { 213 | if (status == AnimationStatus.completed) { 214 | Future.delayed(Duration(milliseconds: 800), () { 215 | if (this.mounted) { 216 | hintAnimationController.forward(from: 0.0); 217 | } 218 | }); 219 | } 220 | }); 221 | } 222 | 223 | @override 224 | Widget build(BuildContext context) { 225 | return Padding( 226 | padding: EdgeInsets.only( 227 | left: screenAwareSize( 228 | _sliderHorizontalMargin + _pacmanWidth + _dotsLeftMargin, 229 | context), 230 | right: screenAwareSize(_sliderHorizontalMargin, context)), 231 | child: Row( 232 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 233 | children: List.generate(numberOfDots, _generateDot) 234 | ..add(Opacity( 235 | opacity: maxOpacity, 236 | child: Dot(size: 14.0), 237 | )), 238 | ), 239 | ); 240 | } 241 | 242 | Widget _generateDot(int dotNumber) { 243 | Animation animation = _initDotAnimation(dotNumber); 244 | return AnimatedBuilder( 245 | animation: animation, 246 | builder: (context, child) => Opacity( 247 | opacity: animation.value, 248 | child: child, 249 | ), 250 | child: Dot(size: 9.0), 251 | ); 252 | } 253 | 254 | Animation _initDotAnimation(int dotNumber) { 255 | double lastDotStartTime = 0.4; 256 | double dotAnimationDuration = 0.5; 257 | double begin = lastDotStartTime * dotNumber / numberOfDots; 258 | double end = begin + dotAnimationDuration; 259 | return SinusoidalAnimation(min: minOpacity, max: maxOpacity).animate( 260 | CurvedAnimation( 261 | parent: hintAnimationController, 262 | curve: Interval(begin, end), 263 | ), 264 | ); 265 | } 266 | } 267 | 268 | class SinusoidalAnimation extends Animatable { 269 | SinusoidalAnimation({this.min, this.max}); 270 | 271 | final double min; 272 | final double max; 273 | 274 | @override 275 | double transform(double t) { 276 | return min + (max - min) * math.sin(math.pi * t); 277 | } 278 | } 279 | 280 | class Dot extends StatelessWidget { 281 | final double size; 282 | 283 | const Dot({Key key, @required this.size}) : super(key: key); 284 | 285 | @override 286 | Widget build(BuildContext context) { 287 | return Container( 288 | height: screenAwareSize(size, context), 289 | width: screenAwareSize(size, context), 290 | decoration: BoxDecoration( 291 | shape: BoxShape.circle, 292 | color: Colors.white, 293 | ), 294 | ); 295 | } 296 | } 297 | 298 | class PacmanIcon extends StatelessWidget { 299 | @override 300 | Widget build(BuildContext context) { 301 | return Padding( 302 | padding: EdgeInsets.only( 303 | right: screenAwareSize(16.0, context), 304 | ), 305 | child: SvgPicture.asset( 306 | 'images/pacman.svg', 307 | height: screenAwareSize(25.0, context), 308 | width: screenAwareSize(21.0, context), 309 | ), 310 | ); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /lib/input_page/transition_dot.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:bmi_calculator/widget_utils.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class TransitionDot extends AnimatedWidget { 7 | TransitionDot({Key key, Listenable animation}) 8 | : super(key: key, listenable: animation); 9 | 10 | Animation get positionAnimation => IntTween( 11 | begin: 0, 12 | end: 50, 13 | ).animate( 14 | CurvedAnimation( 15 | parent: super.listenable, 16 | curve: Interval(0.15, 0.3), 17 | ), 18 | ); 19 | 20 | Animation get sizeAnimation => LoopedSizeAnimation().animate( 21 | CurvedAnimation( 22 | parent: super.listenable, 23 | curve: Interval(0.3, 1.0), 24 | ), 25 | ); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | double scaledSize = screenAwareSize(sizeAnimation.value, context); 30 | double deviceHeight = MediaQuery.of(context).size.height; 31 | double deviceWidth = MediaQuery.of(context).size.width; 32 | double height = math.min(scaledSize, deviceHeight); 33 | double width = math.min(scaledSize, deviceWidth); 34 | Decoration decoration = BoxDecoration( 35 | shape: width < 0.9 * deviceWidth ? BoxShape.circle : BoxShape.rectangle, 36 | color: Theme.of(context).primaryColor, 37 | ); 38 | 39 | Widget dot = Container( 40 | decoration: decoration, 41 | width: width, 42 | height: height, 43 | ); 44 | return IgnorePointer( 45 | child: Opacity( 46 | opacity: positionAnimation.value > 0 ? 1.0 : 0.0, 47 | child: Scaffold( 48 | body: Center( 49 | child: Column( 50 | mainAxisAlignment: MainAxisAlignment.end, 51 | children: [ 52 | Spacer(flex: 104 - positionAnimation.value), 53 | dot, 54 | Spacer(flex: 4 + positionAnimation.value), 55 | ], 56 | ), 57 | ), 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | 64 | class LoopedSizeAnimation extends Animatable { 65 | final double defaultSize = 52.0; 66 | final double expansionRange = 30.0; 67 | final int numberOfCycles = 2; 68 | final double fullExpansionEdge = 0.8; 69 | 70 | @override 71 | double transform(double t) { 72 | if (t < fullExpansionEdge) { 73 | double normalizedT = t / fullExpansionEdge; 74 | return defaultSize + 75 | math.sin(numberOfCycles * 2 * math.pi * normalizedT) * expansionRange; 76 | } else { 77 | double normalizedT = (t - fullExpansionEdge) / (1 - fullExpansionEdge); 78 | return defaultSize + normalizedT * 2000.0; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/input_page/weight/weight_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/card_title.dart'; 2 | import 'package:bmi_calculator/input_page/weight/weight_slider.dart'; 3 | import 'package:bmi_calculator/widget_utils.dart' show screenAwareSize; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_svg/flutter_svg.dart'; 6 | 7 | class WeightCard extends StatelessWidget { 8 | final int weight; 9 | final ValueChanged onChanged; 10 | 11 | const WeightCard({Key key, this.weight = 70, this.onChanged}) 12 | : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Card( 17 | margin: EdgeInsets.only( 18 | left: screenAwareSize(16.0, context), 19 | right: screenAwareSize(4.0, context), 20 | top: screenAwareSize(4.0, context), 21 | ), 22 | child: Column( 23 | crossAxisAlignment: CrossAxisAlignment.center, 24 | children: [ 25 | CardTitle("WEIGHT", subtitle: "(kg)"), 26 | Expanded( 27 | child: Center( 28 | child: Padding( 29 | padding: EdgeInsets.symmetric( 30 | horizontal: screenAwareSize(16.0, context)), 31 | child: _drawSlider(), 32 | ), 33 | ), 34 | ), 35 | ], 36 | ), 37 | ); 38 | } 39 | 40 | Widget _drawSlider() { 41 | return WeightBackground( 42 | child: LayoutBuilder( 43 | builder: (context, constraints) { 44 | return constraints.isTight 45 | ? Container() 46 | : WeightSlider( 47 | minValue: 30, 48 | maxValue: 110, 49 | value: weight, 50 | onChanged: (val) => onChanged(val), 51 | width: constraints.maxWidth, 52 | ); 53 | }, 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class WeightBackground extends StatelessWidget { 60 | final Widget child; 61 | 62 | const WeightBackground({Key key, this.child}) : super(key: key); 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return Stack( 67 | alignment: Alignment.bottomCenter, 68 | children: [ 69 | Container( 70 | height: screenAwareSize(100.0, context), 71 | decoration: BoxDecoration( 72 | color: Color.fromRGBO(244, 244, 244, 1.0), 73 | borderRadius: 74 | new BorderRadius.circular(screenAwareSize(50.0, context)), 75 | ), 76 | child: child, 77 | ), 78 | SvgPicture.asset( 79 | "images/weight_arrow.svg", 80 | height: screenAwareSize(10.0, context), 81 | width: screenAwareSize(18.0, context), 82 | ), 83 | ], 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/input_page/weight/weight_slider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/rendering.dart'; 5 | 6 | class WeightSlider extends StatelessWidget { 7 | WeightSlider({ 8 | Key key, 9 | @required this.minValue, 10 | @required this.maxValue, 11 | @required this.value, 12 | @required this.onChanged, 13 | @required this.width, 14 | }) : scrollController = new ScrollController( 15 | initialScrollOffset: (value - minValue) * width / 3, 16 | ), 17 | super(key: key); 18 | 19 | final int minValue; 20 | final int maxValue; 21 | final int value; 22 | final ValueChanged onChanged; 23 | final double width; 24 | final ScrollController scrollController; 25 | 26 | double get itemExtent => width / 3; 27 | 28 | int _indexToValue(int index) => minValue + (index - 1); 29 | 30 | @override 31 | build(BuildContext context) { 32 | int itemCount = (maxValue - minValue) + 3; 33 | return NotificationListener( 34 | onNotification: _onNotification, 35 | child: new ListView.builder( 36 | controller: scrollController, 37 | scrollDirection: Axis.horizontal, 38 | itemExtent: itemExtent, 39 | itemCount: itemCount, 40 | physics: BouncingScrollPhysics(), 41 | itemBuilder: (BuildContext context, int index) { 42 | int itemValue = _indexToValue(index); 43 | bool isExtra = index == 0 || index == itemCount - 1; 44 | 45 | return isExtra 46 | ? new Container() //empty first and last element 47 | : GestureDetector( 48 | behavior: HitTestBehavior.translucent, 49 | onTap: () => _animateTo(itemValue, durationMillis: 50), 50 | child: FittedBox( 51 | child: Text( 52 | itemValue.toString(), 53 | style: _getTextStyle(context, itemValue), 54 | ), 55 | fit: BoxFit.scaleDown, 56 | ), 57 | ); 58 | }, 59 | ), 60 | ); 61 | } 62 | 63 | TextStyle _getDefaultTextStyle() { 64 | return new TextStyle( 65 | color: Color.fromRGBO(196, 197, 203, 1.0), 66 | fontSize: 14.0, 67 | ); 68 | } 69 | 70 | TextStyle _getHighlightTextStyle(BuildContext context) { 71 | return new TextStyle( 72 | color: Theme.of(context).primaryColor, 73 | fontSize: 28.0, 74 | ); 75 | } 76 | 77 | TextStyle _getTextStyle(BuildContext context, int itemValue) { 78 | return itemValue == value 79 | ? _getHighlightTextStyle(context) 80 | : _getDefaultTextStyle(); 81 | } 82 | 83 | bool _userStoppedScrolling(Notification notification) { 84 | return notification is UserScrollNotification && 85 | notification.direction == ScrollDirection.idle && 86 | scrollController.position.activity is! HoldScrollActivity; 87 | } 88 | 89 | _animateTo(int valueToSelect, {int durationMillis = 200}) { 90 | double targetExtent = (valueToSelect - minValue) * itemExtent; 91 | scrollController.animateTo( 92 | targetExtent, 93 | duration: new Duration(milliseconds: durationMillis), 94 | curve: Curves.decelerate, 95 | ); 96 | } 97 | 98 | int _offsetToMiddleIndex(double offset) => (offset + width / 2) ~/ itemExtent; 99 | 100 | int _offsetToMiddleValue(double offset) { 101 | int indexOfMiddleElement = _offsetToMiddleIndex(offset); 102 | int middleValue = _indexToValue(indexOfMiddleElement); 103 | middleValue = math.max(minValue, math.min(maxValue, middleValue)); 104 | return middleValue; 105 | } 106 | 107 | bool _onNotification(Notification notification) { 108 | if (notification is ScrollNotification) { 109 | int middleValue = _offsetToMiddleValue(notification.metrics.pixels); 110 | 111 | if (_userStoppedScrolling(notification)) { 112 | _animateTo(middleValue); 113 | } 114 | 115 | if (middleValue != value) { 116 | onChanged(middleValue); //update selection 117 | } 118 | } 119 | return true; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/input_page/input_page.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | 5 | import 'styles.dart'; 6 | 7 | void main() { 8 | SystemChrome.setSystemUIOverlayStyle( 9 | SystemUiOverlayStyle( 10 | statusBarColor: Colors.white, //top bar color 11 | statusBarIconBrightness: Brightness.dark, //top bar icons 12 | systemNavigationBarColor: Colors.white, //bottom bar color 13 | systemNavigationBarIconBrightness: Brightness.dark, //bottom bar icons 14 | ) 15 | ); 16 | SystemChrome.setPreferredOrientations( 17 | [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]) 18 | .then((_) => runApp(new MyApp())); 19 | } 20 | 21 | class MyApp extends StatelessWidget { 22 | @override 23 | Widget build(BuildContext context) { 24 | return MaterialApp( 25 | debugShowCheckedModeBanner: false, 26 | title: 'BMI Calculator', 27 | theme: ThemeData( 28 | primaryColor: mainBlue, 29 | fontFamily: 'SF Pro Display', 30 | ), 31 | home: InputPage(), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/model/gender.dart: -------------------------------------------------------------------------------- 1 | enum Gender { male, female, other } 2 | -------------------------------------------------------------------------------- /lib/result_page/result_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmi_calculator/app_bar.dart'; 2 | import 'package:bmi_calculator/calculator.dart' as calculator; 3 | import 'package:bmi_calculator/input_page/input_page_styles.dart'; 4 | import 'package:bmi_calculator/model/gender.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class ResultPage extends StatefulWidget { 8 | final int height; 9 | final int weight; 10 | final Gender gender; 11 | 12 | const ResultPage({Key key, this.height, this.weight, this.gender}) 13 | : super(key: key); 14 | 15 | @override 16 | _ResultPageState createState() => _ResultPageState(); 17 | } 18 | 19 | class _ResultPageState extends State { 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | appBar: PreferredSize( 24 | child: BmiAppBar(isInputPage: false), 25 | preferredSize: Size.fromHeight(appBarHeight(context)), 26 | ), 27 | body: Column( 28 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 29 | children: [ 30 | ResultCard( 31 | bmi: calculator.calculateBMI( 32 | height: widget.height, weight: widget.weight), 33 | minWeight: 34 | calculator.calculateMinNormalWeight(height: widget.height), 35 | maxWeight: 36 | calculator.calculateMaxNormalWeight(height: widget.height), 37 | ), 38 | _buildBottomBar(), 39 | ], 40 | ), 41 | ); 42 | } 43 | 44 | Widget _buildBottomBar() { 45 | return Padding( 46 | padding: const EdgeInsets.only(bottom: 30.0), 47 | child: Row( 48 | crossAxisAlignment: CrossAxisAlignment.center, 49 | mainAxisAlignment: MainAxisAlignment.center, 50 | children: [ 51 | Padding( 52 | padding: const EdgeInsets.only(right: 40.0), 53 | child: IconButton( 54 | icon: Icon( 55 | Icons.delete, 56 | color: Colors.grey, 57 | size: 28.0, 58 | ), 59 | onPressed: () => Navigator.of(context).pop(), 60 | ), 61 | ), 62 | Container( 63 | height: 52.0, 64 | width: 80.0, 65 | child: RaisedButton( 66 | child: Icon( 67 | Icons.refresh, 68 | color: Colors.white, 69 | size: 28.0, 70 | ), 71 | shape: RoundedRectangleBorder( 72 | borderRadius: BorderRadius.circular(6.0), 73 | ), 74 | onPressed: () => Navigator.of(context).pop(), 75 | color: Theme.of(context).primaryColor, 76 | )), 77 | Padding( 78 | padding: const EdgeInsets.only(left: 40.0), 79 | child: IconButton( 80 | icon: Icon( 81 | Icons.share, 82 | color: Colors.grey, 83 | size: 28.0, 84 | ), 85 | onPressed: () {}, 86 | ), 87 | ), 88 | ], 89 | ), 90 | ); 91 | } 92 | } 93 | 94 | class ResultCard extends StatelessWidget { 95 | final double bmi; 96 | final double minWeight; 97 | final double maxWeight; 98 | 99 | ResultCard({Key key, this.bmi, this.minWeight, this.maxWeight}) 100 | : super(key: key); 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | return Card( 105 | margin: EdgeInsets.all(16.0), 106 | child: Padding( 107 | padding: const EdgeInsets.symmetric(vertical: 24.0), 108 | child: Container( 109 | width: double.infinity, 110 | child: Column(children: [ 111 | Text( 112 | '🔥', 113 | style: TextStyle(fontSize: 80.0), 114 | ), 115 | Text( 116 | bmi.toStringAsFixed(1), 117 | style: TextStyle(fontSize: 140.0, fontWeight: FontWeight.bold), 118 | ), 119 | Text( 120 | 'BMI = ${bmi.toStringAsFixed(2)} kg/m²', 121 | style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.w700), 122 | ), 123 | Padding( 124 | padding: const EdgeInsets.only(top: 16.0), 125 | child: Text( 126 | 'Normal BMI weight range for the height:\n${minWeight.round()}kg - ${maxWeight.round()}kg', 127 | style: TextStyle(fontSize: 14.0, color: Colors.grey), 128 | textAlign: TextAlign.center, 129 | ), 130 | ), 131 | ]), 132 | ), 133 | ), 134 | ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const Color mainBlue = const Color.fromRGBO(77, 123, 243, 1.0); -------------------------------------------------------------------------------- /lib/widget_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const double baseHeight = 650.0; 4 | 5 | double screenAwareSize(double size, BuildContext context) { 6 | double drawingHeight = 7 | MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top; 8 | return size * drawingHeight / baseHeight; 9 | } 10 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | analyzer: 5 | dependency: transitive 6 | description: 7 | name: analyzer 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "0.33.0" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.5.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.0.8" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.0.4" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.2" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.14.11" 46 | convert: 47 | dependency: transitive 48 | description: 49 | name: convert 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.0.2" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "2.0.6" 60 | csslib: 61 | dependency: transitive 62 | description: 63 | name: csslib 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.14.6" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_svg: 73 | dependency: "direct main" 74 | description: 75 | name: flutter_svg 76 | url: "https://pub.dartlang.org" 77 | source: hosted 78 | version: "0.5.5+1" 79 | flutter_test: 80 | dependency: "direct dev" 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | front_end: 85 | dependency: transitive 86 | description: 87 | name: front_end 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "0.1.6" 91 | glob: 92 | dependency: transitive 93 | description: 94 | name: glob 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "1.1.7" 98 | html: 99 | dependency: transitive 100 | description: 101 | name: html 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "0.13.3+3" 105 | http: 106 | dependency: transitive 107 | description: 108 | name: http 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "0.12.0" 112 | http_multi_server: 113 | dependency: transitive 114 | description: 115 | name: http_multi_server 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "2.0.5" 119 | http_parser: 120 | dependency: transitive 121 | description: 122 | name: http_parser 123 | url: "https://pub.dartlang.org" 124 | source: hosted 125 | version: "3.1.3" 126 | io: 127 | dependency: transitive 128 | description: 129 | name: io 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "0.3.3" 133 | js: 134 | dependency: transitive 135 | description: 136 | name: js 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "0.6.1+1" 140 | json_rpc_2: 141 | dependency: transitive 142 | description: 143 | name: json_rpc_2 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "2.0.9" 147 | kernel: 148 | dependency: transitive 149 | description: 150 | name: kernel 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "0.3.6" 154 | logging: 155 | dependency: transitive 156 | description: 157 | name: logging 158 | url: "https://pub.dartlang.org" 159 | source: hosted 160 | version: "0.11.3+2" 161 | matcher: 162 | dependency: transitive 163 | description: 164 | name: matcher 165 | url: "https://pub.dartlang.org" 166 | source: hosted 167 | version: "0.12.3+1" 168 | meta: 169 | dependency: transitive 170 | description: 171 | name: meta 172 | url: "https://pub.dartlang.org" 173 | source: hosted 174 | version: "1.1.6" 175 | mime: 176 | dependency: transitive 177 | description: 178 | name: mime 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "0.9.6+2" 182 | multi_server_socket: 183 | dependency: transitive 184 | description: 185 | name: multi_server_socket 186 | url: "https://pub.dartlang.org" 187 | source: hosted 188 | version: "1.0.2" 189 | node_preamble: 190 | dependency: transitive 191 | description: 192 | name: node_preamble 193 | url: "https://pub.dartlang.org" 194 | source: hosted 195 | version: "1.4.4" 196 | package_config: 197 | dependency: transitive 198 | description: 199 | name: package_config 200 | url: "https://pub.dartlang.org" 201 | source: hosted 202 | version: "1.0.5" 203 | package_resolver: 204 | dependency: transitive 205 | description: 206 | name: package_resolver 207 | url: "https://pub.dartlang.org" 208 | source: hosted 209 | version: "1.0.6" 210 | path: 211 | dependency: transitive 212 | description: 213 | name: path 214 | url: "https://pub.dartlang.org" 215 | source: hosted 216 | version: "1.6.2" 217 | path_drawing: 218 | dependency: transitive 219 | description: 220 | name: path_drawing 221 | url: "https://pub.dartlang.org" 222 | source: hosted 223 | version: "0.3.0" 224 | path_parsing: 225 | dependency: transitive 226 | description: 227 | name: path_parsing 228 | url: "https://pub.dartlang.org" 229 | source: hosted 230 | version: "0.1.2" 231 | petitparser: 232 | dependency: transitive 233 | description: 234 | name: petitparser 235 | url: "https://pub.dartlang.org" 236 | source: hosted 237 | version: "2.0.2" 238 | plugin: 239 | dependency: transitive 240 | description: 241 | name: plugin 242 | url: "https://pub.dartlang.org" 243 | source: hosted 244 | version: "0.2.0+3" 245 | pool: 246 | dependency: transitive 247 | description: 248 | name: pool 249 | url: "https://pub.dartlang.org" 250 | source: hosted 251 | version: "1.3.6" 252 | pub_semver: 253 | dependency: transitive 254 | description: 255 | name: pub_semver 256 | url: "https://pub.dartlang.org" 257 | source: hosted 258 | version: "1.4.2" 259 | quiver: 260 | dependency: transitive 261 | description: 262 | name: quiver 263 | url: "https://pub.dartlang.org" 264 | source: hosted 265 | version: "2.0.0+1" 266 | shelf: 267 | dependency: transitive 268 | description: 269 | name: shelf 270 | url: "https://pub.dartlang.org" 271 | source: hosted 272 | version: "0.7.3+3" 273 | shelf_packages_handler: 274 | dependency: transitive 275 | description: 276 | name: shelf_packages_handler 277 | url: "https://pub.dartlang.org" 278 | source: hosted 279 | version: "1.0.4" 280 | shelf_static: 281 | dependency: transitive 282 | description: 283 | name: shelf_static 284 | url: "https://pub.dartlang.org" 285 | source: hosted 286 | version: "0.2.8" 287 | shelf_web_socket: 288 | dependency: transitive 289 | description: 290 | name: shelf_web_socket 291 | url: "https://pub.dartlang.org" 292 | source: hosted 293 | version: "0.2.2+4" 294 | sky_engine: 295 | dependency: transitive 296 | description: flutter 297 | source: sdk 298 | version: "0.0.99" 299 | source_map_stack_trace: 300 | dependency: transitive 301 | description: 302 | name: source_map_stack_trace 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "1.1.5" 306 | source_maps: 307 | dependency: transitive 308 | description: 309 | name: source_maps 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "0.10.8" 313 | source_span: 314 | dependency: transitive 315 | description: 316 | name: source_span 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "1.4.1" 320 | stack_trace: 321 | dependency: transitive 322 | description: 323 | name: stack_trace 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "1.9.3" 327 | stream_channel: 328 | dependency: transitive 329 | description: 330 | name: stream_channel 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "1.6.8" 334 | string_scanner: 335 | dependency: transitive 336 | description: 337 | name: string_scanner 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "1.0.4" 341 | term_glyph: 342 | dependency: transitive 343 | description: 344 | name: term_glyph 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "1.0.1" 348 | test: 349 | dependency: transitive 350 | description: 351 | name: test 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "1.3.4" 355 | typed_data: 356 | dependency: transitive 357 | description: 358 | name: typed_data 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "1.1.6" 362 | utf: 363 | dependency: transitive 364 | description: 365 | name: utf 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "0.9.0+5" 369 | vector_math: 370 | dependency: transitive 371 | description: 372 | name: vector_math 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "2.0.8" 376 | vm_service_client: 377 | dependency: transitive 378 | description: 379 | name: vm_service_client 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "0.2.6" 383 | watcher: 384 | dependency: transitive 385 | description: 386 | name: watcher 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "0.9.7+10" 390 | web_socket_channel: 391 | dependency: transitive 392 | description: 393 | name: web_socket_channel 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "1.0.9" 397 | xml: 398 | dependency: transitive 399 | description: 400 | name: xml 401 | url: "https://pub.dartlang.org" 402 | source: hosted 403 | version: "3.2.3" 404 | yaml: 405 | dependency: transitive 406 | description: 407 | name: yaml 408 | url: "https://pub.dartlang.org" 409 | source: hosted 410 | version: "2.1.15" 411 | sdks: 412 | dart: ">=2.0.0 <3.0.0" 413 | flutter: ">=0.5.1 <2.0.0" 414 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: bmi_calculator 2 | description: A new Flutter project. 3 | version: 1.0.0+1 4 | 5 | dependencies: 6 | flutter: 7 | sdk: flutter 8 | flutter_svg: ^0.5.5+1 9 | 10 | 11 | dev_dependencies: 12 | flutter_test: 13 | sdk: flutter 14 | 15 | flutter: 16 | uses-material-design: true 17 | assets: 18 | - images/ 19 | fonts: 20 | - family: SF Pro Display 21 | fonts: 22 | - asset: fonts/SF-Pro-Display-Ultralight.otf 23 | weight: 100 24 | - asset: fonts/SF-Pro-Display-Thin.otf 25 | weight: 200 26 | - asset: fonts/SF-Pro-Display-Light.otf 27 | weight: 300 28 | - asset: fonts/SF-Pro-Display-Regular.otf 29 | weight: 400 30 | - asset: fonts/SF-Pro-Display-Medium.otf 31 | weight: 500 32 | - asset: fonts/SF-Pro-Display-Semibold.otf 33 | weight: 600 34 | - asset: fonts/SF-Pro-Display-Bold.otf 35 | weight: 700 36 | - asset: fonts/SF-Pro-Display-Heavy.otf 37 | weight: 800 38 | - asset: fonts/SF-Pro-Display-Black.otf 39 | weight: 900 40 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:bmi_calculator/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Dummy test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | expect(true, true); 18 | }); 19 | } 20 | --------------------------------------------------------------------------------