BMI 계산기 - 버튼값 설정하기

Eungae's avatar
Dec 23, 2025
BMI 계산기 - 버튼값 설정하기

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으로 변경 후 변수 선언

  1. class HomePage를 StatefulWidget으로 변경
    1. // 변경 전 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) {
 
  1. 상속 받은 클래스에 변수 선언 및 변경 시 실행될 함수를 정의
    1. 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) {
 
  1. 변수를 각각의 위젯에 넘겨주기
    1. GenderBox
      1. HomePage에 선언된 변수를 GenderBox에 연결하기.
          • 변수 연결 → bool isMale;
          • 함수 연결 → void Function(bool newValue) 타입으로 연결.
          // 변경 전 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), ], ); }
      2. Widget gender에서 “터치 시 값이 바뀌도록” 세팅
          • GestureDetector 세팅 : Container에 Wrap with Widget → onTap → onChanged 연결
          • onChanged가 받을 수 있는 변수 설정: bool isMaleBox
          // 변경 전 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), ), ), ), ], ), ), ), ); } }
      3. bool isMaleBox가 추가되었으므로 생성자에도 그 자리를 마련.
        1. @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*/), ], ); }
    2. HomePage의 GenderBox 내에 변수 할당하기
      1. @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")), ), ], ), ), ); } }
    3. HomePage의 SliderBox 내에 변수 할당하기
      1. @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")), ), ], ), ), ); } }
    4. SliderBox에서 onHeightChangedonWeightChanged의 함수를 받을 수 있도록 void Function 타입의 변수 추가
      1. 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), ], ); } }
    5. HomePage의 SliderBox 내에 변수 추가
        • 변수명: onChanged ← void Function 타입이므로 함수를 호출해야 함.
        • 변수를 통해 호출할 함수명: onHeightChanged , onWeightChanged
        @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

나새끼메이커