DV Health is a comprehensive fitness app designed to facilitate athletes and fitness enthusiasts in tracking and managing their health and exercise routines. The app's core functionality revolves around device connectivity, athlete management, session tracking, and health data analysis. Coaches can harness the power of DV Health to access athlete data, analyze performance metrics, and provide tailored guidance, ensuring athletes reach their peak performance. Welcome to a smarter way to train, compete, and succeed with DV Health.
- Device Connectivity: Seamlessly connect and manage fitness devices.
- Athlete Management: Organize and access athlete profiles.
- Session Tracking: Monitor and record various exercise sessions.
- Health Data Analysis: Gather and analyze heart rate data.
- Description: Defines the
Athlete
class, representing athletes with a name and login code. - Key Components:
Athlete
class with propertiesname
andloginCode
.- Methods for data validation or modification.
- Description: Enumerates the different states of device connection and provides a string representation for each state.
- Key Components:
DeviceConnectionStatus
enum with valuesNOT_CONNECTED
,CONNECTING
, andCONNECTED
.- Extension method
statusName
for getting the string representation.
- Description: Represents a heart rate (HR) exercise session including properties like name, serial, and current heart rate.
- Key Components:
HRExerciseModel
class with methods to update heart rate.
- Description: Manages storage of HR data in a Sembast database.
- Key Components:
HRStorage
class with methods for database initialization, data dumping, and uploading.
- Description: Provides a Flutter widget for displaying a list of athletes.
- Key Components:
AthleteListView
class extendingStatelessWidget
.AthleteTile
class for individual athlete tiles.
- Description: Widget for scanning and connecting to devices.
- Key Components:
_DeviceScanWidgetState
class for managing the scanning state.- UI components for displaying device list and connection status.
- Description: Widget for viewing and managing heart rate data storage.
- Key Components:
HRStorageView
class for managing HR files and displaying data.
- Description: Displays exercise options and data.
- Key Components:
ExerciseOption
andExerciseOptionsPage
classes for managing exercise sessions.ExerciseCard
class for individual exercise options.
- Description: Widget for a link page related to heart rate calculations.
- Key Components:
LinkPage
class with UI elements for heart rate calculation information.
- Description: Options page with various action tiles.
- Key Components:
OptionsPage
andOptionTile
classes for displaying options.
- Description: Pin code input page for logging in.
- Key Components:
PinCode
class for pin code input and validation.
- Description: Manages the list of athletes and their interactions.
- Key Components:
AthleteListViewModel
class with a list ofAthlete
objects.
- Description: Manages device scanning and connection status.
- Key Components:
DeviceScanViewModel
class with methods for starting and stopping scans, connecting and disconnecting devices.
- Description: Manages data and state for specific devices, focusing on subscribing and handling heart rate.
- Key Components:
HRExerciseViewModel
class with methods for subscribing to HR data.
- Description: Manages the retrieval and display of stored HR data.
- Key Components:
HRStorageViewModel
class with methods for finding and reading HR data files.
- Description: Manages navigation to different pages based on user choices.
- Key Components:
OptionsViewModel
class with methods for navigating to record session, HR data, and link pages.
- Description: Manages the validation of pin code entries.
- Key Components:
PincodeViewModel
class with a method for pin code validation.
main.dart
: The main.dart file serves as the entry point for the app. It defines the main MyApp widget, which initializes the app's theme and starts on the home page.
- We used Flutter as the framework for developing the user interface and logic of our application.
- This is the Software Development Kit for Flutter, which provides the necessary tools for Flutter app development.
- Dart is the programming language used for building Flutter apps, and we have used version 3.1.1 of Dart.
- Our development environment was based on Windows 10.
- These were the target devices for our application, and our implementation was tested on Android 9 and 10.
- Our application is designed to run on Android devices and Windows PCs.
- We incorporated the Movesense library to generate signals and collect data from Movesense sensors.
- The Permission Handler library was used to manage permissions required by the application.
- We also used Python as part of our implementation.
- These Python libraries were employed to visualize the 12-hour data collection process, helping us verify the correctness of data collection.
Please be aware that our system has not been tested on iOS devices. It is optimized and intended for use on Android and Windows platforms.
Thank you for using our software, and if you have any questions or encounter any issues, feel free to reach out to our support team.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../model/Device.dart';
import '../view_model/DeviceViewModel.dart';
import '../view_model/AppModel.dart';
import 'options_page.dart';
class ExerciseOption extends StatefulWidget {
// tager en Device objekt som en parameter
final Device device;
const ExerciseOption(this.device);
@override
State<StatefulWidget> createState() {
return ExerciseOptionsPage();
}
}
class ExerciseOptionsPage extends State<ExerciseOption> {
late AppModel _appModel;
@override
void initState() {
// on initialization (iinit), får den instance af appmodel from provvider.of
super.initState();
_appModel = Provider.of<AppModel>(context, listen: false);
_appModel.onDeviceMdsDisconnected(
(device) => Navigator.pop(context)); // navigator back til current page
}
@override
void dispose() {
_appModel.disconnectFromDevice(
widget.device); // disconnector ved at bruge _appModel
super.dispose();
}
@override
Widget build(BuildContext context) {
Device device = widget.device;
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => OptionsPage())),
),
title: Text('Sessions'),
backgroundColor: Color(0xFF15A196),
actions: [
IconButton(
icon: Icon(Icons.account_circle),
onPressed: () {},
),
],
),
body: Container(
color: Color(0xFF15A196),
child: ListView(
children: [
ExerciseCard(
exerciseName: 'WALK',
dataFields: ['Step count', 'Total distance'],
device: device),
ExerciseCard(
exerciseName: 'RUN',
dataFields: ['Total distance', 'Time'],
device: device),
ExerciseCard(
exerciseName: 'BENCH',
dataFields: ['Total reps', 'Total sets'],
device: device),
],
),
),
);
}
}
class ExerciseCard extends StatefulWidget {
final String exerciseName;
final List<String> dataFields;
final Device device;
ExerciseCard({
required this.exerciseName,
required this.dataFields,
required this.device,
});
@override
_ExerciseCardState createState() => _ExerciseCardState();
}
class _ExerciseCardState extends State<ExerciseCard> {
Map<String, TextEditingController> controllers = {};
bool isWorkoutStarted = false;
String hrData = "No data"; // Variable to hold HR data
void startWorkout(DeviceViewModel deviceModel) {
setState(() {
isWorkoutStarted = true;
});
deviceModel.subscribeToHr();
// Listen to HR data updates
deviceModel.addListener(() {
setState(() {
hrData = deviceModel.hrData; // Update HR data on UI
});
});
}
void endWorkout(DeviceViewModel deviceModel) {
setState(() {
isWorkoutStarted = false;
hrData = "No data"; // Reset HR data display
});
deviceModel.unsubscribeFromHr();
}
@override
Widget build(BuildContext context) {
Device device = widget.device;
return ChangeNotifierProvider(
create: (context) => DeviceViewModel(device.name, device.serial),
child: Consumer<DeviceViewModel>(
builder: (context, deviceModel, child) {
return Card(
margin: EdgeInsets.all(8.0),
color: Color(0xFFAFDFDB), // Card color
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.exerciseName,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
Divider(),
for (String field in widget.dataFields) ...[
Row(
children: [
Expanded(
child: TextFormField(
controller: controllers[field],
decoration: InputDecoration(
labelText: field,
fillColor: Colors.white,
filled: true,
isDense: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
keyboardType: TextInputType.number,
),
),
SizedBox(width: 8),
],
),
SizedBox(height: 8),
],
if (isWorkoutStarted) ...[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("HR Data: $hrData"),
),
],
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton.icon(
icon: Icon(Icons.play_arrow, color: Colors.white),
label: Text('Start Workout'),
onPressed: () => startWorkout(deviceModel),
style: ElevatedButton.styleFrom(
primary: Colors.green, // background color
onPrimary: Colors.white, // foreground color
),
),
ElevatedButton.icon(
icon: Icon(Icons.stop, color: Colors.white),
label: Text('End Workout'),
onPressed: () => endWorkout(deviceModel),
style: ElevatedButton.styleFrom(
primary: Colors.red, // background color
onPrimary: Colors.white, // foreground color
),
),
],
),
],
),
),
);
},
),
);
}
}