Compare commits
157 Commits
Author | SHA1 | Date |
---|---|---|
|
ece19ef7d2 | 1 year ago |
|
cd6c43431e | 1 year ago |
|
8ee5055ffc | 1 year ago |
|
a2657b2bba | 1 year ago |
|
8b65bccf9f | 1 year ago |
|
3e00d74e97 | 1 year ago |
|
6e2b6c057e | 1 year ago |
|
359cbfeed8 | 1 year ago |
|
d15eb2bffc | 1 year ago |
|
a10439d1bc | 1 year ago |
![]() |
4a40f1bbd6 | 1 year ago |
![]() |
866a0a7381 | 1 year ago |
|
0784a95f3a | 1 year ago |
|
147c53e1ae | 1 year ago |
![]() |
c5b182411e | 1 year ago |
|
acdb0ce770 | 1 year ago |
![]() |
dcd2198e79 | 1 year ago |
![]() |
404c5f344a | 1 year ago |
![]() |
ad706516df | 1 year ago |
![]() |
e2e5f86e3c | 1 year ago |
![]() |
5f46882c18 | 1 year ago |
![]() |
97e915d31f | 1 year ago |
|
de3a4f0cce | 1 year ago |
|
5b81ff7b17 | 1 year ago |
![]() |
6ded7e7eb0 | 1 year ago |
![]() |
7a3a707c1c | 1 year ago |
|
d5efc26e4e | 1 year ago |
![]() |
f23ff5da02 | 1 year ago |
![]() |
218179b9df | 1 year ago |
![]() |
30ff3a0c3c | 1 year ago |
![]() |
abbb92b564 | 1 year ago |
![]() |
0ed7bd0cd7 | 1 year ago |
|
01c71f7dc6 | 1 year ago |
|
db6128a602 | 1 year ago |
|
3ec628766c | 1 year ago |
![]() |
6fbde68b73 | 1 year ago |
![]() |
6027af0e18 | 1 year ago |
![]() |
1a30f4ea57 | 1 year ago |
![]() |
954343b611 | 1 year ago |
![]() |
11adf4814a | 1 year ago |
|
c58bebb010 | 1 year ago |
|
a6b1049817 | 1 year ago |
|
89652ae23b | 1 year ago |
|
7fc300c050 | 1 year ago |
|
e42a1e724a | 1 year ago |
|
5a5da634b7 | 1 year ago |
|
31ac327002 | 1 year ago |
|
88ef9aaba3 | 1 year ago |
|
abda0403f0 | 1 year ago |
![]() |
f40b3a5631 | 1 year ago |
|
3a21285ff8 | 1 year ago |
|
84718c8bd5 | 1 year ago |
|
7c9f68ba9d | 1 year ago |
|
ebc658680b | 1 year ago |
|
884e5f0b8e | 1 year ago |
|
bbc4e4fb04 | 1 year ago |
![]() |
f4bc001348 | 1 year ago |
|
1eb9d648e9 | 1 year ago |
|
ffc6d93008 | 1 year ago |
|
b95cb7d35d | 1 year ago |
|
a01a2f328e | 1 year ago |
|
af07fc3958 | 1 year ago |
![]() |
a4e9ddded6 | 1 year ago |
|
0c92c3d87d | 1 year ago |
|
2ab8c9207f | 1 year ago |
|
71601e2667 | 1 year ago |
|
e750bf8d5c | 1 year ago |
|
00d7ba9b5a | 1 year ago |
|
769139fa03 | 1 year ago |
|
ed3664d561 | 1 year ago |
![]() |
0f4a5d5932 | 1 year ago |
![]() |
7bf04cd1c7 | 1 year ago |
![]() |
c216ae5bb0 | 1 year ago |
|
73a2903d34 | 1 year ago |
|
0d06931620 | 1 year ago |
![]() |
48ac81bea0 | 1 year ago |
![]() |
2c3da877ca | 1 year ago |
![]() |
a3e77c3759 | 1 year ago |
![]() |
8e658555cc | 1 year ago |
|
87773ac824 | 1 year ago |
![]() |
62948bf1c2 | 1 year ago |
![]() |
ae67f20c13 | 1 year ago |
![]() |
fbbf199b4c | 1 year ago |
|
609cdd1e71 | 1 year ago |
|
425bc391a2 | 1 year ago |
![]() |
bfbfa49ad9 | 1 year ago |
![]() |
bb0c9e2209 | 1 year ago |
|
8d408a274c | 1 year ago |
|
bb91fb9778 | 1 year ago |
![]() |
2941a29a5c | 1 year ago |
|
49bc2bdd16 | 1 year ago |
|
9e768c474c | 1 year ago |
|
71269c8da8 | 1 year ago |
![]() |
6ee3a9cb46 | 1 year ago |
|
5188fd6d95 | 1 year ago |
![]() |
795a26056f | 1 year ago |
![]() |
ab34c5884e | 1 year ago |
![]() |
a4f387e3cd | 1 year ago |
![]() |
6388591ee0 | 1 year ago |
![]() |
8d3f96e2eb | 1 year ago |
![]() |
195e78a705 | 1 year ago |
![]() |
6d0207e94c | 1 year ago |
![]() |
6036337e90 | 1 year ago |
![]() |
8a92895371 | 1 year ago |
|
6580f8324c | 1 year ago |
![]() |
74661ea421 | 1 year ago |
![]() |
9a56419e4a | 1 year ago |
![]() |
2392fc34ce | 1 year ago |
![]() |
129073253a | 1 year ago |
![]() |
fe6291de0e | 1 year ago |
![]() |
52accae66e | 1 year ago |
![]() |
b06c895198 | 1 year ago |
![]() |
540d5e3fb8 | 1 year ago |
|
212cf666d6 | 1 year ago |
![]() |
96ee39d995 | 1 year ago |
![]() |
44f2a6b9ca | 1 year ago |
![]() |
20dc010762 | 1 year ago |
![]() |
222a676d73 | 1 year ago |
![]() |
9604006459 | 1 year ago |
|
cb9bd64873 | 1 year ago |
![]() |
733d6d3774 | 1 year ago |
![]() |
c7b1ab6a6c | 1 year ago |
|
76b32e69ff | 1 year ago |
|
d0f20ec3e0 | 1 year ago |
![]() |
1c3596e3fd | 1 year ago |
![]() |
9ac8265008 | 1 year ago |
![]() |
624993f516 | 1 year ago |
![]() |
c7c2cb2f83 | 1 year ago |
![]() |
fd06d9b55d | 1 year ago |
![]() |
ed66e21aff | 1 year ago |
![]() |
407664c1d5 | 1 year ago |
![]() |
193318b01e | 1 year ago |
![]() |
2ed8ccf2a5 | 1 year ago |
![]() |
38fdb6d4e8 | 1 year ago |
|
2e3793ea6d | 1 year ago |
|
2b9b680774 | 1 year ago |
![]() |
6d473d9238 | 1 year ago |
|
e70129c6ba | 1 year ago |
![]() |
b797031381 | 1 year ago |
![]() |
cfe7879c2f | 1 year ago |
![]() |
4df9430b3c | 1 year ago |
|
8295d36ace | 1 year ago |
![]() |
361c5bed9c | 1 year ago |
![]() |
d77982c9ce | 1 year ago |
![]() |
3470feff12 | 1 year ago |
![]() |
385a784797 | 1 year ago |
![]() |
eb1fa1e57d | 1 year ago |
|
09854a6237 | 1 year ago |
![]() |
35714bfbb7 | 1 year ago |
|
bc89324f2a | 1 year ago |
![]() |
5cdd70de5b | 1 year ago |
|
75cf385d53 | 1 year ago |
|
b7913f010f | 1 year ago |
![]() |
b99b62dc2f | 1 year ago |
![]() |
bdc1a79d85 | 1 year ago |
![]() |
a9b5ac2fc8 | 1 year ago |
![]() |
6f9249ea0e | 1 year ago |
@ -0,0 +1,38 @@
|
|||||||
|
name: Flutter CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-apk-web-sonarqube:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: ghcr.io/cirruslabs/flutter:3.16.4
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Build flutter apk
|
||||||
|
run: |
|
||||||
|
flutter clean
|
||||||
|
flutter pub cache repair
|
||||||
|
flutter pub get
|
||||||
|
dart run build_runner clean
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
flutter build apk
|
||||||
|
sfm_apk=sfm_$(date +"%Y_%m_%d_%H_%M_%S").apk
|
||||||
|
cp ./build/app/outputs/flutter-apk/app-release.apk $sfm_apk
|
||||||
|
curl -F "file=@$sfm_apk" https://anonfiles.me/api/v1/upload > upload.json
|
||||||
|
cat upload.json | cut -d '"' -f 12
|
||||||
|
|
||||||
|
- name: Build flutter web
|
||||||
|
run: |
|
||||||
|
flutter build web --web-renderer canvaskit
|
||||||
|
curl -sL https://firebase.tools | bash
|
||||||
|
- name: Code analysis
|
||||||
|
run: |
|
||||||
|
export SONAR_SCANNER_VERSION=5.0.1.3006
|
||||||
|
export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
|
||||||
|
curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
|
||||||
|
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
|
||||||
|
export PATH=$SONAR_SCANNER_HOME/bin:$PATH
|
||||||
|
export SONAR_SCANNER_OPTS="-server"
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 974 B |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/mobile/mobile_container_stats_activities.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/web/web_container_stats_activities.dart';
|
||||||
|
|
||||||
|
class ContainerStatsActivities extends StatelessWidget {
|
||||||
|
const ContainerStatsActivities(
|
||||||
|
this.value,
|
||||||
|
this.designation,
|
||||||
|
this.icon, {
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String designation;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ScreenTypeLayout.builder(
|
||||||
|
mobile: (_) => MobileContainerStatsActivities(value, designation, icon),
|
||||||
|
desktop: (_) => WebContainerStatsActivities(value, designation, icon),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
import 'package:smartfit_app_mobile/main.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_saver.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/helper.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/workout_row/workout_row_generic.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/workout_row/workout_row_walking.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/manager_file.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/info_message.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/list_activity/list_activity_utile.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class ListActivityWidget extends StatefulWidget {
|
||||||
|
const ListActivityWidget({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ListActivityWidget> createState() => _ListActivityWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ListActivityWidget extends State<ListActivityWidget> {
|
||||||
|
final ApiWrapper api = ApiWrapper();
|
||||||
|
final InfoMessage infoManager = InfoMessage();
|
||||||
|
final ListActivityUtile _utile = ListActivityUtile();
|
||||||
|
final ManagerFile managerFile = ManagerFile();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Future<void> onClick(ActivityOfUser activityObj) async {
|
||||||
|
if (!Provider.of<User>(context, listen: false)
|
||||||
|
.managerSelectedActivity
|
||||||
|
.fileNotSelected(activityObj.fileUuid)) {
|
||||||
|
Provider.of<User>(context, listen: false)
|
||||||
|
.managerSelectedActivity
|
||||||
|
.removeSelectedActivity(activityObj.fileUuid);
|
||||||
|
setState(() {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple2<bool, String> result =
|
||||||
|
await _utile.getContentActivity(context, activityObj, infoManager);
|
||||||
|
if (!result.item1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Hein?
|
||||||
|
Provider.of<User>(context, listen: false).removeActivity(activityObj);
|
||||||
|
Provider.of<User>(context, listen: false).insertActivity(0, activityObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Understand :(
|
||||||
|
Future<void> onDelete(ActivityOfUser activityObj) async {
|
||||||
|
if (await api.deleteFile(Provider.of<User>(context, listen: false).token,
|
||||||
|
activityObj.fileUuid, infoManager)) {
|
||||||
|
if (!Provider.of<User>(context, listen: false)
|
||||||
|
.managerSelectedActivity
|
||||||
|
.fileNotSelected(activityObj.fileUuid)) {
|
||||||
|
Provider.of<User>(context, listen: false)
|
||||||
|
.managerSelectedActivity
|
||||||
|
.removeSelectedActivity(activityObj.fileUuid);
|
||||||
|
}
|
||||||
|
if (!Helper.isPlatformWeb() && localDB.getSaveLocally()) {
|
||||||
|
ActivitySaver actSaver = await ActivitySaver.create();
|
||||||
|
actSaver.deleteActivity(activityObj.fileUuid);
|
||||||
|
localDB.removeActivity(activityObj.fileUuid);
|
||||||
|
}
|
||||||
|
Provider.of<User>(context, listen: false).removeActivity(activityObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelected(ActivityOfUser activityObj) {
|
||||||
|
return !Provider.of<User>(context)
|
||||||
|
.managerSelectedActivity
|
||||||
|
.fileNotSelected(activityObj.fileUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Visibility(
|
||||||
|
visible: infoManager.isVisible,
|
||||||
|
child: Text(infoManager.message,
|
||||||
|
style: TextStyle(color: infoManager.messageColor))),
|
||||||
|
ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount:
|
||||||
|
Provider.of<User>(context, listen: true).listActivity.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
ActivityOfUser activityObj =
|
||||||
|
Provider.of<User>(context, listen: true).listActivity[index];
|
||||||
|
Map<String, dynamic> activityMap;
|
||||||
|
// -- Si categorie == marche
|
||||||
|
if (activityObj.category == managerFile.marche) {
|
||||||
|
activityMap = activityObj.toMapWalking();
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: WorkoutRowWalking(
|
||||||
|
wObj: activityMap,
|
||||||
|
onDelete: () async {
|
||||||
|
await onDelete(activityObj);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
onClick: () async {
|
||||||
|
await onClick(activityObj);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
isSelected: isSelected(activityObj),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// -- Default -- //
|
||||||
|
activityMap = activityObj.toMapGeneric();
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: WorkoutRowGeneric(
|
||||||
|
wObj: activityMap,
|
||||||
|
onDelete: () async {
|
||||||
|
await onDelete(activityObj);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
onClick: () async {
|
||||||
|
await onClick(activityObj);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
isSelected: isSelected(activityObj),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/container_stats_activities.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
|
||||||
|
|
||||||
|
class VolumesList extends StatelessWidget {
|
||||||
|
final Map<String, dynamic> volume;
|
||||||
|
|
||||||
|
const VolumesList({super.key, required this.volume});
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var media = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
// TODO: True message with variables and context aware
|
||||||
|
if (volume["nbActivity"] == 0) {
|
||||||
|
return const Text("No activity the last x days/month/year");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities(volume["nbActivity"].toString(),
|
||||||
|
"Number of activities", Icons.numbers),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${Convertisseur.secondeIntoMinute(volume["durationActiviy"]).toStringAsFixed(0)} min",
|
||||||
|
"Total time",
|
||||||
|
Icons.timer),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
volume["bpmAvg"].toString(), "Average bpm", Icons.favorite),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
" ${Convertisseur.msIntoKmh(volume["speedAvg"]).toStringAsFixed(2)} km/h",
|
||||||
|
"Average speed",
|
||||||
|
Icons.bolt),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${volume["denivelePositif"].toStringAsFixed(2)} m",
|
||||||
|
"Positive height difference",
|
||||||
|
Icons.hiking),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/stats.dart';
|
||||||
|
|
||||||
|
class MobileContainerStats extends StatelessWidget {
|
||||||
|
const MobileContainerStats(this.value, this.designation, this.icon, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String designation;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xffe1e1e1),
|
||||||
|
),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
offset: Offset(3, 3),
|
||||||
|
blurRadius: 3,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
StatIcon(
|
||||||
|
icon: icon,
|
||||||
|
iconColor: TColor.white,
|
||||||
|
iconBackground: TColor.secondaryColor1,
|
||||||
|
sizeIcon: 12,
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
designation,
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/stats.dart';
|
||||||
|
|
||||||
|
class MobileContainerStatsActivities extends StatelessWidget {
|
||||||
|
const MobileContainerStatsActivities(
|
||||||
|
this.value,
|
||||||
|
this.designation,
|
||||||
|
this.icon, {
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String designation;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var media = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
height: media.width * 0.33,
|
||||||
|
width: media.width * 0.28,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xffe1e1e1),
|
||||||
|
),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
offset: Offset(3, 3),
|
||||||
|
blurRadius: 3,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.center, // Centrer horizontalement
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
StatIcon(
|
||||||
|
icon: icon,
|
||||||
|
iconColor: TColor.white,
|
||||||
|
iconBackground: TColor.secondaryColor1,
|
||||||
|
sizeIcon: 30.0,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10), // Espacement entre l'icône et le texte
|
||||||
|
Text(
|
||||||
|
designation,
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/container_stats.dart';
|
||||||
|
|
||||||
|
class MobileLigneContainerStats extends StatelessWidget {
|
||||||
|
const MobileLigneContainerStats(
|
||||||
|
this.value1,
|
||||||
|
this.value2,
|
||||||
|
this.value3,
|
||||||
|
this.designation1,
|
||||||
|
this.designation2,
|
||||||
|
this.designation3,
|
||||||
|
this.icon1,
|
||||||
|
this.icon2,
|
||||||
|
this.icon3,
|
||||||
|
{Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final String value1;
|
||||||
|
final String value2;
|
||||||
|
final String value3;
|
||||||
|
|
||||||
|
final String designation1;
|
||||||
|
final String designation2;
|
||||||
|
final String designation3;
|
||||||
|
|
||||||
|
final IconData icon1;
|
||||||
|
final IconData icon2;
|
||||||
|
final IconData icon3;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const Divider(height: 30),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Stats',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Icon(
|
||||||
|
Icons.pie_chart_rounded,
|
||||||
|
size: 15,
|
||||||
|
color: TColor.secondaryColor1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStats(value1, designation1, icon1),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
ContainerStats(value2, designation2, icon2),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
ContainerStats(value3, designation3, icon3),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
const Divider(height: 30),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/setting_row.dart';
|
||||||
|
import 'package:smartfit_app_mobile/view/profile/change_email.dart';
|
||||||
|
import 'package:smartfit_app_mobile/view/profile/change_password.dart';
|
||||||
|
import 'package:smartfit_app_mobile/view/profile/change_username.dart';
|
||||||
|
|
||||||
|
class ProfileCompte extends StatelessWidget {
|
||||||
|
const ProfileCompte(this.accountArr, {super.key});
|
||||||
|
|
||||||
|
final List accountArr;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 2)]),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Account",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
ListView.builder(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: accountArr.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
var iObj = accountArr[index];
|
||||||
|
return SettingRow(
|
||||||
|
icon: iObj["image"]!,
|
||||||
|
title: iObj["name"]!,
|
||||||
|
onPressed: () {
|
||||||
|
if (iObj["tag"] == "1") {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const ChangeUsernameView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (iObj["tag"] == "2") {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const ChangePasswordView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const ChangeEmailView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
|
||||||
|
class ProfileEntete extends StatelessWidget {
|
||||||
|
const ProfileEntete(this.username, {super.key});
|
||||||
|
|
||||||
|
final String username;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
child: Image.asset(
|
||||||
|
"assets/img/u1.png",
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
username,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Running",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/title_subtitle_cell.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
|
||||||
|
class ProfileInfoUser extends StatelessWidget {
|
||||||
|
const ProfileInfoUser({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TitleSubtitleCell(
|
||||||
|
title: context.watch<User>().listActivity.length.toString(),
|
||||||
|
subtitle: "Number of activities",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TitleSubtitleCell(
|
||||||
|
title: context
|
||||||
|
.watch<User>()
|
||||||
|
.getTotalTimeAllActivity()
|
||||||
|
.toStringAsFixed(2),
|
||||||
|
subtitle: "Total activity time",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TitleSubtitleCell(
|
||||||
|
title:
|
||||||
|
"${context.watch<User>().getTotalDenivelePositifAllActivity().toStringAsFixed(2)} + m",
|
||||||
|
subtitle: "Total positive height difference",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TitleSubtitleCell(
|
||||||
|
title:
|
||||||
|
"${context.watch<User>().getTotalDeniveleNegatifAllActivity().toStringAsFixed(2)} - m",
|
||||||
|
subtitle: "Total negative height difference",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
import 'package:animated_toggle_switch/animated_toggle_switch.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
|
||||||
|
class ProfileNotification extends StatefulWidget {
|
||||||
|
const ProfileNotification(this.positive, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
final bool positive;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfileNotification> createState() => _ProfileNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfileNotification extends State<ProfileNotification> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
bool check = widget.positive;
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 2)]),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Notification",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 30,
|
||||||
|
child:
|
||||||
|
Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
|
||||||
|
Image.asset("assets/img/p_notification.png",
|
||||||
|
height: 15, width: 15, fit: BoxFit.contain),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Push Notifications",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CustomAnimatedToggleSwitch<bool>(
|
||||||
|
current: check,
|
||||||
|
values: const [false, true],
|
||||||
|
spacing: 0.0,
|
||||||
|
indicatorSize: const Size.square(25.0),
|
||||||
|
animationDuration: const Duration(milliseconds: 200),
|
||||||
|
animationCurve: Curves.linear,
|
||||||
|
onChanged: (b) => setState(() => check = b),
|
||||||
|
iconBuilder: (context, local, global) {
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
cursors: const ToggleCursors(
|
||||||
|
defaultCursor: SystemMouseCursors.click),
|
||||||
|
onTap: (_) => setState(() => check = !check),
|
||||||
|
iconsTappable: false,
|
||||||
|
wrapperBuilder: (context, global, child) {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
left: 10.0,
|
||||||
|
right: 10.0,
|
||||||
|
height: 20.0,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient:
|
||||||
|
LinearGradient(colors: TColor.secondaryG),
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(50.0)),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
child,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
foregroundIndicatorBuilder: (context, global) {
|
||||||
|
return SizedBox.fromSize(
|
||||||
|
size: const Size(5, 5),
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.white,
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(50.0)),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black38,
|
||||||
|
spreadRadius: 0.05,
|
||||||
|
blurRadius: 1.1,
|
||||||
|
offset: Offset(0.0, 0.8))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/setting_row.dart';
|
||||||
|
import 'package:smartfit_app_mobile/view/profile/contact_us_view.dart';
|
||||||
|
import 'package:smartfit_app_mobile/view/profile/policy_view.dart';
|
||||||
|
|
||||||
|
class ProfileOther extends StatelessWidget {
|
||||||
|
const ProfileOther(this.otherArr, {super.key});
|
||||||
|
|
||||||
|
final List otherArr;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 2)]),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Others",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
ListView.builder(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: otherArr.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
var iObj = otherArr[index] as Map? ?? {};
|
||||||
|
return SettingRow(
|
||||||
|
icon: iObj["image"].toString(),
|
||||||
|
title: iObj["name"].toString(),
|
||||||
|
onPressed: () {
|
||||||
|
if (iObj["tag"] == "6") {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const PrivacyPolicyView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (iObj["tag"] == "5") {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const ContactUsView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Autre logique si nécessaire pour d'autres éléments de la liste
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/main.dart';
|
||||||
|
|
||||||
|
class ProfileSwitch extends StatefulWidget {
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
final String iconFilename;
|
||||||
|
|
||||||
|
const ProfileSwitch(this.title, this.description, this.iconFilename,
|
||||||
|
{super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfileSwitch> createState() => _ProfileSwitchState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfileSwitchState extends State<ProfileSwitch> {
|
||||||
|
bool switchValue = localDB.getSaveLocally();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 2)]),
|
||||||
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 30,
|
||||||
|
child:
|
||||||
|
Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
|
||||||
|
Image.asset("assets/img/${widget.iconFilename}",
|
||||||
|
height: 28, width: 28, fit: BoxFit.contain),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
widget.description,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Switch(
|
||||||
|
value: switchValue,
|
||||||
|
activeColor: Colors.orange,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
switchValue = value;
|
||||||
|
localDB.setSaveLocally(switchValue);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
]))
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/stats.dart';
|
||||||
|
|
||||||
|
class WebContainerStats extends StatelessWidget {
|
||||||
|
const WebContainerStats(this.value, this.designation, this.icon, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String designation;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 80,
|
||||||
|
width: 70,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xffe1e1e1),
|
||||||
|
),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
offset: Offset(3, 3),
|
||||||
|
blurRadius: 3,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
StatIcon(
|
||||||
|
icon: icon,
|
||||||
|
iconColor: TColor.white,
|
||||||
|
iconBackground: TColor.secondaryColor1,
|
||||||
|
sizeIcon: 8.0,
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
designation,
|
||||||
|
style: const TextStyle(fontSize: 8),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/stats.dart';
|
||||||
|
|
||||||
|
class WebContainerStatsActivities extends StatelessWidget {
|
||||||
|
const WebContainerStatsActivities(
|
||||||
|
this.value,
|
||||||
|
this.designation,
|
||||||
|
this.icon, {
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String designation;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var media = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
height: media.width * 0.2,
|
||||||
|
width: media.width * 0.28,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xffe1e1e1),
|
||||||
|
),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
offset: Offset(3, 3),
|
||||||
|
blurRadius: 3,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.center, // Centrer horizontalement
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
StatIcon(
|
||||||
|
icon: icon,
|
||||||
|
iconColor: TColor.white,
|
||||||
|
iconBackground: TColor.secondaryColor1,
|
||||||
|
sizeIcon: 40.0,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 40), // Espacement entre l'icône et le texte
|
||||||
|
Text(
|
||||||
|
designation,
|
||||||
|
style: const TextStyle(fontSize: 15),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/container_stats.dart';
|
||||||
|
|
||||||
|
class WebLigneContainerStats extends StatelessWidget {
|
||||||
|
const WebLigneContainerStats(
|
||||||
|
this.value1,
|
||||||
|
this.value2,
|
||||||
|
this.value3,
|
||||||
|
this.designation1,
|
||||||
|
this.designation2,
|
||||||
|
this.designation3,
|
||||||
|
this.icon1,
|
||||||
|
this.icon2,
|
||||||
|
this.icon3,
|
||||||
|
{Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final String value1;
|
||||||
|
final String value2;
|
||||||
|
final String value3;
|
||||||
|
|
||||||
|
final String designation1;
|
||||||
|
final String designation2;
|
||||||
|
final String designation3;
|
||||||
|
|
||||||
|
final IconData icon1;
|
||||||
|
final IconData icon2;
|
||||||
|
final IconData icon3;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Stats',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Icon(
|
||||||
|
Icons.pie_chart_rounded,
|
||||||
|
size: 15,
|
||||||
|
color: TColor.secondaryColor1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStats(value1, designation1, icon1),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
ContainerStats(value2, designation2, icon2),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
ContainerStats(value3, designation3, icon3),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
const Divider(height: 30),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:simple_animation_progress_bar/simple_animation_progress_bar.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
|
||||||
|
class WorkoutRow extends StatelessWidget {
|
||||||
|
final Map wObj;
|
||||||
|
const WorkoutRow({super.key, required this.wObj});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 2),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 2)]),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
wObj["image"].toString(),
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
wObj["name"].toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
wObj["value"].toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
|
||||||
|
|
||||||
|
class WorkoutRowWalking extends StatelessWidget {
|
||||||
|
final Map wObj;
|
||||||
|
final bool isSelected;
|
||||||
|
final VoidCallback onDelete;
|
||||||
|
final VoidCallback onClick;
|
||||||
|
|
||||||
|
const WorkoutRowWalking({
|
||||||
|
Key? key,
|
||||||
|
required this.wObj,
|
||||||
|
required this.onDelete,
|
||||||
|
required this.onClick,
|
||||||
|
required this.isSelected,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onClick,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected
|
||||||
|
? const Color.fromARGB(255, 144, 252, 148)
|
||||||
|
: Colors.transparent,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Material(
|
||||||
|
color: isSelected
|
||||||
|
? const Color.fromARGB(255, 240, 255, 240)
|
||||||
|
: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(10), // Utiliser le même borderRadius
|
||||||
|
splashColor: const Color.fromARGB(255, 42, 94, 44)
|
||||||
|
.withOpacity(0.3), // Couleur du fond au survol
|
||||||
|
onTap: onClick,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
wObj["image"].toString(),
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Type : ${wObj["categorie"].toString()}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Date : ${wObj["date"].toString()}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Time : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(0)} minutes",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Average speed : ${Convertisseur.msIntoKmh(wObj["VitesseAvg"]).toStringAsFixed(2)} km/h",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: onClick,
|
||||||
|
icon: Image.asset(
|
||||||
|
"assets/img/next_icon.png",
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: onDelete,
|
||||||
|
icon: Image.asset(
|
||||||
|
"assets/img/corbeille.png",
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,213 +1,28 @@
|
|||||||
import 'package:fl_chart/fl_chart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/mobile/mobile_bpm_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/web/web_bpm_by_time.dart';
|
||||||
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
class GraphBpmByTime extends StatefulWidget {
|
class BpmByTime extends StatefulWidget {
|
||||||
final Size media;
|
final Size media;
|
||||||
final DataHomeView data;
|
final DataHomeView data;
|
||||||
|
|
||||||
const GraphBpmByTime(this.media, this.data, {Key? key}) : super(key: key);
|
const BpmByTime(this.media, this.data, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GraphBpmByTime> createState() => _GraphBpmByTime();
|
State<BpmByTime> createState() => _BpmByTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GraphBpmByTime extends State<GraphBpmByTime> {
|
class _BpmByTime extends State<BpmByTime> {
|
||||||
TextEditingController bpmController = TextEditingController();
|
|
||||||
|
|
||||||
// Il faut chercher à le suprimer
|
|
||||||
List<int> showingTooltipOnSpots = [0];
|
|
||||||
|
|
||||||
List<LineChartBarData> get lineBarsData1 => [
|
|
||||||
lineChartBarData1_1,
|
|
||||||
lineChartBarData1_2,
|
|
||||||
];
|
|
||||||
|
|
||||||
LineChartBarData get lineChartBarData1_1 => LineChartBarData(
|
|
||||||
isCurved: true,
|
|
||||||
gradient: LinearGradient(colors: [
|
|
||||||
TColor.primaryColor2.withOpacity(0.5),
|
|
||||||
TColor.primaryColor1.withOpacity(0.5),
|
|
||||||
]),
|
|
||||||
barWidth: 4,
|
|
||||||
isStrokeCapRound: true,
|
|
||||||
dotData: const FlDotData(show: false),
|
|
||||||
belowBarData: BarAreaData(show: false),
|
|
||||||
spots: widget.data.vitesseSecondes,
|
|
||||||
);
|
|
||||||
|
|
||||||
LineChartBarData get lineChartBarData1_2 => LineChartBarData(
|
|
||||||
isCurved: true,
|
|
||||||
gradient: LinearGradient(colors: [
|
|
||||||
TColor.secondaryColor2.withOpacity(0.5),
|
|
||||||
TColor.secondaryColor1.withOpacity(0.5),
|
|
||||||
]),
|
|
||||||
barWidth: 2,
|
|
||||||
isStrokeCapRound: true,
|
|
||||||
dotData: const FlDotData(show: false),
|
|
||||||
belowBarData: BarAreaData(
|
|
||||||
show: false,
|
|
||||||
),
|
|
||||||
spots: widget.data.bpmSecondes2,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Size media = MediaQuery.of(context).size;
|
final FuncBpmByTime funcBpm = FuncBpmByTime(widget.data);
|
||||||
|
|
||||||
final lineBarsData = [
|
|
||||||
LineChartBarData(
|
|
||||||
spots: widget.data.bpmSecondes,
|
|
||||||
isCurved: false,
|
|
||||||
barWidth: 2,
|
|
||||||
belowBarData: BarAreaData(
|
|
||||||
show: true,
|
|
||||||
gradient: LinearGradient(colors: [
|
|
||||||
TColor.secondaryColor1.withOpacity(0.4),
|
|
||||||
TColor.secondaryColor2.withOpacity(0.1),
|
|
||||||
], begin: Alignment.topCenter, end: Alignment.bottomCenter),
|
|
||||||
),
|
|
||||||
dotData: const FlDotData(show: false),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: TColor.secondaryG,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
final tooltipsOnBar = lineBarsData[0];
|
|
||||||
|
|
||||||
return ClipRRect(
|
return ScreenTypeLayout.builder(
|
||||||
borderRadius: BorderRadius.circular(25),
|
mobile: (_) => MobileBpmByTime(widget.media, widget.data, funcBpm),
|
||||||
child: Container(
|
desktop: (_) => WebBpmByTime(widget.media, widget.data, funcBpm),
|
||||||
height: media.height * 0.3,
|
|
||||||
width: double.maxFinite,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: TColor.primaryColor2.withOpacity(0.3),
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Rythme cardiaque",
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.black,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w700),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
controller: bpmController,
|
|
||||||
readOnly: true,
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.primaryColor1.withOpacity(0.8),
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
fontSize: 18),
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
border: InputBorder
|
|
||||||
.none, // Ajoutez cette ligne pour supprimer la bordure
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
LineChart(
|
|
||||||
LineChartData(
|
|
||||||
showingTooltipIndicators: showingTooltipOnSpots.map((index) {
|
|
||||||
return ShowingTooltipIndicators([
|
|
||||||
LineBarSpot(
|
|
||||||
tooltipsOnBar,
|
|
||||||
lineBarsData.indexOf(tooltipsOnBar),
|
|
||||||
tooltipsOnBar.spots[index],
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}).toList(),
|
|
||||||
lineTouchData: LineTouchData(
|
|
||||||
enabled: true,
|
|
||||||
handleBuiltInTouches: false,
|
|
||||||
touchCallback:
|
|
||||||
(FlTouchEvent event, LineTouchResponse? response) {
|
|
||||||
if (response == null || response.lineBarSpots == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event is FlTapUpEvent) {
|
|
||||||
final spotIndex = response.lineBarSpots!.first.spotIndex;
|
|
||||||
showingTooltipOnSpots.clear();
|
|
||||||
setState(() {
|
|
||||||
showingTooltipOnSpots.add(spotIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mouseCursorResolver:
|
|
||||||
(FlTouchEvent event, LineTouchResponse? response) {
|
|
||||||
if (response == null || response.lineBarSpots == null) {
|
|
||||||
return SystemMouseCursors.basic;
|
|
||||||
}
|
|
||||||
return SystemMouseCursors.click;
|
|
||||||
},
|
|
||||||
getTouchedSpotIndicator:
|
|
||||||
(LineChartBarData barData, List<int> spotIndexes) {
|
|
||||||
return spotIndexes.map((index) {
|
|
||||||
return TouchedSpotIndicatorData(
|
|
||||||
FlLine(
|
|
||||||
color: TColor.secondaryColor1,
|
|
||||||
),
|
|
||||||
FlDotData(
|
|
||||||
show: true,
|
|
||||||
getDotPainter: (spot, percent, barData, index) =>
|
|
||||||
FlDotCirclePainter(
|
|
||||||
radius: 3,
|
|
||||||
color: Colors.white,
|
|
||||||
strokeWidth: 3,
|
|
||||||
strokeColor: TColor.secondaryColor1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
touchTooltipData: LineTouchTooltipData(
|
|
||||||
tooltipBgColor: TColor.secondaryColor1,
|
|
||||||
tooltipRoundedRadius: 20,
|
|
||||||
getTooltipItems: (List<LineBarSpot> lineBarsSpot) {
|
|
||||||
return lineBarsSpot.map((lineBarSpot) {
|
|
||||||
bpmController.text = "${lineBarSpot.y} BPM";
|
|
||||||
return LineTooltipItem(
|
|
||||||
"Seconde ${lineBarSpot.x.toInt() / 10}",
|
|
||||||
const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
lineBarsData: lineBarsData,
|
|
||||||
minY: 50,
|
|
||||||
maxY: 250,
|
|
||||||
titlesData: const FlTitlesData(
|
|
||||||
show: false,
|
|
||||||
),
|
|
||||||
gridData: const FlGridData(show: false),
|
|
||||||
borderData: FlBorderData(
|
|
||||||
show: true,
|
|
||||||
border: Border.all(
|
|
||||||
color: Colors.transparent,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,188 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class FuncBpmAndSpeedByTime {
|
||||||
|
final DataHomeView data;
|
||||||
|
|
||||||
|
FuncBpmAndSpeedByTime(this.data);
|
||||||
|
|
||||||
|
List<int> showingTooltipOnSpots = [0];
|
||||||
|
|
||||||
|
SideTitles get rightTitles => SideTitles(
|
||||||
|
getTitlesWidget: rightTitleWidgets,
|
||||||
|
showTitles: true,
|
||||||
|
interval: 20,
|
||||||
|
reservedSize: 42,
|
||||||
|
);
|
||||||
|
|
||||||
|
SideTitles get leftTitles => SideTitles(
|
||||||
|
getTitlesWidget: leftTitleWidgets,
|
||||||
|
showTitles: true,
|
||||||
|
interval: 20,
|
||||||
|
reservedSize: 42,
|
||||||
|
);
|
||||||
|
SideTitles get bottomTitles => SideTitles(
|
||||||
|
getTitlesWidget: bottomTitleWidgets,
|
||||||
|
showTitles: true,
|
||||||
|
interval: 20,
|
||||||
|
reservedSize: 20,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final lineBarsData = [
|
||||||
|
LineChartBarData(
|
||||||
|
spots: data.bpmSecondes,
|
||||||
|
isCurved: false,
|
||||||
|
barWidth: 2,
|
||||||
|
belowBarData: BarAreaData(
|
||||||
|
show: true,
|
||||||
|
gradient: LinearGradient(colors: [
|
||||||
|
TColor.secondaryColor1.withOpacity(0.4),
|
||||||
|
TColor.secondaryColor2.withOpacity(0.1),
|
||||||
|
], begin: Alignment.topCenter, end: Alignment.bottomCenter),
|
||||||
|
),
|
||||||
|
dotData: const FlDotData(show: false),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: TColor.secondaryG,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
late final tooltipsOnBar = lineBarsData[0];
|
||||||
|
|
||||||
|
Widget rightTitleWidgets(double value, TitleMeta meta) {
|
||||||
|
double interval = data.maxBPM / 5;
|
||||||
|
String text;
|
||||||
|
switch (value.toInt()) {
|
||||||
|
case 0:
|
||||||
|
text = '0 BPM';
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
text = "${(interval).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
text = "${(interval * 2).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
text = "${(interval * 3).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
text = "${(interval * 4).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
text = "${interval * 5} BPM";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget bottomTitleWidgets(double value, TitleMeta meta) {
|
||||||
|
double interval = data.time / 5;
|
||||||
|
String text;
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
text = '0 s';
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
text = "${(interval).toStringAsFixed(2)} s";
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
text = "${(interval * 2).toStringAsFixed(2)} s";
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
text = "${(interval * 3).toStringAsFixed(2)} s";
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
text = "${(interval * 4).toStringAsFixed(2)} s";
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
text = "${(interval * 5).toStringAsFixed(2)} s";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget leftTitleWidgets(double value, TitleMeta meta) {
|
||||||
|
double interval = data.maxSpeed / 5;
|
||||||
|
|
||||||
|
String text;
|
||||||
|
switch (value.toInt()) {
|
||||||
|
case 0:
|
||||||
|
text = '0 m/s';
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
text = "${(interval / 5).toStringAsFixed(2)} m/s";
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
text = "${(interval * 2).toStringAsFixed(2)} m/s";
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
text = "${(interval * 3).toStringAsFixed(2)} m/s";
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
text = "${(interval * 4).toStringAsFixed(2)} m/s";
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
text = "${(interval * 5).toStringAsFixed(2)} m/s";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LineChartBarData> get lineBarsData1 => [
|
||||||
|
lineChartBarData1_1,
|
||||||
|
lineChartBarData1_2,
|
||||||
|
];
|
||||||
|
|
||||||
|
LineChartBarData get lineChartBarData1_1 => LineChartBarData(
|
||||||
|
isCurved: true,
|
||||||
|
gradient: LinearGradient(colors: [
|
||||||
|
TColor.primaryColor2.withOpacity(0.5),
|
||||||
|
TColor.primaryColor1.withOpacity(0.5),
|
||||||
|
]),
|
||||||
|
barWidth: 4,
|
||||||
|
isStrokeCapRound: true,
|
||||||
|
dotData: const FlDotData(show: false),
|
||||||
|
belowBarData: BarAreaData(show: false),
|
||||||
|
spots: data.vitesseSecondes,
|
||||||
|
);
|
||||||
|
|
||||||
|
LineChartBarData get lineChartBarData1_2 => LineChartBarData(
|
||||||
|
isCurved: true,
|
||||||
|
gradient: LinearGradient(colors: [
|
||||||
|
TColor.secondaryColor2.withOpacity(0.5),
|
||||||
|
TColor.secondaryColor1.withOpacity(0.5),
|
||||||
|
]),
|
||||||
|
barWidth: 2,
|
||||||
|
isStrokeCapRound: true,
|
||||||
|
dotData: const FlDotData(show: false),
|
||||||
|
belowBarData: BarAreaData(
|
||||||
|
show: false,
|
||||||
|
),
|
||||||
|
spots: data.bpmSecondes2,
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class FuncBpmByTime {
|
||||||
|
final DataHomeView data;
|
||||||
|
|
||||||
|
FuncBpmByTime(this.data);
|
||||||
|
|
||||||
|
SideTitles get rightTitles => SideTitles(
|
||||||
|
getTitlesWidget: rightTitleWidgets,
|
||||||
|
showTitles: true,
|
||||||
|
interval: 20,
|
||||||
|
reservedSize: 40,
|
||||||
|
);
|
||||||
|
|
||||||
|
SideTitles get bottomTitles => SideTitles(
|
||||||
|
getTitlesWidget: bottomTitleWidgets,
|
||||||
|
showTitles: true,
|
||||||
|
interval: 20,
|
||||||
|
reservedSize: 20,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget rightTitleWidgets(double value, TitleMeta meta) {
|
||||||
|
int minBpm = data.minBPM;
|
||||||
|
int maxBpm = data.maxBPM;
|
||||||
|
double interval = (maxBpm - minBpm) / 5;
|
||||||
|
|
||||||
|
String text;
|
||||||
|
switch (value.toInt()) {
|
||||||
|
case 0:
|
||||||
|
text = "${(minBpm).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
text = "${(minBpm + interval).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
text = "${(minBpm + interval * 2).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
text = "${(minBpm + interval * 3).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
text = "${(minBpm + interval * 4).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
text = "${(maxBpm).toStringAsFixed(2)} BPM";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget bottomTitleWidgets(double value, TitleMeta meta) {
|
||||||
|
double interval = data.time / 5;
|
||||||
|
String text;
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
text = '0 s';
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
text = "${(interval).toStringAsFixed(0)} s";
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
text = "${(interval * 2).toStringAsFixed(0)} s";
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
text = "${(interval * 3).toStringAsFixed(0)} s";
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
text = "${(interval * 4).toStringAsFixed(0)} s";
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
text = "${(interval * 5).toStringAsFixed(0)} s";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class MobileGraphAltitudeByTime extends StatefulWidget {
|
||||||
|
final Size media;
|
||||||
|
final DataHomeView data;
|
||||||
|
final FuncBpmByTime func;
|
||||||
|
|
||||||
|
const MobileGraphAltitudeByTime(this.media, this.data, this.func, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileGraphAltitudeByTime> createState() =>
|
||||||
|
_MobileGraphAltitudeByTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileGraphAltitudeByTime extends State<MobileGraphAltitudeByTime> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double maxY = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.activitySelected
|
||||||
|
.first
|
||||||
|
.activityInfo
|
||||||
|
.altitudeMax +
|
||||||
|
2;
|
||||||
|
final double minY = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.activitySelected
|
||||||
|
.first
|
||||||
|
.activityInfo
|
||||||
|
.altitudeMin -
|
||||||
|
2;
|
||||||
|
|
||||||
|
final lineBarsData = [
|
||||||
|
LineChartBarData(
|
||||||
|
spots: widget.data.altitudeSeconde,
|
||||||
|
isCurved: false,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: TColor.primaryG,
|
||||||
|
),
|
||||||
|
dotData: const FlDotData(show: false))
|
||||||
|
];
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 15),
|
||||||
|
height: widget.media.width * 0.3,
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: LineChart(LineChartData(
|
||||||
|
lineBarsData: lineBarsData,
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
maxY: maxY,
|
||||||
|
minY: minY,
|
||||||
|
gridData: FlGridData(
|
||||||
|
drawVerticalLine: false,
|
||||||
|
drawHorizontalLine: true,
|
||||||
|
horizontalInterval: (maxY - minY) / 5,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: TColor.gray.withOpacity(0.15),
|
||||||
|
strokeWidth: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
leftTitles: const AxisTitles(),
|
||||||
|
topTitles: const AxisTitles(),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.bottomTitles,
|
||||||
|
),
|
||||||
|
rightTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
reservedSize: 60,
|
||||||
|
showTitles: true,
|
||||||
|
getTitlesWidget: (value, meta) {
|
||||||
|
return Text("${double.parse(value.toStringAsFixed(2))} m",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class MobileGraphBpmAndSpeedByTime extends StatefulWidget {
|
||||||
|
final Size media;
|
||||||
|
final DataHomeView data;
|
||||||
|
final FuncBpmAndSpeedByTime func;
|
||||||
|
|
||||||
|
const MobileGraphBpmAndSpeedByTime(this.media, this.data, this.func,
|
||||||
|
{Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileGraphBpmAndSpeedByTime> createState() =>
|
||||||
|
_MobileGraphBpmAndSpeedByTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileGraphBpmAndSpeedByTime
|
||||||
|
extends State<MobileGraphBpmAndSpeedByTime> {
|
||||||
|
TextEditingController bpmController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double maxY = widget.data.maxBPM + 2;
|
||||||
|
final double minY = widget.data.minBPM - 2;
|
||||||
|
final double maxX =
|
||||||
|
widget.data.bpmSecondes[widget.data.bpmSecondes.length - 1].x;
|
||||||
|
const double minX = 0.0;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 15),
|
||||||
|
height: widget.media.width * 0.3,
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: LineChart(
|
||||||
|
LineChartData(
|
||||||
|
|
||||||
|
|
||||||
|
lineBarsData: widget.func.lineBarsData1,
|
||||||
|
minY: 0,
|
||||||
|
maxY: 110,
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
show: true,
|
||||||
|
leftTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.leftTitles,
|
||||||
|
),
|
||||||
|
topTitles: const AxisTitles(),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.bottomTitles,
|
||||||
|
),
|
||||||
|
rightTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.rightTitles,
|
||||||
|
|
||||||
|
),),
|
||||||
|
gridData: FlGridData(
|
||||||
|
drawVerticalLine: true,
|
||||||
|
drawHorizontalLine: true,
|
||||||
|
horizontalInterval: (maxY - minY) / 5,
|
||||||
|
verticalInterval: (maxX - minX) / 4,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: TColor.gray.withOpacity(0.15),
|
||||||
|
strokeWidth: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
borderData: FlBorderData(
|
||||||
|
show: true,
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class MobileBpmByTime extends StatefulWidget {
|
||||||
|
final Size media;
|
||||||
|
final DataHomeView data;
|
||||||
|
final FuncBpmByTime func;
|
||||||
|
|
||||||
|
const MobileBpmByTime(this.media, this.data,this.func, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileBpmByTime> createState() => _MobileBpmByTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileBpmByTime extends State<MobileBpmByTime> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double maxY = widget.data.maxBPM + 2;
|
||||||
|
final double minY = widget.data.minBPM - 2;
|
||||||
|
final double maxX =
|
||||||
|
widget.data.bpmSecondes[widget.data.bpmSecondes.length - 1].x;
|
||||||
|
const double minX = 0.0;
|
||||||
|
final lineBarsData = [
|
||||||
|
LineChartBarData(
|
||||||
|
spots: widget.data.bpmSecondes,
|
||||||
|
isCurved: true,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: TColor.primaryG,
|
||||||
|
),
|
||||||
|
dotData: const FlDotData(show: false))
|
||||||
|
];
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 15),
|
||||||
|
height: widget.media.width * 0.4,
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: LineChart(LineChartData(
|
||||||
|
lineBarsData: lineBarsData,
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
maxY: maxY,
|
||||||
|
minY: minY,
|
||||||
|
maxX: maxX,
|
||||||
|
minX: minX,
|
||||||
|
gridData: FlGridData(
|
||||||
|
drawVerticalLine: true,
|
||||||
|
drawHorizontalLine: true,
|
||||||
|
horizontalInterval: (maxY - minY) / 5,
|
||||||
|
verticalInterval: (maxX - minX) / 4,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: TColor.gray.withOpacity(0.15),
|
||||||
|
strokeWidth: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
leftTitles: const AxisTitles(),
|
||||||
|
topTitles: const AxisTitles(),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.bottomTitles,
|
||||||
|
),
|
||||||
|
rightTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
reservedSize: 70,
|
||||||
|
showTitles: true,
|
||||||
|
getTitlesWidget: (value, meta) {
|
||||||
|
return Text("${double.parse(value.toStringAsFixed(2))} BPM",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class WebGraphAltitudeByTime extends StatefulWidget {
|
||||||
|
final Size media;
|
||||||
|
final DataHomeView data;
|
||||||
|
final FuncBpmByTime func;
|
||||||
|
|
||||||
|
const WebGraphAltitudeByTime(this.media, this.data, this.func, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WebGraphAltitudeByTime> createState() => _WebGraphAltitudeByTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebGraphAltitudeByTime extends State<WebGraphAltitudeByTime> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double maxY = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.activitySelected
|
||||||
|
.first
|
||||||
|
.activityInfo
|
||||||
|
.altitudeMax +
|
||||||
|
2;
|
||||||
|
final double minY = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.activitySelected
|
||||||
|
.first
|
||||||
|
.activityInfo
|
||||||
|
.altitudeMin -
|
||||||
|
2;
|
||||||
|
|
||||||
|
final lineBarsData = [
|
||||||
|
LineChartBarData(
|
||||||
|
spots: widget.data.altitudeSeconde,
|
||||||
|
isCurved: false,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: TColor.primaryG,
|
||||||
|
),
|
||||||
|
dotData: const FlDotData(show: false))
|
||||||
|
];
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 15),
|
||||||
|
height: widget.media.width * 0.20,
|
||||||
|
width: widget.media.width * 0.35,
|
||||||
|
child: LineChart(LineChartData(
|
||||||
|
lineBarsData: lineBarsData,
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
maxY: maxY,
|
||||||
|
minY: minY,
|
||||||
|
gridData: FlGridData(
|
||||||
|
drawVerticalLine: false,
|
||||||
|
drawHorizontalLine: true,
|
||||||
|
horizontalInterval: (maxY - minY) / 5,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: TColor.gray.withOpacity(0.15),
|
||||||
|
strokeWidth: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
leftTitles: const AxisTitles(),
|
||||||
|
topTitles: const AxisTitles(),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.bottomTitles,
|
||||||
|
),
|
||||||
|
rightTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
reservedSize: 60,
|
||||||
|
showTitles: true,
|
||||||
|
getTitlesWidget: (value, meta) {
|
||||||
|
return Text("${double.parse(value.toStringAsFixed(2))} m",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class WebGraphBpmAndSpeedByTime extends StatefulWidget {
|
||||||
|
final Size media;
|
||||||
|
final DataHomeView data;
|
||||||
|
final FuncBpmAndSpeedByTime func;
|
||||||
|
|
||||||
|
const WebGraphBpmAndSpeedByTime(this.media, this.data, this.func, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WebGraphBpmAndSpeedByTime> createState() =>
|
||||||
|
_WebGraphBpmAndSpeedByTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebGraphBpmAndSpeedByTime extends State<WebGraphBpmAndSpeedByTime> {
|
||||||
|
TextEditingController bpmController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double maxY = widget.data.maxBPM + 2;
|
||||||
|
final double minY = widget.data.minBPM - 2;
|
||||||
|
final double maxX =
|
||||||
|
widget.data.bpmSecondes[widget.data.bpmSecondes.length - 1].x;
|
||||||
|
const double minX = 0.0;
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 15),
|
||||||
|
height: widget.media.width * 0.20,
|
||||||
|
width: widget.media.width * 0.35,
|
||||||
|
child: LineChart(
|
||||||
|
LineChartData(
|
||||||
|
|
||||||
|
lineBarsData: widget.func.lineBarsData1,
|
||||||
|
minY: -10,
|
||||||
|
maxY: 110,
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
show: true,
|
||||||
|
leftTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.leftTitles,
|
||||||
|
),
|
||||||
|
topTitles: const AxisTitles(),
|
||||||
|
bottomTitles:AxisTitles(
|
||||||
|
sideTitles: widget.func.bottomTitles,
|
||||||
|
),
|
||||||
|
rightTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.rightTitles,
|
||||||
|
),),
|
||||||
|
gridData: FlGridData(
|
||||||
|
drawVerticalLine: true,
|
||||||
|
drawHorizontalLine: true,
|
||||||
|
horizontalInterval: (maxY - minY) / 5,
|
||||||
|
verticalInterval: (maxX - minX) / 5 ,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: TColor.gray.withOpacity(0.15),
|
||||||
|
strokeWidth: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
borderData: FlBorderData(
|
||||||
|
show: true,
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
|
||||||
|
class WebBpmByTime extends StatefulWidget {
|
||||||
|
final Size media;
|
||||||
|
final DataHomeView data;
|
||||||
|
final FuncBpmByTime func;
|
||||||
|
|
||||||
|
const WebBpmByTime(this.media, this.data, this.func, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WebBpmByTime> createState() => _WebBpmByTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebBpmByTime extends State<WebBpmByTime> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final lineBarsData = [
|
||||||
|
LineChartBarData(
|
||||||
|
spots: widget.data.bpmSecondes,
|
||||||
|
isCurved: true,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: TColor.primaryG,
|
||||||
|
),
|
||||||
|
dotData: const FlDotData(show: false))
|
||||||
|
];
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 15),
|
||||||
|
height: widget.media.width * 0.20,
|
||||||
|
width: widget.media.width * 0.35,
|
||||||
|
child: LineChart(LineChartData(
|
||||||
|
lineBarsData: lineBarsData,
|
||||||
|
minY: widget.data.minBPM.toDouble() * 0.95,
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
gridData: FlGridData(
|
||||||
|
drawVerticalLine: true,
|
||||||
|
drawHorizontalLine: true,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: TColor.gray.withOpacity(0.15),
|
||||||
|
strokeWidth: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
leftTitles: const AxisTitles(),
|
||||||
|
topTitles: const AxisTitles(),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: widget.func.bottomTitles,
|
||||||
|
),
|
||||||
|
rightTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
reservedSize: 70,
|
||||||
|
showTitles: true,
|
||||||
|
getTitlesWidget: (value, meta) {
|
||||||
|
return Text("${double.parse(value.toStringAsFixed(2))} BPM",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,545 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:smartfit_app_mobile/modele/manager_file.dart';
|
||||||
|
|
||||||
|
class ActivityInfo {
|
||||||
|
ManagerFile managerFile = ManagerFile();
|
||||||
|
ActivityInfo();
|
||||||
|
|
||||||
|
// -- Time -- // Ne pas calculer (Ligne session)
|
||||||
|
DateTime startTime = DateTime.now();
|
||||||
|
double timeOfActivity = 0.0;
|
||||||
|
double distance = 0.0;
|
||||||
|
int calories = 0;
|
||||||
|
int steps = 0;
|
||||||
|
// ----------- BPM ------------ //
|
||||||
|
int bpmMax = 0;
|
||||||
|
int bpmMin = 300;
|
||||||
|
int bpmAvg = 0;
|
||||||
|
bool bpmNotZero = false;
|
||||||
|
// ----------- Denivelé ------------ //
|
||||||
|
double denivelePositif = 0.0;
|
||||||
|
double deniveleNegatif = 0.0;
|
||||||
|
// ----------- Altitude ------------ //
|
||||||
|
double altitudeMax = 0.0;
|
||||||
|
double altitudeMin = 30000.0;
|
||||||
|
double altitudeAvg = 0.0;
|
||||||
|
bool altitudeNotZero = false;
|
||||||
|
// ----------- Température --------- //
|
||||||
|
int temperatureMax = 0;
|
||||||
|
int temperatureMin = 3000;
|
||||||
|
int temperatureAvg = 0;
|
||||||
|
bool temperatureNotZero = false;
|
||||||
|
// ----------- Vitesse ------------- //
|
||||||
|
double vitesseMax = 0.0;
|
||||||
|
double vitesseMin = 999999.0;
|
||||||
|
double vitesseAvg = 0.0;
|
||||||
|
bool vitesseNotZero = false;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
// -- Fonction pour lire le csv et remplir la classe -- //
|
||||||
|
ActivityInfo getDataWalking(List<List<String>> csv) {
|
||||||
|
// - Entete - //
|
||||||
|
Map<String, int> enteteCSV = getEntete(csv.first);
|
||||||
|
// ------------- Var tmp ---------- //
|
||||||
|
// -- BPM -- //
|
||||||
|
int bpmSomme = 0;
|
||||||
|
int bpmNb = 0;
|
||||||
|
// -- Denivelé -- //
|
||||||
|
double lastDenivele = 0.0;
|
||||||
|
// -- Altitude -- //
|
||||||
|
double altitudeSomme = 0;
|
||||||
|
int alititudeNb = 0;
|
||||||
|
// -- Température -- //
|
||||||
|
int temperatureSomme = 0;
|
||||||
|
int temperatureNb = 0;
|
||||||
|
// -- Vitesse -- //
|
||||||
|
double vitesseSomme = 0.0;
|
||||||
|
int vitesseNb = 0;
|
||||||
|
|
||||||
|
// --- Boucle -- //
|
||||||
|
for (int i = 1; i < csv.length; i++) {
|
||||||
|
//
|
||||||
|
// ---------------------- BPM ---------------------- //
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fielBPM}"]!, csv[i])) {
|
||||||
|
int value =
|
||||||
|
int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]);
|
||||||
|
bpmSomme += value;
|
||||||
|
bpmNb += 1;
|
||||||
|
bpmNotZero = true;
|
||||||
|
if (value > bpmMax) {
|
||||||
|
bpmMax = value;
|
||||||
|
}
|
||||||
|
if (value < bpmMin) {
|
||||||
|
bpmMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------ Denivele et Altitude --------------- //
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fieldAltitude}"]!, csv[i])) {
|
||||||
|
double value = double.parse(
|
||||||
|
csv[i][enteteCSV["Value_${managerFile.fieldAltitude}"]!]);
|
||||||
|
// -- Denivelé -- //
|
||||||
|
if (value > lastDenivele) {
|
||||||
|
denivelePositif += value - lastDenivele;
|
||||||
|
} else {
|
||||||
|
deniveleNegatif += (value - lastDenivele) * -1;
|
||||||
|
}
|
||||||
|
lastDenivele = value;
|
||||||
|
// -- Altitude -- //
|
||||||
|
if (value > altitudeMax) {
|
||||||
|
altitudeMax = value;
|
||||||
|
}
|
||||||
|
if (value < altitudeMin) {
|
||||||
|
altitudeMin = value;
|
||||||
|
}
|
||||||
|
altitudeSomme += value;
|
||||||
|
alititudeNb += 1;
|
||||||
|
altitudeNotZero = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Température ----------------------- //
|
||||||
|
if (!isNull(
|
||||||
|
enteteCSV["Value_${managerFile.fieldTemperature}"]!, csv[i])) {
|
||||||
|
int value = int.parse(
|
||||||
|
csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]);
|
||||||
|
temperatureSomme += value;
|
||||||
|
temperatureNb += 1;
|
||||||
|
temperatureNotZero = true;
|
||||||
|
if (value > temperatureMax) {
|
||||||
|
temperatureMax = value;
|
||||||
|
}
|
||||||
|
if (value < temperatureMin) {
|
||||||
|
temperatureMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Vitesse -----------------------------//
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fieldSpeed}"]!, csv[i])) {
|
||||||
|
double value =
|
||||||
|
double.parse(csv[i][enteteCSV["Value_${managerFile.fieldSpeed}"]!]);
|
||||||
|
vitesseSomme += value;
|
||||||
|
vitesseNb += 1;
|
||||||
|
vitesseNotZero = true;
|
||||||
|
if (value > vitesseMax) {
|
||||||
|
vitesseMax = value;
|
||||||
|
}
|
||||||
|
if (value < vitesseMin) {
|
||||||
|
vitesseMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- BPM -- //
|
||||||
|
if (bpmNotZero) {
|
||||||
|
bpmAvg = bpmSomme ~/ bpmNb;
|
||||||
|
}
|
||||||
|
// -- Atitude -- //
|
||||||
|
if (altitudeNotZero) {
|
||||||
|
altitudeAvg = altitudeSomme / alititudeNb;
|
||||||
|
}
|
||||||
|
// -- Température -- //
|
||||||
|
if (temperatureNotZero) {
|
||||||
|
temperatureAvg = temperatureSomme ~/ temperatureNb;
|
||||||
|
}
|
||||||
|
// -- Vitesse -- //
|
||||||
|
if (vitesseNotZero) {
|
||||||
|
vitesseAvg = vitesseSomme / vitesseNb;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Fonction pour lire le csv et remplir la classe -- //
|
||||||
|
ActivityInfo getDataCycling(List<List<String>> csv) {
|
||||||
|
// - Entete - //
|
||||||
|
Map<String, int> enteteCSV = getEntete(csv.first);
|
||||||
|
// ------------- Var tmp ---------- //
|
||||||
|
// -- BPM -- //
|
||||||
|
int bpmSomme = 0;
|
||||||
|
int bpmNb = 0;
|
||||||
|
// -- Denivelé -- //
|
||||||
|
double lastDenivele = 0.0;
|
||||||
|
// -- Altitude -- //
|
||||||
|
double altitudeSomme = 0;
|
||||||
|
int alititudeNb = 0;
|
||||||
|
// -- Température -- //
|
||||||
|
int temperatureSomme = 0;
|
||||||
|
int temperatureNb = 0;
|
||||||
|
// -- Vitesse -- //
|
||||||
|
double vitesseSomme = 0.0;
|
||||||
|
int vitesseNb = 0;
|
||||||
|
|
||||||
|
// --- Boucle -- //
|
||||||
|
for (int i = 1; i < csv.length; i++) {
|
||||||
|
//
|
||||||
|
// ---------------------- BPM ---------------------- //
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fielBPM}"]!, csv[i])) {
|
||||||
|
int value =
|
||||||
|
int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]);
|
||||||
|
bpmSomme += value;
|
||||||
|
bpmNb += 1;
|
||||||
|
bpmNotZero = true;
|
||||||
|
if (value > bpmMax) {
|
||||||
|
bpmMax = value;
|
||||||
|
}
|
||||||
|
if (value < bpmMin) {
|
||||||
|
bpmMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------ Denivele et Altitude --------------- //
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fieldAltitude}"]!, csv[i])) {
|
||||||
|
double value = double.parse(
|
||||||
|
csv[i][enteteCSV["Value_${managerFile.fieldAltitude}"]!]);
|
||||||
|
// -- Denivelé -- //
|
||||||
|
if (value > lastDenivele) {
|
||||||
|
denivelePositif += value - lastDenivele;
|
||||||
|
} else {
|
||||||
|
deniveleNegatif += (value - lastDenivele) * -1;
|
||||||
|
}
|
||||||
|
lastDenivele = value;
|
||||||
|
// -- Altitude -- //
|
||||||
|
if (value > altitudeMax) {
|
||||||
|
altitudeMax = value;
|
||||||
|
}
|
||||||
|
if (value < altitudeMin) {
|
||||||
|
altitudeMin = value;
|
||||||
|
}
|
||||||
|
altitudeSomme += value;
|
||||||
|
alititudeNb += 1;
|
||||||
|
altitudeNotZero = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Température ----------------------- //
|
||||||
|
if (!isNull(
|
||||||
|
enteteCSV["Value_${managerFile.fieldTemperature}"]!, csv[i])) {
|
||||||
|
int value = int.parse(
|
||||||
|
csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]);
|
||||||
|
temperatureSomme += value;
|
||||||
|
temperatureNb += 1;
|
||||||
|
vitesseNotZero = true;
|
||||||
|
if (value > temperatureMax) {
|
||||||
|
temperatureMax = value;
|
||||||
|
}
|
||||||
|
if (value < temperatureMin) {
|
||||||
|
temperatureMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Vitesse -----------------------------//
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fieldSpeed}"]!, csv[i])) {
|
||||||
|
double value =
|
||||||
|
double.parse(csv[i][enteteCSV["Value_${managerFile.fieldSpeed}"]!]);
|
||||||
|
vitesseSomme += value;
|
||||||
|
vitesseNb += 1;
|
||||||
|
vitesseNotZero = true;
|
||||||
|
if (value > vitesseMax) {
|
||||||
|
vitesseMax = value;
|
||||||
|
}
|
||||||
|
if (value < vitesseMin) {
|
||||||
|
vitesseMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- BPM -- //
|
||||||
|
if (bpmNotZero) {
|
||||||
|
bpmAvg = bpmSomme ~/ bpmNb;
|
||||||
|
}
|
||||||
|
// -- Atitude -- //
|
||||||
|
if (altitudeNotZero) {
|
||||||
|
altitudeAvg = altitudeSomme / alititudeNb;
|
||||||
|
}
|
||||||
|
// -- Température -- //
|
||||||
|
if (temperatureNotZero) {
|
||||||
|
temperatureAvg = temperatureSomme ~/ temperatureNb;
|
||||||
|
}
|
||||||
|
// -- Vitesse -- //
|
||||||
|
if (vitesseNotZero) {
|
||||||
|
vitesseAvg = vitesseSomme / vitesseNb;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Fonction pour lire le csv et remplir la classe -- //
|
||||||
|
ActivityInfo getDataGeneric(List<List<String>> csv) {
|
||||||
|
// - Entete - //
|
||||||
|
Map<String, int> enteteCSV = getEntete(csv.first);
|
||||||
|
// ------------- Var tmp ---------- //
|
||||||
|
// -- BPM -- //
|
||||||
|
int bpmSomme = 0;
|
||||||
|
int bpmNb = 0;
|
||||||
|
// -- Denivelé -- //
|
||||||
|
double lastDenivele = 0.0;
|
||||||
|
// -- Altitude -- //
|
||||||
|
double altitudeSomme = 0;
|
||||||
|
int alititudeNb = 0;
|
||||||
|
// -- Température -- //
|
||||||
|
int temperatureSomme = 0;
|
||||||
|
int temperatureNb = 0;
|
||||||
|
// -- Vitesse -- //
|
||||||
|
double vitesseSomme = 0.0;
|
||||||
|
int vitesseNb = 0;
|
||||||
|
|
||||||
|
// --- Boucle -- //
|
||||||
|
for (int i = 1; i < csv.length; i++) {
|
||||||
|
//
|
||||||
|
// ---------------------- BPM ---------------------- //
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fielBPM}"]!, csv[i])) {
|
||||||
|
int value =
|
||||||
|
int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]);
|
||||||
|
bpmSomme += value;
|
||||||
|
bpmNb += 1;
|
||||||
|
bpmNotZero = true;
|
||||||
|
if (value > bpmMax) {
|
||||||
|
bpmMax = value;
|
||||||
|
}
|
||||||
|
if (value < bpmMin) {
|
||||||
|
bpmMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------ Denivele et Altitude --------------- //
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fieldAltitude}"]!, csv[i])) {
|
||||||
|
double value = double.parse(
|
||||||
|
csv[i][enteteCSV["Value_${managerFile.fieldAltitude}"]!]);
|
||||||
|
// -- Denivelé -- //
|
||||||
|
if (value > lastDenivele) {
|
||||||
|
denivelePositif += value - lastDenivele;
|
||||||
|
} else {
|
||||||
|
deniveleNegatif += (value - lastDenivele) * -1;
|
||||||
|
}
|
||||||
|
lastDenivele = value;
|
||||||
|
// -- Altitude -- //
|
||||||
|
if (value > altitudeMax) {
|
||||||
|
altitudeMax = value;
|
||||||
|
}
|
||||||
|
if (value < altitudeMin) {
|
||||||
|
altitudeMin = value;
|
||||||
|
}
|
||||||
|
altitudeSomme += value;
|
||||||
|
alititudeNb += 1;
|
||||||
|
altitudeNotZero = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Température ----------------------- //
|
||||||
|
if (!isNull(
|
||||||
|
enteteCSV["Value_${managerFile.fieldTemperature}"]!, csv[i])) {
|
||||||
|
int value = int.parse(
|
||||||
|
csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]);
|
||||||
|
temperatureSomme += value;
|
||||||
|
temperatureNb += 1;
|
||||||
|
temperatureNotZero = true;
|
||||||
|
if (value > temperatureMax) {
|
||||||
|
temperatureMax = value;
|
||||||
|
}
|
||||||
|
if (value < temperatureMin) {
|
||||||
|
temperatureMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Vitesse -----------------------------//
|
||||||
|
if (!isNull(enteteCSV["Value_${managerFile.fieldSpeed}"]!, csv[i])) {
|
||||||
|
double value =
|
||||||
|
double.parse(csv[i][enteteCSV["Value_${managerFile.fieldSpeed}"]!]);
|
||||||
|
vitesseSomme += value;
|
||||||
|
vitesseNb += 1;
|
||||||
|
vitesseNotZero = true;
|
||||||
|
if (value > vitesseMax) {
|
||||||
|
vitesseMax = value;
|
||||||
|
}
|
||||||
|
if (value < vitesseMin) {
|
||||||
|
vitesseMin = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- BPM -- //
|
||||||
|
if (bpmNotZero) {
|
||||||
|
bpmAvg = bpmSomme ~/ bpmNb;
|
||||||
|
}
|
||||||
|
// -- Atitude -- //
|
||||||
|
if (altitudeNotZero) {
|
||||||
|
altitudeAvg = altitudeSomme / alititudeNb;
|
||||||
|
}
|
||||||
|
// -- Température -- //
|
||||||
|
if (temperatureNotZero) {
|
||||||
|
temperatureAvg = temperatureSomme ~/ temperatureNb;
|
||||||
|
}
|
||||||
|
// -- Vitesse -- //
|
||||||
|
if (vitesseNotZero) {
|
||||||
|
vitesseAvg = vitesseSomme / vitesseNb;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------ Fonction utile ------------------- //
|
||||||
|
Map<String, int> getEntete(List<dynamic> content) {
|
||||||
|
Map<String, int> enteteCSV = {};
|
||||||
|
for (int i = 0; i < content.length; i++) {
|
||||||
|
enteteCSV.addAll({content[i]: i});
|
||||||
|
}
|
||||||
|
return enteteCSV;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNull(int colonne, List<dynamic> ligne) {
|
||||||
|
return ligne[colonne] == "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------- Pour print ----------------- //
|
||||||
|
Map<String, dynamic> toMapWalking() {
|
||||||
|
return {
|
||||||
|
// -- Denivelé -- //
|
||||||
|
"DenivelePositif": denivelePositif,
|
||||||
|
"DeniveleNegatif": denivelePositif,
|
||||||
|
// -- Altitude -- //
|
||||||
|
"AltitudeMax": altitudeMax,
|
||||||
|
"AltitudeMin": altitudeMin,
|
||||||
|
"AltitudeAvg": altitudeAvg,
|
||||||
|
// -- Vitesse -- //
|
||||||
|
"VitesseAvg": vitesseAvg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------- JSON --------- //
|
||||||
|
// -- Lecture -- //
|
||||||
|
ActivityInfo.fromJson(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// -- Ligne session -- //
|
||||||
|
try {
|
||||||
|
startTime = DateTime.parse(map["startTime"]);
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> startTime");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
timeOfActivity = map["timeOfActivity"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> timeOfActivity");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
distance = map["distance"].toDouble();
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> distance");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
calories = map["calories"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> calories");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
steps = map["steps"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> steps");
|
||||||
|
}
|
||||||
|
// -- BPM -- //
|
||||||
|
try {
|
||||||
|
bpmAvg = map["bpmAvg"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> ");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
bpmMax = map["bpmMax"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> ");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
bpmMin = map["bpmMin"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> ");
|
||||||
|
}
|
||||||
|
// -- Denivelé -- //
|
||||||
|
try {
|
||||||
|
deniveleNegatif = map["deniveleNegatif"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> deniveleNegatif");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
denivelePositif = map["denivelePositif"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> denivelePositif");
|
||||||
|
}
|
||||||
|
// -- Altitude -- //
|
||||||
|
try {
|
||||||
|
altitudeMax = map["altitudeMax"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> altitudeMax");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
altitudeMin = map["altitudeMin"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> altitudeMin");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
altitudeAvg = map["altitudeAvg"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> altitudeAvg");
|
||||||
|
}
|
||||||
|
// -- Température -- //
|
||||||
|
try {
|
||||||
|
temperatureMax = map["temperatureMax"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> temperatureMax");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
temperatureMin = map["temperatureMin"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> temperatureMin");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
temperatureAvg = map["temperatureAvg"];
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> temperatureAvg");
|
||||||
|
}
|
||||||
|
// -- Vitesse -- //
|
||||||
|
try {
|
||||||
|
vitesseMax = map["vitesseMax"].toDouble();
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> vitesseMax");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
vitesseMin = map["vitesseMin"].toDouble();
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> vitesseMin");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
vitesseAvg = map["vitesseAvg"].toDouble();
|
||||||
|
} catch (e) {
|
||||||
|
print("Impossible de recup -> vitesseAvg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Ecriture -- //
|
||||||
|
String toJson() {
|
||||||
|
Map<String, dynamic> jsonMap = {
|
||||||
|
// -- BPM -- //
|
||||||
|
'bpmAvg': bpmAvg,
|
||||||
|
'bpmMax': bpmMax,
|
||||||
|
'bpmMin': bpmMin,
|
||||||
|
// -- Denivelé -- //
|
||||||
|
'denivelePositif': denivelePositif,
|
||||||
|
'deniveleNegatif': deniveleNegatif,
|
||||||
|
// -- Altitude -- //
|
||||||
|
'altitudeMax': altitudeMax,
|
||||||
|
'altitudeMin': altitudeMin,
|
||||||
|
'altitudeAvg': altitudeAvg,
|
||||||
|
// -- Température -- //
|
||||||
|
'temperatureMax': temperatureMax,
|
||||||
|
'temperatureMin': temperatureMin,
|
||||||
|
'temperatureAvg': temperatureAvg,
|
||||||
|
// -- Vitesse -- //
|
||||||
|
'vitesseMax': vitesseMax,
|
||||||
|
'vitesseMin': vitesseMin,
|
||||||
|
'vitesseAvg': vitesseAvg,
|
||||||
|
// Ligne session
|
||||||
|
'startTime': startTime.toIso8601String(),
|
||||||
|
'timeOfActivity': timeOfActivity,
|
||||||
|
'distance': distance,
|
||||||
|
'calories': calories,
|
||||||
|
'steps': steps
|
||||||
|
};
|
||||||
|
return jsonEncode(jsonMap);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import "package:path_provider/path_provider.dart";
|
||||||
|
import 'package:smartfit_app_mobile/main.dart';
|
||||||
|
|
||||||
|
class ActivitySaver {
|
||||||
|
String saveDirectory = "activities";
|
||||||
|
late final Directory applicationDocumentsDir;
|
||||||
|
|
||||||
|
ActivitySaver._create(this.applicationDocumentsDir);
|
||||||
|
|
||||||
|
Uint8List getActivity(String uuid) {
|
||||||
|
String filename = localDB.getActivityFilenameByUuid(uuid);
|
||||||
|
|
||||||
|
final file =
|
||||||
|
File(p.join(applicationDocumentsDir.path, saveDirectory, filename));
|
||||||
|
return file.readAsBytesSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveActivity(Uint8List activityFile, String filename) async {
|
||||||
|
stdout.write("Creating activity file...\n");
|
||||||
|
final file = await File(
|
||||||
|
p.join(applicationDocumentsDir.path, saveDirectory, filename))
|
||||||
|
.create(recursive: true); // To create dir if not exists
|
||||||
|
file.writeAsBytesSync(activityFile);
|
||||||
|
stdout.write("Activity file created\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteActivity(String uuid) {
|
||||||
|
String filename = localDB.getActivityFilenameByUuid(uuid);
|
||||||
|
final file =
|
||||||
|
File(p.join(applicationDocumentsDir.path, saveDirectory, filename));
|
||||||
|
file.deleteSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<ActivitySaver> create() async {
|
||||||
|
stdout.write("Activity Saver: Created\n");
|
||||||
|
final appDir = await getApplicationDocumentsDirectory();
|
||||||
|
return ActivitySaver._create(appDir);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
class Convertisseur {
|
||||||
|
// Mettre que des trucs static
|
||||||
|
|
||||||
|
static double secondeIntoMinute(double seconde) {
|
||||||
|
return (seconde / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double milisecondeIntoMinute(double miliseconde) {
|
||||||
|
return (miliseconde / 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double msIntoKmh(double metreSeconde) {
|
||||||
|
return metreSeconde * 3.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double millisecondeIntoSeconde(double milliseconde) {
|
||||||
|
return (milliseconde / 1000);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class Helper {
|
||||||
|
static bool isPlatformWeb() {
|
||||||
|
if (kIsWeb) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/local_db/db_impl.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
|
||||||
|
class DbDummy implements DbImpl {
|
||||||
|
DbDummy._create();
|
||||||
|
DbDummy();
|
||||||
|
@override
|
||||||
|
Future<DbDummy> create() async {
|
||||||
|
return DbDummy._create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== USER ====
|
||||||
|
@override
|
||||||
|
void addUser(String username, String email, String token) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
User getUser() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hasUser() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void deleteUser() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setUserMail(String email) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setUserName(String username) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setUserToken(String token) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== ACTIVITY ====
|
||||||
|
@override
|
||||||
|
void addActivity(String uuid, String filename, String category, String info) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void removeActivity(String uuid) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void removeAllActivities() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getActivityFilenameByUuid(String uuid) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ActivityOfUser> getAllActivities() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== CONFIG ====
|
||||||
|
@override
|
||||||
|
void initConfig() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setSaveLocally(bool saveLocally) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool getSaveLocally() {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
|
||||||
|
abstract class DbImpl {
|
||||||
|
DbImpl._create();
|
||||||
|
|
||||||
|
Future<DbImpl> create();
|
||||||
|
|
||||||
|
Future<void> init();
|
||||||
|
|
||||||
|
// ==== USER ====
|
||||||
|
void addUser(String username, String email, String token);
|
||||||
|
User getUser();
|
||||||
|
bool hasUser();
|
||||||
|
void deleteUser();
|
||||||
|
void setUserMail(String email);
|
||||||
|
void setUserName(String username);
|
||||||
|
void setUserToken(String token);
|
||||||
|
|
||||||
|
// ==== ACTIVITY ====
|
||||||
|
void addActivity(String uuid, String filename, String category, String info);
|
||||||
|
void removeActivity(String uuid);
|
||||||
|
void removeAllActivities();
|
||||||
|
String getActivityFilenameByUuid(String uuid);
|
||||||
|
List<ActivityOfUser> getAllActivities();
|
||||||
|
|
||||||
|
// ==== CONFIG ====
|
||||||
|
void initConfig();
|
||||||
|
void setSaveLocally(bool saveLocally);
|
||||||
|
bool getSaveLocally();
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:smartfit_app_mobile/modele/local_db/db_impl.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/local_db/objectbox.dart';
|
||||||
|
|
||||||
|
DbImpl getDbImpl() {
|
||||||
|
DbImpl db = ObjectBox();
|
||||||
|
return db;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:smartfit_app_mobile/modele/local_db/db_impl.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/local_db/db_dummy.dart';
|
||||||
|
|
||||||
|
DbImpl getDbImpl() {
|
||||||
|
DbImpl db = DbDummy();
|
||||||
|
return db;
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class User {
|
||||||
|
@Id()
|
||||||
|
int id = 0;
|
||||||
|
String username;
|
||||||
|
String email;
|
||||||
|
String token;
|
||||||
|
|
||||||
|
User(this.id, this.username, this.email, this.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class Activity {
|
||||||
|
int id;
|
||||||
|
@Unique()
|
||||||
|
String uuid;
|
||||||
|
String filename;
|
||||||
|
String category;
|
||||||
|
String info;
|
||||||
|
|
||||||
|
Activity(this.id, this.uuid, this.filename, this.category, this.info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class Config {
|
||||||
|
@Id()
|
||||||
|
int id = 0;
|
||||||
|
bool saveLocally;
|
||||||
|
|
||||||
|
Config(this.id, this.saveLocally);
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:smartfit_app_mobile/objectbox.g.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/local_db/model.dart' as db;
|
||||||
|
import 'package:smartfit_app_mobile/modele/local_db/db_impl.dart';
|
||||||
|
|
||||||
|
class ObjectBox implements DbImpl {
|
||||||
|
late final Store store;
|
||||||
|
late final Box userBox;
|
||||||
|
late final Box activityBox;
|
||||||
|
late final Box configBox;
|
||||||
|
late final Directory applicationDocumentDir;
|
||||||
|
|
||||||
|
ObjectBox._create(this.store);
|
||||||
|
|
||||||
|
ObjectBox();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ObjectBox> create() async {
|
||||||
|
final docsDir = await getApplicationDocumentsDirectory();
|
||||||
|
final store = await openStore(directory: p.join(docsDir.path, "database"));
|
||||||
|
return ObjectBox._create(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
applicationDocumentDir = await getApplicationDocumentsDirectory();
|
||||||
|
userBox = store.box<db.User>();
|
||||||
|
activityBox = store.box<db.Activity>();
|
||||||
|
configBox = store.box<db.Config>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== USER =====
|
||||||
|
@override
|
||||||
|
bool hasUser() {
|
||||||
|
return !userBox.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
User getUser() {
|
||||||
|
db.User userRes = userBox.get(1);
|
||||||
|
return User.create(userRes.username, userRes.email, userRes.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setUserMail(String email) {
|
||||||
|
db.User user = userBox.get(1);
|
||||||
|
user.email = email;
|
||||||
|
userBox.put(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setUserName(String username) {
|
||||||
|
db.User user = userBox.get(1);
|
||||||
|
user.username = username;
|
||||||
|
userBox.put(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setUserToken(String token) {
|
||||||
|
db.User user = userBox.get(1);
|
||||||
|
user.token = token;
|
||||||
|
userBox.put(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void deleteUser() {
|
||||||
|
userBox.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addUser(String username, String email, String token) {
|
||||||
|
userBox.put(db.User(0, username, email, token));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Activity =====
|
||||||
|
@override
|
||||||
|
void addActivity(String uuid, String filename, String category, String info) {
|
||||||
|
db.Activity act = db.Activity(0, uuid, filename, category, info);
|
||||||
|
|
||||||
|
try {
|
||||||
|
activityBox.put(act);
|
||||||
|
} on ObjectBoxException {
|
||||||
|
print("Activity already exists");
|
||||||
|
} catch (e) {
|
||||||
|
print("Unknown exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: try catch
|
||||||
|
@override
|
||||||
|
void removeActivity(String uuid) {
|
||||||
|
final Query query = activityBox.query(Activity_.uuid.equals(uuid)).build();
|
||||||
|
final db.Activity act = query.findFirst();
|
||||||
|
|
||||||
|
activityBox.remove(act.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getActivityFilenameByUuid(String uuid) {
|
||||||
|
final Query query = activityBox.query(Activity_.uuid.equals(uuid)).build();
|
||||||
|
final db.Activity act = query.findFirst();
|
||||||
|
|
||||||
|
return act.filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void removeAllActivities() {
|
||||||
|
activityBox.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== FIT Files =====
|
||||||
|
@override
|
||||||
|
List<ActivityOfUser> getAllActivities() {
|
||||||
|
List<dynamic> activityDBList = activityBox.getAll();
|
||||||
|
List<ActivityOfUser> userActivityList = List.empty(growable: true);
|
||||||
|
|
||||||
|
for (db.Activity act in activityDBList) {
|
||||||
|
ActivityInfo actInfo = ActivityInfo.fromJson(jsonDecode(act.info));
|
||||||
|
|
||||||
|
userActivityList
|
||||||
|
.add(ActivityOfUser(actInfo, act.category, act.uuid, act.filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
return userActivityList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Config =====
|
||||||
|
@override
|
||||||
|
void initConfig() {
|
||||||
|
db.Config config = db.Config(0, true);
|
||||||
|
configBox.put(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setSaveLocally(bool saveLocally) {
|
||||||
|
db.Config config = configBox.get(1);
|
||||||
|
config.saveLocally = saveLocally;
|
||||||
|
configBox.put(config);
|
||||||
|
stdout.write("(Config) setSaveLocally: $saveLocally\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool getSaveLocally() {
|
||||||
|
db.Config config = configBox.get(1);
|
||||||
|
stdout.write("(Config) getSaveLocally: ${config.saveLocally}\n");
|
||||||
|
return config.saveLocally;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_saver.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:smartfit_app_mobile/main.dart';
|
||||||
|
|
||||||
|
class RequestLocal implements IDataStrategy {
|
||||||
|
@override
|
||||||
|
Future<Tuple2> getInfoUser(String token) async {
|
||||||
|
final User user = localDB.getUser();
|
||||||
|
Map<String, String> json = {"email": user.email, "username": user.username};
|
||||||
|
return Tuple2(true, jsonEncode(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to save file on request_api.upload() beforehand.
|
||||||
|
@override
|
||||||
|
Future<Tuple2> getFile(String token, String fileUuid) async {
|
||||||
|
ActivitySaver actSaver = await ActivitySaver.create();
|
||||||
|
Uint8List fileBytes = actSaver.getActivity(fileUuid);
|
||||||
|
|
||||||
|
return Tuple2(true, fileBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2> getFiles(String token) async {
|
||||||
|
final List<ActivityOfUser> activities = localDB.getAllActivities();
|
||||||
|
List<Map<String, dynamic>> jsonList = List.empty(growable: true);
|
||||||
|
|
||||||
|
for (ActivityOfUser act in activities) {
|
||||||
|
Map<String, dynamic> json = {
|
||||||
|
"uuid": act.fileUuid,
|
||||||
|
"filename": act.nameFile,
|
||||||
|
"category": act.category,
|
||||||
|
"info": act.activityInfo
|
||||||
|
};
|
||||||
|
jsonList.add(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tuple2(true, jsonList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2<bool, String>> modifAttribut(
|
||||||
|
String token, String nameAttribut, String newValue) async {
|
||||||
|
return const Tuple2(false, "not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2<bool, String>> postUser(
|
||||||
|
String email, String hash, String username) async {
|
||||||
|
return const Tuple2(false, "not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2<bool, String>> deleteUser(String token) async {
|
||||||
|
return const Tuple2(false, "not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2<bool, String>> connexion(String email, String hash) async {
|
||||||
|
return const Tuple2(false, "not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2<bool, String>> uploadFile(String token, File file) async {
|
||||||
|
return const Tuple2(false, "not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2<bool, String>> uploadFileByte(
|
||||||
|
String token,
|
||||||
|
Uint8List contentFile,
|
||||||
|
String nameFile,
|
||||||
|
String category,
|
||||||
|
DateTime date,
|
||||||
|
ActivityInfo activityInfo) async {
|
||||||
|
return const Tuple2(false, "not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> deleteFile(String token, String fileUuid) async {
|
||||||
|
throw Exception("Not Implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tuple2> getModeleAI(String token, String category) async {
|
||||||
|
return const Tuple2(false, "Not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,362 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/graph/graph.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/manager_file.dart';
|
||||||
|
import 'package:latlong2/latlong.dart' as osm;
|
||||||
|
|
||||||
|
class ManagerSelectedActivity {
|
||||||
|
final ManagerFile _managerFile = ManagerFile();
|
||||||
|
List<ActivityOfUser> activitySelected = List.empty(growable: true);
|
||||||
|
|
||||||
|
bool addSelectedActivity(ActivityOfUser activityOfUser) {
|
||||||
|
if (activitySelected.isNotEmpty &&
|
||||||
|
activityOfUser.category != activitySelected.first.category) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
activitySelected.add(activityOfUser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeSelectedActivity(String fileUuid) {
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.fileUuid == fileUuid) {
|
||||||
|
activitySelected.remove(activityOfUser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Function utile ---- //
|
||||||
|
// -- func utile -- //
|
||||||
|
bool fileNotSelected(String fileUuid) {
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.fileUuid == fileUuid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _notNull(int indexActivitySelected, int ligne, int colonne) {
|
||||||
|
if (activitySelected[indexActivitySelected].contentActivity[ligne]
|
||||||
|
[colonne] ==
|
||||||
|
"null") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// ------------------- Fonction pour calcul --------- //
|
||||||
|
// ----------------- X WithTime ------------ //
|
||||||
|
|
||||||
|
List<FlSpot> getXWithTime(String field) {
|
||||||
|
List<FlSpot> result = List.empty(growable: true);
|
||||||
|
int firstTimestamp = 0;
|
||||||
|
|
||||||
|
for (int c = 0; c < activitySelected.length; c++) {
|
||||||
|
for (int i = 0; i < activitySelected[c].contentActivity.length; i++) {
|
||||||
|
if (_notNull(c, i, activitySelected[c].enteteCSV["Value_$field"]!)) {
|
||||||
|
if (firstTimestamp == 0) {
|
||||||
|
firstTimestamp = activitySelected[c].contentActivity[i][
|
||||||
|
activitySelected[c]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldTimeStamp}"]!];
|
||||||
|
}
|
||||||
|
result.add(FlSpot(
|
||||||
|
(((activitySelected[c].contentActivity[i][
|
||||||
|
activitySelected[c].enteteCSV[
|
||||||
|
"Value_${_managerFile.fieldTimeStamp}"]!]) -
|
||||||
|
firstTimestamp) ~/
|
||||||
|
100)
|
||||||
|
.toDouble(),
|
||||||
|
activitySelected[c]
|
||||||
|
.contentActivity[i]
|
||||||
|
[activitySelected[c].enteteCSV["Value_$field"]!]
|
||||||
|
.toDouble()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
// ----------------- BPM ------------------ //
|
||||||
|
int getBpmMaxAllActivitieSelected() {
|
||||||
|
int max = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.bpmMax > max) {
|
||||||
|
max = activityOfUser.activityInfo.bpmMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBpmMinAllActivitieSelected() {
|
||||||
|
int min = 999;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.bpmMax < min) {
|
||||||
|
min = activityOfUser.activityInfo.bpmMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBpmAvgAllActivitieSelected() {
|
||||||
|
int somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.bpmAvg;
|
||||||
|
}
|
||||||
|
if (somme != 0) {
|
||||||
|
return somme ~/ activitySelected.length;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------ Fin BPM ------------------- //
|
||||||
|
// ------------------ Altitude ------------------ //
|
||||||
|
double getMaxAltitudeAllActivitySelected() {
|
||||||
|
double max = 0.0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.altitudeMax > max) {
|
||||||
|
max = activityOfUser.activityInfo.altitudeMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getMinAltitudeAllActivitySelected() {
|
||||||
|
double min = 99999.0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.altitudeMax < min) {
|
||||||
|
min = activityOfUser.activityInfo.altitudeMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getAvgAltitudeAllActivitySelected() {
|
||||||
|
double somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.altitudeAvg;
|
||||||
|
}
|
||||||
|
return somme / activitySelected.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------ Fin Altitude ------------------- //
|
||||||
|
// ------------------ Denivelé ----------------------- //
|
||||||
|
|
||||||
|
double getTotalDenivelePositifAllActivitySelected() {
|
||||||
|
double somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.denivelePositif;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getTotalDeniveleNegatifAllActivitySelected() {
|
||||||
|
double somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.denivelePositif;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------ Fin Denivelé ------------------- //
|
||||||
|
// ------------------ Température -------------------- //
|
||||||
|
int getAvgTemperatureAllActivitySelected() {
|
||||||
|
int somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.temperatureAvg;
|
||||||
|
}
|
||||||
|
return somme ~/ activitySelected.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMaxTemperatureAllActivitySelected() {
|
||||||
|
int max = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.temperatureMax > max) {
|
||||||
|
max = activityOfUser.activityInfo.temperatureMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMinTemperatureAllActivitySelected() {
|
||||||
|
int min = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.temperatureMin > min) {
|
||||||
|
min = activityOfUser.activityInfo.temperatureMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
// -------------------------- FIN Température ---------------------- //
|
||||||
|
|
||||||
|
// ---------------------- Distance ---------------------- //
|
||||||
|
|
||||||
|
double getDistanceAllActivitySelected() {
|
||||||
|
double somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.distance;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- FIN Distance ---------------------- //
|
||||||
|
|
||||||
|
// ---------------------- Calories ---------------------- //
|
||||||
|
int getCalorieAllActivitySelected() {
|
||||||
|
int somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.calories;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- FIN Calories ---------------------- //
|
||||||
|
// ---------------------- Step ------------------------------//
|
||||||
|
|
||||||
|
int getStepsAllActivitySelected() {
|
||||||
|
int somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.steps;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
// ----------------------- FIN Step ------------------------ //
|
||||||
|
|
||||||
|
// ------------------------- Time ----------------------------- //
|
||||||
|
|
||||||
|
double getTimeAllActivitySelected() {
|
||||||
|
double somme = 0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.timeOfActivity;
|
||||||
|
}
|
||||||
|
return somme;
|
||||||
|
}
|
||||||
|
// ---------------------------- FIN time -------------------- //
|
||||||
|
|
||||||
|
// -------------------------- Speed ---------------------- //
|
||||||
|
|
||||||
|
// -- CSV -- //
|
||||||
|
List<DataPoint> getSpeedWithTimeActivity() {
|
||||||
|
List<DataPoint> result = List.empty(growable: true);
|
||||||
|
int firstTimestamp = 0;
|
||||||
|
|
||||||
|
for (int c = 0; c < activitySelected.length; c++) {
|
||||||
|
for (int i = 0; i < activitySelected[c].contentActivity.length; i++) {
|
||||||
|
if (_notNull(
|
||||||
|
c,
|
||||||
|
i,
|
||||||
|
activitySelected[c]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldTimeStamp}"]!) &&
|
||||||
|
_notNull(
|
||||||
|
c,
|
||||||
|
i,
|
||||||
|
activitySelected[c]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldSpeed}"]!)) {
|
||||||
|
if (firstTimestamp == 0) {
|
||||||
|
firstTimestamp = activitySelected[c].contentActivity[i][
|
||||||
|
activitySelected[c]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldTimeStamp}"]!];
|
||||||
|
}
|
||||||
|
result.add(DataPoint(
|
||||||
|
(((activitySelected[c].contentActivity[i][
|
||||||
|
activitySelected[c].enteteCSV[
|
||||||
|
"Value_${_managerFile.fieldTimeStamp}"]!]) -
|
||||||
|
firstTimestamp) ~/
|
||||||
|
100)
|
||||||
|
.toDouble(),
|
||||||
|
activitySelected[c]
|
||||||
|
.contentActivity[i][activitySelected[c]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldSpeed}"]!]
|
||||||
|
.toDouble()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getMaxSpeedAllActivitySelected() {
|
||||||
|
double max = 0.00;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.vitesseMax > max) {
|
||||||
|
max = activityOfUser.activityInfo.vitesseMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getMinSpeedAllActivitySelected() {
|
||||||
|
double min = 99999.9;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
if (activityOfUser.activityInfo.vitesseMin < min) {
|
||||||
|
min = activityOfUser.activityInfo.vitesseMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getAvgSpeedAllActivitySelected() {
|
||||||
|
double somme = 0.0;
|
||||||
|
for (ActivityOfUser activityOfUser in activitySelected) {
|
||||||
|
somme += activityOfUser.activityInfo.vitesseAvg;
|
||||||
|
}
|
||||||
|
return somme / activitySelected.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------- FIN Speed ---------------------- //
|
||||||
|
|
||||||
|
// -------------------------- Localisation ------------------- //
|
||||||
|
|
||||||
|
// Retourne les positions (Fichier CSV)
|
||||||
|
// Utilisable que si qu'une seule activité à été utilisé !!!
|
||||||
|
List<LatLng> getPosition() {
|
||||||
|
List<LatLng> list = List.empty(growable: true);
|
||||||
|
|
||||||
|
for (int i = 0; i < activitySelected[0].contentActivity.length; i++) {
|
||||||
|
if (_notNull(
|
||||||
|
0,
|
||||||
|
i,
|
||||||
|
activitySelected[0]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldPositionLatitude}"]!) &&
|
||||||
|
_notNull(
|
||||||
|
0,
|
||||||
|
i,
|
||||||
|
activitySelected[0].enteteCSV[
|
||||||
|
"Value_${_managerFile.fieldPositionLongitude}"]!)) {
|
||||||
|
list.add(LatLng(
|
||||||
|
activitySelected[0].contentActivity[i][activitySelected[0]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldPositionLatitude}"]!],
|
||||||
|
activitySelected[0].contentActivity[i][activitySelected[0]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldPositionLongitude}"]!]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<osm.LatLng> getPositionOSM() {
|
||||||
|
List<osm.LatLng> list = List.empty(growable: true);
|
||||||
|
|
||||||
|
for (int i = 0; i < activitySelected[0].contentActivity.length; i++) {
|
||||||
|
if (_notNull(
|
||||||
|
0,
|
||||||
|
i,
|
||||||
|
activitySelected[0]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldPositionLatitude}"]!) &&
|
||||||
|
_notNull(
|
||||||
|
0,
|
||||||
|
i,
|
||||||
|
activitySelected[0].enteteCSV[
|
||||||
|
"Value_${_managerFile.fieldPositionLongitude}"]!)) {
|
||||||
|
list.add(osm.LatLng(
|
||||||
|
activitySelected[0].contentActivity[i][activitySelected[0]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldPositionLatitude}"]!],
|
||||||
|
activitySelected[0].contentActivity[i][activitySelected[0]
|
||||||
|
.enteteCSV["Value_${_managerFile.fieldPositionLongitude}"]!]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
class AttributUser {
|
|
||||||
final String _email = "email";
|
|
||||||
final String _username = "username";
|
|
||||||
String get email => _email;
|
|
||||||
String get username => _username;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
class AttributFileFit {
|
|
||||||
// Ajouter une variable privé qui contient le champs + un getter
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart';
|
|
||||||
import 'package:smartfit_app_mobile/modele/api/request_api.dart';
|
|
||||||
import 'package:smartfit_app_mobile/modele/manager_file.dart';
|
|
||||||
import 'package:smartfit_app_mobile/modele/user.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class ListActivityUtile {
|
|
||||||
final IDataStrategy _strategy = RequestApi();
|
|
||||||
final ManagerFile _managerFile = ManagerFile();
|
|
||||||
|
|
||||||
Future<void> getContentOnTheFirstFileMobile(BuildContext context) async {
|
|
||||||
Tuple2 result = await _strategy.getFile(
|
|
||||||
Provider.of<User>(context, listen: false).token,
|
|
||||||
Provider.of<User>(context, listen: false).listActivity[0].fileUuid);
|
|
||||||
if (result.item1 == false) {
|
|
||||||
//Erreur
|
|
||||||
//print(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File file = File(
|
|
||||||
"${await _managerFile.localPath}/${Provider.of<User>(context, listen: false).listActivity[0].nameFile}");
|
|
||||||
await file.create();
|
|
||||||
await file.writeAsBytes(result.item2);
|
|
||||||
Provider.of<User>(context, listen: false).listActivity[0].contentActivity =
|
|
||||||
await _managerFile.readFitFileWhithFile(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> getContentOnTheFirstFileWeb(BuildContext context) async {
|
|
||||||
User user = Provider.of<User>(context, listen: false);
|
|
||||||
Tuple2 result =
|
|
||||||
await _strategy.getFile(user.token, user.listActivity[0].fileUuid);
|
|
||||||
if (result.item1 == false) {
|
|
||||||
//Erreur
|
|
||||||
//print(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Provider.of<User>(context, listen: false).listActivity[0].contentActivity =
|
|
||||||
await _managerFile.readFitFileWeb(result.item2);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,154 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:smartfit_app_mobile/main.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_saver.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/helper.dart';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:csv/csv.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/manager_file.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/info_message.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class ListActivityUtile {
|
||||||
|
final ApiWrapper api = ApiWrapper();
|
||||||
|
final ManagerFile _managerFile = ManagerFile();
|
||||||
|
|
||||||
|
Future<Tuple2<bool, String>> getContentActivity(BuildContext context,
|
||||||
|
ActivityOfUser activityOfUser, InfoMessage infoManager) async {
|
||||||
|
Tuple2 result = await api.getFile(
|
||||||
|
Provider.of<User>(context, listen: false).token,
|
||||||
|
activityOfUser.fileUuid,
|
||||||
|
infoManager);
|
||||||
|
if (result.item1 == false) {
|
||||||
|
return Tuple2(result.item1, result.item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
activityOfUser.contentActivity =
|
||||||
|
List.from(_managerFile.convertByteIntoCSV(result.item2));
|
||||||
|
|
||||||
|
// TODO: Not sure this line as an utility
|
||||||
|
// localDB.saveActivityFile(activityOfUser.contentActivity);
|
||||||
|
|
||||||
|
// TODO: Check if file exists, right now it overwrites each time
|
||||||
|
// TODO: Make ActivitySaver member of the class
|
||||||
|
if (!Helper.isPlatformWeb() && localDB.getSaveLocally()) {
|
||||||
|
ActivitySaver actSaver = await ActivitySaver.create();
|
||||||
|
actSaver.saveActivity(result.item2,
|
||||||
|
localDB.getActivityFilenameByUuid(activityOfUser.fileUuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Provider.of<User>(context, listen: false)
|
||||||
|
.managerSelectedActivity
|
||||||
|
.addSelectedActivity(activityOfUser)) {
|
||||||
|
return const Tuple2(false, "Pas de même categorie");
|
||||||
|
}
|
||||||
|
return const Tuple2(true, "Yeah");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tuple2<bool, String>> getFiles(
|
||||||
|
String token, BuildContext context, InfoMessage infoManager) async {
|
||||||
|
bool notZero = false;
|
||||||
|
Tuple2 result = await api.getFiles(
|
||||||
|
Provider.of<User>(context, listen: false).token, infoManager);
|
||||||
|
if (result.item1 == false) {
|
||||||
|
return Tuple2(result.item1, result.item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var element in result.item2) {
|
||||||
|
if (!notZero) {
|
||||||
|
Provider.of<User>(context, listen: false).listActivity.clear();
|
||||||
|
notZero = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Provider.of<User>(context, listen: false).addActivity(ActivityOfUser(
|
||||||
|
ActivityInfo.fromJson(element["info"]),
|
||||||
|
element["category"].toString(),
|
||||||
|
element["uuid"].toString(),
|
||||||
|
element["filename"].toString()));
|
||||||
|
|
||||||
|
// Save to local db
|
||||||
|
if (!kIsWeb) {
|
||||||
|
localDB.addActivity(element["uuid"], element["filename"],
|
||||||
|
element["category"], jsonEncode(element["info"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return const Tuple2(true, "Yeah");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tuple2<bool, String>> addFile(Uint8List bytes, String filename,
|
||||||
|
String token, InfoMessage infoManager) async {
|
||||||
|
// -- Transormer le fit en CSV
|
||||||
|
Tuple4<bool, List<List<String>>, ActivityInfo, String> resultData =
|
||||||
|
_managerFile.convertBytesFitFileIntoCSVListAndGetInfo(bytes);
|
||||||
|
|
||||||
|
String csvString = const ListToCsvConverter().convert(resultData.item2);
|
||||||
|
Uint8List byteCSV = Uint8List.fromList(utf8.encode(csvString));
|
||||||
|
|
||||||
|
/* -- Ne pas décomenter sinon web ne marche plus (Utilisé pour déboguer)
|
||||||
|
File x = await File("${await _managerFile.localPath}\\what")
|
||||||
|
.writeAsString(csvString);*/
|
||||||
|
|
||||||
|
Tuple2<bool, String> result = await api.uploadFileByte(
|
||||||
|
token,
|
||||||
|
byteCSV,
|
||||||
|
filename,
|
||||||
|
resultData.item4, // category
|
||||||
|
resultData.item3.startTime, // activityInfo
|
||||||
|
resultData.item3,
|
||||||
|
infoManager);
|
||||||
|
if (result.item1 == false) {
|
||||||
|
return Tuple2(false, result.item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save on local storage if plateform not browser
|
||||||
|
if (!Helper.isPlatformWeb() && localDB.getSaveLocally()) {
|
||||||
|
ActivitySaver actSaver = await ActivitySaver.create();
|
||||||
|
actSaver.saveActivity(byteCSV, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const Tuple2(true, "Yeah");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Ne marche pas sous windows !! Jsp linux (mettre en format mobile) -- //
|
||||||
|
Future<void> addFileWeb(Uint8List? bytes, String token, String filename,
|
||||||
|
BuildContext context, InfoMessage infoManager) async {
|
||||||
|
if (bytes == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Tuple2<bool, String> resultAdd =
|
||||||
|
await addFile(bytes, filename, token, infoManager);
|
||||||
|
if (!resultAdd.item1) {
|
||||||
|
//print("Message error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Tuple2<bool, String> resultGet =
|
||||||
|
await getFiles(token, context, infoManager);
|
||||||
|
if (!resultGet.item1) {
|
||||||
|
//print("Message error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addFileMobile(String path, String token, String filename,
|
||||||
|
BuildContext context, InfoMessage infoManager) async {
|
||||||
|
Tuple2<bool, String> resultAdd = await addFile(
|
||||||
|
await File(path).readAsBytes(), filename, token, infoManager);
|
||||||
|
if (!resultAdd.item1) {
|
||||||
|
//print("Message error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: What is that ?
|
||||||
|
Tuple2<bool, String> resultGet =
|
||||||
|
await getFiles(token, context, infoManager);
|
||||||
|
if (!resultGet.item1) {
|
||||||
|
//print("Message error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart';
|
|
||||||
import 'package:smartfit_app_mobile/modele/api/request_api.dart';
|
|
||||||
|
|
||||||
class ProfileUtil {
|
|
||||||
final IDataStrategy _dataStrategy = RequestApi();
|
|
||||||
|
|
||||||
void modifyDataUser(String token, String attribut, String newUsername) {
|
|
||||||
_dataStrategy.modifAttribut(token, attribut, newUsername);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,430 +0,0 @@
|
|||||||
import 'package:fl_chart/fl_chart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../common/colo_extension.dart';
|
|
||||||
import '../../common_widget/today_target_cell.dart';
|
|
||||||
|
|
||||||
class ActivityTrackerView extends StatefulWidget {
|
|
||||||
const ActivityTrackerView({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ActivityTrackerView> createState() => _ActivityTrackerViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ActivityTrackerViewState extends State<ActivityTrackerView> {
|
|
||||||
int touchedIndex = -1;
|
|
||||||
|
|
||||||
List latestArr = [
|
|
||||||
{
|
|
||||||
"image": "assets/img/workout1.svg",
|
|
||||||
"title": "Drinking 300ml Water",
|
|
||||||
"time": "About 1 minutes ago"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"image": "assets/img/workout1.svg",
|
|
||||||
"title": "Eat Snack (Fitbar)",
|
|
||||||
"time": "About 3 hours ago"
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var media = MediaQuery.of(context).size;
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: TColor.white,
|
|
||||||
centerTitle: true,
|
|
||||||
elevation: 0,
|
|
||||||
leading: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(8),
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: TColor.lightGray,
|
|
||||||
borderRadius: BorderRadius.circular(10)),
|
|
||||||
child: Image.asset(
|
|
||||||
"assets/img/black_btn.png",
|
|
||||||
width: 15,
|
|
||||||
height: 15,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
"Suivi d'activité",
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
InkWell(
|
|
||||||
onTap: () {},
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(8),
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: TColor.lightGray,
|
|
||||||
borderRadius: BorderRadius.circular(10)),
|
|
||||||
child: Image.asset(
|
|
||||||
"assets/img/more_btn.png",
|
|
||||||
width: 15,
|
|
||||||
height: 15,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
backgroundColor: TColor.white,
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 25),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(colors: [
|
|
||||||
TColor.primaryColor2.withOpacity(0.3),
|
|
||||||
TColor.primaryColor1.withOpacity(0.3)
|
|
||||||
]),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Objectif d'aujourd'hui",
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.black,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w700),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: TColor.primaryG,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: MaterialButton(
|
|
||||||
onPressed: () {},
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
height: 30,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25)),
|
|
||||||
textColor: TColor.primaryColor1,
|
|
||||||
minWidth: double.maxFinite,
|
|
||||||
elevation: 0,
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: const Icon(
|
|
||||||
Icons.add,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 15,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 15,
|
|
||||||
),
|
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TodayTargetCell(
|
|
||||||
icon: "assets/img/workout1.svg",
|
|
||||||
value: "800",
|
|
||||||
title: "Calories",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 15,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TodayTargetCell(
|
|
||||||
icon: "assets/img/workout1.svg",
|
|
||||||
value: "2400",
|
|
||||||
title: "Nombre pas",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: media.width * 0.1,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Activité journalière",
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.black,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w700),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 30,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(colors: TColor.primaryG),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
child: DropdownButtonHideUnderline(
|
|
||||||
child: DropdownButton(
|
|
||||||
items: ["Semaine", "Mois"]
|
|
||||||
.map((name) => DropdownMenuItem(
|
|
||||||
value: name,
|
|
||||||
child: Text(
|
|
||||||
name,
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.gray, fontSize: 14),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
onChanged: (value) {},
|
|
||||||
icon: Icon(Icons.expand_more, color: TColor.white),
|
|
||||||
hint: Text(
|
|
||||||
"Semaine",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: TColor.white, fontSize: 12),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: media.width * 0.05,
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: media.width * 0.5,
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(vertical: 15, horizontal: 0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: TColor.white,
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(color: Colors.black12, blurRadius: 3)
|
|
||||||
]),
|
|
||||||
child: BarChart(BarChartData(
|
|
||||||
barTouchData: BarTouchData(
|
|
||||||
touchTooltipData: BarTouchTooltipData(
|
|
||||||
tooltipBgColor: Colors.grey,
|
|
||||||
tooltipHorizontalAlignment: FLHorizontalAlignment.right,
|
|
||||||
tooltipMargin: 10,
|
|
||||||
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
|
||||||
String weekDay;
|
|
||||||
switch (group.x) {
|
|
||||||
case 0:
|
|
||||||
weekDay = 'Monday';
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
weekDay = 'Tuesday';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
weekDay = 'Wednesday';
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
weekDay = 'Thursday';
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
weekDay = 'Friday';
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
weekDay = 'Saturday';
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
weekDay = 'Sunday';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
return BarTooltipItem(
|
|
||||||
'$weekDay\n',
|
|
||||||
const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
children: <TextSpan>[
|
|
||||||
TextSpan(
|
|
||||||
text: (rod.toY - 1).toString(),
|
|
||||||
style: TextStyle(
|
|
||||||
color: TColor.white,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
touchCallback: (FlTouchEvent event, barTouchResponse) {
|
|
||||||
setState(() {
|
|
||||||
if (!event.isInterestedForInteractions ||
|
|
||||||
barTouchResponse == null ||
|
|
||||||
barTouchResponse.spot == null) {
|
|
||||||
touchedIndex = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
touchedIndex =
|
|
||||||
barTouchResponse.spot!.touchedBarGroupIndex;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
titlesData: FlTitlesData(
|
|
||||||
show: true,
|
|
||||||
rightTitles: AxisTitles(
|
|
||||||
sideTitles: SideTitles(showTitles: false),
|
|
||||||
),
|
|
||||||
topTitles: AxisTitles(
|
|
||||||
sideTitles: SideTitles(showTitles: false),
|
|
||||||
),
|
|
||||||
bottomTitles: AxisTitles(
|
|
||||||
sideTitles: SideTitles(
|
|
||||||
showTitles: true,
|
|
||||||
getTitlesWidget: getTitles,
|
|
||||||
reservedSize: 38,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
leftTitles: AxisTitles(
|
|
||||||
sideTitles: SideTitles(
|
|
||||||
showTitles: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
borderData: FlBorderData(
|
|
||||||
show: false,
|
|
||||||
),
|
|
||||||
barGroups: showingGroups(),
|
|
||||||
gridData: FlGridData(show: false),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: media.width * 0.05,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: media.width * 0.1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget getTitles(double value, TitleMeta meta) {
|
|
||||||
var style = TextStyle(
|
|
||||||
color: TColor.gray,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 12,
|
|
||||||
);
|
|
||||||
Widget text;
|
|
||||||
switch (value.toInt()) {
|
|
||||||
case 0:
|
|
||||||
text = Text('Dim', style: style);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
text = Text('Lun', style: style);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
text = Text('Mar', style: style);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
text = Text('Mer', style: style);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
text = Text('Jeu', style: style);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
text = Text('Ven', style: style);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
text = Text('Sam', style: style);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text = Text('', style: style);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return SideTitleWidget(
|
|
||||||
axisSide: meta.axisSide,
|
|
||||||
space: 16,
|
|
||||||
child: text,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BarChartGroupData> showingGroups() => List.generate(7, (i) {
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
return makeGroupData(0, 5, TColor.primaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
case 1:
|
|
||||||
return makeGroupData(1, 10.5, TColor.secondaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
case 2:
|
|
||||||
return makeGroupData(2, 5, TColor.primaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
case 3:
|
|
||||||
return makeGroupData(3, 7.5, TColor.secondaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
case 4:
|
|
||||||
return makeGroupData(4, 15, TColor.primaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
case 5:
|
|
||||||
return makeGroupData(5, 5.5, TColor.secondaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
case 6:
|
|
||||||
return makeGroupData(6, 8.5, TColor.primaryG,
|
|
||||||
isTouched: i == touchedIndex);
|
|
||||||
default:
|
|
||||||
return throw Error();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
BarChartGroupData makeGroupData(
|
|
||||||
int x,
|
|
||||||
double y,
|
|
||||||
List<Color> barColor, {
|
|
||||||
bool isTouched = false,
|
|
||||||
double width = 22,
|
|
||||||
List<int> showTooltips = const [],
|
|
||||||
}) {
|
|
||||||
return BarChartGroupData(
|
|
||||||
x: x,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: isTouched ? y + 1 : y,
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: barColor,
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter),
|
|
||||||
width: width,
|
|
||||||
borderSide: isTouched
|
|
||||||
? const BorderSide(color: Colors.green)
|
|
||||||
: const BorderSide(color: Colors.white, width: 0),
|
|
||||||
backDrawRodData: BackgroundBarChartRodData(
|
|
||||||
show: true,
|
|
||||||
toY: 20,
|
|
||||||
color: TColor.lightGray,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: showTooltips,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,180 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/button/round_button.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/workout_row/workout_row.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/manager_file.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/info_message.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class Prediction extends StatefulWidget {
|
||||||
|
const Prediction({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Prediction> createState() => _PredictionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PredictionState extends State<Prediction> {
|
||||||
|
List<Map<String, dynamic>> lastWorkoutArr = [
|
||||||
|
{
|
||||||
|
"name": "Time",
|
||||||
|
"image": "assets/img/time-icon2.svg",
|
||||||
|
"value": "..",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Heart rate",
|
||||||
|
"image": "assets/img/bpm2-icon.svg",
|
||||||
|
"value": "..",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Speed",
|
||||||
|
"image": "assets/img/vitesse2-icon.svg",
|
||||||
|
"value": "..",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Distance",
|
||||||
|
"image": "assets/img/distance2-icon.svg",
|
||||||
|
"value": "..",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
final ManagerFile _managerFile = ManagerFile();
|
||||||
|
String selectedCategory = "Select an activity category";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<String> listCategory = [_managerFile.marche, _managerFile.velo];
|
||||||
|
|
||||||
|
void prediction() async {
|
||||||
|
InfoMessage tmp = InfoMessage();
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (selectedCategory != _managerFile.marche ||
|
||||||
|
selectedCategory != _managerFile.velo) return;*/
|
||||||
|
|
||||||
|
Tuple2<bool, ActivityInfo> resultat =
|
||||||
|
await Provider.of<User>(context, listen: false)
|
||||||
|
.predictActivity(DateTime.now(), selectedCategory, tmp);
|
||||||
|
if (!resultat.item1) return;
|
||||||
|
setState(() {
|
||||||
|
lastWorkoutArr[0]["value"] =
|
||||||
|
"${Convertisseur.secondeIntoMinute(resultat.item2.timeOfActivity).toStringAsFixed(0)} min";
|
||||||
|
lastWorkoutArr[1]["value"] =
|
||||||
|
"${resultat.item2.bpmAvg.toStringAsFixed(2)} bpm";
|
||||||
|
lastWorkoutArr[2]["value"] =
|
||||||
|
"${Convertisseur.msIntoKmh(resultat.item2.vitesseAvg).toStringAsFixed(1)} km/h ";
|
||||||
|
lastWorkoutArr[3]["value"] =
|
||||||
|
"${resultat.item2.distance.toStringAsFixed(2)} m";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
Text(
|
||||||
|
"Prediction",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const Row(
|
||||||
|
children: [],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: TColor.lightGray,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: const Color.fromARGB(255, 234, 234, 234)
|
||||||
|
.withOpacity(0.9),
|
||||||
|
spreadRadius: 2,
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
"assets/img/Profile_tab.svg",
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton(
|
||||||
|
items: listCategory
|
||||||
|
.map((name) => DropdownMenuItem(
|
||||||
|
value: name,
|
||||||
|
child: Text(
|
||||||
|
name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
selectedCategory = value!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isExpanded: true,
|
||||||
|
hint: Text(
|
||||||
|
selectedCategory,
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.gray,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Bouton "Valider" prenant 30% de la largeur du parent
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
RoundButton(
|
||||||
|
title: "Save",
|
||||||
|
onPressed: () async {
|
||||||
|
prediction();
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: lastWorkoutArr.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
var wObj = lastWorkoutArr[index];
|
||||||
|
return InkWell(
|
||||||
|
child: WorkoutRow(wObj: wObj),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,246 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/container/container_stats_activities.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common_widget/other/entete_home_view.dart';
|
||||||
|
import 'package:smartfit_app_mobile/common/colo_extension.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/manager_selected_activity.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/user.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
|
||||||
|
import 'package:smartfit_app_mobile/modele/utile/home_view/home_view_util.dart';
|
||||||
|
|
||||||
|
class StatAtivities extends StatefulWidget {
|
||||||
|
const StatAtivities({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatAtivities> createState() => _StatAtivities();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StatAtivities extends State<StatAtivities> {
|
||||||
|
late DataHomeView data;
|
||||||
|
TextEditingController bpmController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var media = MediaQuery.of(context).size;
|
||||||
|
data = HomeViewUtil().initData(context);
|
||||||
|
ManagerSelectedActivity managerSelectedActivity =
|
||||||
|
context.watch<User>().managerSelectedActivity;
|
||||||
|
|
||||||
|
// -- BPM -- //
|
||||||
|
int maxBpm = managerSelectedActivity.getBpmMaxAllActivitieSelected();
|
||||||
|
int minBpm = managerSelectedActivity.getBpmMinAllActivitieSelected();
|
||||||
|
int avgBpm = managerSelectedActivity.getBpmAvgAllActivitieSelected();
|
||||||
|
// -- Altitude -- //
|
||||||
|
double maxAltitude =
|
||||||
|
managerSelectedActivity.getMaxAltitudeAllActivitySelected();
|
||||||
|
double minAltitude =
|
||||||
|
managerSelectedActivity.getMinAltitudeAllActivitySelected();
|
||||||
|
double avgAltitude =
|
||||||
|
managerSelectedActivity.getAvgAltitudeAllActivitySelected();
|
||||||
|
|
||||||
|
// -- Température -- //
|
||||||
|
double avgTemperature = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getAvgTemperatureAllActivitySelected()
|
||||||
|
.toDouble();
|
||||||
|
double maxTemperature = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getMaxTemperatureAllActivitySelected()
|
||||||
|
.toDouble();
|
||||||
|
double minTemperature = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getMinTemperatureAllActivitySelected()
|
||||||
|
.toDouble();
|
||||||
|
// ----- Distance ---- //
|
||||||
|
double getTotalDistance = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getDistanceAllActivitySelected();
|
||||||
|
// ---- Calories --- //
|
||||||
|
int totalCalories = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getCalorieAllActivitySelected();
|
||||||
|
// --- Steps --- //
|
||||||
|
int totalSteps = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getStepsAllActivitySelected();
|
||||||
|
// -- Time -- //
|
||||||
|
double totalTime = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getTimeAllActivitySelected();
|
||||||
|
|
||||||
|
// -- Speed -- //
|
||||||
|
double avgSpeed = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getAvgSpeedAllActivitySelected();
|
||||||
|
double maxSpeed = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getMaxSpeedAllActivitySelected();
|
||||||
|
double minSpeed = context
|
||||||
|
.watch<User>()
|
||||||
|
.managerSelectedActivity
|
||||||
|
.getMinSpeedAllActivitySelected();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: TColor.white,
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
const EnteteHomeView(),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.05,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Activity Status",
|
||||||
|
style: TextStyle(
|
||||||
|
color: TColor.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"$avgBpm BPM", "Average bpm", Icons.favorite),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"$maxBpm BPM", "Maximum bpm", Icons.trending_up),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"$minBpm BPM", "Minimum bpm", Icons.trending_down)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${Convertisseur.msIntoKmh(avgSpeed).toStringAsFixed(0)} km/h",
|
||||||
|
"Average speed",
|
||||||
|
Icons.bolt),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${Convertisseur.msIntoKmh(maxSpeed).toStringAsFixed(0)} km/h",
|
||||||
|
"Maximum speed",
|
||||||
|
Icons.trending_up),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${Convertisseur.msIntoKmh(minSpeed).toStringAsFixed(0)} km/h",
|
||||||
|
"Minimum speed",
|
||||||
|
Icons.trending_down)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities("$avgTemperature °C",
|
||||||
|
"Average Temperature", Icons.thermostat),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities("$maxTemperature °C",
|
||||||
|
"Maximum Temperature", Icons.trending_up),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities("$minTemperature °C",
|
||||||
|
"Minimum Temperature", Icons.trending_down)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${avgAltitude.toStringAsFixed(2)} m",
|
||||||
|
"Average altitude",
|
||||||
|
Icons.landscape),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities("$maxAltitude m",
|
||||||
|
"Maximum altitude", Icons.trending_up),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities("$minAltitude m",
|
||||||
|
"Minimum altitude", Icons.trending_down)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities("$getTotalDistance m",
|
||||||
|
"Total distance", Icons.double_arrow),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities("$totalSteps", "Total steps",
|
||||||
|
Icons.do_not_step_rounded),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ContainerStatsActivities(
|
||||||
|
"${Convertisseur.secondeIntoMinute(totalTime)} min",
|
||||||
|
"Total time",
|
||||||
|
Icons.timer),
|
||||||
|
SizedBox(
|
||||||
|
width: media.width * 0.03,
|
||||||
|
),
|
||||||
|
ContainerStatsActivities("$totalCalories kCal",
|
||||||
|
"Burned calories", Icons.local_fire_department),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: media.width * 0.03,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|