Fitness App using Flutter

Last Updated : 23 Jul, 2025

The Fitness app is an essential companion for fitness enthusiasts who want to keep track of their exercises. In this article, we will learn how to build a Fitness App using Flutter for mobile devices. The app features a list of common exercises, allowing users to start and record the time spent on each exercise. It utilizes a timer widget in Flutter to function effectively. The app also offers a user-friendly interface that focuses on the main goals of fitness tracking.

Fitness-App-using-Flutter


Step-by-Step Implementation to Create a Fitness Application

Step 1: Create a new Flutter Application

Create a new Flutter application using the command Prompt. To create a new app, write the following command and run it.

flutter create app_name

To know more about it refer this article: Creating a Simple Application in Flutter

Step 2: Adding the Dependency

To add the dependency to the pubspec.yaml file, add  stop_watch_timer and expandable as a dependency in the dependencies part of the pubspec.yaml file, as shown below:

Dart
dependencies:
     flutter:
       sdk: flutter
     stop_watch_timer: ^3.2.1
     expandable: ^5.0.1

Now run the below command in the terminal.

flutter pub get

Or

Run the below command in the terminal.

flutter pub add stop_watch_timer expandable 


Step 3 : Working With main.dart

Add the boilerplate code below in main.dart to create a basic structure with an AppBar and body using a Scaffold.

main.dart:

Dart
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';

void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePageWidget(),
      title: 'FitnessApp',
      debugShowCheckedModeBanner: false,
    );
  }
}

// This is the main widget for the home page of the app.
class HomePageWidget extends StatefulWidget {
  const HomePageWidget({super.key});

  @override
  State<HomePageWidget> createState() => _HomePageWidgetState();
}

// This is the state class for the HomePageWidget.
class _HomePageWidgetState extends State<HomePageWidget> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Set the background color to white.
      backgroundColor: Colors.white, 
      appBar: AppBar(
        // Set the app bar color.
        backgroundColor: const Color(0xFF308D46), 
        // Hide the back button.
        automaticallyImplyLeading: false, 
        title: const Align(
          alignment: Alignment.center,
          child: Text(
            // App bar title.
            'Fitness App', 
            style: TextStyle(
              fontSize: 40.0,
              color: Colors.white,
            ),
          ),
        ),
        // Add a shadow to the app bar.
        elevation: 2.0, 
      ),
      body: SafeArea(
       // Write next part of the code here
      ),
    );
  }
}


Step 4 : Initialize variables

Intialize required variables in HomePageWidget class.

Dart
  // Controllers for expandable panels (to control their open/close state).
  final ExpandableController _expandableController1 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController2 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController3 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController4 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController5 =
      ExpandableController(initialExpanded: false);

  // Timers for each exercise.
  final StopWatchTimer _timerA = StopWatchTimer();
  final StopWatchTimer _timerB = StopWatchTimer();
  final StopWatchTimer _timerC = StopWatchTimer();
  final StopWatchTimer _timerD = StopWatchTimer();
  final StopWatchTimer _timerE = StopWatchTimer();

  @override
  void dispose() {
    // Clean up the timers when the widget is removed from the screen.
    _timerA.dispose();
    _timerB.dispose();
    _timerC.dispose();
    _timerD.dispose();
    _timerE.dispose();
    super.dispose();
  }


Step 6 : Create StatelessWidgets

Develop StatelessWidgets to create a stopwatch, and demonstrate the user interface using the ExpandableNotifier widget.

stopWatchWidget.dart:

Dart
// This widget displays the stopwatch and controls for starting/stopping the timer.
class StopWatchWidget extends StatelessWidget {
  const StopWatchWidget({
    super.key,
    required StopWatchTimer timer,
  }) : _timer = timer;
  // Timer for the stopwatch.
  final StopWatchTimer _timer; 

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      // Listen to the timer's stream.
      stream: _timer.rawTime, 
      initialData: 0,
      builder: (context, snapshot) {
        final value = snapshot.data!;
        // Format the time to display minutes and seconds.
        final displayTime = StopWatchTimer.getDisplayTime(value,
            hours: false, milliSecond: false);
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Display the current time.
            Text(
                  displayTime,
                  style: const TextStyle(
                        fontSize: 24.0,
                  ),
            ),
            SizedBox(
              width: 30,
            ),
            // Button to start or stop the timer.
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                  backgroundColor: _timer.isRunning ? Colors.red : Colors.green,
                  foregroundColor: Colors.white),
              onPressed: () {
                // Toggle between start and stop.
                _timer.isRunning ? _timer.onStopTimer() : _timer.onStartTimer();
              },
              child: Text(_timer.isRunning ? 'Stop' : 'Start'),
            ),
          ],
        );
      },
    );
  }
}


