# BMI Calulator with Flutter
# 0. Final Product
- input_page의
StatefulWidget
에서 값을 touch/click과 slider로 인풋을 받고 - results_page 에서 BMI 결과를 보여주는 어플리케이션.
- 간단한 Navigator가 이용된다.
- (성별과 나이는 BMI 연산에 사용되지 않지만 연습겸 추가된 기능이다)
- 그렇지만 인바디 결과는 안 좋다...
# 1. BMI Brain
bmi_brain.dart
import 'dart:math';
class BMIBrain {
final int weight;
final double height;
double _bmi;
String _result;
String _advice;
BMIBrain(this.weight, this.height) {
_bmi = weight / pow(height / 100, 2);
if (_bmi >= 25) {
_result = 'overweight';
_advice = 'Exercise more!';
} else if (_bmi >= 18.5) {
_result = 'normal';
_advice = 'Good job!';
} else {
_result = 'underweight';
_advice = 'Eat More!';
}
}
String calculateBMI() {
return _bmi.toStringAsFixed(1);
}
String result() {
return _result;
}
String advice() {
return _advice;
}
}
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
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
BMIBrain
클래스에서는 입력된 값들을 바탕으로 bmi, result, advice를 리턴한다
# 2. Components
konstants.dart
import 'package:flutter/material.dart';
const kBoxTextStyle = TextStyle(fontSize: 18.0, color: Color(0xFF8d8e98));
const kLargeButtonTextStyle =
TextStyle(fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold);
const kBoxNumberStyle = TextStyle(
fontSize: 50.0, fontWeight: FontWeight.w900, color: Color(0xFF000000));
const kTitleTextStyle = TextStyle(fontSize: 50, fontWeight: FontWeight.bold);
const kResultTextStyle = TextStyle(
color: Color(0xFF24D876), fontSize: 22, fontWeight: FontWeight.bold);
const kBMITextStyle = TextStyle(fontSize: 100, fontWeight: FontWeight.bold);
const kBodyTextStyle = TextStyle(fontSize: 22);
const kBottomContainerHeight = 80.0;
const Color kActiveBoxColor = Color(0xFFABD9CF);
const Color kInactiveBoxColor = Color(0xFF89B5AF);
const Color kBottomColor = Color(0xffD0CAB2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 사용한 constants
components/bottom_bar.dart
import 'package:bmi_calculator/konstants.dart';
import 'package:flutter/material.dart';
class BottomBar extends StatelessWidget {
final Function onTap;
final String text;
BottomBar(this.onTap, this.text);
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
color: kBottomColor,
margin: EdgeInsets.only(top: 10),
width: double.infinity,
height: kBottomContainerHeight,
child: Center(
child: Text(
text,
style: kLargeButtonTextStyle,
textAlign: TextAlign.center,
))),
);
}
}
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
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
- 생성시에 onTap함수와 text를 전달해 만든다.
components/icon_content.dart
import 'package:flutter/material.dart';
import '../konstants.dart';
class CardChildColumn extends StatelessWidget {
CardChildColumn(this.text, this.icon);
final String text;
final IconData icon;
Widget build(BuildContext context) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Icon(icon, size: 80.0),
SizedBox(height: 15.0),
Text(text, style: kBoxTextStyle)
]);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
components/reusable_card
import 'package:flutter/material.dart';
class ReusableCard extends StatelessWidget {
ReusableCard({ this.color, this.cardChild, this.onPress});
final Color color;
final Widget cardChild;
final Function onPress;
Widget build(BuildContext context) {
return GestureDetector(
onTap:onPress,
child: Container(
child: cardChild,
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: color,
)),
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
components/round_icon_button.dart
import 'package:bmi_calculator/konstants.dart';
import 'package:flutter/material.dart';
class RoundIconButton extends StatelessWidget {
final IconData icon;
final Function onPressed;
RoundIconButton(this.icon, this.onPressed);
Widget build(BuildContext context) {
return RawMaterialButton(
elevation: 0.0,
child: Icon(icon),
onPressed: () {
onPressed();
},
constraints: BoxConstraints.tightFor(width: 56, height: 56),
shape: CircleBorder(),
fillColor: Color(0XFF4C4F5E));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3. Pages
pages/input_page
import 'package:bmi_calculator/konstants.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../components/reusable_card.dart';
import '../components/icon_content.dart';
import '../konstants.dart';
import '../components/bottom_bar.dart';
import '../components/round_icon_button.dart';
import 'package:bmi_calculator/bmi_brain.dart';
enum Gender { Male, Female }
class InputPage extends StatefulWidget {
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
Gender selectedGender;
double _height = 170;
int _weight = 70;
int _age = 20;
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(
color: selectedGender == Gender.Male
? kActiveBoxColor
: kInactiveBoxColor,
cardChild: CardChildColumn('MALE', FontAwesomeIcons.mars),
onPress: () {
setState(() {
selectedGender = Gender.Male;
});
},
)),
Expanded(
child: ReusableCard(
color: selectedGender == Gender.Female
? kActiveBoxColor
: kInactiveBoxColor,
cardChild:
CardChildColumn('FEMALE', FontAwesomeIcons.venus),
onPress: () {
setState(() {
selectedGender = Gender.Female;
});
}))
],
)),
Expanded(
child: ReusableCard(
color: kActiveBoxColor,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('HEIGHT', style: kBoxTextStyle),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(_height.round().toString(), style: kBoxNumberStyle),
Text('cm', style: kBoxTextStyle)
],
),
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white,
inactiveTrackColor: Color(0XFF8D8E98),
thumbColor: Color(0XFFEB1555),
overlayColor: Color(0X292B1555),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 15),
overlayShape: RoundSliderOverlayShape(overlayRadius: 30)),
child: Slider(
value: _height,
min: 120,
max: 220,
label: _height.round().toString(),
onChanged: (double value) {
setState(() {
_height = value;
});
},
),
)
],
),
)),
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(
color: kActiveBoxColor,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('WEIGHT', style: kBoxTextStyle),
Text(_weight.toString(), style: kBoxNumberStyle),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RoundIconButton(FontAwesomeIcons.minus, () {
setState(() {
_weight -= 1;
});
}),
SizedBox(width: 20),
RoundIconButton(FontAwesomeIcons.plus, () {
setState(() {
_weight += 1;
});
})
],
)
],
),
),
),
Expanded(
child: ReusableCard(
color: kActiveBoxColor,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('AGE', style: kBoxTextStyle),
Text(_age.toString(), style: kBoxNumberStyle),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RoundIconButton(FontAwesomeIcons.minus, () {
setState(() {
_age -= 1;
});
}),
SizedBox(width: 20),
RoundIconButton(FontAwesomeIcons.plus, () {
setState(() {
_age += 1;
});
})
],
)
],
),
),
)
],
)),
BottomBar(() {
BMIBrain bmiBrain = BMIBrain(_weight, _height);
Navigator.pushNamed(context, '/result', arguments: bmiBrain);
}, 'To Result'),
]),
);
}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
results_page
import 'package:flutter/material.dart';
import '../components/reusable_card.dart';
import '../components/bottom_bar.dart';
import '../konstants.dart';
import '../bmi_brain.dart';
class ResultsPage extends StatelessWidget {
Widget build(BuildContext context) {
final BMIBrain _brain = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text('BMI CALCULATOR!'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(10),
alignment: Alignment.bottomCenter,
child: Text(
'Your Result',
style: kTitleTextStyle,
))),
Expanded(
flex: 5,
child: Container(
child: ReusableCard(
color: kActiveBoxColor,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(_brain.result(), style: kResultTextStyle),
Text(_brain.calculateBMI(), style: kBMITextStyle),
Text(_brain.advice(), style: kBodyTextStyle)
],
),
),
),
),
BottomBar(() {
Navigator.pushNamed(context, '/');
}, 'Retry')
],
));
}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
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
39
40
41
42
43
44
45
46
47
48
49
50
51
# 4. Main
main.dart
import 'package:flutter/material.dart';
import 'pages/input_page.dart';
import 'pages/results_page.dart';
void main() => runApp(BMICalculator());
class BMICalculator extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData().copyWith(
colorScheme: ThemeData().colorScheme.copyWith(
primary: Color(0xff96C7C1),
secondary: Color(0xff96C7C1),
background: Color(0xffD0CAB2)),
scaffoldBackgroundColor: Color(0xffDED9C4),
),
routes: {
'/': (context) => InputPage(),
'/result': (context) => ResultsPage()
},
initialRoute: '/',
);
}
}
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
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
- namedRoute를 사용했다.
- Themedata().colorScheme.copyWith 를 이용해 커스터마이징을 해줬다.