diff --git a/asset/icon/gomb_orange_c.png b/asset/icon/gomb_orange_c.png new file mode 100644 index 0000000..50ec00a Binary files /dev/null and b/asset/icon/gomb_orange_c.png differ diff --git a/asset/image/WT_Results_for_female.png b/asset/image/WT_Results_for_female.png new file mode 100644 index 0000000..afbbccb Binary files /dev/null and b/asset/image/WT_Results_for_female.png differ diff --git a/asset/image/WT_Results_for_men.png b/asset/image/WT_Results_for_men.png new file mode 100644 index 0000000..6ce93c8 Binary files /dev/null and b/asset/image/WT_Results_for_men.png differ diff --git a/asset/image/WT_Results_for_runners.png b/asset/image/WT_Results_for_runners.png new file mode 100644 index 0000000..a9be810 Binary files /dev/null and b/asset/image/WT_Results_for_runners.png differ diff --git a/asset/image/pict_calorie.png b/asset/image/pict_calorie.png new file mode 100644 index 0000000..db7d6ad Binary files /dev/null and b/asset/image/pict_calorie.png differ diff --git a/asset/image/pict_development_by_bodypart_percent.png b/asset/image/pict_development_by_bodypart_percent.png new file mode 100644 index 0000000..8221afb Binary files /dev/null and b/asset/image/pict_development_by_bodypart_percent.png differ diff --git a/asset/image/pict_distance_m.png b/asset/image/pict_distance_m.png new file mode 100644 index 0000000..53a5def Binary files /dev/null and b/asset/image/pict_distance_m.png differ diff --git a/asset/image/pict_fatburn_percent.png b/asset/image/pict_fatburn_percent.png new file mode 100644 index 0000000..c8fd2c0 Binary files /dev/null and b/asset/image/pict_fatburn_percent.png differ diff --git a/asset/image/pict_hravg_bpm.png b/asset/image/pict_hravg_bpm.png new file mode 100644 index 0000000..e557f63 Binary files /dev/null and b/asset/image/pict_hravg_bpm.png differ diff --git a/asset/image/pict_hrmax_bpm.png b/asset/image/pict_hrmax_bpm.png new file mode 100644 index 0000000..510614e Binary files /dev/null and b/asset/image/pict_hrmax_bpm.png differ diff --git a/asset/image/pict_hrmin_bpm.png b/asset/image/pict_hrmin_bpm.png new file mode 100644 index 0000000..53e42fc Binary files /dev/null and b/asset/image/pict_hrmin_bpm.png differ diff --git a/asset/image/pict_hypertrophy.png b/asset/image/pict_hypertrophy.png new file mode 100644 index 0000000..f112eb5 Binary files /dev/null and b/asset/image/pict_hypertrophy.png differ diff --git a/asset/image/pict_maxspeed_kmh.png b/asset/image/pict_maxspeed_kmh.png new file mode 100644 index 0000000..cb939cc Binary files /dev/null and b/asset/image/pict_maxspeed_kmh.png differ diff --git a/asset/image/pict_reps_volumen_db.png b/asset/image/pict_reps_volumen_db.png new file mode 100644 index 0000000..14f1841 Binary files /dev/null and b/asset/image/pict_reps_volumen_db.png differ diff --git a/asset/image/pict_steps.png b/asset/image/pict_steps.png new file mode 100644 index 0000000..fff74ae Binary files /dev/null and b/asset/image/pict_steps.png differ diff --git a/asset/image/pict_time_h.png b/asset/image/pict_time_h.png new file mode 100644 index 0000000..29ce2f3 Binary files /dev/null and b/asset/image/pict_time_h.png differ diff --git a/asset/image/pict_weight_volumen_tonna.png b/asset/image/pict_weight_volumen_tonna.png new file mode 100644 index 0000000..fd22d7f Binary files /dev/null and b/asset/image/pict_weight_volumen_tonna.png differ diff --git a/i18n/en.json b/i18n/en.json index 911f212..855f830 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -235,5 +235,20 @@ "Live-Server":"Live-Server", "Test-Server":"Test-Server", "All Exercises has been filtered out":"All Exercises has been filtered out", - "base":"base" + "base":"base", + "Hypertrophy":"Hypertrophy", + "Gain Strength":"Gain Strength", + "repeats":"repeats", + "minutes":"minutes", + "Rest time":"Rest time", + "Suggestions based on your test":"Suggestions based on your test", + "Repeats volume":"Repeats volume", + "Weight volume":"Weight volume", + "Calorie":"Calorie", + "Max BPM":"Max BPM", + "Min BPM":"Min BPM", + "Average BPM":"Average BPM", + "Fatburn %":"Fatburn %", + "Health Data Summary":"Health Data Summary" + } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index 82e41c8..e9a009b 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -236,5 +236,20 @@ "Live-Server":"Live-Server", "Test-Server":"Test-Server", "All Exercises has been filtered out":"Az összes gyakorlatot kiszűrted", - "base":"alap" + "base":"alap", + "Hypertrophy":"Izomnövelés", + "Gain Strength":"Erőnövelés", + "repeats":"ismétlés", + "Rest time":"Pihenőidő", + "minutes":"perc", + "Suggestions based on your test":"Javaslatok a teszted alapján", + "Repeats volume":"Össz. ismétlés", + "Weight volume":"Össztömeg", + "Calorie":"Kalória", + "Max BPM":"Max pulzus", + "Min BPM":"Min pulzus", + "Average BPM":"Átl pulzus", + "Fatburn %":"Zsírégetés %", + "Health Data Summary":"Egészségadatok összefoglalás" + } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3451587..b7d775b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - device_info (0.0.1): + - Flutter - devicelocale (0.0.1): - Flutter - Firebase/Auth (6.33.0): @@ -84,6 +86,8 @@ PODS: - GoogleUtilities/UserDefaults (6.7.2): - GoogleUtilities/Logger - GTMSessionFetcher/Core (1.5.0) + - health (1.0.1): + - Flutter - nanopb (1.30906.0): - nanopb/decode (= 1.30906.0) - nanopb/encode (= 1.30906.0) @@ -100,6 +104,7 @@ PODS: - FMDB (>= 2.7.5) DEPENDENCIES: + - device_info (from `.symlinks/plugins/device_info/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) @@ -107,6 +112,7 @@ DEPENDENCIES: - flurry (from `.symlinks/plugins/flurry/ios`) - Flutter (from `Flutter`) - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) + - health (from `.symlinks/plugins/health/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -130,6 +136,8 @@ SPEC REPOS: - Protobuf EXTERNAL SOURCES: + device_info: + :path: ".symlinks/plugins/device_info/ios" devicelocale: :path: ".symlinks/plugins/devicelocale/ios" firebase_auth: @@ -144,6 +152,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_keyboard_visibility: :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" + health: + :path: ".symlinks/plugins/health/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" shared_preferences: @@ -152,6 +162,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite/ios" SPEC CHECKSUMS: + device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 devicelocale: feebbe5e7a30adb8c4f83185de1b50ff19b44f00 Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 firebase_auth: cb33b01b51904969161403bbcb20036519f0c578 @@ -171,6 +182,7 @@ SPEC CHECKSUMS: GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 + health: 44840ad4328aa5586e77bef289898bfed644a81c nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 64cc574..6c4a57b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; BB69292B2521AF45001FBA4C /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB69292A2521AF45001FBA4C /* Launch Screen.storyboard */; }; BB81345024BB4BE10078D9A4 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB81344F24BB4BE10078D9A4 /* GoogleService-Info.plist */; }; + BBDBEBB825862170006762F6 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBDBEBB725862170006762F6 /* HealthKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -48,6 +49,7 @@ BB43773E2540715900D74BFA /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; BB69292A2521AF45001FBA4C /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; BB81344F24BB4BE10078D9A4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + BBDBEBB725862170006762F6 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; D5EDDC52125075FB9E21AD35 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; F39E6E227EB942E5663A6086 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -58,6 +60,7 @@ buildActionMask = 2147483647; files = ( 42B6B159AF35AFB6DE777DFB /* Pods_Runner.framework in Frameworks */, + BBDBEBB825862170006762F6 /* HealthKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -77,6 +80,7 @@ 3ADC50290ED054951FAC1F56 /* Frameworks */ = { isa = PBXGroup; children = ( + BBDBEBB725862170006762F6 /* HealthKit.framework */, 09BD889296C5C90D989820C8 /* Pods_Runner.framework */, ); name = Frameworks; @@ -362,7 +366,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 38; + CURRENT_PROJECT_VERSION = 39; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -505,7 +509,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 38; + CURRENT_PROJECT_VERSION = 39; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -540,7 +544,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 38; + CURRENT_PROJECT_VERSION = 39; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 80c67d1..04f6f47 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -46,6 +46,10 @@ 10.0 LSRequiresIPhoneOS + NSHealthShareUsageDescription + We will sync your data with the Apple Health app to give you better insights + NSHealthUpdateUsageDescription + We will sync your data with the Apple Health app to give you better insights UIBackgroundModes fetch diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 903def2..57cd459 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -4,5 +4,11 @@ aps-environment development + com.apple.developer.healthkit + + com.apple.developer.healthkit.access + + health-records + diff --git a/lib/bloc/exercise_control/exercise_control_bloc.dart b/lib/bloc/exercise_control/exercise_control_bloc.dart index 60adb37..e1d39ff 100644 --- a/lib/bloc/exercise_control/exercise_control_bloc.dart +++ b/lib/bloc/exercise_control/exercise_control_bloc.dart @@ -23,6 +23,8 @@ class ExerciseControlBloc extends Bloc { double mediaHeight = 0; bool isMan = true; + final StopWatchTimer stopWatchTimer = StopWatchTimer( + isLapHours: false, + ); + int timerValue; + @override ExerciseNewBloc({this.exerciseRepository, this.menuBloc, this.customerRepository, ExerciseType exerciseType}) : super(ExerciseNewInitial()) { @@ -52,14 +58,15 @@ class ExerciseNewBloc extends Bloc { exerciseRepository.setQuantity(quantity); exerciseRepository.setUnitQuantity(unitQuantity); exerciseRepository.exercise.exercisePlanDetailId = 0; + exerciseRepository.start = DateTime.now(); if (Cache().userLoggedIn != null) { customerRepository.customer = Cache().userLoggedIn; weight = customerRepository.customer.getProperty("Weight"); height = customerRepository.customer.getProperty("Height"); fitnessLevel = customerRepository.customer.fitnessLevel; this.isMan = (customerRepository.customer.sex == "m"); - print("Sex Man? " + isMan.toString() + " " + customerRepository.customer.sex); } + stopWatchTimer.rawTime.listen((value) => timerValue = value); } void setMediaDimensions(double width, double height) { @@ -75,7 +82,6 @@ class ExerciseNewBloc extends Bloc { } final double distortionWidth = mediaWidth / baseWidth; final double distortionHeight = mediaHeight / baseHeight; - print("distiortionW " + distortionWidth.toString() + " dH " + distortionHeight.toString()); if (isMan) { properties.forEach((element) { if (element.propertyName == "Shoulder") { @@ -146,12 +152,10 @@ class ExerciseNewBloc extends Bloc { element.top = (122 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); element.value = customerRepository.customer.getProperty("Shoulder"); - print("CHEST top: " + element.top.toString() + " left " + element.left.toString()); manSizes.add(element); } else if (element.propertyName == "Neck") { element.top = (78 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); - print("Neck top: " + element.top.toString() + " left " + element.left.toString()); element.value = customerRepository.customer.getProperty("Neck"); manSizes.add(element); } else if (element.propertyName == "Biceps") { @@ -279,6 +283,7 @@ class ExerciseNewBloc extends Bloc { yield ExerciseNewReady(); } else if (event is ExerciseNewSubmit) { yield ExerciseNewLoading(); + exerciseRepository.end = DateTime.now(); await exerciseRepository.addExercise(); menuBloc.add(MenuTreeDown(parent: 0)); Cache().initBadges(); @@ -373,4 +378,9 @@ class ExerciseNewBloc extends Bloc { return goalBMI; } + + @override + void dispose() async { + await stopWatchTimer.dispose(); + } } diff --git a/lib/bloc/menu/menu_bloc.dart b/lib/bloc/menu/menu_bloc.dart index 23062db..acd78d3 100644 --- a/lib/bloc/menu/menu_bloc.dart +++ b/lib/bloc/menu/menu_bloc.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/exercise_device_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; @@ -15,8 +16,6 @@ import 'package:meta/meta.dart'; part 'menu_event.dart'; part 'menu_state.dart'; -enum ExerciseAbility { oneRepMax, endurance, none } - class MenuBloc extends Bloc with Trans { final WorkoutTreeRepository menuTreeRepository; final ExerciseRepository exerciseRepository = ExerciseRepository(); @@ -106,8 +105,9 @@ class MenuBloc extends Bloc with Trans { yield MenuLoading(); parent = event.parent; workoutItem = event.item; - setAbility(workoutItem.nameEnglish); - //print("menuitem " + workoutItem.id.toString() + " parent "+workoutItem.parent.toString()); + if (workoutItem != null) { + setAbility(workoutItem.nameEnglish); + } menuTreeRepository.getBranch(event.parent); yield MenuReady(); } else if (event is MenuTreeUp) { @@ -117,7 +117,6 @@ class MenuBloc extends Bloc with Trans { workoutItem = menuTreeRepository.getParentItem(parent); if (workoutItem != null) { - //print("UP menuitem " + workoutItem.id.toString() + " parent " + workoutItem.parent.toString()); menuTreeRepository.getBranch(workoutItem.parent); setAbility(workoutItem.nameEnglish); } @@ -128,7 +127,6 @@ class MenuBloc extends Bloc with Trans { workoutItem = menuTreeRepository.getParentItem(parent); if (workoutItem != null) { - //print("JUMP menuitem " + workoutItem.id.toString() + " parent " + workoutItem.parent.toString()); menuTreeRepository.getBranch(workoutItem.parent); setAbility(workoutItem.nameEnglish); } diff --git a/lib/bloc/result/result_bloc.dart b/lib/bloc/result/result_bloc.dart new file mode 100644 index 0000000..bcfa78c --- /dev/null +++ b/lib/bloc/result/result_bloc.dart @@ -0,0 +1,225 @@ +import 'dart:async'; + +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/result.dart'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:aitrainer_app/repository/exercise_result_repository.dart'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; +import 'package:health/health.dart'; + +part 'result_event.dart'; +part 'result_state.dart'; + +class ResultBloc extends Bloc { + final ExerciseResultRepository resultRepository; + final ExerciseRepository exerciseRepository; + List _healthDataList = List(); + DateTime startTime; + DateTime endTime; + final HealthFactory health = HealthFactory(); + final List types = [ + HealthDataType.ACTIVE_ENERGY_BURNED, + HealthDataType.WATER, + HealthDataType.STEPS, + HealthDataType.HEART_RATE, + HealthDataType.BASAL_ENERGY_BURNED, + HealthDataType.BODY_TEMPERATURE, + HealthDataType.HIGH_HEART_RATE_EVENT, + HealthDataType.LOW_HEART_RATE_EVENT, + HealthDataType.RESTING_HEART_RATE + ]; + + ResultBloc({this.resultRepository, this.exerciseRepository}) : super(ResultInitial()) { + this.startTime = exerciseRepository.start; + this.endTime = exerciseRepository.end; + } + + @override + Stream mapEventToState( + ResultEvent event, + ) async* { + try { + if (event is ResultLoad) { + yield ResultLoading(); + + await _fetchHealthData(); + _matchExerciseData(); + yield ResultReady(); + } + } on Exception catch (ex) { + yield ResultError(error: ex.toString()); + } + } + + void _matchExerciseData() { + resultRepository.getResults().forEach((element) { + switch (element.item) { + case ResultItem.bpm_avg: + element.data = _gethHealthDataPointValueAvg(HealthDataType.HEART_RATE).toStringAsFixed(0); + break; + case ResultItem.bpm_min: + element.data = element.data = _gethHealthDataPointValueMin(HealthDataType.HEART_RATE).toStringAsFixed(0); + break; + case ResultItem.bpm_max: + element.data = element.data = _gethHealthDataPointValueMax(HealthDataType.HEART_RATE).toStringAsFixed(0); + break; + case ResultItem.calorie: + element.data = _gethHealthDataPointValueSum(HealthDataType.ACTIVE_ENERGY_BURNED).toStringAsFixed(0); + break; + case ResultItem.development_percent_bodypart: + // TODO: Handle this case. + break; + case ResultItem.distance: + if (exerciseRepository.exerciseType.unit == "meter") { + element.data = exerciseRepository.exercise.quantity.toStringAsFixed(0); + } + break; + case ResultItem.fatburn_percent: + DateTime today = DateTime.now(); + int age = today.year - Cache().userLoggedIn.birthYear; + double minBpm = (200 - age) * 0.6; + double maxBpm = (200 - age) * 0.7; + int counter = 0; + int burnCounter = 0; + _healthDataList.forEach((dataPoint) { + if (dataPoint.type == HealthDataType.ACTIVE_ENERGY_BURNED) { + if (dataPoint.value >= minBpm && dataPoint.value <= maxBpm) { + burnCounter++; + } + counter++; + } + }); + if (counter > 0) { + element.data = (burnCounter / counter * 100).toStringAsFixed(2); + } else { + element.data = "0"; + } + break; + case ResultItem.speed_max: + // TODO: Handle this case. + break; + case ResultItem.reps_volume: + if (exerciseRepository.exerciseType.unit == "repeat") { + double value = 0; + exerciseRepository.actualExerciseList.forEach((element) { + value += element.quantity; + }); + element.data = value.toStringAsFixed(0); + } + break; + case ResultItem.steps: + element.data = _gethHealthDataPointValueSum(HealthDataType.STEPS).toStringAsFixed(0); + break; + /* case ResultItem.time: + final Duration duration = this.endTime.difference(this.startTime); + element.data = _printDuration(duration); + break; */ + case ResultItem.weight_volume: + if (exerciseRepository.exerciseType.unitQuantityUnit == "kilogram") { + double value = 0; + exerciseRepository.actualExerciseList.forEach((element) { + value += element.quantity * element.unitQuantity; + }); + element.data = value.toStringAsFixed(0); + } + break; + } + }); + } + + String _printDuration(Duration duration) { + String twoDigits(int n) => n.toString().padLeft(2, "0"); + String twoDigitMinutes = twoDigits(duration.inMinutes); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + String twoDigitMilliSeconds = duration.inMilliseconds.remainder(1000).toString(); + return "$twoDigitMinutes:$twoDigitSeconds:$twoDigitMilliSeconds" + '"'; + } + + double _gethHealthDataPointValueAvg(HealthDataType dataType) { + double value = 0; + double counter = 0; + _healthDataList.forEach((dataPoint) { + if (dataPoint.type == dataType) { + value += dataPoint.value; + counter++; + } + }); + double avg = 0; + if (counter > 0) { + avg = value / counter; + } + return avg; + } + + double _gethHealthDataPointValueSum(HealthDataType dataType) { + double value = 0; + _healthDataList.forEach((dataPoint) { + if (dataPoint.type == dataType) { + value += dataPoint.value; + } + }); + return value; + } + + double _gethHealthDataPointValueMax(HealthDataType dataType) { + double max = 0; + _healthDataList.forEach((dataPoint) { + if (dataPoint.type == dataType) { + if (max < dataPoint.value) { + max = dataPoint.value; + } + } + }); + return max; + } + + double _gethHealthDataPointValueMin(HealthDataType dataType) { + double min = 9999999; + _healthDataList.forEach((dataPoint) { + if (dataPoint.type == dataType) { + if (min > dataPoint.value && dataPoint.value != 0) { + min = dataPoint.value; + } + } + }); + if (min == 9999999) { + min = 0; + } + return min; + } + + Future _fetchHealthData() async { + if (health == null) { + return; + } + try { + print("Get Health data between " + startTime.toString() + " AND " + endTime.toString()); + _healthDataList = await health.getHealthDataFromTypes(this.startTime, this.endTime, types); + _healthDataList.forEach((element) { + print(element.toString()); + }); + } on Exception catch (e) { + print("Caught exception in getHealthDataFromTypes: $e"); + throw Exception(e); + } + } + + double calculate1RM({double percent}) { + if (exerciseRepository.exercise == null) { + exerciseRepository.getLastExercise(); + } + double weight = exerciseRepository.exercise.unitQuantity; + double repeat = exerciseRepository.exercise.quantity; + if (weight == 0 || repeat == 0) { + return 0; + } + + double rmWendler = weight * repeat * 0.0333 + weight; + double rmOconner = weight * (1 + repeat / 40); + double average = (rmWendler + rmOconner) / 2; + + return average * percent; + } +} diff --git a/lib/bloc/result/result_event.dart b/lib/bloc/result/result_event.dart new file mode 100644 index 0000000..487ab70 --- /dev/null +++ b/lib/bloc/result/result_event.dart @@ -0,0 +1,12 @@ +part of 'result_bloc.dart'; + +abstract class ResultEvent extends Equatable { + const ResultEvent(); + + @override + List get props => []; +} + +class ResultLoad extends ResultEvent { + const ResultLoad(); +} diff --git a/lib/bloc/result/result_state.dart b/lib/bloc/result/result_state.dart new file mode 100644 index 0000000..d9fa5ae --- /dev/null +++ b/lib/bloc/result/result_state.dart @@ -0,0 +1,28 @@ +part of 'result_bloc.dart'; + +abstract class ResultState extends Equatable { + const ResultState(); + + @override + List get props => []; +} + +class ResultInitial extends ResultState { + const ResultInitial(); +} + +class ResultLoading extends ResultState { + const ResultLoading(); +} + +class ResultReady extends ResultState { + const ResultReady(); +} + +class ResultError extends ResultState { + final String error; + const ResultError({this.error}); + + @override + List get props => [this.error]; +} diff --git a/lib/bloc/settings/settings_bloc.dart b/lib/bloc/settings/settings_bloc.dart index 226d445..3bba465 100644 --- a/lib/bloc/settings/settings_bloc.dart +++ b/lib/bloc/settings/settings_bloc.dart @@ -2,9 +2,11 @@ import 'dart:async'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/cupertino.dart'; +import 'package:health/health.dart'; import 'package:meta/meta.dart'; part 'settings_event.dart'; @@ -31,18 +33,52 @@ class SettingsBloc extends Bloc { ) async* { if (event is SettingsChangeLanguage) { yield SettingsLoading(); - await _changeLang( event.language); + await _changeLang(event.language); yield SettingsReady(_locale); - } else if ( event is SettingsGetLanguage) { + } else if (event is SettingsGetLanguage) { await AppLanguage().fetchLocale(); _locale = AppLanguage().appLocal; yield SettingsReady(_locale); + } else if (event is SettingsSetServer) { + yield SettingsLoading(); + final bool live = event.live; + Cache().setServer(live); + yield SettingsReady(_locale); + } else if (event is SettingsSetHardware) { + yield SettingsLoading(); + + bool selectedHardwareBefore = await Cache().selectedHardwareBefore(); + print("selectedBefore " + selectedHardwareBefore.toString()); + if (!selectedHardwareBefore) { + await _accessHealthData(); + } + final bool hasHardware = event.hasHardware; + await Cache().setHardware(hasHardware); + yield SettingsReady(_locale); } } - Future _changeLang( String lang ) async{ + Future _accessHealthData() async { + final List types = [ + HealthDataType.ACTIVE_ENERGY_BURNED, + HealthDataType.WATER, + HealthDataType.STEPS, + HealthDataType.HEART_RATE, + HealthDataType.BASAL_ENERGY_BURNED, + HealthDataType.BODY_TEMPERATURE, + HealthDataType.HIGH_HEART_RATE_EVENT, + HealthDataType.LOW_HEART_RATE_EVENT, + HealthDataType.RESTING_HEART_RATE + ]; + final HealthFactory health = HealthFactory(); + DateTime now = DateTime.now(); + List _healthDataList = await health.getHealthDataFromTypes(now.subtract(Duration(minutes: 5)), now, types); + print(_healthDataList.toString()); + } + + Future _changeLang(String lang) async { print("_change to $lang"); - switch ( lang ) { + switch (lang) { case "English": case "en": case "Angol": @@ -59,8 +95,8 @@ class SettingsBloc extends Bloc { await loadLang(); } - Future loadLang() async{ - if ( _locale != null ) { + Future loadLang() async { + if (_locale != null) { print(" -- Loading lang $_locale"); if (context != null) { AppLocalizations.of(context).setLocale(_locale); diff --git a/lib/bloc/settings/settings_event.dart b/lib/bloc/settings/settings_event.dart index c0f741b..0605cbc 100644 --- a/lib/bloc/settings/settings_event.dart +++ b/lib/bloc/settings/settings_event.dart @@ -15,3 +15,19 @@ class SettingsChangeLanguage extends SettingsEvent { class SettingsGetLanguage extends SettingsEvent { const SettingsGetLanguage(); } + +class SettingsSetServer extends SettingsEvent { + final bool live; + const SettingsSetServer({this.live}); + + @override + List get props => [this.live]; +} + +class SettingsSetHardware extends SettingsEvent { + final bool hasHardware; + const SettingsSetHardware({this.hasHardware}); + + @override + List get props => [this.hasHardware]; +} diff --git a/lib/main.dart b/lib/main.dart index e5be538..16a3cc7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:aitrainer_app/view/customer_fitness_page.dart'; import 'package:aitrainer_app/view/customer_goal_page.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_welcome_page.dart'; +import 'package:aitrainer_app/view/evaluation.dart'; import 'package:aitrainer_app/view/exercise_control_page.dart'; import 'package:aitrainer_app/view/exercise_execute_page.dart'; import 'package:aitrainer_app/view/exercise_execute_plan_add_page.dart'; @@ -216,6 +217,7 @@ class WorkoutTestApp extends StatelessWidget { 'exerciseExecuteAddPage': (context) => ExerciseExecutePlanAddPage(), 'mydevelopmentMusclePage': (context) => MyDevelopmentMusclePage(), 'mydevelopmentBodyPage': (context) => MyDevelopmentBodyPage(), + 'evaluationPage': (context) => EvaluationPage(), }, initialRoute: 'home', title: 'WorkoutTest', diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 22e3a8d..4e5604c 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -55,6 +55,7 @@ class Cache { static final String isLoggedInKey = 'is_logged_in'; static final String langKey = 'lang'; static final String serverKey = 'live'; + static final String hardwareKey = 'hardware'; static String baseUrl = 'http://aitrainer.info:8888/api/'; static final String mediaUrl = 'https://aitrainer.info:4343/media/'; @@ -92,6 +93,7 @@ class Cache { String startPage; String testEnvironment; bool liveServer = true; + bool hasHardware = false; factory Cache() { return _singleton; @@ -114,7 +116,7 @@ class Cache { return this.authToken; } - void setServer(bool live) async { + Future setServer(bool live) async { if (this.testEnvironment == "1") { liveServer = false; live = false; @@ -123,10 +125,34 @@ class Cache { SharedPreferences sharedPreferences; sharedPreferences = await prefs; liveServer = live; - print("Set Live servier. live? " + live.toString() + " env? " + this.testEnvironment); sharedPreferences.setBool(Cache.serverKey, live); } + void getHardware(SharedPreferences prefs) { + final bool hasHardware = prefs.getBool(Cache.hardwareKey); + print("Has Hardware: " + hasHardware.toString()); + this.hasHardware = hasHardware; + if (hasHardware == null) { + this.hasHardware = false; + } + } + + Future selectedHardwareBefore() async { + Future prefs = SharedPreferences.getInstance(); + SharedPreferences sharedPreferences = await prefs; + + final bool selectedHardware = sharedPreferences.getBool(Cache.hardwareKey); + return selectedHardware == null; + } + + Future setHardware(bool hasHardware) async { + Future prefs = SharedPreferences.getInstance(); + SharedPreferences sharedPreferences; + sharedPreferences = await prefs; + sharedPreferences.setBool(Cache.hardwareKey, hasHardware); + this.hasHardware = hasHardware; + } + void setServerAddress(SharedPreferences prefs) { if (this.testEnvironment == "1") { baseUrl = 'http://aitrainer.app:8899/api/'; diff --git a/lib/model/exercise_ability.dart b/lib/model/exercise_ability.dart new file mode 100644 index 0000000..88113d9 --- /dev/null +++ b/lib/model/exercise_ability.dart @@ -0,0 +1,6 @@ +enum ExerciseAbility { oneRepMax, endurance, running, none } + +extension ExerciseAbilityExt on ExerciseAbility { + bool equalsTo(ExerciseAbility ability) => this.toString() == ability.toString(); + bool equalsStringTo(String ability) => this.toString() == ability; +} diff --git a/lib/model/exercise_result.dart b/lib/model/exercise_result.dart new file mode 100644 index 0000000..3a9d40f --- /dev/null +++ b/lib/model/exercise_result.dart @@ -0,0 +1,44 @@ +import 'package:aitrainer_app/model/result.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +class ExerciseResult { + int exerciseResultId; + int customerId; + int exerciseId; + int exercisePlanId; + String resultType; + double value; + DateTime dateFrom; + DateTime dateTo; + + ResultExt resultExtension; + + Map toJson() { + String formattedDateTo; + if (dateTo != null) { + formattedDateTo = DateFormat('yyyy-MM-dd HH:mm').format(dateTo); + } + + return { + "customerId": customerId, + "exerciseId": exerciseId, + "exercisePlanId": exercisePlanId, + "resultType": resultType, + "value": value, + "dateFrom": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateFrom), + "dateTo": formattedDateTo, + }; + } + + ExerciseResult.fromJson(Map json) { + this.exerciseResultId = json['exerciseResultId']; + this.exerciseId = json['exerciseId']; + this.exercisePlanId = json['exercisePlanId']; + this.customerId = json['customerId']; + this.resultType = json['resultType']; + this.value = json["value"]; + this.dateFrom = DateTime.parse(json['dateFrom']); + this.dateTo = DateTime.parse(json['dateTo']); + this.resultExtension = ResultExt(itemString: this.resultType); + } +} diff --git a/lib/model/exercise_tree.dart b/lib/model/exercise_tree.dart index 5523f14..b7b4c49 100644 --- a/lib/model/exercise_tree.dart +++ b/lib/model/exercise_tree.dart @@ -37,6 +37,7 @@ class ExerciseTree { if (parentId != -1) { newTree.parentId = parentId; } + newTree.active = this.active; return newTree; } diff --git a/lib/model/exercise_type.dart b/lib/model/exercise_type.dart index eacf50c..5af9122 100644 --- a/lib/model/exercise_type.dart +++ b/lib/model/exercise_type.dart @@ -1,3 +1,4 @@ +import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:flutter/services.dart'; class ExerciseType { @@ -17,8 +18,7 @@ class ExerciseType { List devices = List(); List parents = List(); - bool is1RM; - bool isEndurance; + ExerciseAbility ability; ExerciseType({this.name, this.description}); @@ -70,19 +70,19 @@ class ExerciseType { "parents": this.parents.toString() }; - void set1RM(bool is1RM) { - this.is1RM = is1RM; + void setAbility(ExerciseAbility ability) { + this.ability = ability; } - bool get1RM() { - return this.is1RM; + ExerciseAbility getAbility() { + return this.ability; } - void setEndurance(bool isEndurance) { - this.isEndurance = isEndurance; + bool isEndurance() { + return this.ability.equalsTo(ExerciseAbility.endurance); } - bool getEndurance() { - return this.isEndurance; + bool is1RM() { + return this.ability.equalsTo(ExerciseAbility.oneRepMax); } } diff --git a/lib/model/result.dart b/lib/model/result.dart new file mode 100644 index 0000000..fb4442c --- /dev/null +++ b/lib/model/result.dart @@ -0,0 +1,93 @@ +enum ResultItem { + calorie, + development_percent_bodypart, + distance, + fatburn_percent, + bpm_avg, + bpm_min, + bpm_max, + speed_max, + reps_volume, + steps, + //time, + weight_volume +} + +extension ResultItemExt on ResultItem { + static const ResultItemDesc = { + ResultItem.calorie: "Calorie", + ResultItem.development_percent_bodypart: "Development in %", + ResultItem.distance: "Distance", + ResultItem.bpm_avg: "Average BPM", + ResultItem.bpm_min: "Min BPM", + ResultItem.bpm_max: "Max BPM", + ResultItem.speed_max: "Max speed", + ResultItem.reps_volume: "Repeats volume", + ResultItem.steps: "Steps", + //ResultItem.time: "Time", + ResultItem.weight_volume: "Weight volume", + ResultItem.fatburn_percent: "Fatburn %", + }; + + static const ResultItemImg = { + ResultItem.calorie: "pict_calorie.png", + ResultItem.development_percent_bodypart: "pic_development_by_bodypart_percent.png", + ResultItem.distance: "pict_distance_m.png", + ResultItem.bpm_avg: "pict_hravg_bpm.png", + ResultItem.bpm_min: "pict_hrmin_bpm.png", + ResultItem.bpm_max: "pict_hrmax_bpm.png", + ResultItem.speed_max: "pict_maxspeed_kmh.png", + ResultItem.reps_volume: "pict_reps_volumen_db.png", + ResultItem.steps: "pict_steps.png", + //ResultItem.time: "pict_time_h.png", + ResultItem.weight_volume: "pict_weight_volumen_tonna.png", + ResultItem.fatburn_percent: "pict_fatburn_percent.png", + }; + + static const HardwareData = { + ResultItem.calorie: true, + ResultItem.development_percent_bodypart: false, + ResultItem.distance: true, + ResultItem.bpm_avg: true, + ResultItem.bpm_min: true, + ResultItem.bpm_max: true, + ResultItem.speed_max: true, + ResultItem.reps_volume: false, + ResultItem.steps: true, + //ResultItem.time: false, + ResultItem.weight_volume: false, + ResultItem.fatburn_percent: true, + }; + + bool equals(ResultItem item) => this.toString() == item.toString(); + bool equalsString(String item) => this.description == item; + + String get description => ResultItemDesc[this]; + String get image => ResultItemImg[this]; + bool get isHardware => HardwareData[this]; + + String displayString() => description; +} + +class ResultExt { + final String itemString; + ResultItem item; + String data = "0"; + + ResultExt({this.itemString}) { + ResultItem.values.forEach((element) { + if (element.equalsString(itemString)) { + item = element; + } + }); + } + + String getDescription() => item.description; + String getImage() => "asset/image/" + item.image; + bool isHardware() { + return item.isHardware; + } + + bool equals(ResultItem item) => this.item.equals(item); + bool equalsString(String item) => this.item.equalsString(item); +} diff --git a/lib/model/workout_menu_tree.dart b/lib/model/workout_menu_tree.dart index 0f38d94..3a0258e 100644 --- a/lib/model/workout_menu_tree.dart +++ b/lib/model/workout_menu_tree.dart @@ -2,6 +2,22 @@ import 'dart:ui'; import 'exercise_type.dart'; +enum WorkoutType { endurance, oneRepMax, cardio, staticExercise } + +extension WorkoutTypeExt on WorkoutType { + static const WorkoutTypeMenu = { + WorkoutType.endurance: "Endurance", + WorkoutType.cardio: "Cardio", + WorkoutType.oneRepMax: "One Rep Max", + WorkoutType.staticExercise: "Static" + }; + + bool equals(WorkoutType type) => this.toString() == type.toString(); + bool equalsString(String type) => this.toString() == type; + + String get menu => WorkoutTypeMenu[this]; +} + class WorkoutMenuTree { int id; int parent; @@ -16,13 +32,15 @@ class WorkoutMenuTree { bool is1RM; bool isEndurance; + bool isRunning; + List workoutTypes = List(); bool selected = false; bool executed = false; String exerciseDetail; String nameEnglish; WorkoutMenuTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child, this.exerciseTypeId, - this.exerciseType, this.base, this.is1RM, this.isEndurance, this.nameEnglish); + this.exerciseType, this.base, this.is1RM, this.isEndurance, this.isRunning, this.nameEnglish); Map toJson() { return { @@ -37,6 +55,7 @@ class WorkoutMenuTree { "base": base.toString(), "is1RM": is1RM.toString(), "isEndurance": isEndurance.toString(), + "isRunning": isRunning.toString(), }; } diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart index 63cf07c..ae45356 100644 --- a/lib/repository/exercise_repository.dart +++ b/lib/repository/exercise_repository.dart @@ -12,6 +12,7 @@ class ExerciseRepository { Customer customer; ExerciseType exerciseType; List exerciseList; + List actualExerciseList = List(); double rmWendler = 0; double rmMcglothlin = 0; @@ -20,28 +21,35 @@ class ExerciseRepository { double rmOconner = 0; double rmWathen = 0; + DateTime start; + DateTime end; + + ExerciseRepository() { + this.createNew(); + } + createNew() { this.exercise = Exercise(); exercise.dateAdd = DateTime.now(); } setQuantity(double quantity) { - if ( this.exercise == null ) { + if (this.exercise == null) { this.createNew(); } this.exercise.quantity = quantity; } setUnitQuantity(double unitQuantity) { - if ( this.exercise == null ) { + if (this.exercise == null) { this.createNew(); } this.exercise.unitQuantity = unitQuantity; } - setUnit( String unit) { - if ( this.exercise == null ) { + setUnit(String unit) { + if (this.exercise == null) { this.createNew(); } @@ -49,7 +57,7 @@ class ExerciseRepository { } setDatetimeExercise(DateTime datetimeExercise) { - if ( this.exercise == null ) { + if (this.exercise == null) { this.createNew(); } @@ -66,11 +74,16 @@ class ExerciseRepository { final Exercise modelExercise = this.exercise; modelExercise.customerId = this.customer.customerId; modelExercise.exerciseTypeId = this.exerciseType.exerciseTypeId; + if (exerciseType.unitQuantity != "1") { + modelExercise.unitQuantity = null; + } + this.actualExerciseList.add(modelExercise); Exercise savedExercise = await ExerciseApi().addExercise(modelExercise); - if ( customer.customerId == Cache().userLoggedIn.customerId) { - Cache().addExercise(savedExercise); - } else if ( Cache().getTrainee() != null && customer.customerId == Cache().getTrainee().customerId ) { - Cache().addExerciseTrainee(savedExercise); + + if (customer.customerId == Cache().userLoggedIn.customerId) { + Cache().addExercise(savedExercise); + } else if (Cache().getTrainee() != null && customer.customerId == Cache().getTrainee().customerId) { + Cache().addExerciseTrainee(savedExercise); } } @@ -78,21 +91,17 @@ class ExerciseRepository { await ExerciseApi().deleteExercise(exercise); } - setCustomer(Customer customer) => this.customer = customer; - setExerciseType( ExerciseType exerciseType) => this.exerciseType = exerciseType; + setExerciseType(ExerciseType exerciseType) => this.exerciseType = exerciseType; - - Future> getExercisesByCustomer( int customerId ) async { - final results = await ExerciseApi().getExercisesByCustomer(customerId); + Future> getExercisesByCustomer(int customerId) async { + final results = await ExerciseApi().getExercisesByCustomer(customerId); this.exerciseList = results; - if ( Cache().userLoggedIn != null ) { + if (Cache().userLoggedIn != null) { if (customerId == Cache().userLoggedIn.customerId) { Cache().setExercises(exerciseList); - } else if (Cache().getTrainee() != null && customerId == Cache() - .getTrainee() - .customerId) { + } else if (Cache().getTrainee() != null && customerId == Cache().getTrainee().customerId) { Cache().setExercisesTrainee(exerciseList); } } @@ -110,11 +119,11 @@ class ExerciseRepository { } String nextMissingBaseExercise(SplayTreeMap sortedTree) { - if ( exerciseList == null ) { + if (exerciseList == null) { exerciseList = Cache().getExercises(); } - if ( exerciseList == null ) { + if (exerciseList == null) { return ""; } String missingTreeName; @@ -127,12 +136,12 @@ class ExerciseRepository { treeName = treeName.substring(3); foundTreeName = null; listByMuscle.forEach((exercise) { - if ( missingTreeName == null ) { + if (missingTreeName == null) { missingTreeName = treeName; } - if ( exercise.base ) { - exerciseList.forEach((element) { - if ( element.exerciseTypeId == exercise.exerciseTypeId ) { + if (exercise.base) { + exerciseList.forEach((element) { + if (element.exerciseTypeId == exercise.exerciseTypeId) { foundTreeName = treeName; //print("Found " + foundTreeName + " Missing actual: " + missingTreeName); isBreak = true; @@ -140,7 +149,7 @@ class ExerciseRepository { }); } }); - if ( foundTreeName == null &&! isBreak ) { + if (foundTreeName == null && !isBreak) { missingTreeName = treeName; isBreak = true; } @@ -156,40 +165,38 @@ class ExerciseRepository { int count1RMExercises = 0; LinkedHashMap tree = Cache().getWorkoutMenuTree(); - if ( tree == null ) { + if (tree == null) { return; } tree.forEach((key, value) { WorkoutMenuTree treeItem = value; - if (treeItem.exerciseType != null && - treeItem.exerciseType.base == true && - !baseTreeItem.contains(treeItem.parent)) { + if (treeItem.exerciseType != null && treeItem.exerciseType.base == true && !baseTreeItem.contains(treeItem.parent)) { baseTreeItem.add(treeItem.parent); } }); - if ( exerciseList == null ) { + if (exerciseList == null) { exerciseList = Cache().getExercises(); } - if ( exerciseList == null ) { + if (exerciseList == null) { return; } exerciseList.forEach((element) { Exercise exercise = element; - if ( !checkedExerciseTypeId.contains(exercise.exerciseTypeId )) { + if (!checkedExerciseTypeId.contains(exercise.exerciseTypeId)) { checkedExerciseTypeId.add(exercise.exerciseTypeId); tree.forEach((key, value) { WorkoutMenuTree treeItem = value; - if (treeItem.exerciseType != null - && treeItem.exerciseType.base == true - && exercise.exerciseTypeId == treeItem.exerciseType.exerciseTypeId - && !checkedBaseTreeItem.contains(treeItem.parent)) { - //print ("id: " + exercise.exerciseTypeId.toString()); - checkedBaseTreeItem.add(treeItem.parent); - count1RMExercises++; + if (treeItem.exerciseType != null && + treeItem.exerciseType.base == true && + exercise.exerciseTypeId == treeItem.exerciseType.exerciseTypeId && + !checkedBaseTreeItem.contains(treeItem.parent)) { + //print ("id: " + exercise.exerciseTypeId.toString()); + checkedBaseTreeItem.add(treeItem.parent); + count1RMExercises++; } }); } @@ -207,7 +214,7 @@ class ExerciseRepository { Exercise lastExercise = exercises[0]; exercises.forEach((element) { Exercise actualExercise = element; - if ( actualExercise.dateAdd.compareTo(lastExercise.dateAdd) > 0 ) { + if (actualExercise.dateAdd.compareTo(lastExercise.dateAdd) > 0) { lastExercise = actualExercise; } }); @@ -221,16 +228,15 @@ class ExerciseRepository { ExerciseType actualExerciseType; Cache().getExerciseTypes().forEach((element) { ExerciseType exerciseType = element; - if ( exerciseType.exerciseTypeId == exerciseTypeId) { + if (exerciseType.exerciseTypeId == exerciseTypeId) { actualExerciseType = exerciseType; } }); - if ( actualExerciseType == null ) { - throw Exception("Data error, no ExerciseType for exerciseTypeId $exerciseTypeId" ); + if (actualExerciseType == null) { + throw Exception("Data error, no ExerciseType for exerciseTypeId $exerciseTypeId"); } return actualExerciseType; } - void sortByDate() => exerciseList.sort( (a, b) => b.dateAdd.compareTo(a.dateAdd) ); - -} \ No newline at end of file + void sortByDate() => exerciseList.sort((a, b) => b.dateAdd.compareTo(a.dateAdd)); +} diff --git a/lib/repository/exercise_result_repository.dart b/lib/repository/exercise_result_repository.dart new file mode 100644 index 0000000..79acf37 --- /dev/null +++ b/lib/repository/exercise_result_repository.dart @@ -0,0 +1,40 @@ +import 'package:aitrainer_app/model/result.dart'; + +enum ResultType { running, man, woman, none } + +extension ResultTypeExt on ResultType { + bool equals(ResultType type) => this.toString() == type.toString(); +} + +class ExerciseResultRepository { + final List _results = List(); + ResultType resultType; + + ExerciseResultRepository({this.resultType}) { + if (resultType == null) { + resultType = ResultType.man; + } + if (resultType.equals(ResultType.man) || resultType.equals(ResultType.woman)) { + //_results.add(ResultExt(itemString: ResultItem.time.description)); + _results.add(ResultExt(itemString: ResultItem.reps_volume.description)); + _results.add(ResultExt(itemString: ResultItem.weight_volume.description)); + _results.add(ResultExt(itemString: ResultItem.bpm_max.description)); + _results.add(ResultExt(itemString: ResultItem.calorie.description)); + _results.add(ResultExt(itemString: ResultItem.bpm_avg.description)); + _results.add(ResultExt(itemString: ResultItem.fatburn_percent.description)); + _results.add(ResultExt(itemString: ResultItem.bpm_min.description)); + } else { + //_results.add(ResultExt(itemString: ResultItem.time.description)); + _results.add(ResultExt(itemString: ResultItem.distance.description)); + _results.add(ResultExt(itemString: ResultItem.bpm_max.description)); + _results.add(ResultExt(itemString: ResultItem.calorie.description)); + _results.add(ResultExt(itemString: ResultItem.bpm_avg.description)); + _results.add(ResultExt(itemString: ResultItem.fatburn_percent.description)); + _results.add(ResultExt(itemString: ResultItem.bpm_min.description)); + _results.add(ResultExt(itemString: ResultItem.steps.description)); + _results.add(ResultExt(itemString: ResultItem.speed_max.description)); + } + } + + List getResults() => this._results; +} diff --git a/lib/repository/workout_tree_repository.dart b/lib/repository/workout_tree_repository.dart index 9ec8b97..c4da015 100644 --- a/lib/repository/workout_tree_repository.dart +++ b/lib/repository/workout_tree_repository.dart @@ -1,6 +1,7 @@ import 'dart:collection'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:aitrainer_app/model/exercise_tree.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; @@ -32,6 +33,7 @@ class WorkoutTreeRepository { final LinkedHashMap tree = LinkedHashMap(); SplayTreeMap sortedTree = SplayTreeMap>(); bool isEnglish; + WorkoutType workoutType; final Map _antagonist = { Antagonist.chest: Antagonist.chestNr, @@ -59,60 +61,71 @@ class WorkoutTreeRepository { } exerciseTree.forEach((treeItem) async { - //print(" -- TreeItem " + treeItem.toJson().toString()); + //print(" -- TreeItem " + treeItem.toJson().toString() + " active " + treeItem.active.toString()); + if (treeItem.active == true) { + String treeName = isEnglish ? treeItem.name : treeItem.nameTranslation; + String assetImage = 'asset/menu/' + treeItem.imageUrl.substring(7); - String treeName = isEnglish ? treeItem.name : treeItem.nameTranslation; - String assetImage = 'asset/menu/' + treeItem.imageUrl.substring(7); + bool is1RM = treeItem.name == 'One Rep Max' ? true : false; + if (is1RM == false && treeItem.parentId != 0) { + is1RM = isParent1RM(treeItem.parentId); + } - bool is1RM = treeItem.name == 'One Rep Max' ? true : false; - if (is1RM == false && treeItem.parentId != 0) { - is1RM = isParent1RM(treeItem.parentId); + bool isEndurance = treeItem.name == 'Endurance' ? true : false; + if (isEndurance == false && treeItem.parentId != 0) { + isEndurance = isParentEndurance(treeItem.parentId); + } + + bool isRunning = treeItem.name == "Cardio" ? true : false; + if (isRunning == false && treeItem.parentId != 0) { + isRunning = isParentRunning(treeItem.parentId); + } + + WorkoutMenuTree menuItem = WorkoutMenuTree( + treeItem.treeId, + treeItem.parentId, + treeName, + assetImage, + Colors.white, + 30, + false, + 0, + null, + false, + is1RM, + isEndurance, + isRunning, + treeItem.name, + ); + menuItem = this.setWorkoutTypes(menuItem, treeItem); + this.tree[treeItem.name + "_" + treeItem.parentId.toString()] = menuItem; + //print("WorkoutMenuTree item " + menuItem.toJson().toString()); } - - bool isEndurance = treeItem.name == 'Endurance' ? true : false; - if (isEndurance == false && treeItem.parentId != 0) { - isEndurance = isParentEndurance(treeItem.parentId); - } - - final WorkoutMenuTree menuItem = WorkoutMenuTree( - treeItem.treeId, - treeItem.parentId, - treeName, - assetImage, - Colors.white, - 30, - false, - 0, - null, - false, - is1RM, - isEndurance, - treeItem.name, - ); - this.tree[treeItem.name + "_" + treeItem.parentId.toString()] = menuItem; - //print("WorkoutMenuTree item " + menuItem.toJson().toString()); }); exerciseTypes.forEach((exerciseType) { - if (!(exerciseType.imageUrl.isEmpty || exerciseType.name.isEmpty || exerciseType.nameTranslation.isEmpty)) { + if (!(exerciseType.imageUrl.isEmpty || exerciseType.name.isEmpty || exerciseType.nameTranslation.isEmpty) && + exerciseType.active == true) { String exerciseTypeName = isEnglish ? exerciseType.name : exerciseType.nameTranslation; String assetImage = 'asset/menu/' + exerciseType.imageUrl.substring(7); if (exerciseType.parents.isNotEmpty) { exerciseType.parents.forEach((parentId) { bool is1RM = this.isParent1RM(parentId); bool isEndurance = this.isParentEndurance(parentId); - exerciseType.is1RM = is1RM; - exerciseType.isEndurance = isEndurance; + if (is1RM) exerciseType.setAbility(ExerciseAbility.oneRepMax); + if (isEndurance) exerciseType.setAbility(ExerciseAbility.endurance); + bool isRunning = this.isParentRunning(parentId); + if (isRunning) exerciseType.setAbility(ExerciseAbility.running); WorkoutMenuTree menuItem = WorkoutMenuTree(exerciseType.exerciseTypeId, parentId, exerciseTypeName, assetImage, Colors.white, - 24, true, exerciseType.exerciseTypeId, exerciseType, exerciseType.base, is1RM, isEndurance, exerciseType.name); + 24, true, exerciseType.exerciseTypeId, exerciseType, exerciseType.base, is1RM, isEndurance, isRunning, exerciseType.name); this.tree[exerciseType.name] = menuItem; - //print("WorkoutMenuTree item " + menuItem.toJson().toString()); - /*print("ExerciseType in Menu item " + + print("WorkoutMenuTree item " + menuItem.toJson().toString()); + print("ExerciseType in Menu item " + exerciseType.toJson().toString() + " is1RM: " + is1RM.toString() + " isEndurance: " + - isEndurance.toString());*/ + isEndurance.toString()); }); } else { //print("No Parents " + exerciseType.toJson().toString()); @@ -127,6 +140,39 @@ class WorkoutTreeRepository { exerciseRepository.getBaseExerciseFinishedPercent(); } + WorkoutMenuTree setWorkoutTypes(WorkoutMenuTree menu, ExerciseTree treeItem) { + WorkoutType.values.forEach((workoutType) { + if (workoutType.equalsString(treeItem.name) || existWorkoutTypeInTree(workoutType)) { + menu.workoutTypes.add(workoutType); + } + }); + return menu; + } + + bool existWorkoutTypeInTree(WorkoutType workoutType) { + bool exists = false; + this.tree.forEach((key, treeItem) { + if (workoutType.equalsString(treeItem.name)) { + exists = true; + } + }); + return exists; + } + + bool isParentRunning(int treeId) { + bool isTreeItemRunning = false; + + this.tree.forEach((key, value) { + WorkoutMenuTree treeItem = value as WorkoutMenuTree; + if (treeItem.id == treeId) { + isTreeItemRunning = isTreeItemRunning || treeItem.isRunning; + //print (treeItem.name + " 1RM " + treeItem.is1RM.toString() ); + } + }); + + return isTreeItemRunning; + } + bool isParent1RM(int treeId) { bool isTreeItem1RM = false; diff --git a/lib/service/exercise_tree_service.dart b/lib/service/exercise_tree_service.dart index faf6d90..fbf9989 100644 --- a/lib/service/exercise_tree_service.dart +++ b/lib/service/exercise_tree_service.dart @@ -35,7 +35,7 @@ class ExerciseTreeApi { if (index > 0) { ExerciseTree newElement = element.copy(parent.exerciseTreeParentId); exerciseTree.add(newElement); - print("ExerciseTree " + newElement.toJson().toString()); + //print("ExerciseTree " + newElement.toJson().toString()); } else { element.parentId = parent.exerciseTreeParentId; exerciseTree[treeIndex].parentId = parent.exerciseTreeParentId; diff --git a/lib/util/session.dart b/lib/util/session.dart index daf019a..b3130ce 100644 --- a/lib/util/session.dart +++ b/lib/util/session.dart @@ -31,6 +31,7 @@ class Session { await AppLocalizations.delegate.load(AppLanguage().appLocal); print(" -- Session: fetch token.."); Cache().setServerAddress(_sharedPreferences); + Cache().getHardware(_sharedPreferences); await _fetchToken(_sharedPreferences); print(" -- FireBase init.."); await FirebaseApi().initializeFlutterFire(); diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart index a875854..0c11c30 100644 --- a/lib/view/customer_modify_page.dart +++ b/lib/view/customer_modify_page.dart @@ -3,7 +3,6 @@ import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; import 'package:aitrainer_app/library/numberpicker.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar_min.dart'; -import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; diff --git a/lib/view/evaluation.dart b/lib/view/evaluation.dart new file mode 100644 index 0000000..6ee9922 --- /dev/null +++ b/lib/view/evaluation.dart @@ -0,0 +1,503 @@ +import 'dart:collection'; +import 'dart:ui'; + +import 'package:aitrainer_app/bloc/result/result_bloc.dart'; +import 'package:aitrainer_app/localization/app_language.dart'; +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/exercise_ability.dart'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:aitrainer_app/repository/exercise_result_repository.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar_min.dart'; +import 'package:aitrainer_app/widgets/bottom_nav.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:aitrainer_app/model/result.dart'; + +// ignore: must_be_immutable +class EvaluationPage extends StatelessWidget with Trans { + @override + Widget build(BuildContext context) { + LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; + ExerciseRepository exerciseRepository; + // ignore: close_sinks + if (arguments != null) { + exerciseRepository = arguments['exerciseRepository']; + } else { + exerciseRepository = ExerciseRepository(); + } + ResultType resultType = ResultType.none; + String imageUrl = ""; + if (Cache().userLoggedIn.sex == "m") { + resultType = ResultType.man; + imageUrl = 'asset/image/WT_Results_for_men.png'; + } else { + resultType = ResultType.man; + imageUrl = 'asset/image/WT_Results_for_female.png'; + } + if (exerciseRepository.exerciseType.getAbility().equalsTo(ExerciseAbility.running)) { + resultType = ResultType.running; + imageUrl = 'asset/image/WT_Results_for_runners.png'; + } + print("ResultType: " + resultType.toString()); + setContext(context); + return Scaffold( + appBar: AppBarMin( + back: true, + ), + body: Container( + height: double.infinity, + width: double.infinity, + alignment: Alignment.center, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(imageUrl), + fit: BoxFit.cover, + alignment: Alignment.topCenter, + ), + ), + child: BlocProvider( + create: (context) => + ResultBloc(resultRepository: ExerciseResultRepository(resultType: resultType), exerciseRepository: exerciseRepository) + ..add(ResultLoad()), + child: BlocConsumer(listener: (context, state) { + if (state is ResultError) { + Scaffold.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.error, style: TextStyle(color: Colors.white)))); + } else if (state is ResultLoading) { + Scaffold.of(context).showSnackBar(SnackBar( + duration: Duration(milliseconds: 100), + backgroundColor: Colors.transparent, + content: Container(child: Center(child: CircularProgressIndicator())))); + } + }, builder: (context, state) { + final resultBloc = BlocProvider.of(context); + return getEvaluationWidgets(resultBloc); + }))), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 0)); + } + + Widget getEvaluationWidgets(ResultBloc resultBloc) { + String exerciseName = AppLanguage().appLocal == Locale("en") + ? resultBloc.exerciseRepository.exerciseType.name + : resultBloc.exerciseRepository.exerciseType.nameTranslation; + return Container( + padding: EdgeInsets.only(left: 10, right: 10), + child: CustomScrollView(scrollDirection: Axis.vertical, slivers: [ + SliverAppBar( + pinned: true, + backgroundColor: Colors.transparent, + expandedHeight: 50.0, + automaticallyImplyLeading: false, + flexibleSpace: FlexibleSpaceBar( + title: Text(exerciseName, + textAlign: TextAlign.center, + maxLines: 3, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 24, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ), + ), + SliverList( + delegate: SliverChildListDelegate([ + Text(DateFormat('y-M-d HH:mm', AppLanguage().appLocal.toString()).format(resultBloc.exerciseRepository.start), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 20, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + Divider(color: Colors.transparent), + Divider(color: Colors.transparent), + Text(t("Summary of your test"), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 20, + color: Colors.yellow[300], + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ]), + ), + getResultSummary(resultBloc), + getSuggestionTitle(resultBloc), + getSuggestion(resultBloc), + emptySliver(), + getResultTitle(resultBloc), + getResults(resultBloc), + ])); + } + + Widget getSuggestionTitle(ResultBloc resultBloc) { + if (resultBloc.exerciseRepository.exerciseType.unitQuantityUnit != null) { + return SliverList( + delegate: SliverChildListDelegate( + [ + Divider(color: Colors.transparent), + Divider(color: Colors.transparent), + Text(t("Suggestions based on your test"), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 20, + color: Colors.yellow[300], + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ], + ), + ); + } else + return emptySliver(); + } + + Widget getResultTitle(ResultBloc resultBloc) { + return SliverList( + delegate: SliverChildListDelegate( + [ + Divider(color: Colors.transparent), + Divider(color: Colors.transparent), + Text(t("Health Data Summary"), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 20, + color: Colors.yellow[300], + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + Divider(), + ], + ), + ); + } + + Widget getSuggestion(ResultBloc resultBloc) { + if (resultBloc.exerciseRepository.exerciseType.unitQuantityUnit != null) { + return SliverList( + delegate: SliverChildListDelegate( + [ + getSuggestionWidget(resultBloc, "Hypertrophy", "asset/image/pict_hypertrophy.png", "3x10-12", 0.9, "2"), + Divider(color: Colors.transparent), + getSuggestionWidget(resultBloc, "Gain Strength", "asset/image/pict_weight_volumen_tonna.png", "3x10-12", 0.75, "3-5"), + Divider(color: Colors.transparent), + getSuggestionWidget(resultBloc, "Endurance", "asset/image/pict_reps_volumen_db.png", "4x25-35", 0.50, "3"), + ], + ), + ); + } else + return emptySliver(); + } + + Widget emptySliver({int count = 1}) { + return SliverList( + delegate: SliverChildListDelegate( + [ + Container(), + Divider(color: Colors.transparent), + ], + ), + ); + } + + Widget getSuggestionWidget(ResultBloc resultBloc, String title, String picture, String repeats, double percent, String restTime) { + String unitQuantityUnit = resultBloc.exerciseRepository.exerciseType.unitQuantityUnit == null + ? "" + : resultBloc.exerciseRepository.exerciseType.unitQuantityUnit; + return Column( + children: [ + Text(t(title), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 30, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + Row(children: [ + Image.asset( + picture, + height: 80, + ), + SizedBox( + width: 10, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text(repeats + " " + t("repeats"), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 18, + color: Colors.orange, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )) + ], + ), + Row( + children: [ + Text(t("Weight") + ": " + resultBloc.calculate1RM(percent: percent).toStringAsFixed(0) + " " + unitQuantityUnit, + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 18, + color: Colors.orange, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ], + ), + Row( + children: [ + Text(t("Rest time") + ": " + restTime + " " + t("minutes"), + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.archivoBlack( + fontSize: 18, + color: Colors.orange, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ], + ), + ], + ) + ]), + ], + ); + } + + Widget getSummary(ResultBloc bloc) { + int index = 0; + List resultList = List(); + bloc.exerciseRepository.actualExerciseList.forEach((exercise) { + final String unit = t(bloc.exerciseRepository.exerciseType.unit); + String exerciseElement = ""; + final String exerciseRepeats = exercise.quantity.toStringAsFixed(0); + final String exerciseUnitQuantity = exercise.unitQuantity != null ? "x" + exercise.unitQuantity.toStringAsFixed(0) : ""; + if (index == 0) { + exerciseElement = t("Test") + ": "; + } else if (index == 1) { + exerciseElement = t("1st Control") + ": "; + } else if (index == 2) { + exerciseElement = t("2nd Control") + ": "; + } else if (index == 3) { + exerciseElement = t("3rd Control") + ": "; + } + index++; + resultList.add( + Text(exerciseElement + exerciseRepeats + exerciseUnitQuantity + " " + unit, + textAlign: TextAlign.center, + maxLines: 2, + softWrap: true, + style: GoogleFonts.inter( + fontSize: 18, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ); + }); + return Column(children: resultList); + } + + Widget getResultSummary(ResultBloc resultBloc) { + return SliverList( + delegate: SliverChildListDelegate( + [Divider(color: Colors.transparent), getSummary(resultBloc)], + ), + ); + } + + Widget getResults(ResultBloc resultBloc) { + return SliverGrid( + delegate: SliverChildListDelegate( + getResultData(resultBloc), + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 5.0, + crossAxisSpacing: 5.0, + childAspectRatio: 1.1, + ), + ); + } + + List getResultData(ResultBloc resultBloc) { + List data = List(); + + resultBloc.resultRepository.getResults().forEach((element) { + data.add(getResultWidget(element)); + }); + return data; + } + + Widget getResultWidget(ResultExt element) { + bool hasHardware = Cache().hasHardware; + bool blur = (!hasHardware && element.isHardware()); + /* print("Blur: " + + element.getDescription() + + ": " + + blur.toString() + + " hasHardware:" + + hasHardware.toString() + + " isHw: " + + element.isHardware().toString()); */ + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + blur + ? Stack(children: [ + Image.asset( + element.getImage(), + height: 80, + //color: Colors.white12, + ), + Image.asset( + element.getImage(), + height: 80, + color: Colors.black54, + ), + ]) + : Image.asset( + element.getImage(), + height: 80, + ), + SizedBox( + width: 10, + ), + Text( + element.data, + style: GoogleFonts.archivoBlack(fontSize: 28, color: blur ? Colors.white30 : Colors.white), + ), + Text( + t(element.getDescription()), + style: GoogleFonts.archivoBlack(fontSize: 14, color: blur ? Colors.white30 : Colors.white), + textAlign: TextAlign.left, + ), + ], + ); + } +} diff --git a/lib/view/exercise_control_page.dart b/lib/view/exercise_control_page.dart index e786ace..2e32dfb 100644 --- a/lib/view/exercise_control_page.dart +++ b/lib/view/exercise_control_page.dart @@ -2,16 +2,17 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/exercise_control/exercise_control_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; +import 'package:aitrainer_app/widgets/number_picker.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; -import 'package:aitrainer_app/library/numberpicker.dart'; import 'package:google_fonts/google_fonts.dart'; class ExerciseControlPage extends StatefulWidget { @@ -32,16 +33,16 @@ class _ExerciseControlPage extends State with Trans { ..add(ExerciseControlLoad()), child: BlocConsumer(listener: (context, state) { if (state is ExerciseControlError) { - //LoadingDialog.hide(context); Scaffold.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } else if (state is ExerciseControlLoading) { - //LoadingDialog.show(context); + return LoadingDialog(); } }, builder: (context, state) { final exerciseBloc = BlocProvider.of(context); - if (state is ExerciseControlReady) { - //LoadingDialog.hide(context); + if (state is ExerciseControlLoading) { + return LoadingDialog(); + } else if (state is ExerciseControlReady) { return getControlForm(exerciseBloc); } else { return getControlForm(exerciseBloc); @@ -53,49 +54,80 @@ class _ExerciseControlPage extends State with Trans { String exerciseName = AppLanguage().appLocal == Locale("en") ? exerciseBloc.exerciseRepository.exerciseType.name : exerciseBloc.exerciseRepository.exerciseType.nameTranslation; - return Form( child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBarNav(depth: 1), body: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, + height: double.infinity, + width: double.infinity, + alignment: Alignment.center, decoration: BoxDecoration( image: DecorationImage( - image: AssetImage('asset/image/WT_black_background.png'), - fit: BoxFit.fill, - alignment: Alignment.center, + image: Cache().userLoggedIn.sex == "m" + ? AssetImage("asset/image/WT_Results_for_men.png") + : AssetImage("asset/image/WT_Results_for_female.png"), + fit: BoxFit.cover, + alignment: Alignment.topCenter, ), ), child: Container( - padding: const EdgeInsets.only(top: 25, left: 25, right: 25), + padding: const EdgeInsets.only(top: 10, left: 25, right: 25), child: SingleChildScrollView( scrollDirection: Axis.vertical, + controller: ScrollController( + initialScrollOffset: exerciseBloc.scrollOffset, + ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( exerciseName, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.orange), + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + fontSize: 20, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), overflow: TextOverflow.fade, - maxLines: 1, + textAlign: TextAlign.center, + maxLines: 2, softWrap: true, ), - FlatButton( + Divider( + color: Colors.transparent, + ), + Divider( + color: Colors.transparent, + ), + Divider( + color: Colors.transparent, + ), + /* FlatButton( child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon( Icons.info, - color: Colors.yellow[300], + color: Colors.yellow[50], ), Flexible( child: Text(t("Why do you need Exercise Control?"), - style: TextStyle(color: Colors.yellow[300], fontWeight: FontWeight.normal, fontSize: 14)), + style: TextStyle(color: Colors.yellow[50], fontWeight: FontWeight.normal, fontSize: 14)), ), Icon( Icons.arrow_forward_ios, - color: Colors.yellow[300], + color: Colors.yellow[50], ), ]), textColor: Colors.blueAccent, @@ -103,20 +135,21 @@ class _ExerciseControlPage extends State with Trans { onPressed: () => { //Navigator.of(context).pushNamed('exerciseTypeDescription', arguments: exerciseBloc.exerciseRepository), }, - ), + ), */ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text(t("Your 1RM:"), style: GoogleFonts.inter( color: Colors.yellow[300], + fontSize: 18, )), Text( " " + exerciseBloc.initialRM.toStringAsFixed(0) + " " + exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit, - style: GoogleFonts.inter(color: Colors.yellow[300], fontWeight: FontWeight.bold), + style: GoogleFonts.inter(color: Colors.yellow[300], fontSize: 18, fontWeight: FontWeight.bold), ), ], ), @@ -134,7 +167,7 @@ class _ExerciseControlPage extends State with Trans { } Widget numberPickForm(ExerciseControlBloc exerciseBloc, int step) { - String strTimes = step == 2 ? exerciseBloc.origQuantity.toString() : "max."; + String strTimes = step == 2 ? exerciseBloc.origQuantity.toStringAsFixed(0) : "max."; String textInstruction = ""; textInstruction = t("Please repeat with ") + exerciseBloc.unitQuantity.toStringAsFixed(0) + @@ -149,44 +182,46 @@ class _ExerciseControlPage extends State with Trans { ); String title = step.toString() + ". " + t("Control Exercise:"); + LinkedHashMap args = LinkedHashMap(); List listWidgets = [ Text( title, - style: GoogleFonts.inter(color: Colors.yellow[300], fontWeight: FontWeight.bold), + style: GoogleFonts.inter(color: Colors.yellow[300], fontSize: 18, fontWeight: FontWeight.bold), ), Text( textInstruction, - style: GoogleFonts.inter(color: Colors.yellow[300], fontSize: 12), + style: GoogleFonts.inter(color: Colors.yellow[300], fontSize: 16), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - NumberPicker.horizontal( - highlightSelectedValue: step == exerciseBloc.step, - initialValue: exerciseBloc.quantity.toInt(), - minValue: 0, - maxValue: 200, - step: 1, - onChanged: (value) => {exerciseBloc.add(ExerciseControlQuantityChange(quantity: value.toDouble(), step: step))}, - listViewHeight: 80, - textStyleHighlighted: GoogleFonts.archivoBlack(color: Colors.orange[300], fontSize: 24), - //decoration: _decoration, - ), + NumberPickerWidget( + minValue: 0, + maxValue: 200, + initalValue: exerciseBloc.quantity.toInt(), + unit: t("reps"), + color: Colors.yellow[50], + onChange: (value) => {exerciseBloc.add(ExerciseControlQuantityChange(quantity: value.toDouble(), step: step))}), FlatButton( padding: EdgeInsets.all(0), textColor: Colors.white, - //color: step == exerciseBloc.step ? Colors.orange : Colors.black26, focusColor: Colors.blueAccent, onPressed: () => { exerciseBloc.add(ExerciseControlSubmit(step: step)), - if (step == 3) {confirmationDialog(exerciseBloc)} + if (step == 3) + { + //confirmationDialog(exerciseBloc) + Navigator.of(context).pop(), + args['exerciseRepository'] = exerciseBloc.exerciseRepository, + Navigator.of(context).pushNamed('evaluationPage', arguments: args) + } }, child: step == exerciseBloc.step ? Stack( alignment: Alignment.center, children: [ - Image.asset('asset/icon/gomb_orange_a.png', width: 140, height: 60), + Image.asset('asset/icon/gomb_orange_c.png', width: 140, height: 60), Text( t("Save"), style: TextStyle(fontSize: 16, color: Colors.white), @@ -206,6 +241,7 @@ class _ExerciseControlPage extends State with Trans { void confirmationDialog(ExerciseControlBloc bloc) { String unit = t(bloc.exerciseRepository.exerciseType.unit); + LinkedHashMap args = LinkedHashMap(); showCupertinoDialog( useRootNavigator: true, @@ -235,7 +271,11 @@ class _ExerciseControlPage extends State with Trans { actions: [ FlatButton( child: Text(t("OK")), - onPressed: () => {Navigator.of(context).pop(), Navigator.of(context).pop()}, + onPressed: () => { + Navigator.of(context).pop(), + args['exerciseRepository'] = bloc.exerciseRepository, + Navigator.of(context).pushNamed('evaluationPage', arguments: args) + }, ) ], )); diff --git a/lib/view/exercise_log_page.dart b/lib/view/exercise_log_page.dart index 4106f32..9da9909 100644 --- a/lib/view/exercise_log_page.dart +++ b/lib/view/exercise_log_page.dart @@ -241,11 +241,11 @@ class _ExerciseLogPage extends State with Trans, Common { style: (TextStyle(color: Colors.blue)), ), Text( - exercise.quantity.toStringAsFixed(0) + - "x" + - exercise.unitQuantity.toStringAsFixed(0) + - " " + - exerciseType.unitQuantityUnit, + exercise.quantity.toStringAsFixed(0) + "x" + exercise.unitQuantity.toStringAsFixed(0) + " ", + //+ + //exerciseType.unitQuantityUnit == null + // ? "" + // : exerciseType.unitQuantityUnit, style: (TextStyle(color: Colors.deepOrange)), ), Text( diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart index 0d22534..22201a0 100644 --- a/lib/view/exercise_new_page.dart +++ b/lib/view/exercise_new_page.dart @@ -2,8 +2,10 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/exercise_new/exercise_new_bloc.dart'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; @@ -19,6 +21,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; +import 'package:stop_watch_timer/stop_watch_timer.dart'; class ExerciseNewPage extends StatefulWidget { _ExerciseNewPageState createState() => _ExerciseNewPageState(); @@ -132,14 +135,19 @@ class _ExerciseNewPageState extends State with Trans { return SizeWidget(exerciseBloc: exerciseBloc); } + bool isSecond = false; String exerciseTask = ""; - if (exerciseBloc.exerciseRepository.exerciseType.is1RM && menuBloc.ability.toString() == ExerciseAbility.oneRepMax.toString()) { - exerciseTask = "Please take a relative bigger weight and repeat 12-20 times"; - exerciseBloc.quantity = 12; - } else if (exerciseBloc.exerciseRepository.exerciseType.isEndurance && - menuBloc.ability.toString() == ExerciseAbility.endurance.toString()) { - exerciseTask = "Please take a medium weight and repeat 20-30 times"; - exerciseBloc.quantity = 20; + if (exerciseBloc.exerciseRepository.exerciseType.unit != "second") { + if (exerciseBloc.exerciseRepository.exerciseType.is1RM() && menuBloc.ability.toString() == ExerciseAbility.oneRepMax.toString()) { + exerciseTask = "Please take a relative bigger weight and repeat 12-20 times"; + exerciseBloc.quantity = 12; + } else if (exerciseBloc.exerciseRepository.exerciseType.isEndurance() && + menuBloc.ability.toString() == ExerciseAbility.endurance.toString()) { + exerciseTask = "Please take a medium weight and repeat 20-30 times"; + exerciseBloc.quantity = 20; + } + } else { + isSecond = true; } return Form( @@ -159,20 +167,34 @@ class _ExerciseNewPageState extends State with Trans { child: KeyboardActions( config: _buildConfig(context), child: Container( - padding: const EdgeInsets.only(top: 5, left: 55, right: 55), + padding: const EdgeInsets.only(top: 25, left: 55, right: 55), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text(t('Save Exercise'), style: TextStyle(fontSize: 14, color: Colors.blue[200])), - Divider(), Text( exerciseName, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.orange), + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + fontSize: 24, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), overflow: TextOverflow.fade, - maxLines: 2, + maxLines: 4, softWrap: true, textAlign: TextAlign.center, ), @@ -235,7 +257,7 @@ class _ExerciseNewPageState extends State with Trans { child: Stack( alignment: Alignment.center, children: [ - Image.asset('asset/icon/gomb_orange_a.png', width: 140, height: 60), + Image.asset('asset/icon/gomb_orange_c.png', width: 140, height: 60), Text( t("Save"), style: TextStyle(fontSize: 16, color: Colors.white), @@ -280,6 +302,80 @@ class _ExerciseNewPageState extends State with Trans { Column columnQuantity(ExerciseNewBloc bloc) { if (bloc.exerciseRepository.exerciseType.unit == "second") { return Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Padding( + padding: const EdgeInsets.only(bottom: 0), + child: StreamBuilder( + stream: bloc.stopWatchTimer.rawTime, + initialData: bloc.stopWatchTimer.rawTime.value, + builder: (context, snap) { + final value = snap.data; + final displayTime = StopWatchTimer.getDisplayTime(value, hours: false); + return Column(children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Text( + displayTime, + style: const TextStyle(fontSize: 40, fontFamily: 'Helvetica', fontWeight: FontWeight.bold, color: Colors.white), + ), + ), + ]); + })), + Padding( + padding: const EdgeInsets.all(2), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: IconButton( + padding: const EdgeInsets.all(2), + color: Colors.white70, + //shape: const StadiumBorder(), + onPressed: () async { + bloc.stopWatchTimer.onExecute.add(StopWatchExecute.start); + }, + icon: Icon(CustomIcon.play_1), + iconSize: 40, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: IconButton( + padding: const EdgeInsets.all(2), + iconSize: 40, + color: Colors.white70, + //shape: const StadiumBorder(), + onPressed: () async { + bloc.stopWatchTimer.onExecute.add(StopWatchExecute.stop); + }, + icon: Icon(CustomIcon.stop), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: IconButton( + padding: const EdgeInsets.all(2), + iconSize: 40, + color: Colors.white70, + onPressed: () async { + bloc.stopWatchTimer.onExecute.add(StopWatchExecute.reset); + }, + icon: Icon(CustomIcon.creative_commons_zero), + ), + ), + ], + ), + ), + ], + ), + ), + Divider(), + Divider(), + Text("Or type the time manually:", style: GoogleFonts.inter(color: Colors.white)), TimePickerWidget( onChange: (value) => {bloc.add(ExerciseNewQuantityChange(quantity: value))}, ) @@ -360,18 +456,26 @@ class _ExerciseNewPageState extends State with Trans { child: Text(t("Yes")), onPressed: () => { bloc.exerciseRepository.setCustomer(Cache().userLoggedIn), - bloc.exerciseRepository.addExercise(), + bloc.add(ExerciseNewSubmit()), Navigator.pop(context), Navigator.pop(context), - if (bloc.exerciseRepository.exerciseType.is1RM && menuBloc.ability.toString() == ExerciseAbility.oneRepMax.toString()) + print("Ability " + + menuBloc.ability.toString() + + " exerciseType 1rm " + + bloc.exerciseRepository.exerciseType.is1RM().toString()), + if (bloc.exerciseRepository.exerciseType.unitQuantityUnit == null) + { + args['exerciseRepository'] = bloc.exerciseRepository, + Navigator.of(context).pushNamed('evaluationPage', arguments: args) + } + else if (menuBloc.ability.equalsTo(ExerciseAbility.oneRepMax)) { args['exerciseRepository'] = bloc.exerciseRepository, args['percent'] = 0.75, args['readonly'] = false, Navigator.of(context).pushNamed('exerciseControlPage', arguments: args) } - else if (bloc.exerciseRepository.exerciseType.isEndurance && - menuBloc.ability.toString() == ExerciseAbility.endurance.toString()) + else if (menuBloc.ability.equalsTo(ExerciseAbility.endurance)) { args['exerciseRepository'] = bloc.exerciseRepository, args['percent'] = 0.50, diff --git a/lib/view/exercise_plan_custom_detail_add_page.dart b/lib/view/exercise_plan_custom_detail_add_page.dart index 3f5db28..4a0136e 100644 --- a/lib/view/exercise_plan_custom_detail_add_page.dart +++ b/lib/view/exercise_plan_custom_detail_add_page.dart @@ -7,7 +7,6 @@ import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/exercise_plan_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; -import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -161,7 +160,7 @@ class _ExercisePlanDetailAddPage extends State with T Text( exerciseName, textAlign: TextAlign.center, - style: GoogleFonts.archivoBlack(fontSize: 18, color: Colors.deepOrange), + style: GoogleFonts.archivoBlack(fontSize: 18, color: Colors.yellow[200]), overflow: TextOverflow.fade, maxLines: 3, softWrap: true, diff --git a/lib/view/exercise_plan_custom_page.dart b/lib/view/exercise_plan_custom_page.dart index b9ce346..8ca19e7 100644 --- a/lib/view/exercise_plan_custom_page.dart +++ b/lib/view/exercise_plan_custom_page.dart @@ -9,7 +9,6 @@ import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; -import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; diff --git a/lib/view/settings.dart b/lib/view/settings.dart index 18a3f50..680a59b 100644 --- a/lib/view/settings.dart +++ b/lib/view/settings.dart @@ -1,5 +1,6 @@ import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/util/trans.dart'; @@ -36,20 +37,16 @@ class SettingsPage extends StatelessWidget with Trans { if (state is SettingsError) { } else if (state is SettingsReady) { menuBloc.add(MenuRecreateTree()); + } else if (state is SettingsLoading) { + Scaffold.of(context).showSnackBar(SnackBar( + duration: Duration(milliseconds: 100), + backgroundColor: Colors.transparent, + content: Container(child: Center(child: CircularProgressIndicator())))); } }, // ignore: missing_return builder: (context, state) { - if (state is SettingsLoading) { - //LoadingDialog.show(context); - } else if (state is SettingsInitial) { - return settingsUI(context, settingsBloc, menuBloc); - } else if (state is SettingsReady) { - //LoadingDialog.hide(context); - return settingsUI(context, settingsBloc, menuBloc); - } else { - return Container(); - } + return settingsUI(context, settingsBloc, menuBloc); }), ), ), @@ -72,11 +69,12 @@ class SettingsPage extends StatelessWidget with Trans { onChanged: (String lang) => { settingsBloc.add(SettingsChangeLanguage(language: lang)), })), - getServer(), + getServer(settingsBloc), + getDevice(settingsBloc), ]); } - ListTile getServer() { + ListTile getServer(SettingsBloc settingsBloc) { if (Cache().userLoggedIn.admin != 1) { return ListTile( title: Container(), @@ -100,7 +98,29 @@ class SettingsPage extends StatelessWidget with Trans { inactiveFgColor: Colors.grey[900], labels: [t('Live-Server'), t('Test-Server')], onToggle: (index) { - Cache().setServer(index == 0); + //Cache().setServer(index == 0); + settingsBloc.add(SettingsSetServer(live: index == 0)); + }, + ), + ); + } + + ListTile getDevice(SettingsBloc settingsBloc) { + return ListTile( + leading: Icon(CustomIcon.cog), + subtitle: Text("Do you have Smart watch, or any device which collects the fit/health data?"), + title: ToggleSwitch( + minWidth: 120.0, + minHeight: 30.0, + fontSize: 14.0, + initialLabelIndex: Cache().hasHardware ? 0 : 1, + activeBgColor: Colors.indigo, + activeFgColor: Colors.white, + inactiveBgColor: Colors.white60, + inactiveFgColor: Colors.grey[900], + labels: [t('Yes'), t('No')], + onToggle: (index) { + settingsBloc.add(SettingsSetHardware(hasHardware: index == 0)); }, ), ); diff --git a/lib/widgets/loading.dart b/lib/widgets/loading.dart index 02ab2d8..08139a3 100644 --- a/lib/widgets/loading.dart +++ b/lib/widgets/loading.dart @@ -5,7 +5,8 @@ import 'package:flutter/material.dart'; class LoadingScreenMain extends StatelessWidget { @override Widget build(BuildContext context) { - Image _backgroundImage = Image.asset('asset/image/WT01_loading_layers.png', + final Image _backgroundImage = Image.asset( + 'asset/image/WT01_loading_layers.png', fit: BoxFit.cover, height: double.infinity, width: double.infinity, @@ -23,13 +24,14 @@ class LoadingScreenMain extends StatelessWidget { new Container( decoration: BoxDecoration(color: Colors.white), ), + /// Render the background image new Container( - child: SafeArea( - bottom: false, - child: _backgroundImage, - ) - ), + child: SafeArea( + bottom: false, + child: _backgroundImage, + )), + /// Render the Title widget, loader and messages below each other new Column( mainAxisAlignment: MainAxisAlignment.center, @@ -37,15 +39,14 @@ class LoadingScreenMain extends StatelessWidget { new Expanded( flex: 3, child: new Container( - child: new Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - new Padding( - padding: const EdgeInsets.only(top: 30.0), - ), - - ], - )), + child: new Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new Padding( + padding: const EdgeInsets.only(top: 30.0), + ), + ], + )), ), Expanded( flex: 1, @@ -54,8 +55,7 @@ class LoadingScreenMain extends StatelessWidget { children: [ /// Loader Animation Widget CircularProgressIndicator( - valueColor: new AlwaysStoppedAnimation( - Colors.lightGreenAccent), + valueColor: new AlwaysStoppedAnimation(Colors.lightGreenAccent), ), Padding( padding: const EdgeInsets.only(top: 20.0), diff --git a/lib/widgets/number_picker.dart b/lib/widgets/number_picker.dart new file mode 100644 index 0000000..831159e --- /dev/null +++ b/lib/widgets/number_picker.dart @@ -0,0 +1,71 @@ +import 'package:aitrainer_app/util/trans.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class NumberPickerWidget extends StatefulWidget { + final Function(double) onChange; + final int minValue; + final int maxValue; + final int initalValue; + final String unit; + final Color color; + + const NumberPickerWidget({Key key, this.minValue, this.maxValue, this.initalValue, this.unit, this.color, this.onChange}) + : super(key: key); + @override + _NumberPickerWidgetState createState() => _NumberPickerWidgetState(); +} + +class _NumberPickerWidgetState extends State with Trans { + Widget durationPicker({bool inSeconds = false, bool inHundredths = false}) { + double value = 0; + return CupertinoPicker( + scrollController: FixedExtentScrollController(initialItem: widget.initalValue), + backgroundColor: Colors.transparent, + onSelectedItemChanged: (x) { + currentData = x.toDouble(); + value = x.toDouble(); + //print("sec" + seconds.toStringAsFixed(2)); + setState(() {}); + widget.onChange(value); + }, + children: List.generate(widget.maxValue, (index) => Text('$index ' + widget.unit, style: TextStyle(color: widget.color))), + itemExtent: 40, + ); + } + + double currentData = 0; + @override + Widget build(BuildContext context) { + setContext(context); + return Container( + //color: Colors.white24, + width: MediaQuery.of(context).size.width * .45, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.transparent, + width: MediaQuery.of(context).size.width * .3, + child: Center( + child: Container( + color: Colors.transparent, + width: MediaQuery.of(context).size.width * 0.95, + height: MediaQuery.of(context).size.height * 0.25, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded(child: durationPicker()), + ], + )), + ), + ), + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 59d60a8..5eab3d6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -211,6 +211,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.10" + device_info: + dependency: transitive + description: + name: device_info + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2+10" + device_info_platform_interface: + dependency: transitive + description: + name: device_info_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" devicelocale: dependency: "direct main" description: @@ -430,6 +444,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0" + health: + dependency: "direct main" + description: + name: health + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" http: dependency: "direct dev" description: @@ -869,6 +890,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.10.0-nullsafety.1" + stop_watch_timer: + dependency: "direct main" + description: + name: stop_watch_timer + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.0+1" stream_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b71276e..cae11d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.2+36 +version: 1.1.2+39 environment: sdk: ">=2.10.0 <3.0.0" @@ -42,7 +42,9 @@ dependencies: keyboard_actions: ^3.3.1+1 dropdown_search: ^0.4.8 badges: ^1.1.4 - #health: ^2.0.9 + health: ^2.0.9 + stop_watch_timer: ^0.6.0+1 + #geolocator: ^6.1.13 firebase_core: ^0.5.2 @@ -97,6 +99,7 @@ flutter: assets: - asset/icon/gomb_kek_a-2.png - asset/icon/gomb_orange_a.png + - asset/icon/gomb_orange_c.png - asset/icon/gomb_pink_a.png - asset/icon/gomb_pink_b.png - asset/icon/gomb_zold_b-1.png @@ -116,6 +119,9 @@ flutter: - asset/image/Gain_muscle.png - asset/image/WT_weight_loss.png - asset/image/WT_welcome.png + - asset/image/WT_Results_for_runners.png + - asset/image/WT_Results_for_female.png + - asset/image/WT_Results_for_men.png - asset/image/login_fb.png - asset/image/lock.png - asset/image/Congrats_N1.jpg @@ -157,6 +163,19 @@ flutter: - asset/image/equipment_gym_place.jpg - asset/image/equipment_street_place.jpg - asset/image/haken.png + - asset/image/pict_calorie.png + - asset/image/pict_development_by_bodypart_percent.png + - asset/image/pict_distance_m.png + - asset/image/pict_fatburn_percent.png + - asset/image/pict_hravg_bpm.png + - asset/image/pict_hrmin_bpm.png + - asset/image/pict_hrmax_bpm.png + - asset/image/pict_maxspeed_kmh.png + - asset/image/pict_reps_volumen_db.png + - asset/image/pict_steps.png + - asset/image/pict_time_h.png + - asset/image/pict_hypertrophy.png + - asset/image/pict_weight_volumen_tonna.png - asset/menu/1.cardio.png - asset/menu/1.1.aerob.png - asset/menu/1.2.anaerob.png diff --git a/test/result_test.dart b/test/result_test.dart new file mode 100644 index 0000000..ade0e2b --- /dev/null +++ b/test/result_test.dart @@ -0,0 +1,21 @@ +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/result.dart'; +import 'package:test/test.dart'; + +main() { + setUp(() { + Cache().setTestBaseUrl(); + }); + + group('Result', () { + test('test ResultExt constructor', () async { + final ResultExt resultExt = ResultExt(itemString: "Calorie"); + + expect(resultExt.getImage(), "asset/image/pic_calorie.png"); + expect(resultExt.getDescription(), "Calorie"); + expect(resultExt.item.equals(ResultItem.calorie), true); + expect(resultExt.equals(ResultItem.calorie), true); + expect(resultExt.equalsString("Calorie"), true); + }); + }); +}