exerciseCardWidget.dart:

Dart
// This widget represents a single exercise with an expandable panel.
class ExerciseWidget extends StatelessWidget {
  const ExerciseWidget({
    super.key,
    required ExpandableController expandableController,
    required StopWatchTimer timer,
    required String title,
    required IconData iconData,
  })  : _expandableController = expandableController,
        _timer = timer,
        title = title,
        iconData = iconData;
  // Controller for the expandable panel.
  final ExpandableController _expandableController; 
  // Timer for the exercise.
  final StopWatchTimer _timer;
  // Title of the exercise. 
  final String title; 
  // Icon for the exercise.
  final IconData iconData; 

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        width: double.infinity,
        color: Colors.white,
        child: ExpandableNotifier(
          controller: _expandableController,
          child: ExpandablePanel(
            header: Center(
              child: Text(
                // Display the exercise title.
                title, 
                style: TextStyle(fontWeight: FontWeight.w600, fontSize: 24),
              ),
            ),
            collapsed: Center(
                child: Icon(
              // Display the exercise icon when collapsed.
              iconData, 
              size: 100,
            )),
            // Show the stopwatch when expanded.
            expanded: StopWatchWidget(timer: _timer), 
            theme: const ExpandableThemeData(
              // Allow tapping the header to expand.
              tapHeaderToExpand: true, 
              tapBodyToExpand: false,
              tapBodyToCollapse: false,
              headerAlignment: ExpandablePanelHeaderAlignment.center,
              // Show an arrow icon in the header.
              hasIcon: true, 
            ),
          ),
        ),
      ),
    );
  }
}


Step 7 : Develop UI

- Column : Display Column of all Exercises With a title named "Select Exercise" at the top.

Dart
Column(
    mainAxisSize: MainAxisSize.min,
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      // Title for the exercise selection section.
      const Padding(
        padding: EdgeInsets.all(10.0),
        child: Text(
          'Select Exercise',
          style: TextStyle(
            color: Color(0xFF308D46),
            fontSize: 25.0,
          ),
        ),
      ),
      // Widgets for each exercise.
      // Walk exercise.
      ExerciseWidget(
          expandableController: _expandableController1,
          timer: _timerA,
          title: "Walk",
          iconData: Icons.directions_walk), 
      // Swim exercise.
      ExerciseWidget(
          expandableController: _expandableController2,
          timer: _timerB,
          title: "Swim",
          iconData: Icons.pool),
      // Gymnastics exercise. 
      ExerciseWidget(
          expandableController: _expandableController3,
          timer: _timerC,
          title: "Gymnastics",
          iconData: Icons.sports_gymnastics_outlined),
      // Running exercise.
      ExerciseWidget(
          expandableController: _expandableController4,
          timer: _timerD,
          title: "Running",
          iconData: Icons.run_circle_outlined),
      // Cycling exercise. 
      ExerciseWidget(
          expandableController: _expandableController5,
          timer: _timerE,
          title: "Cycling",
          iconData: Icons.directions_bike_outlined), 
    ],
  ),


Then implement the above UI code in main.dart.

main.dart:

Dart
// This is the main widget for the home page of the app.
class HomePageWidget extends StatefulWidget {
  const HomePageWidget({super.key});

  @override
  State<HomePageWidget> createState() => _HomePageWidgetState();
}

// This is the state class for the HomePageWidget.
class _HomePageWidgetState extends State<HomePageWidget> {
  // Controllers for expandable panels (to control their open/close state).
  final ExpandableController _expandableController1 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController2 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController3 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController4 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController5 =
      ExpandableController(initialExpanded: false);

  // Timers for each exercise.
  final StopWatchTimer _timerA = StopWatchTimer();
  final StopWatchTimer _timerB = StopWatchTimer();
  final StopWatchTimer _timerC = StopWatchTimer();
  final StopWatchTimer _timerD = StopWatchTimer();
  final StopWatchTimer _timerE = StopWatchTimer();

  @override
  void dispose() {
    // Clean up the timers when the widget is removed from the screen.
    _timerA.dispose();
    _timerB.dispose();
    _timerC.dispose();
    _timerD.dispose();
    _timerE.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Set the background color to white.
      backgroundColor: Colors.white, 
      appBar: AppBar(
        // Set the app bar color.
        backgroundColor: const Color(0xFF308D46), 
        // Hide the back button.
        automaticallyImplyLeading: false, 
        title: const Align(
          alignment: Alignment.center,
          child: Text(
            // App bar title.
            'Fitness App', 
            style: TextStyle(
              fontSize: 40.0,
              color: Colors.white,
            ),
          ),
        ),
        // Add a shadow to the app bar.
        elevation: 2.0, 
      ),
      body: Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          // Title for the exercise selection section.
          const Padding(
            padding: EdgeInsets.all(10.0),
            child: Text(
              'Select Exercise',
              style: TextStyle(
                color: Color(0xFF308D46),
                fontSize: 25.0,
              ),
            ),
          ),
          // Widgets for each exercise.
          // Walk exercise.
          ExerciseWidget(
              expandableController: _expandableController1,
              timer: _timerA,
              title: "Walk",
              iconData: Icons.directions_walk), 
          // Swim exercise.
          ExerciseWidget(
              expandableController: _expandableController2,
              timer: _timerB,
              title: "Swim",
              iconData: Icons.pool),
          // Gymnastics exercise. 
          ExerciseWidget(
              expandableController: _expandableController3,
              timer: _timerC,
              title: "Gymnastics",
              iconData: Icons.sports_gymnastics_outlined),
          // Running exercise.
          ExerciseWidget(
              expandableController: _expandableController4,
              timer: _timerD,
              title: "Running",
              iconData: Icons.run_circle_outlined),
          // Cycling exercise. 
          ExerciseWidget(
              expandableController: _expandableController5,
              timer: _timerE,
              title: "Cycling",
              iconData: Icons.directions_bike_outlined), 
        ],
      ),
    );
  }
}


Complete Source code

Note: We can use all above code in main.dart only.

main.dart:

Dart
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';

void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePageWidget(),
      title: 'FitnessApp',
      debugShowCheckedModeBanner: false,
    );
  }
}

// This is the main widget for the home page of the app.
class HomePageWidget extends StatefulWidget {
  const HomePageWidget({super.key});

  @override
  State<HomePageWidget> createState() => _HomePageWidgetState();
}

// This is the state class for the HomePageWidget.
class _HomePageWidgetState extends State<HomePageWidget> {
  // Controllers for expandable panels (to control their open/close state).
  final ExpandableController _expandableController1 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController2 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController3 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController4 =
      ExpandableController(initialExpanded: false);
  final ExpandableController _expandableController5 =
      ExpandableController(initialExpanded: false);

  // Timers for each exercise.
  final StopWatchTimer _timerA = StopWatchTimer();
  final StopWatchTimer _timerB = StopWatchTimer();
  final StopWatchTimer _timerC = StopWatchTimer();
  final StopWatchTimer _timerD = StopWatchTimer();
  final StopWatchTimer _timerE = StopWatchTimer();

  @override
  void dispose() {
    // Clean up the timers when the widget is removed from the screen.
    _timerA.dispose();
    _timerB.dispose();
    _timerC.dispose();
    _timerD.dispose();
    _timerE.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Set the background color to white.
      backgroundColor: Colors.white, 
      appBar: AppBar(
        // Set the app bar color.
        backgroundColor: const Color(0xFF308D46), 
        // Hide the back button.
        automaticallyImplyLeading: false, 
        title: const Align(
          alignment: Alignment.center,
          child: Text(
            // App bar title.
            'Fitness App', 
            style: TextStyle(
              fontSize: 40.0,
              color: Colors.white,
            ),
          ),
        ),
        // Add a shadow to the app bar.
        elevation: 2.0, 
      ),
      body: Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          // Title for the exercise selection section.
          const Padding(
            padding: EdgeInsets.all(10.0),
            child: Text(
              'Select Exercise',
              style: TextStyle(
                color: Color(0xFF308D46),
                fontSize: 25.0,
              ),
            ),
          ),
          // Widgets for each exercise.
          // Walk exercise.
          ExerciseWidget(
              expandableController: _expandableController1,
              timer: _timerA,
              title: "Walk",
              iconData: Icons.directions_walk), 
          // Swim exercise.
          ExerciseWidget(
              expandableController: _expandableController2,
              timer: _timerB,
              title: "Swim",
              iconData: Icons.pool),
          // Gymnastics exercise. 
          ExerciseWidget(
              expandableController: _expandableController3,
              timer: _timerC,
              title: "Gymnastics",
              iconData: Icons.sports_gymnastics_outlined),
          // Running exercise.
          ExerciseWidget(
              expandableController: _expandableController4,
              timer: _timerD,
              title: "Running",
              iconData: Icons.run_circle_outlined),
          // Cycling exercise. 
          ExerciseWidget(
              expandableController: _expandableController5,
              timer: _timerE,
              title: "Cycling",
              iconData: Icons.directions_bike_outlined), 
        ],
      ),
    );
  }
}

// This widget represents a single exercise with an expandable panel.
class ExerciseWidget extends StatelessWidget {
  const ExerciseWidget({
    super.key,
    required ExpandableController expandableController,
    required StopWatchTimer timer,
    required String title,
    required IconData iconData,
  })  : _expandableController = expandableController,
        _timer = timer,
        title = title,
        iconData = iconData;
  // Controller for the expandable panel.
  final ExpandableController _expandableController; 
  // Timer for the exercise.
  final StopWatchTimer _timer;
  // Title of the exercise. 
  final String title; 
  // Icon for the exercise.
  final IconData iconData; 

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        width: double.infinity,
        color: Colors.white,
        child: ExpandableNotifier(
          controller: _expandableController,
          child: ExpandablePanel(
            header: Center(
              child: Text(
                // Display the exercise title.
                title, 
                style: TextStyle(fontWeight: FontWeight.w600, fontSize: 24),
              ),
            ),
            collapsed: Center(
                child: Icon(
              // Display the exercise icon when collapsed.
              iconData, 
              size: 100,
            )),
            // Show the stopwatch when expanded.
            expanded: StopWatchWidget(timer: _timer), 
            theme: const ExpandableThemeData(
              // Allow tapping the header to expand.
              tapHeaderToExpand: true, 
              tapBodyToExpand: false,
              tapBodyToCollapse: false,
              headerAlignment: ExpandablePanelHeaderAlignment.center,
              // Show an arrow icon in the header.
              hasIcon: true, 
            ),
          ),
        ),
      ),
    );
  }
}

// This widget displays the stopwatch and controls for starting/stopping the timer.
class StopWatchWidget extends StatelessWidget {
  const StopWatchWidget({
    super.key,
    required StopWatchTimer timer,
  }) : _timer = timer;
  // Timer for the stopwatch.
  final StopWatchTimer _timer; 

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      // Listen to the timer's stream.
      stream: _timer.rawTime, 
      initialData: 0,
      builder: (context, snapshot) {
        final value = snapshot.data!;
        // Format the time to display minutes and seconds.
        final displayTime = StopWatchTimer.getDisplayTime(value,
            hours: false, milliSecond: false);
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Display the current time.
            Text(
              displayTime,
              style: const TextStyle(
                fontSize: 24.0,
              ),
            ),
            SizedBox(
              width: 30,
            ),
            // Button to start or stop the timer.
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                  backgroundColor: _timer.isRunning ? Colors.red : Colors.green,
                  foregroundColor: Colors.white),
              onPressed: () {
                // Toggle between start and stop.
                _timer.isRunning ? _timer.onStopTimer() : _timer.onStartTimer();
              },
              child: Text(_timer.isRunning ? 'Stop' : 'Start'),
            ),
          ],
        );
      },
    );
  }
}

Output:


Comment

Explore