
BMI 계산기 위젯트리
Screen: HomePage
- HomePage — home_page.dart
- Scaffold
- AppBar
- Body
- Padding
- Column
- GenderBox — gender_box.dart
- Spacer
- SliderBox (HEIGHT) — slide_box.dart
- Spacer
- SliderBox (WEIGHT)
- Spacer
- ElevatedButton
State
- isMale : bool
- height : double
- weight : double
State Flow
- GenderBox → onGenderChanged → setState
- SliderBox → onChanged → setState
Notes
- 모든 상태는 HomePage가 소유
- 하위 위젯은 Stateless 유지
Screen : ResultPage
- ResultPage
- Scaffold
- AppBar
- Column
- ResultGauge
- ResultText
- OutlinedButton
요런 구조인데.
내가 어려워하는 부분은 입력되는 값이 변동하는 것을 코드에 반영하는 것.
이 순서를 기록해보려고 한다.
1. HomePage에서 관리되어야 할 변수 확인
- 성별 (bool)
- 키 (double)
- 몸무게 (double)
2. HomePage StatefulWidget으로 변경 후 변수 선언
- class HomePage를 StatefulWidget으로 변경
// 변경 전
class HomePage extends StatelessWidget { // `HomePage` 위에 커서를 올려놓고 ctrl+. , `Convert to StatefulWidget` 선택
@override
Widget build(BuildContext context) {// 변경 후
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
- 상속 받은 클래스에 변수 선언 및 변경 시 실행될 함수를 정의
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isMale = true;
void onGenderChanged(bool male) {
setState(() {
isMale = male;
});
}
double height = 170;
void onHeightChanged(double newHeight) {
setState(() {
height = newHeight;
});
}
double weight = 70;
void onWeightChanged(double newWeight) {
setState(() {
weight = newWeight;
});
}
@override
Widget build(BuildContext context) {- 변수를 각각의 위젯에 넘겨주기
- GenderBox
- HomePage에 선언된 변수를 GenderBox에 연결하기.
- 변수 연결 → bool isMale;
- 함수 연결 →
void Function(bool newValue)타입으로 연결. - Widget gender에서 “터치 시 값이 바뀌도록” 세팅
- GestureDetector 세팅 : Container에 Wrap with Widget → onTap → onChanged 연결
- onChanged가 받을 수 있는 변수 설정: bool isMaleBox
- bool isMaleBox가 추가되었으므로 생성자에도 그 자리를 마련.
- HomePage의 GenderBox 내에 변수 할당하기
- HomePage의 SliderBox 내에 변수 할당하기
- SliderBox에서
onHeightChanged와onWeightChanged의 함수를 받을 수 있도록void Function타입의 변수 추가 - HomePage의 SliderBox 내에 변수 추가
- 변수명: onChanged ← void Function 타입이므로 함수를 호출해야 함.
- 변수를 통해 호출할 함수명:
onHeightChanged,onWeightChanged
// 변경 전
import 'package:flutter/material.dart';
class GenderBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
gender(context, Icons.male, "MALE", true),
SizedBox(width: 8),
gender(context, Icons.female, "FEMALE", false),
],
);
}// 변경 후
import 'package:flutter/material.dart';
class GenderBox extends StatelessWidget {
GenderBox(this.isMale, this.onChanged);
bool isMale;
void Function(bool newValue) onChanged;
@override
Widget build(BuildContext context) {
return Row(
children: [
gender(context, Icons.male, "MALE", isMale),
SizedBox(width: 8),
gender(context, Icons.female, "FEMALE", !isMale),
],
);
}
// 변경 전
Widget gender(
BuildContext context,
IconData icon,
String text,
bool selected,
) {
return Expanded(
child: Container(
height: 150,
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
),
child: Stack(
children: [
Positioned(
top: 10,
left: 10,
child: Opacity(opacity: 0.3, child: Icon(icon, size: 80)),
),
Positioned(
right: 20,
bottom: 20,
child: Opacity(
opacity: selected ? 1 : 0.3,
child: Text(
text,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
],
),
),
);
}
}// 변경 후
Widget gender(
BuildContext context,
IconData icon,
String text,
bool selected,
/*bool isMaleBox,*/
) {
return Expanded(
child: /*GestureDetector(
onTap: () {
onChanged(isMaleBox);
},
child: */Container(
height: 150,
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
),
child: Stack(
children: [
Positioned(
top: 10,
left: 10,
child: Opacity(opacity: 0.3, child: Icon(icon, size: 80)),
),
Positioned(
right: 20,
bottom: 20,
child: Opacity(
opacity: selected ? 1 : 0.3,
child: Text(
text,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
],
),
),
),
);
}
} @override
Widget build(BuildContext context) {
return Row(
children: [
gender(context, Icons.male, "MALE", isMale, /*true*/),
SizedBox(width: 8),
gender(context, Icons.female, "FEMALE", !isMale, /*false*/),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("BMI CALCULATOR"), centerTitle: true),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
child: Column(
children: [
GenderBox(/*isMale, onGenderChanged*/),
Spacer(),
SliderBox(label: 'HEIGHT', value: 170, unit: 'cm'),
Spacer(),
SliderBox(label: 'WEIGHT', value: 70, unit: 'kg'),
Spacer(),
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(onPressed: () {}, child: Text("CALCULATE")),
),
],
),
),
);
}
}@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("BMI CALCULATOR"), centerTitle: true),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
child: Column(
children: [
GenderBox(isMale, onGenderChanged),
Spacer(),
SliderBox(label: 'HEIGHT', value: /*height*/, unit: 'cm'),
Spacer(),
SliderBox(label: 'WEIGHT', value: /*weight*/, unit: 'kg'),
Spacer(),
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(onPressed: () {}, child: Text("CALCULATE")),
),
],
),
),
);
}
}import 'package:flutter/material.dart';
class SliderBox extends StatelessWidget {
SliderBox({
required this.label,
required this.value,
required this.unit,
/*required this.onChanged,*/
});
String label;
double value;
String unit;
/*void Function(double newValue) onChanged;*/
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(label, style: TextStyle(fontSize: 20)),
Spacer(),
Text(
value.toStringAsFixed(0),
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
Text(unit, style: TextStyle(fontSize: 20)),
],
),
Slider(value: value, onChanged: /*onChanged*/, min: 1, max: 300),
],
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("BMI CALCULATOR"), centerTitle: true),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
child: Column(
children: [
GenderBox(isMale, onGenderChanged),
Spacer(),
SliderBox(
label: 'HEIGHT',
value: height,
unit: 'cm',
/*onChanged: onHeightChanged,*/
),
Spacer(),
SliderBox(
label: 'WEIGHT',
value: weight,
unit: 'kg',
/*onChanged: onWeightChanged,*/
),
Spacer(),
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(onPressed: () {}, child: Text("CALCULATE")),
),
],
),
),
);
}
}
영상을 보고서 따라할 수는 있겠는데, 직접 해보라고 하면 못 하겠다.
세 번 해보자. 세 번 해보고 안 되면 다섯 번 해보자.
다섯 번 해보고 안 되면 열 번 해보자.
다른 방법 없다.
Share article