Flutter Tutorial

Flutter Beginner Lesson 125 of 135 3 min read

BMI Calculator App in Flutter (Beginner Project with Source Code)

Make a BMI calculator in Flutter with sliders, metric and imperial units, and a coloured result card. Beginner project with full runnable code.

On this page

A small, useful app that practises sliders, segmented buttons, cards and turning numbers into a result with colour.

BMI (body mass index) = weight in kg ÷ (height in m)². It is a rough guide, not medical advice.

The full app

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.teal),
      home: const BmiPage(),
    ));

enum Units { metric, imperial }

class BmiPage extends StatefulWidget {
  const BmiPage({super.key});

  @override
  State<BmiPage> createState() => _BmiPageState();
}

class _BmiPageState extends State<BmiPage> {
  Units units = Units.metric;
  double heightCm = 170;
  double weightKg = 65;

  double get bmi => weightKg / ((heightCm / 100) * (heightCm / 100));

  (String, Color) get category => switch (bmi) {
        < 18.5 => ('Underweight', Colors.blue),
        < 25 => ('Normal', Colors.green),
        < 30 => ('Overweight', Colors.orange),
        _ => ('Obese', Colors.red),
      };

  String get heightLabel {
    if (units == Units.metric) return '${heightCm.round()} cm';
    final totalInches = heightCm / 2.54;
    return "${totalInches ~/ 12} ft ${(totalInches % 12).round()} in";
  }

  String get weightLabel =>
      units == Units.metric ? '${weightKg.round()} kg' : '${(weightKg * 2.20462).round()} lb';

  @override
  Widget build(BuildContext context) {
    final (label, color) = category;
    return Scaffold(
      appBar: AppBar(title: const Text('BMI Calculator')),
      body: ListView(
        padding: const EdgeInsets.all(20),
        children: [
          SegmentedButton<Units>(
            segments: const [
              ButtonSegment(value: Units.metric, label: Text('Metric')),
              ButtonSegment(value: Units.imperial, label: Text('Imperial')),
            ],
            selected: {units},
            onSelectionChanged: (s) => setState(() => units = s.first),
          ),
          const SizedBox(height: 24),
          Text('Height: $heightLabel', style: Theme.of(context).textTheme.titleMedium),
          Slider(
            min: 120,
            max: 220,
            value: heightCm,
            onChanged: (v) => setState(() => heightCm = v),
          ),
          Text('Weight: $weightLabel', style: Theme.of(context).textTheme.titleMedium),
          Slider(
            min: 30,
            max: 150,
            value: weightKg,
            onChanged: (v) => setState(() => weightKg = v),
          ),
          const SizedBox(height: 24),
          Card(
            color: color.withValues(alpha: 0.12),
            child: Padding(
              padding: const EdgeInsets.all(24),
              child: Column(
                children: [
                  const Text('Your BMI'),
                  Text(
                    bmi.toStringAsFixed(1),
                    style: TextStyle(fontSize: 56, fontWeight: FontWeight.bold, color: color),
                  ),
                  Text(label, style: TextStyle(fontSize: 22, color: color)),
                  const SizedBox(height: 12),
                  const Text('Healthy range: 18.5 to 24.9', textAlign: TextAlign.center),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

How it works

  • State is just three values: the unit system, height and weight. Everything else (BMI, category, labels) is calculated from them in getters, so it can never be out of date.
  • The category getter returns a record (String, Color) from a switch expression with relational patterns (< 18.5). Read more about records.
  • Values are always stored in metric; the imperial setting only changes the labels. Storing one unit avoids rounding errors.
  • SegmentedButton is the Material 3 way to pick one option from a few. See selection controls.

Make it yours

  1. Save the last height and weight with shared_preferences.
  2. Show a history of BMI values with dates in a ListView.
  3. Add an age field and show a short tip for each category.
Practise in the playground Updated