키, 몸무게 2개의 숫자값을 입력받아 비만도를 계산하는 어플
1. 입력 화면
2. 결과 화면
3. 값 검증 및 화면 전환
4. 최종 화면 및 소스
1. 입력 화면
키, 몸무게 입력받는 양식을 Form 위젯으로 wrap해준다. 그리고 submit을 누를 때 키, 몸무게값을 검증하기 위해 form의 상태를
얻기위한 key가 필요하다. 키는 GlobalKey<FormState>타입으로 선언하고 Form 위젯의 key 프로퍼티로 선언하면 상태를 얻을 수 있다.
키, 몸무게 입력필드는 검증 로직을 작성할 수 있는 TextFormField를 사용한다.
form key의 currentState.validate()로 Form 입력값에 대한 검증이 가능하다. (아래 소스는 검증부분 미작성)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TextEditingController example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: InputForm(),
);
}
}
class InputForm extends StatefulWidget {
@override
_InputFormState createState() => _InputFormState();
}
class _InputFormState extends State<InputForm> {
final _formKey = GlobalKey<FormState>(); // form 상태를 얻기 위한 키
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI CALCULATE'),
),
body: Center(
child: Container(
padding: const EdgeInsets.all(15.0),
child: Form(
key: _formKey, // form의 key 할당
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'height',
),
keyboardType: TextInputType.number, // 숫자 입력만
),
SizedBox(
height: 20,
),
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'weight'
),
keyboardType: TextInputType.number,
),
Container(
margin: const EdgeInsets.all(20.0),
alignment: Alignment.centerRight,
child: RaisedButton(
onPressed: () {
// form 값 검증
if(_formKey.currentState.validate()) {
// 검증되면 처리
}
},
child: Text('submit'),
)
)
],
)
)
),
)
);
}
}
2. 결과화면
결과화면은 별도의 State를 갖지 않으므로 statelesswidget으로 작성하자.
우선은 예상 결과 화면만 그려보자
import 'package:flutter/material.dart';
class Result extends StatelessWidget {
final double height;
final double weight;
Result(this.height, this.weight);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Result')
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'정상',
style: TextStyle(fontSize: 40),
),
SizedBox(height: 20,),
Icon(
Icons.sentiment_very_satisfied,
color: Colors.lightGreen,
size: 90,
),
],
)
)
);
}
}
3. 값 검증 및 화면 전환
키, 몸무게값을 검증하고 결과화면으로 전달해야 한다.
- 키, 몸무게 값을 얻기 위한 TextEditingController
- 컨트롤러 TextFormField에 연결
- submit 클릭시 값 검증하고 결과화면으로 값 전달 및 화면 전환
- 키, 몸무게 값을 얻기 위한 TextEditingController
컨트롤러 객체 2개 생성해주자. (반드시 dispose() 해줘야 한다)
...
class _InputFormState extends State<InputForm> {
final _formKey = GlobalKey<FormState>(); // form 상태를 얻기 위한 키
final _heightController = TextEditingController();
final _weightController = TextEditingController();
@override
void dispose() {
// 화면 종료될 때 컨트롤러도 반드시 종료시켜주기
_heightController.dispose();
_weightController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
...
}
- 컨트롤러 TextFormField에 연결
TextFormField는 TextField와 모양,기능은 동일하나 validator 프로퍼티를 추가로 가지고 있다.
(validator는 에러메시지를 표시할 규칙을 함수로 작성할 수 있기 때문에 회원가입 등 폼에 사용하면 간단한 입력검증이 가능)
각 controller를 TextFormField에 연결해주기
...
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'height',
),
keyboardType: TextInputType.number, // 숫자 입력만
controller: _heightController,
validator: (value) {
if(value.trim().isEmpty) {
return 'Input your height';
}
return null;
},
),
...
- submit 클릭시 값 검증하고 결과화면으로 값 전달 및 화면 전환
Container(
margin: const EdgeInsets.all(20.0),
alignment: Alignment.centerRight,
child: RaisedButton(
onPressed: () {
// form 값 검증
if(_formKey.currentState.validate()) {
// 검증되면 처리
Navigator.push( // 내비게이션
context,
MaterialPageRoute(
builder: (context) => Result( // 결과화면에 입력값 넘겨주기
double.parse(_heightController.text.trim()),
double.parse(_weightController.text.trim())
),
),
);
}
4. 최종 소스 및 화면
- main.dart
import 'package:flutter/material.dart';
import 'bmi_result.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TextEditingController example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: InputForm(),
);
}
}
class InputForm extends StatefulWidget {
@override
_InputFormState createState() => _InputFormState();
}
class _InputFormState extends State<InputForm> {
final _formKey = GlobalKey<FormState>(); // form 상태를 얻기 위한 키
final _heightController = TextEditingController();
final _weightController = TextEditingController();
@override
void dispose() {
// 화면 종료될 때 컨트롤러도 반드시 종료시켜주기
_heightController.dispose();
_weightController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI CALCULATE'),
),
body: Center(
child: Container(
padding: const EdgeInsets.all(15.0),
child: Form(
key: _formKey, // form의 key 할당
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'height',
),
keyboardType: TextInputType.number, // 숫자 입력만
controller: _heightController,
validator: (value) {
if(value.trim().isEmpty) {
return 'Input your height';
}
return null;
},
),
SizedBox(
height: 20,
),
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'weight'
),
keyboardType: TextInputType.number,
controller: _weightController,
validator: (value) {
if(value.trim().isEmpty) {
return 'Input your weight';
}
return null;
},
),
Container(
margin: const EdgeInsets.all(20.0),
alignment: Alignment.centerRight,
child: RaisedButton(
onPressed: () {
// form 값 검증
if(_formKey.currentState.validate()) {
// 검증되면 처리
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Result(
double.parse(_heightController.text.trim()),
double.parse(_weightController.text.trim())
),
),
);
}
},
child: Text('submit'),
)
)
],
)
)
),
)
);
}
}
- bmi_result.dart
import 'package:flutter/material.dart';
class Result extends StatelessWidget {
final double height;
final double weight;
Result(this.height, this.weight);
@override
Widget build(BuildContext context) {
final bmi = weight / ((height / 100) * (height / 100));
return Scaffold(
appBar: AppBar(
title: Text('Result')
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
_calculate(bmi),
style: TextStyle(fontSize: 40),
),
SizedBox(height: 20,),
_sketchIcon(bmi),
],
)
)
);
}
Widget _sketchIcon(bmi) {
if(bmi >= 23) {
return Icon(
Icons.sentiment_very_dissatisfied,
size: 100,
color: Colors.red,
);
} else if(bmi >= 18.5) {
return Icon(
Icons.sentiment_neutral,
size: 100,
color: Colors.lightGreen,
);
} else {
return Icon(
Icons.sentiment_very_dissatisfied,
size: 100,
color: Colors.amber,
);
}
}
String _calculate(bmi) {
var result = '';
if(bmi >= 35) {
result = '고도 비만';
} else if(bmi >= 30) {
result = '2단계 비만';
} else if(bmi >= 25) {
result = '1단계 비만';
} else if(bmi >= 23) {
result = '과체중';
} else if(bmi >= 18.5) {
result = '정상';
} else {
result = '저체중';
}
return result;
}
}
댓글