diff --git a/README.md b/README.md index f2129e3b..1e78044d 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,12 @@ -![App Brewery Banner](https://github.com/londonappbrewery/Images/blob/master/AppBreweryBanner.png) - - # BMI Calculator 💪 -## Our Goal - -The objective of this tutorial is to look at how we can customise Flutter Widgets to achieve our own beautiful user interface designs. If you have a designer on board, no matter how unconventional their designs are, we can create them using Flutter. - - ## What you will create -We’re going to make a Body Mass Index Calculator inspired by the beautiful designs made by [Ruben Vaalt](https://dribbble.com/shots/4585382-Simple-BMI-Calculator). It will be a multi screen app with simple functionality but full-on custom styling. +It is a Body Mass Index Calculator inspired by the beautiful designs made by [Ruben Vaalt](https://dribbble.com/shots/4585382-Simple-BMI-Calculator). It will be a multi screen app with simple functionality but full-on custom styling. ![Finished App](https://github.com/londonappbrewery/Images/blob/master/bmi-calc-demo.gif) -## What you will learn +## What will this include - How to use Flutter themes to create coherent branding. - How to create multi-page apps using Flutter Routes and Navigator. @@ -27,6 +19,3 @@ We’re going to make a Body Mass Index Calculator inspired by the beautiful des - Learn about composition vs. inheritance and the Flutter way of creating custom UI. - Understand the difference between const and final in Dart and when to use each. ->This is a companion project to The App Brewery's Complete Flutter Development Bootcamp, check out the full course at [www.appbrewery.co](https://www.appbrewery.co/) - -![End Banner](https://github.com/londonappbrewery/Images/blob/master/readme-end-banner.png) diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100644 index 00000000..264a8817 --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\M Avinash Naidu\AndroidStudioProjects\bmi-calculator-flutter" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "SYMROOT=${SOURCE_ROOT}/../build\ios" +export "OTHER_LDFLAGS=$(inherited) -framework Flutter" +export "FLUTTER_FRAMEWORK_DIR=C:\flutter\bin\cache\artifacts\engine\ios" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" diff --git a/lib/Calculator_brain.dart b/lib/Calculator_brain.dart new file mode 100644 index 00000000..bae47825 --- /dev/null +++ b/lib/Calculator_brain.dart @@ -0,0 +1,37 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +class CalculatorBrain { + CalculatorBrain({@required this.height, @required this.weight}); + + final int height; + final int weight; + + double _bmi; + + String calculateBmi() { + _bmi = weight / pow(height / 100, 2); + return _bmi.toStringAsFixed(1); + } + + String getResult() { + if (_bmi >= 25) { + return 'Overweight'; + } else if (_bmi > 18.5) { + return 'Normal'; + } else { + return 'Underweight'; + } + } + + String getInterpretation() { + if (_bmi >= 25) { + return 'You have a higher than normal body weight. Try to exercise more.'; + } else if (_bmi >= 18.5) { + return 'You have a normal body weight. Good job!'; + } else { + return 'You have a lower than normal body weight. You can eat a bit more.'; + } + } +} diff --git a/lib/Components/RoundIconButton.dart b/lib/Components/RoundIconButton.dart new file mode 100644 index 00000000..3701b042 --- /dev/null +++ b/lib/Components/RoundIconButton.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class RoundIconButton extends StatelessWidget { + RoundIconButton({@required this.icon, @required this.onPressed}); + final IconData icon; + final Function onPressed; + @override + Widget build(BuildContext context) { + return RawMaterialButton( + child: Icon(icon), + shape: CircleBorder(), + fillColor: Color(0xFF4C4F5E), + onPressed: onPressed, + elevation: 6.0, + constraints: BoxConstraints.tightFor( + width: 56.0, + height: 56.0, + ), + ); + } +} diff --git a/lib/Components/bottombutton.dart b/lib/Components/bottombutton.dart new file mode 100644 index 00000000..0ac769ca --- /dev/null +++ b/lib/Components/bottombutton.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import '../constants.dart'; + +class BottomButton extends StatelessWidget { + BottomButton({@required this.buttonTitle, @required this.onTap}); + + final String buttonTitle; + final Function onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + child: Center( + child: Text( + buttonTitle, + style: kLargeButtonTextStyle, + ), + ), + color: Color(0xFFEB1555), + margin: EdgeInsets.only(top: 10.0), + padding: EdgeInsets.only(bottom: 20.0), + width: double.infinity, + height: kBottomContainerHeight, + ), + ); + } +} diff --git a/lib/Components/icon_content.dart b/lib/Components/icon_content.dart new file mode 100644 index 00000000..b2f8125c --- /dev/null +++ b/lib/Components/icon_content.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import '../constants.dart'; + +class IconContent extends StatelessWidget { + IconContent({@required this.icon, this.lable}); + final IconData icon; + final String lable; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icon, + size: 80.0, + ), + SizedBox(height: 15.0), + Text( + lable, + style: kLableTextStyle, + ) + ], + ); + } +} diff --git a/lib/Components/reusablecard.dart b/lib/Components/reusablecard.dart new file mode 100644 index 00000000..5d929883 --- /dev/null +++ b/lib/Components/reusablecard.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ReusableCard extends StatelessWidget { + ReusableCard({@required this.colour, this.cardClass, this.onPress}); + final Color colour; + final Widget cardClass; + final Function onPress; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onPress, + child: Container( + child: cardClass, + margin: EdgeInsets.all(15.0), + decoration: BoxDecoration( + color: colour, + borderRadius: BorderRadius.circular(10.0), + ), + ), + ); + } +} diff --git a/lib/Screens/input.dart b/lib/Screens/input.dart new file mode 100644 index 00000000..af24a623 --- /dev/null +++ b/lib/Screens/input.dart @@ -0,0 +1,238 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:bmi_calculator/Components/reusablecard.dart'; +import 'package:bmi_calculator/Components/icon_content.dart'; +import 'package:bmi_calculator/Components/RoundIconButton.dart'; +import 'package:bmi_calculator/Components/bottombutton.dart'; +import 'results_page.dart'; +import 'package:bmi_calculator/constants.dart'; +import 'package:bmi_calculator/Calculator_brain.dart'; + +enum Gender { male, female } + +class InputPage extends StatefulWidget { + @override + _InputPageState createState() => _InputPageState(); +} + +class _InputPageState extends State { + Gender selectedGender; + int height = 180; + int weight = 60; + int age = 20; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('BMI CALCULATOR'), + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: Row( + children: [ + Expanded( + child: ReusableCard( + onPress: () { + setState(() { + selectedGender = Gender.male; + }); + }, + colour: selectedGender == Gender.male + ? kActiveCardColour + : kInactiveCardColour, + cardClass: IconContent( + icon: FontAwesomeIcons.mars, + lable: "MALE", + ), + ), + ), + Expanded( + child: ReusableCard( + onPress: () { + setState(() { + selectedGender = Gender.female; + }); + }, + colour: selectedGender == Gender.female + ? kActiveCardColour + : kInactiveCardColour, + cardClass: IconContent( + icon: FontAwesomeIcons.venus, + lable: "FEMALE", + ), + ), + ), + ], + ), + ), + Expanded( + child: ReusableCard( + colour: kInactiveCardColour, + cardClass: Column( + children: [ + Text("HEIGHT", style: kLableTextStyle), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text("$height", style: kNumberTextStyle), + Text("cm", style: kUnitTextStyle), + ], + ), + SliderTheme( + data: SliderTheme.of(context).copyWith( + activeTrackColor: Colors.white, + inactiveTrackColor: Color(0xFF8D8E98), + thumbColor: Color(0xFFEB1555), + overlayColor: Color(0x29EB1555), + overlayShape: + RoundSliderOverlayShape(overlayRadius: 30.0), + thumbShape: + RoundSliderThumbShape(enabledThumbRadius: 15.0)), + child: Slider( + value: height.toDouble(), + min: 100.0, + max: 210.0, + onChanged: (double newValue) { + setState(() { + height = newValue.round(); + }); + }, + ), + ) + ], + ), + ), + ), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: ReusableCard( + colour: kInactiveCardColour, + cardClass: Column( + children: [ + Text("WEIGHT", style: kLableTextStyle), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("$weight", style: kNumberTextStyle) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RoundIconButton( + icon: FontAwesomeIcons.minus, + onPressed: () { + setState( + () { + weight--; + }, + ); + }, + ), + SizedBox( + width: 10.0, + ), + RoundIconButton( + icon: FontAwesomeIcons.plus, + onPressed: () { + setState( + () { + weight++; + }, + ); + }, + ) + ], + ) + ], + ), + ), + ), + Expanded( + child: ReusableCard( + colour: kInactiveCardColour, + cardClass: Column( + children: [ + Text( + "AGE", + style: kLableTextStyle, + ), + Text( + "$age", + style: kNumberTextStyle, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RoundIconButton( + icon: FontAwesomeIcons.minus, + onPressed: () { + setState( + () { + if (age == 0) { + age = 0; + } else { + age--; + } + }, + ); + }, + ), + SizedBox( + width: 10.0, + ), + RoundIconButton( + icon: FontAwesomeIcons.plus, + onPressed: () { + setState(() { + age++; + }); + }, + ) + ], + ) + ], + ), + ), + ), + ], + ), + ), + Container( + color: kBottomContainerColor, + padding: EdgeInsets.only(bottom: 10.0), + margin: EdgeInsets.only(top: 10.0), + width: double.infinity, + height: kBottomContainerHeight, + child: BottomButton( + buttonTitle: "CALCULATE", + onTap: () { + CalculatorBrain calc = + CalculatorBrain(height: height, weight: weight); + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ResultsPage( + bmiResult: calc.calculateBmi(), + resultText: calc.getResult(), + Interpetation: calc.getInterpretation(), + ), + ), + ); + }, + ), + ) + ], + ), + ); + } +} diff --git a/lib/Screens/results_page.dart b/lib/Screens/results_page.dart new file mode 100644 index 00000000..2377b99d --- /dev/null +++ b/lib/Screens/results_page.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import '../constants.dart'; +import '../Components/reusablecard.dart'; +import 'input.dart'; +import '../Components/bottombutton.dart'; +import 'package:bmi_calculator/Calculator_brain.dart'; + +class ResultsPage extends StatelessWidget { + ResultsPage( + {@required this.bmiResult, + @required this.resultText, + @required this.Interpetation}); + + final String bmiResult; + final String resultText; + final String Interpetation; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("BMI CALCULATOR"), + ), + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: Container( + padding: EdgeInsets.all(10.0), + alignment: Alignment.bottomCenter, + child: Text( + "Your Result", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 50.0, + color: Color(0xFFFFFFFF)), + ), + ), + ), + Expanded( + flex: 5, + child: ReusableCard( + colour: kActiveCardColour, + cardClass: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + resultText.toUpperCase(), + style: kTitleTextStyle, + ), + Text( + bmiResult, + style: kBMITextStyle, + ), + Text( + Interpetation, + style: kBodyTextStyle, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + BottomButton( + buttonTitle: "RECALCULATE", + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => InputPage())); + }, + ), + ], + ), + ); + } +} diff --git a/lib/constants.dart b/lib/constants.dart new file mode 100644 index 00000000..cb48247a --- /dev/null +++ b/lib/constants.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +const kBottomContainerHeight = 80.0; + +const kActiveCardColour = Color(0xFF1D1E33); +const kInactiveCardColour = Color(0xFF111328); +const kBottomContainerColor = Color(0xFFEB1555); + +const kLableTextStyle = TextStyle( + fontSize: 20.0, + color: Color(0xFF8D8E98), +); + +const kNumberTextStyle = TextStyle( + fontWeight: FontWeight.w900, + fontSize: 50.0, +); + +const kUnitTextStyle = TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w200, +); + +const kLargeButtonTextStyle = TextStyle( + fontSize: 25.0, + fontWeight: FontWeight.bold, +); + +const kTitleTextStyle = + TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold, color: Colors.green); + +const kBMITextStyle = TextStyle( + fontSize: 100.0, + fontWeight: FontWeight.bold, +); + +const kBodyTextStyle = TextStyle( + fontSize: 22.0, +); diff --git a/lib/main.dart b/lib/main.dart index 78f51b11..7a9e61a3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:bmi_calculator/Screens/input.dart'; void main() => runApp(BMICalculator()); @@ -6,29 +7,11 @@ class BMICalculator extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - home: InputPage(), - ); - } -} - -class InputPage extends StatefulWidget { - @override - _InputPageState createState() => _InputPageState(); -} - -class _InputPageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('BMI CALCULATOR'), - ), - body: Center( - child: Text('Body Text'), - ), - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), + theme: ThemeData.dark().copyWith( + primaryColor: Color(0xFF0A0E21), + scaffoldBackgroundColor: Color(0xFF0A0E21), ), + home: InputPage(), ); } } diff --git a/pubspec.yaml b/pubspec.yaml index 6dc027c6..f93591cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: sdk: flutter cupertino_icons: ^0.1.2 + font_awesome_flutter: ^8.8.1 dev_dependencies: flutter_test: