Open In App

Fitness App using Flutter

Last Updated : 04 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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:



Next Article

Similar Reads