WT 1.1.20 Training Plan improvements, InApp Messageing
This commit is contained in:
parent
a595ffb358
commit
211307e63e
BIN
asset/menu/FG_2_edz.jpg
Normal file
BIN
asset/menu/FG_2_edz.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
BIN
asset/menu/stretching.jpg
Normal file
BIN
asset/menu/stretching.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
BIN
asset/menu/warmup.jpg
Normal file
BIN
asset/menu/warmup.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 KiB |
@ -436,7 +436,7 @@
|
||||
"Do you want to restart, or select a new Training Plan?": "Do you want to restart, or select a new Training Plan?",
|
||||
"New Training Plan": "New Training Plan",
|
||||
"Restart": "Restart",
|
||||
"Training Day": "Training Day",
|
||||
"Training Day": "Day",
|
||||
"No Active Training Plan": "No Active Training Plan",
|
||||
"Please select one in the Training menu, or create your custom plan": "Please select one in the Training menu, or create your custom plan",
|
||||
"Continue your training": "Continue your training",
|
||||
@ -473,5 +473,8 @@
|
||||
"A new version of Workout Test is available!": "A new version of Workout Test is available!",
|
||||
"Update Now": "Update Now",
|
||||
"Update App?": "Update App?",
|
||||
"Want to update?": "Please update the app"
|
||||
"Want to update?": "Please update the app",
|
||||
"Attention!": "Attention!",
|
||||
"The safe and exact execution of this exercise you need a training buddy or a trainer": "The safe and exact execution of this exercise you need a training buddy or a trainer",
|
||||
"Execution at your own risk!": "Execution at your own risk!"
|
||||
}
|
@ -434,7 +434,7 @@
|
||||
"Do you want to restart, or select a new Training Plan?": "Újra akarod indítani, vagy inkább egy másikat választasz?",
|
||||
"New Training Plan": "Másik edzésterv",
|
||||
"Restart": "Újraindítom",
|
||||
"Training Day": "Edzésnap",
|
||||
"Training Day": "Nap",
|
||||
"No Active Training Plan": "Nincs aktív edzésterv",
|
||||
"Please select one in the Training menu, or create your custom plan": "Kérlek válassz egyet a Tréning menüben, vagy hozd létre a saját egyéni edzésedet",
|
||||
"Continue your training": "Folytasd az edzést",
|
||||
@ -471,5 +471,8 @@
|
||||
"A new version of Workout Test is available!": "Egy új Workout Test verzió elérhető!",
|
||||
"Update Now": "Töltsd le most",
|
||||
"Update App?": "App frissítés",
|
||||
"Want to update?": "Kérlek töltsd le"
|
||||
"Want to update?": "Kérlek töltsd le",
|
||||
"Attention!": "Figyelem!",
|
||||
"The safe and exact execution of this exercise you need a training buddy or a trainer": "A gyakorlat biztonságos és maximális pontosságú végrehajtásához edzőtárs vagy edző segítsége szükséges",
|
||||
"Execution at your own risk!": "Végrehajtás CSAK saját felelőségre!"
|
||||
}
|
@ -4,7 +4,7 @@ PODS:
|
||||
- AppAuth/ExternalUserAgent (= 1.4.0)
|
||||
- AppAuth/Core (1.4.0)
|
||||
- AppAuth/ExternalUserAgent (1.4.0)
|
||||
- apple_sign_in (0.0.1):
|
||||
- awesome_notifications (0.0.2):
|
||||
- Flutter
|
||||
- device_info (0.0.1):
|
||||
- Flutter
|
||||
@ -116,10 +116,10 @@ PODS:
|
||||
- FirebaseInstallations (~> 8.0)
|
||||
- GoogleUtilities/Environment (~> 7.4)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.4)"
|
||||
- flurry (0.0.4):
|
||||
- Flurry-iOS-SDK/FlurrySDK (11.2.1)
|
||||
- flurry_data (0.0.1):
|
||||
- Flurry-iOS-SDK/FlurrySDK
|
||||
- Flutter
|
||||
- Flurry-iOS-SDK/FlurrySDK (11.2.1)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_app_badger (0.0.1):
|
||||
- Flutter
|
||||
@ -127,8 +127,6 @@ PODS:
|
||||
- FBSDKCoreKit (~> 9.1.0)
|
||||
- FBSDKLoginKit (~> 9.1.0)
|
||||
- Flutter
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage (3.3.1):
|
||||
- Flutter
|
||||
- flutter_uxcam (2.0.0-beta.1):
|
||||
@ -215,6 +213,8 @@ PODS:
|
||||
- Sentry (~> 7.0.3)
|
||||
- shared_preferences (0.0.1):
|
||||
- Flutter
|
||||
- sign_in_with_apple (0.0.1):
|
||||
- Flutter
|
||||
- sqflite (0.0.2):
|
||||
- Flutter
|
||||
- FMDB (>= 2.7.5)
|
||||
@ -229,7 +229,7 @@ PODS:
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- apple_sign_in (from `.symlinks/plugins/apple_sign_in/ios`)
|
||||
- awesome_notifications (from `.symlinks/plugins/awesome_notifications/ios`)
|
||||
- device_info (from `.symlinks/plugins/device_info/ios`)
|
||||
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
|
||||
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
|
||||
@ -237,11 +237,10 @@ DEPENDENCIES:
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`)
|
||||
- flurry (from `.symlinks/plugins/flurry/ios`)
|
||||
- flurry_data (from `.symlinks/plugins/flurry_data/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_app_badger (from `.symlinks/plugins/flutter_app_badger/ios`)
|
||||
- flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- flutter_uxcam (from `.symlinks/plugins/flutter_uxcam/ios`)
|
||||
- google_sign_in (from `.symlinks/plugins/google_sign_in/ios`)
|
||||
@ -252,6 +251,7 @@ DEPENDENCIES:
|
||||
- purchases_flutter (from `.symlinks/plugins/purchases_flutter/ios`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||
- video_player (from `.symlinks/plugins/video_player/ios`)
|
||||
@ -289,8 +289,8 @@ SPEC REPOS:
|
||||
- UXCam
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
apple_sign_in:
|
||||
:path: ".symlinks/plugins/apple_sign_in/ios"
|
||||
awesome_notifications:
|
||||
:path: ".symlinks/plugins/awesome_notifications/ios"
|
||||
device_info:
|
||||
:path: ".symlinks/plugins/device_info/ios"
|
||||
devicelocale:
|
||||
@ -305,16 +305,14 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||
firebase_remote_config:
|
||||
:path: ".symlinks/plugins/firebase_remote_config/ios"
|
||||
flurry:
|
||||
:path: ".symlinks/plugins/flurry/ios"
|
||||
flurry_data:
|
||||
:path: ".symlinks/plugins/flurry_data/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_app_badger:
|
||||
:path: ".symlinks/plugins/flutter_app_badger/ios"
|
||||
flutter_facebook_auth:
|
||||
:path: ".symlinks/plugins/flutter_facebook_auth/ios"
|
||||
flutter_local_notifications:
|
||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||
flutter_secure_storage:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
flutter_uxcam:
|
||||
@ -335,6 +333,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||
shared_preferences:
|
||||
:path: ".symlinks/plugins/shared_preferences/ios"
|
||||
sign_in_with_apple:
|
||||
:path: ".symlinks/plugins/sign_in_with_apple/ios"
|
||||
sqflite:
|
||||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
url_launcher:
|
||||
@ -348,7 +348,7 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
|
||||
apple_sign_in: 7716c7ddfa195aeab7dec0dc374ef4ff45d1adb4
|
||||
awesome_notifications: 74462bc8e68b11f8235d78422266886759e9da61
|
||||
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
|
||||
devicelocale: b22617f40038496deffba44747101255cee005b0
|
||||
FBSDKCoreKit: a00fe2efd780c195a5e09201bf51c56106245b40
|
||||
@ -367,12 +367,11 @@ SPEC CHECKSUMS:
|
||||
FirebaseInstallations: c4aab1005d6547b00a7529777fe52f5d4d45165b
|
||||
FirebaseMessaging: 1a33b4af3c8042ed6ddacb6c031894af2064bfab
|
||||
FirebaseRemoteConfig: 055f6b5ba1751547596ded5032c4d5c6054ca501
|
||||
flurry: 15b01f664ab1367c62b50291541ea7f78ca85aad
|
||||
Flurry-iOS-SDK: 5831da8fc6bedb31fa1f94aac6fd204d36dd351d
|
||||
flurry_data: 49b7066a283aa41f4306974c1f2d74c61231ad74
|
||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||
flutter_app_badger: 65de4d6f0c34a891df49e6cfb8a1c0496426fa68
|
||||
flutter_facebook_auth: 4b170c07b7fce791497093fcc3f134fb215f3f07
|
||||
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
flutter_uxcam: ab8e5d3954eb448febd581375e2622e9eecb1066
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
@ -396,6 +395,7 @@ SPEC CHECKSUMS:
|
||||
Sentry: 5b16f877da362d23716d827e04db642455b26b40
|
||||
sentry_flutter: 602dc1902e152269256115e2386e1029511f3440
|
||||
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
||||
sign_in_with_apple: 34f3f5456a45fd7ac5fb42905e2ad31dae061b4a
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||
UXCam: c2c00873595ab89be227f197213dc3679ff88ae5
|
||||
|
@ -388,7 +388,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 11;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_TEAM = SFJJBDCU6Z;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@ -405,7 +405,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 1.1.18;
|
||||
MARKETING_VERSION = 1.1.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@ -531,7 +531,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 11;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_TEAM = SFJJBDCU6Z;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@ -548,7 +548,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 1.1.18;
|
||||
MARKETING_VERSION = 1.1.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@ -566,7 +566,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 11;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_TEAM = SFJJBDCU6Z;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@ -583,7 +583,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 1.1.18;
|
||||
MARKETING_VERSION = 1.1.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
@ -87,9 +87,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
yield LoginSuccess();
|
||||
} else if (event is RegistrationSubmit) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
/* if (!this.dataPolicyAllowed) {
|
||||
throw Exception("Please accept our data policy");
|
||||
}
|
||||
} */
|
||||
final String? validationError = validate();
|
||||
if (validationError != null) {
|
||||
yield LoginError(message: validationError);
|
||||
@ -104,9 +104,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
}
|
||||
} else if (event is RegistrationFB) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
/* if (!this.dataPolicyAllowed) {
|
||||
throw Exception("Please accept our data policy");
|
||||
}
|
||||
} */
|
||||
Cache().setLoginType(LoginType.fb);
|
||||
await userRepository.addUserFB();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
|
||||
@ -116,9 +116,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
yield LoginSuccess();
|
||||
} else if (event is RegistrationGoogle) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
/* if (!this.dataPolicyAllowed) {
|
||||
throw Exception("Please accept our data policy");
|
||||
}
|
||||
} */
|
||||
Cache().setLoginType(LoginType.google);
|
||||
await userRepository.addUserGoogle();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
|
||||
@ -128,9 +128,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
yield LoginSuccess();
|
||||
} else if (event is RegistrationApple) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
/* if (!this.dataPolicyAllowed) {
|
||||
throw Exception("Please accept our data policy");
|
||||
}
|
||||
} */
|
||||
Cache().setLoginType(LoginType.apple);
|
||||
await userRepository.addUserApple();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
|
||||
|
@ -58,15 +58,16 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
await Cache().saveMyTrainingPlan();
|
||||
yield TrainingPlanFinished();
|
||||
} else if (event is TrainingPlanWeightChange) {
|
||||
yield TrainingPlanLoading();
|
||||
yield TrainingPlanExerciseLoading();
|
||||
event.detail.weight = event.weight;
|
||||
|
||||
yield TrainingPlanExerciseReady();
|
||||
yield TrainingPlanReady();
|
||||
} else if (event is TrainingPlanRepeatsChange) {
|
||||
yield TrainingPlanLoading();
|
||||
yield TrainingPlanExerciseLoading();
|
||||
|
||||
event.detail.repeats = event.repeats;
|
||||
|
||||
yield TrainingPlanExerciseReady();
|
||||
yield TrainingPlanReady();
|
||||
} else if (event is TrainingPlanSetChange) {
|
||||
yield TrainingPlanLoading();
|
||||
@ -91,10 +92,13 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
} else if (event.detail.exercises.length >= 0) {
|
||||
event.detail.state = ExercisePlanDetailState.inProgress;
|
||||
}
|
||||
|
||||
// recalculate the weight to the original planned repeats
|
||||
if (event.detail.isTest && event.detail.exercises.length == 1) {
|
||||
trainingPlanRepository.recalculateDetail(_myPlan!.trainingPlanId!, event.detail);
|
||||
// recalculate the weight to the original planned repeats for the next details
|
||||
if (exercise.unitQuantity != null && exercise.unitQuantity! > 0) {
|
||||
for (var nextDetail in _myPlan!.details) {
|
||||
if (nextDetail.exerciseTypeId == event.detail.exerciseTypeId && nextDetail.weight == -2) {
|
||||
trainingPlanRepository.recalculateDetail(_myPlan!.trainingPlanId!, event.detail, nextDetail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exercise.trainingPlanDetailsId = _myPlan!.trainingPlanId;
|
||||
@ -230,28 +234,61 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
dayNames.clear();
|
||||
_myPlan!.days.clear();
|
||||
String dayName = ".";
|
||||
String previousDay = ".";
|
||||
_myPlan!.details.forEach((element) {
|
||||
if (element.day != null && element.day != dayName) {
|
||||
dayNames.add(element.day!);
|
||||
dayName = element.day!;
|
||||
if (previousDay != ".") {
|
||||
this.addExtraExerciseType("Stretching", previousDay);
|
||||
}
|
||||
}
|
||||
if (_myPlan!.days[dayName] == null) {
|
||||
if (dayName == ".") {
|
||||
dayName = "";
|
||||
}
|
||||
_myPlan!.days[dayName] = [];
|
||||
|
||||
this.addExtraExerciseType("Warming Up", dayName);
|
||||
previousDay = dayName;
|
||||
}
|
||||
_myPlan!.days[dayName]!.add(element);
|
||||
});
|
||||
|
||||
if (dayNames.length == 0) {
|
||||
dayNames.add("");
|
||||
_myPlan!.days[""] = [];
|
||||
_myPlan!.days[""]!.addAll(_myPlan!.details);
|
||||
dayName = "";
|
||||
dayNames.add(dayName);
|
||||
_myPlan!.days[dayName] = [];
|
||||
_myPlan!.days[dayName]!.addAll(_myPlan!.details);
|
||||
}
|
||||
getActiveDayIndex();
|
||||
}
|
||||
|
||||
void addExtraExerciseType(String name, String dayName) {
|
||||
if (Cache().getExerciseTypes() == null) {
|
||||
return;
|
||||
}
|
||||
for (var exerciseType in Cache().getExerciseTypes()!) {
|
||||
if (exerciseType.name == name) {
|
||||
CustomerTrainingPlanDetails detail = CustomerTrainingPlanDetails();
|
||||
detail.customerTrainingPlanDetailsId = 0;
|
||||
detail.trainingPlanDetailsId = 0;
|
||||
detail.exerciseTypeId = exerciseType.exerciseTypeId;
|
||||
detail.repeats = 1;
|
||||
detail.set = 1;
|
||||
detail.day = "";
|
||||
detail.parallel = false;
|
||||
detail.restingTime = 0;
|
||||
detail.exerciseType = exerciseType;
|
||||
if (_myPlan!.days[dayName] == null) {
|
||||
_myPlan!.days[dayName] = [];
|
||||
}
|
||||
_myPlan!.days[dayName]!.add(detail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomerTrainingPlanDetails? getTrainingPlanDetail(int trainingPlanDetailsId) {
|
||||
CustomerTrainingPlanDetails? detail;
|
||||
if (_myPlan == null || _myPlan!.details.isEmpty) {
|
||||
@ -297,13 +334,14 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
int minStep = 99;
|
||||
for (final detail in this._myPlan!.details) {
|
||||
if (!detail.state.equalsTo(ExercisePlanDetailState.finished)) {
|
||||
if (detail.exercises.isEmpty && !detail.state.equalsTo(ExercisePlanDetailState.skipped)) {
|
||||
final day = dayNames[this.activeDayIndex];
|
||||
if (detail.exercises.isEmpty && !detail.state.equalsTo(ExercisePlanDetailState.skipped) && day == detail.day) {
|
||||
next = detail;
|
||||
minStep = 1;
|
||||
break;
|
||||
} else {
|
||||
final int step = detail.exercises.length;
|
||||
if (step < minStep && !detail.state.equalsTo(ExercisePlanDetailState.skipped)) {
|
||||
if (step < minStep && !detail.state.equalsTo(ExercisePlanDetailState.skipped) && day == detail.day) {
|
||||
next = detail;
|
||||
minStep = step;
|
||||
if (detail.parallel != true) {
|
||||
@ -313,7 +351,7 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
}
|
||||
}
|
||||
}
|
||||
print("Next detail $next");
|
||||
//print("Next detail $next");
|
||||
|
||||
return next;
|
||||
}
|
||||
@ -379,20 +417,19 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
}
|
||||
|
||||
int getActiveDayIndex() {
|
||||
activeDayIndex = 0;
|
||||
if (restarting) {
|
||||
return 0;
|
||||
}
|
||||
if (_myPlan == null || _myPlan!.details.isEmpty) {
|
||||
// throw Exception("No defined Training Plan");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dayNames.isEmpty || dayNames.length == 1) {
|
||||
activeDayIndex = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
activeDayIndex = 0;
|
||||
|
||||
for (var day in dayNames) {
|
||||
if (_myPlan!.days[day] == null) {
|
||||
throw Exception("Wrong activated day: $day does not exist");
|
||||
@ -413,7 +450,7 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
activeDayIndex = 0;
|
||||
this.add(TrainingPlanGoToRestart());
|
||||
}
|
||||
|
||||
print("ActiveDayIndex $activeDayIndex");
|
||||
return activeDayIndex;
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,18 @@ class TrainingPlanLoading extends TrainingPlanState {
|
||||
const TrainingPlanLoading();
|
||||
}
|
||||
|
||||
class TrainingPlanExerciseLoading extends TrainingPlanState {
|
||||
const TrainingPlanExerciseLoading();
|
||||
}
|
||||
|
||||
class TrainingPlanReady extends TrainingPlanState {
|
||||
const TrainingPlanReady();
|
||||
}
|
||||
|
||||
class TrainingPlanExerciseReady extends TrainingPlanState {
|
||||
const TrainingPlanExerciseReady();
|
||||
}
|
||||
|
||||
class TrainingPlanFinished extends TrainingPlanState {
|
||||
const TrainingPlanFinished();
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart';
|
||||
import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
|
||||
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
|
||||
import 'package:aitrainer_app/push_notifications.dart';
|
||||
import 'package:aitrainer_app/repository/customer_repository.dart';
|
||||
import 'package:aitrainer_app/repository/training_plan_repository.dart';
|
||||
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
|
||||
@ -44,7 +43,7 @@ import 'package:aitrainer_app/widgets/home.dart';
|
||||
import 'package:aitrainer_app/library/facebook_app_events/facebook_app_events.dart';
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:firebase_analytics/observer.dart';
|
||||
import 'package:flurry/flurry.dart';
|
||||
import 'package:flurry_data/flurry_data.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -110,6 +109,7 @@ Future<Null> main() async {
|
||||
if (isInDebugMode) {
|
||||
// In development mode simply print to console.
|
||||
FlutterError.dumpErrorToConsole(details);
|
||||
FlurryData.logEvent("enter_test");
|
||||
} else {
|
||||
// In production mode report to the application zone to report to
|
||||
// Sentry.
|
||||
@ -192,10 +192,9 @@ Future<Null> main() async {
|
||||
|
||||
Future<void> initThirdParty() async {
|
||||
if (!isInDebugMode) {
|
||||
await Flurry.initialize(androidKey: "JNYCTCWBT34FM3J8TV36", iosKey: "3QBG7BSMGPDH24S8TRQP", enableLog: true);
|
||||
await FlurryData.initialize(androidKey: "JNYCTCWBT34FM3J8TV36", iosKey: "3QBG7BSMGPDH24S8TRQP", enableLog: true);
|
||||
FlutterUxcam.optIntoSchematicRecordings();
|
||||
}
|
||||
PushNotificationsManager().init();
|
||||
}
|
||||
|
||||
class WorkoutTestApp extends StatelessWidget {
|
||||
|
@ -19,6 +19,7 @@ import 'package:aitrainer_app/model/purchase.dart';
|
||||
import 'package:aitrainer_app/model/split_test.dart';
|
||||
import 'package:aitrainer_app/model/sport.dart';
|
||||
import 'package:aitrainer_app/model/training_plan.dart';
|
||||
import 'package:aitrainer_app/model/training_plan_day.dart';
|
||||
import 'package:aitrainer_app/model/tutorial.dart';
|
||||
import 'package:aitrainer_app/model/workout_menu_tree.dart';
|
||||
import 'package:aitrainer_app/repository/customer_repository.dart';
|
||||
@ -30,7 +31,7 @@ import 'package:aitrainer_app/util/enums.dart';
|
||||
import 'package:aitrainer_app/util/env.dart';
|
||||
import 'package:aitrainer_app/util/track.dart';
|
||||
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
||||
import 'package:flurry/flurry.dart';
|
||||
import 'package:flurry_data/flurry_data.dart';
|
||||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
||||
import 'package:flutter_uxcam/flutter_uxcam.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
@ -137,6 +138,7 @@ class Cache with Logging {
|
||||
List<wt_product.Product>? _products;
|
||||
List<Purchase> _purchases = [];
|
||||
List<SplitTest> _splitTests = [];
|
||||
List<TrainingPlanDay> _trainingPlanDays = [];
|
||||
|
||||
List<ExercisePlanTemplate> _exercisePlanTemplates = [];
|
||||
|
||||
@ -184,6 +186,7 @@ class Cache with Logging {
|
||||
String testEnv = EnvironmentConfig.test_env;
|
||||
this.testEnvironment = testEnv;
|
||||
if (testEnv == "1") {
|
||||
print("testEnv $testEnv");
|
||||
baseUrl = baseUrlTest;
|
||||
liveServer = false;
|
||||
}
|
||||
@ -668,7 +671,7 @@ class Cache with Logging {
|
||||
await PackageApi().getCustomerPackage(customerId);
|
||||
|
||||
if (!isInDebugMode) {
|
||||
Flurry.setUserId(customerId.toString());
|
||||
FlurryData.setUserId(customerId.toString());
|
||||
//Smartlook.setUserIdentifier(customerId.toString());
|
||||
FlutterUxcam.setUserProperty("username", customerId.toString());
|
||||
FlutterUxcam.setUserIdentity(customerId.toString());
|
||||
@ -737,4 +740,7 @@ class Cache with Logging {
|
||||
|
||||
List<SplitTest> getSplitTests() => this._splitTests;
|
||||
setSplitTests(value) => this._splitTests = value;
|
||||
|
||||
List<TrainingPlanDay> getTrainingPlanDays() => this._trainingPlanDays;
|
||||
setTrainingPlanDays(value) => this._trainingPlanDays = value;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ class CustomerProperty {
|
||||
int? customerPropertyId;
|
||||
late int propertyId;
|
||||
late int customerId;
|
||||
late DateTime dateAdd;
|
||||
DateTime? dateAdd;
|
||||
late double propertyValue;
|
||||
bool newData = false;
|
||||
|
||||
@ -14,7 +14,7 @@ class CustomerProperty {
|
||||
this.customerPropertyId = json['customerPropertyId'];
|
||||
this.propertyId = json['propertyId'];
|
||||
this.customerId = json['customerId'];
|
||||
this.dateAdd = json['propertyName'];
|
||||
this.dateAdd = json['dataAdd'] ?? DateTime.now();
|
||||
this.propertyValue = json['propertyValue'];
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ class CustomerProperty {
|
||||
"customerPropertyId": this.customerPropertyId,
|
||||
"propertyId": this.propertyId,
|
||||
"customerId": this.customerId,
|
||||
"dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd),
|
||||
"dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd!),
|
||||
"propertyValue": this.propertyValue
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
"propertyId": this.propertyId,
|
||||
"customerId": this.customerId,
|
||||
"dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd),
|
||||
"dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd!),
|
||||
"propertyValue": this.propertyValue
|
||||
};
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/exercise.dart';
|
||||
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
|
||||
import 'package:aitrainer_app/model/exercise_type.dart';
|
||||
import 'package:aitrainer_app/repository/training_plan_day_repository.dart';
|
||||
|
||||
class CustomerTrainingPlanDetails {
|
||||
/// customerTrainingPlanDetails
|
||||
@ -26,6 +27,8 @@ class CustomerTrainingPlanDetails {
|
||||
bool? parallel;
|
||||
String? day;
|
||||
|
||||
int? dayId;
|
||||
|
||||
/// exerciseType
|
||||
ExerciseType? exerciseType;
|
||||
|
||||
@ -63,10 +66,10 @@ class CustomerTrainingPlanDetails {
|
||||
: json['parallel'] == "true"
|
||||
? true
|
||||
: null;
|
||||
this.day = json['day'].toString();
|
||||
if (this.day == null || this.day == "null") {
|
||||
this.day = "";
|
||||
}
|
||||
this.dayId = json['dayId'] == "null" ? null : json['dayId'];
|
||||
TrainingPlanDayRepository trainingPlanDayRepository = TrainingPlanDayRepository();
|
||||
this.day = trainingPlanDayRepository.getNameById(this.dayId);
|
||||
|
||||
try {
|
||||
Iterable iterable = json['exercises'];
|
||||
this.exercises = iterable.map((exercise) => Exercise.fromJson(exercise)).toList();
|
||||
@ -114,15 +117,30 @@ class CustomerTrainingPlanDetails {
|
||||
'exercises': exercises.isEmpty ? [].toString() : exercises.map((exercise) => exercise.toJson()).toList().toString(),
|
||||
'state': this.state.toStr(),
|
||||
"isTest": this.isTest,
|
||||
"dayId": this.dayId,
|
||||
};
|
||||
if (this.day != null && this.day!.isNotEmpty) {
|
||||
jsonMap["day"] = this.day;
|
||||
}
|
||||
|
||||
//print("Detail $jsonMap");
|
||||
//print("Detail toJson $jsonMap");
|
||||
return jsonMap;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => this.toJsonWithExercises().toString();
|
||||
|
||||
void copy(CustomerTrainingPlanDetails from) {
|
||||
this.customerTrainingPlanDetailsId = from.customerTrainingPlanDetailsId;
|
||||
this.trainingPlanDetailsId = from.trainingPlanDetailsId;
|
||||
this.exerciseTypeId = from.exerciseTypeId;
|
||||
this.exerciseType = from.exerciseType;
|
||||
this.set = from.set;
|
||||
this.repeats = from.repeats;
|
||||
this.weight = from.weight;
|
||||
this.restingTime = from.restingTime;
|
||||
this.parallel = from.parallel;
|
||||
this.exercises = from.exercises;
|
||||
this.state = from.state;
|
||||
this.isTest = from.isTest;
|
||||
this.day = from.day;
|
||||
this.dayId = from.dayId;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import 'package:aitrainer_app/model/evaluation_attribute.dart';
|
||||
|
||||
class Evaluation {
|
||||
late int evaluationId;
|
||||
int? evaluationId;
|
||||
late String name;
|
||||
late int exerciseTypeId;
|
||||
late String unit;
|
||||
int? exerciseTypeId;
|
||||
String? unit;
|
||||
late List attributes;
|
||||
|
||||
Evaluation.fromJson(Map json) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
class EvaluationAttribute {
|
||||
late int evaluationAttrId;
|
||||
late int evaluationId;
|
||||
int? evaluationId;
|
||||
late String name;
|
||||
late String sex;
|
||||
late int ageMin;
|
||||
|
@ -17,7 +17,7 @@ class ExerciseType {
|
||||
late String unit;
|
||||
|
||||
/// unitQuantity
|
||||
late String unitQuantity;
|
||||
String? unitQuantity;
|
||||
|
||||
/// unitQuantityUnit
|
||||
String? unitQuantityUnit;
|
||||
@ -28,6 +28,8 @@ class ExerciseType {
|
||||
/// base
|
||||
late bool base;
|
||||
|
||||
late bool buddyWarning;
|
||||
|
||||
/// imageUrl
|
||||
String imageUrl = "";
|
||||
|
||||
@ -65,6 +67,7 @@ class ExerciseType {
|
||||
this.unitQuantityUnit = json['unitQuantityUnit'];
|
||||
this.active = json['active'];
|
||||
this.base = json['base'];
|
||||
this.buddyWarning = json['buddyWarning'];
|
||||
if (json['images'].length > 0) {
|
||||
this.imageUrl = json['images'][0]['url'];
|
||||
}
|
||||
@ -105,6 +108,8 @@ class ExerciseType {
|
||||
"unitQuantity": unitQuantity,
|
||||
"unitQuantityUnit": unitQuantityUnit,
|
||||
"active": active,
|
||||
"base": base,
|
||||
"buddyWarning": buddyWarning,
|
||||
"devices": this.devices.toString(),
|
||||
"nameTranslation": this.nameTranslation,
|
||||
"parents": this.parents.toString()
|
||||
|
@ -4,10 +4,10 @@ import 'package:aitrainer_app/model/training_plan_detail.dart';
|
||||
|
||||
class TrainingPlan {
|
||||
late int trainingPlanId;
|
||||
late String type;
|
||||
String? type;
|
||||
late String name;
|
||||
late String internalName;
|
||||
late String description;
|
||||
String? internalName;
|
||||
String? description;
|
||||
late bool free;
|
||||
late bool active;
|
||||
int? treeId;
|
||||
@ -28,7 +28,7 @@ class TrainingPlan {
|
||||
this.treeId = json['treeId'];
|
||||
|
||||
nameTranslations['en'] = name;
|
||||
descriptionTranslations['en'] = description;
|
||||
descriptionTranslations['en'] = description ?? "";
|
||||
if (json['translations'] != null && json['translations'].length > 0) {
|
||||
json['translations'].forEach((translation) {
|
||||
nameTranslations[translation['languageCode']] = translation['nameTranslation'];
|
||||
|
29
lib/model/training_plan_day.dart
Normal file
29
lib/model/training_plan_day.dart
Normal file
@ -0,0 +1,29 @@
|
||||
import 'dart:collection';
|
||||
|
||||
class TrainingPlanDay {
|
||||
late int dayId;
|
||||
late String name;
|
||||
|
||||
HashMap<String, String> nameTranslations = HashMap();
|
||||
|
||||
TrainingPlanDay.fromJson(Map json) {
|
||||
this.dayId = json['dayId'];
|
||||
this.name = json['name'];
|
||||
|
||||
nameTranslations['en'] = name;
|
||||
if (json['translations'] != null && json['translations'].length > 0) {
|
||||
json['translations'].forEach((translation) {
|
||||
nameTranslations[translation['languageCode']] = translation['nameTranslation'];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"dayId": this.dayId,
|
||||
"name": this.name,
|
||||
"nameTranslation": this.nameTranslations.toString(),
|
||||
};
|
||||
|
||||
@override
|
||||
String toString() => this.toJson().toString();
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
class TrainingPlanDetail {
|
||||
late int trainingPlanDetailId;
|
||||
late int trainingPlanId;
|
||||
int? trainingPlanId;
|
||||
late int exerciseTypeId;
|
||||
late int sort;
|
||||
late int set;
|
||||
late int repeats;
|
||||
late double weight;
|
||||
late int restingTime;
|
||||
late bool parallel;
|
||||
late String day;
|
||||
int? repeats;
|
||||
double? weight;
|
||||
int? restingTime;
|
||||
bool? parallel;
|
||||
int? dayId;
|
||||
String? day;
|
||||
|
||||
TrainingPlanDetail.fromJson(Map<String, dynamic> json) {
|
||||
this.trainingPlanDetailId = json['trainingPlanDetailId'];
|
||||
@ -20,7 +21,7 @@ class TrainingPlanDetail {
|
||||
this.weight = json['weight'];
|
||||
this.restingTime = json['restingTime'];
|
||||
this.parallel = json['parallel'];
|
||||
this.day = json['day'];
|
||||
this.dayId = json['dayId'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
@ -32,6 +33,7 @@ class TrainingPlanDetail {
|
||||
"weight": this.weight,
|
||||
"restingTime": this.restingTime,
|
||||
"parallel": this.parallel,
|
||||
"dayId": this.dayId,
|
||||
"day": this.day,
|
||||
};
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
class PushNotificationsManager with Logging {
|
||||
PushNotificationsManager._();
|
||||
|
||||
factory PushNotificationsManager() => _instance;
|
||||
|
||||
static final PushNotificationsManager _instance = PushNotificationsManager._();
|
||||
|
||||
Future<void> init() async {
|
||||
log(" --- Firebase Messagein init..");
|
||||
const AndroidNotificationChannel channel = AndroidNotificationChannel(
|
||||
'high_importance_channel', // id
|
||||
'High Importance Notifications', // title
|
||||
'This channel is used for important notifications.', // description
|
||||
importance: Importance.max,
|
||||
);
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
await flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.createNotificationChannel(channel);
|
||||
|
||||
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
|
||||
alert: true, // Required to display a heads up notification
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
String? token = await FirebaseMessaging.instance.getToken();
|
||||
log("FirebaseMessaging token $token");
|
||||
}
|
||||
}
|
35
lib/repository/training_plan_day_repository.dart
Normal file
35
lib/repository/training_plan_day_repository.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/training_plan.dart';
|
||||
import 'package:aitrainer_app/util/app_language.dart';
|
||||
|
||||
class TrainingPlanDayRepository {
|
||||
const TrainingPlanDayRepository();
|
||||
|
||||
void assignTrainingPlanDays() {
|
||||
List<TrainingPlan>? plans = Cache().getTrainingPlans();
|
||||
if (plans == null) {
|
||||
return;
|
||||
}
|
||||
plans.forEach((plan) {
|
||||
if (plan.details != null) {
|
||||
plan.details!.forEach((element) {
|
||||
element.day = this.getNameById(element.dayId);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String? getNameById(int? dayId) {
|
||||
if (dayId == null) {
|
||||
return "";
|
||||
}
|
||||
String? name;
|
||||
for (var day in Cache().getTrainingPlanDays()) {
|
||||
if (day.dayId == dayId) {
|
||||
name = day.nameTranslations[AppLanguage().appLocal.languageCode];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import 'package:aitrainer_app/model/exercise.dart';
|
||||
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
|
||||
import 'package:aitrainer_app/model/exercise_tree.dart';
|
||||
import 'package:aitrainer_app/model/training_plan.dart';
|
||||
import 'package:aitrainer_app/repository/training_plan_day_repository.dart';
|
||||
import 'package:aitrainer_app/service/training_plan_service.dart';
|
||||
import 'package:aitrainer_app/util/common.dart';
|
||||
|
||||
@ -66,6 +67,7 @@ class TrainingPlanRepository {
|
||||
|
||||
// 3 calculate weights
|
||||
int index = 0;
|
||||
|
||||
trainingPlan.details!.forEach((elem) {
|
||||
CustomerTrainingPlanDetails detail = CustomerTrainingPlanDetails();
|
||||
detail.customerTrainingPlanDetailsId = ++index;
|
||||
@ -73,7 +75,9 @@ class TrainingPlanRepository {
|
||||
detail.exerciseTypeId = elem.exerciseTypeId;
|
||||
detail.repeats = elem.repeats;
|
||||
detail.set = elem.set;
|
||||
detail.day = elem.day;
|
||||
detail.dayId = elem.dayId;
|
||||
TrainingPlanDayRepository trainingPlanDayRepository = TrainingPlanDayRepository();
|
||||
detail.day = trainingPlanDayRepository.getNameById(elem.dayId);
|
||||
detail.parallel = elem.parallel;
|
||||
detail.restingTime = elem.restingTime;
|
||||
detail.exerciseType = Cache().getExerciseTypeById(detail.exerciseTypeId!);
|
||||
@ -83,6 +87,13 @@ class TrainingPlanRepository {
|
||||
} else {
|
||||
detail.weight = 0;
|
||||
}
|
||||
} else if (elem.weight == -2) {
|
||||
final CustomerTrainingPlanDetails calculated = this.isWeightCalculatedByExerciseType(elem.exerciseTypeId, detail, plan);
|
||||
if (calculated.weight != -1) {
|
||||
detail.weight = calculated.weight;
|
||||
} else {
|
||||
detail.weight = -2;
|
||||
}
|
||||
} else {
|
||||
detail.weight = elem.weight;
|
||||
}
|
||||
@ -98,6 +109,19 @@ class TrainingPlanRepository {
|
||||
return plan;
|
||||
}
|
||||
|
||||
CustomerTrainingPlanDetails isWeightCalculatedByExerciseType(
|
||||
int exerciseTypeId, CustomerTrainingPlanDetails detail, CustomerTrainingPlan plan) {
|
||||
CustomerTrainingPlanDetails calculated = detail;
|
||||
for (var element in plan.details) {
|
||||
if (element.exerciseTypeId == exerciseTypeId) {
|
||||
calculated = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return calculated;
|
||||
}
|
||||
|
||||
TrainingPlan? getTrainingPlanById(int trainingPlanId) {
|
||||
TrainingPlan? plan;
|
||||
if (Cache().getTrainingPlans() == null) {
|
||||
@ -148,8 +172,9 @@ class TrainingPlanRepository {
|
||||
return detail;
|
||||
}
|
||||
|
||||
CustomerTrainingPlanDetails recalculateDetail(int trainingPlanId, CustomerTrainingPlanDetails detail) {
|
||||
CustomerTrainingPlanDetails recalculatedDetail = detail;
|
||||
CustomerTrainingPlanDetails recalculateDetail(
|
||||
int trainingPlanId, CustomerTrainingPlanDetails detail, CustomerTrainingPlanDetails nextDetail) {
|
||||
CustomerTrainingPlanDetails recalculatedDetail = nextDetail;
|
||||
|
||||
// 1. get original repeats
|
||||
|
||||
@ -164,14 +189,14 @@ class TrainingPlanRepository {
|
||||
plan.details!.forEach((element) {
|
||||
if (element.trainingPlanDetailId == detail.trainingPlanDetailsId) {
|
||||
print("element $element");
|
||||
originalRepeats = element.repeats;
|
||||
originalRepeats = element.repeats ?? 0;
|
||||
}
|
||||
});
|
||||
|
||||
// 2 get recalculated repeats
|
||||
|
||||
recalculatedDetail.weight =
|
||||
Common.calculateWeigthByChangedQuantity(detail.weight!, detail.repeats!.toDouble(), originalRepeats.toDouble());
|
||||
recalculatedDetail.weight = Common.roundWeight(recalculatedDetail.weight!);
|
||||
print("recalculated repeats for $originalRepeats: ${recalculatedDetail.weight}");
|
||||
recalculatedDetail.repeats = originalRepeats;
|
||||
|
||||
|
@ -112,10 +112,10 @@ class WorkoutTreeRepository with Logging {
|
||||
0,
|
||||
"");
|
||||
this.tree[exerciseType.name] = menuItem;
|
||||
if (isRunning || is1RM) {
|
||||
if (isRunning || is1RM || exerciseType.name == "Warming Up" || exerciseType.name == "Stretching") {
|
||||
menuAsExercise.add(menuItem);
|
||||
}
|
||||
//log("ExerciseType in Menu item ${exerciseType.toJson()} is1RM: $is1RM");
|
||||
//log("ExerciseType in Menu item ${exerciseType.toJson()}");
|
||||
});
|
||||
} else {
|
||||
//log("No Parents " + exerciseType.toJson().toString());
|
||||
|
@ -1,15 +1,20 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart' as logging;
|
||||
import 'package:apple_sign_in/apple_sign_in.dart';
|
||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
|
||||
class FirebaseApi with logging.Logging {
|
||||
bool appleSignInAvailable = false;
|
||||
//late FirebaseApi _instance;
|
||||
|
||||
static final FirebaseAuth auth = FirebaseAuth.instance;
|
||||
|
||||
@ -29,15 +34,69 @@ class FirebaseApi with logging.Logging {
|
||||
Future<void> initializeFlutterFire() async {
|
||||
try {
|
||||
// Wait for Firebase to initialize and set `_initialized` state to true
|
||||
FirebaseApp app = await Firebase.initializeApp();
|
||||
await Firebase.initializeApp();
|
||||
|
||||
this.appleSignInAvailable = await AppleSignIn.isAvailable();
|
||||
this.appleSignInAvailable = await SignInWithApple.isAvailable();
|
||||
AwesomeNotifications().initialize(
|
||||
// set the icon to null if you want to use the default app icon
|
||||
null,
|
||||
[
|
||||
NotificationChannel(
|
||||
channelKey: 'basic_channel',
|
||||
channelName: 'Basic notifications',
|
||||
channelDescription: 'Notification channel for basic tests',
|
||||
defaultColor: Color(0xFF9D50DD),
|
||||
ledColor: Colors.white)
|
||||
]);
|
||||
|
||||
AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
|
||||
if (!isAllowed) {
|
||||
// Insert here your friendly dialog box before call the request method
|
||||
// This is very important to not harm the user experience
|
||||
AwesomeNotifications().requestPermissionToSendNotifications();
|
||||
}
|
||||
});
|
||||
|
||||
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
|
||||
alert: true, // Required to display a heads up notification
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
String? token = await FirebaseMessaging.instance.getToken();
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
log("FirebaseMessaging token $token");
|
||||
} catch (e) {
|
||||
// Set `_error` state to true if Firebase initialization fails
|
||||
log("Error initializing Firebase");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
print('Handling a background message: ${message.messageId}');
|
||||
|
||||
if (!StringUtils.isNullOrEmpty(message.notification?.title, considerWhiteSpaceAsEmpty: true) ||
|
||||
!StringUtils.isNullOrEmpty(message.notification?.body, considerWhiteSpaceAsEmpty: true)) {
|
||||
print('message also contained a notification: ${message.notification}');
|
||||
|
||||
String? imageUrl;
|
||||
imageUrl ??= message.notification!.android?.imageUrl;
|
||||
imageUrl ??= message.notification!.apple?.imageUrl;
|
||||
|
||||
Map<String, dynamic> notificationAdapter = {
|
||||
NOTIFICATION_CHANNEL_KEY: 'basic_channel',
|
||||
NOTIFICATION_ID: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_ID] ?? message.messageId ?? math.Random().nextInt(2147483647),
|
||||
NOTIFICATION_TITLE: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_TITLE] ?? message.notification?.title,
|
||||
NOTIFICATION_BODY: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_BODY] ?? message.notification?.body,
|
||||
NOTIFICATION_LAYOUT: StringUtils.isNullOrEmpty(imageUrl) ? 'Default' : 'BigPicture',
|
||||
NOTIFICATION_BIG_PICTURE: imageUrl
|
||||
};
|
||||
|
||||
AwesomeNotifications().createNotificationFromJsonData(notificationAdapter);
|
||||
} else {
|
||||
AwesomeNotifications().createNotificationFromJsonData(message.data);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> signInEmail(String? email, String? password) async {
|
||||
if (email == null) {
|
||||
throw Exception("Please type an email address");
|
||||
@ -85,20 +144,33 @@ class FirebaseApi with logging.Logging {
|
||||
return rc;
|
||||
}
|
||||
|
||||
String generateNonce([int length = 32]) {
|
||||
final charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
|
||||
final random = math.Random.secure();
|
||||
return List.generate(length, (_) => charset[random.nextInt(charset.length)]).join();
|
||||
}
|
||||
|
||||
/// Returns the sha256 hash of [input] in hex notation.
|
||||
String sha256ofString(String input) {
|
||||
final bytes = utf8.encode(input);
|
||||
final digest = sha256.convert(bytes);
|
||||
return digest.toString();
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> signInWithApple() async {
|
||||
Map<String, dynamic> userData = Map();
|
||||
|
||||
final AuthorizationResult result = await AppleSignIn.performRequests([
|
||||
AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName])
|
||||
/* final apple.AuthorizationResult result = await SignInWithApple.performRequests([
|
||||
apple.AppleIdRequest(requestedScopes: [apple.Scope.email, apple.Scope.fullName])
|
||||
]);
|
||||
switch (result.status) {
|
||||
case AuthorizationStatus.authorized:
|
||||
case apple.AuthorizationStatus.authorized:
|
||||
print('User authorized');
|
||||
break;
|
||||
case AuthorizationStatus.error:
|
||||
case apple.AuthorizationStatus.error:
|
||||
print('User error');
|
||||
throw Exception("Apple Sign-In failed");
|
||||
case AuthorizationStatus.cancelled:
|
||||
case apple.AuthorizationStatus.cancelled:
|
||||
print('User cancelled');
|
||||
throw Exception("Apple Sign-In cancelled");
|
||||
}
|
||||
@ -106,20 +178,36 @@ class FirebaseApi with logging.Logging {
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: String.fromCharCodes(result.credential.identityToken),
|
||||
accessToken: String.fromCharCodes(result.credential.authorizationCode));
|
||||
accessToken: String.fromCharCodes(result.credential.authorizationCode!));
|
||||
*/
|
||||
// To prevent replay attacks with the credential returned from Apple, we
|
||||
// include a nonce in the credential request. When signing in with
|
||||
// Firebase, the nonce in the id token returned by Apple, is expected to
|
||||
// match the sha256 hash of `rawNonce`.
|
||||
final rawNonce = generateNonce();
|
||||
final nonce = sha256ofString(rawNonce);
|
||||
|
||||
// Request credential for the currently signed in Apple account.
|
||||
final appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: appleCredential.identityToken,
|
||||
rawNonce: rawNonce,
|
||||
);
|
||||
// Sign in the user with Firebase. If the nonce we generated earlier does
|
||||
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
|
||||
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
|
||||
Cache().firebaseUid = userCredential.user!.uid;
|
||||
log("userCredential: " + userCredential.toString());
|
||||
|
||||
log("Apple Credentials: " +
|
||||
result.credential.user.toString() +
|
||||
" state " +
|
||||
result.credential.state.toString() +
|
||||
" email " +
|
||||
userCredential.user!.email!);
|
||||
log("Apple Credentials: ${appleCredential.userIdentifier} state ${appleCredential.state} email ${userCredential.user!.email!}");
|
||||
userData['email'] = userCredential.user!.email;
|
||||
|
||||
return userData;
|
||||
@ -127,26 +215,43 @@ class FirebaseApi with logging.Logging {
|
||||
|
||||
Future<Map<String, dynamic>> registerWithApple() async {
|
||||
Map<String, dynamic> userData = Map();
|
||||
final AuthorizationResult result = await AppleSignIn.performRequests([
|
||||
AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName])
|
||||
/* final apple.AuthorizationResult result = await apple.TheAppleSignIn.performRequests([
|
||||
apple.AppleIdRequest(requestedScopes: [apple.Scope.email, apple.Scope.fullName])
|
||||
]);
|
||||
switch (result.status) {
|
||||
case AuthorizationStatus.authorized:
|
||||
case apple.AuthorizationStatus.authorized:
|
||||
print('Apple User authorized');
|
||||
break;
|
||||
case AuthorizationStatus.error:
|
||||
case apple.AuthorizationStatus.error:
|
||||
print('Apple User error');
|
||||
throw Exception("Apple Sign-In failed");
|
||||
case AuthorizationStatus.cancelled:
|
||||
case apple.AuthorizationStatus.cancelled:
|
||||
print('User cancelled');
|
||||
throw Exception("Apple Sign-In cancelled");
|
||||
}
|
||||
*/
|
||||
final rawNonce = generateNonce();
|
||||
final nonce = sha256ofString(rawNonce);
|
||||
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
// Request credential for the currently signed in Apple account.
|
||||
final appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: appleCredential.identityToken,
|
||||
rawNonce: rawNonce,
|
||||
);
|
||||
|
||||
/* // Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: String.fromCharCodes(result.credential.identityToken),
|
||||
accessToken: String.fromCharCodes(result.credential.authorizationCode));
|
||||
|
||||
*/
|
||||
// Sign in the user with Firebase. If the nonce we generated earlier does
|
||||
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
|
||||
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
|
||||
|
@ -19,7 +19,9 @@ import 'package:aitrainer_app/model/property.dart';
|
||||
import 'package:aitrainer_app/model/purchase.dart';
|
||||
import 'package:aitrainer_app/model/split_test.dart';
|
||||
import 'package:aitrainer_app/model/training_plan.dart';
|
||||
import 'package:aitrainer_app/model/training_plan_day.dart';
|
||||
import 'package:aitrainer_app/model/tutorial.dart';
|
||||
import 'package:aitrainer_app/repository/training_plan_day_repository.dart';
|
||||
import 'package:aitrainer_app/service/api.dart';
|
||||
import 'package:aitrainer_app/service/exercise_type_service.dart';
|
||||
import 'package:aitrainer_app/util/not_found_exception.dart';
|
||||
@ -105,6 +107,10 @@ class PackageApi {
|
||||
final List<SplitTest>? tests = json.map((test) => SplitTest.fromJson(test)).toList();
|
||||
//print("A/B tests: $tests");
|
||||
Cache().setSplitTests(tests);
|
||||
} else if (headRecord[0] == "TrainingPlanDay") {
|
||||
final Iterable json = jsonDecode(headRecord[1]);
|
||||
final List<TrainingPlanDay>? days = json.map((day) => TrainingPlanDay.fromJson(day)).toList();
|
||||
Cache().setTrainingPlanDays(days);
|
||||
}
|
||||
});
|
||||
|
||||
@ -114,9 +120,11 @@ class PackageApi {
|
||||
ExerciseTree tree = element as ExerciseTree;
|
||||
tree.imageUrl = await ExerciseTreeApi().buildImage(tree.imageUrl, tree.treeId);
|
||||
});
|
||||
//print("tree: $exerciseTree");
|
||||
Cache().setExerciseTree(exerciseTree);
|
||||
|
||||
TrainingPlanDayRepository trainingPlanDayRepository = TrainingPlanDayRepository();
|
||||
trainingPlanDayRepository.assignTrainingPlanDays();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import 'package:aitrainer_app/service/tracking_service.dart';
|
||||
import 'package:aitrainer_app/util/enums.dart';
|
||||
import 'package:aitrainer_app/model/tracking.dart' as model;
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:flurry/flurry.dart';
|
||||
import 'package:flurry_data/flurry_data.dart';
|
||||
import 'package:flutter_uxcam/flutter_uxcam.dart';
|
||||
|
||||
class Track with Logging {
|
||||
@ -20,7 +20,7 @@ class Track with Logging {
|
||||
|
||||
void track(TrackingEvent event, {String eventValue = ""}) {
|
||||
if (!isInDebugMode) {
|
||||
Flurry.logEvent(event.toString());
|
||||
FlurryData.logEvent(event.toString());
|
||||
// Smartlook.setGlobalEventProperty(event.toString(), eventValue, false);
|
||||
FlutterUxcam.logEventWithProperties(event.enumToString(), {"value": eventValue});
|
||||
model.Tracking tracking = model.Tracking();
|
||||
|
@ -202,8 +202,8 @@ class RegistrationPage extends StatelessWidget with Trans {
|
||||
Divider(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
getDataProtection(loginBloc),
|
||||
loginBloc.emailCheckbox ? getEmailSubscription(loginBloc) : Offstage(),
|
||||
// getDataProtection(loginBloc),
|
||||
// loginBloc.emailCheckbox ? getEmailSubscription(loginBloc) : Offstage(),
|
||||
Divider(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
|
@ -254,7 +254,7 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
onPrimary: Colors.white,
|
||||
primary: Colors.orange,
|
||||
primary: restricted ? Colors.grey[600] : Colors.orange,
|
||||
),
|
||||
child: Text(t("Start")),
|
||||
onPressed: () {
|
||||
@ -294,15 +294,25 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
}
|
||||
},
|
||||
),
|
||||
restricted
|
||||
/* restricted
|
||||
? Container(
|
||||
padding: EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
t("This is a premium function"),
|
||||
style: GoogleFonts.inter(color: Colors.blue[700]),
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.deepOrange[800],
|
||||
fontWeight: FontWeight.bold,
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(2.0, 2.0),
|
||||
blurRadius: 4.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage(),
|
||||
: Offstage(), */
|
||||
]),
|
||||
)));
|
||||
|
||||
@ -352,7 +362,7 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc) {
|
||||
return SfDataGrid(
|
||||
headerRowHeight: 30,
|
||||
rowHeight: 45,
|
||||
rowHeight: 60,
|
||||
source: TrainingPlanDetailSource(
|
||||
plan: plan,
|
||||
menuBloc: bloc.menuBloc,
|
||||
@ -390,14 +400,33 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
Navigator.of(context).pop(),
|
||||
},
|
||||
);
|
||||
}),
|
||||
},
|
||||
onDropsetTap: () => {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return DialogCommon(
|
||||
title: t("Dropset"),
|
||||
descriptions: t("A drop set is an advanced resistance training technique "),
|
||||
description2:
|
||||
t(" in which you focus on completing a set until failure - or the inability to do another repetition."),
|
||||
text: "OK",
|
||||
onTap: () => {
|
||||
Navigator.of(context).pop(),
|
||||
},
|
||||
onCancel: () => {
|
||||
Navigator.of(context).pop(),
|
||||
},
|
||||
);
|
||||
})
|
||||
}),
|
||||
headerGridLinesVisibility: GridLinesVisibility.both,
|
||||
gridLinesVisibility: GridLinesVisibility.both,
|
||||
columns: [
|
||||
GridTextColumn(
|
||||
//columnWidthMode: ColumnWidthMode.lastColumnFill,
|
||||
maximumWidth: 120,
|
||||
columnWidthMode: ColumnWidthMode.lastColumnFill,
|
||||
maximumWidth: 160,
|
||||
columnName: 'exerciseImage',
|
||||
label: Container(
|
||||
color: Colors.green[50],
|
||||
@ -472,11 +501,13 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
class TrainingPlanDetailSource extends DataGridSource {
|
||||
final TrainingPlan plan;
|
||||
final MenuBloc menuBloc;
|
||||
final VoidCallback onDropsetTap;
|
||||
final VoidCallback onWeightTap;
|
||||
final VoidCallback onRepeatTap;
|
||||
TrainingPlanDetailSource({
|
||||
required this.plan,
|
||||
required this.menuBloc,
|
||||
required this.onDropsetTap,
|
||||
required this.onWeightTap,
|
||||
required this.onRepeatTap,
|
||||
}) {
|
||||
@ -540,7 +571,7 @@ class TrainingPlanDetailSource extends DataGridSource {
|
||||
]),
|
||||
))
|
||||
])
|
||||
: dataGridCell.columnName == "weight" && dataGridCell.value == -1
|
||||
: dataGridCell.columnName == "weight" && (dataGridCell.value == -1 || dataGridCell.value == -2)
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
onWeightTap();
|
||||
@ -549,21 +580,30 @@ class TrainingPlanDetailSource extends DataGridSource {
|
||||
CustomIcon.question_circle,
|
||||
color: Colors.indigo[300],
|
||||
))
|
||||
: dataGridCell.columnName == "reps" && dataGridCell.value == -1
|
||||
: dataGridCell.columnName == "weight" && dataGridCell.value == -3
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
onRepeatTap();
|
||||
onDropsetTap();
|
||||
},
|
||||
child: Icon(
|
||||
CustomIcon.question_circle,
|
||||
color: Colors.indigo[600],
|
||||
color: Colors.orange[400],
|
||||
))
|
||||
: Text(dataGridCell.value.toString(),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
)));
|
||||
: dataGridCell.columnName == "reps" && dataGridCell.value == -1
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
onRepeatTap();
|
||||
},
|
||||
child: Icon(
|
||||
CustomIcon.question_circle,
|
||||
color: Colors.indigo[600],
|
||||
))
|
||||
: Text(dataGridCell.value.toString(),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
)));
|
||||
}).toList());
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,10 @@ import 'package:aitrainer_app/util/app_localization.dart';
|
||||
import 'package:aitrainer_app/util/trans.dart';
|
||||
import 'package:aitrainer_app/widgets/app_bar.dart';
|
||||
import 'package:aitrainer_app/widgets/dialog_common.dart';
|
||||
import 'package:aitrainer_app/widgets/dialog_html.dart';
|
||||
import 'package:aitrainer_app/widgets/menu_image.dart';
|
||||
import 'package:aitrainer_app/widgets/victory_widget.dart';
|
||||
import 'package:badges/badges.dart';
|
||||
import 'package:extended_tabs/extended_tabs.dart';
|
||||
import 'package:ezanimation/ezanimation.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@ -125,6 +127,7 @@ class _ExerciseTabs extends State<ExerciseTabs> with TickerProviderStateMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
print("init TAB ${widget.bloc.dayNames.length} index ${widget.bloc.activeDayIndex}");
|
||||
tabController = TabController(length: widget.bloc.dayNames.length, vsync: this);
|
||||
tabController.animateTo(widget.bloc.activeDayIndex, duration: Duration(milliseconds: 300));
|
||||
}
|
||||
@ -143,6 +146,13 @@ class _ExerciseTabs extends State<ExerciseTabs> with TickerProviderStateMixin {
|
||||
Widget getTabs(TrainingPlanBloc bloc) {
|
||||
return Column(children: [
|
||||
ExtendedTabBar(
|
||||
indicator: BoxDecoration(
|
||||
color: Colors.black87,
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 4.0, color: Colors.blue),
|
||||
top: BorderSide(width: 4.0, color: Colors.blue),
|
||||
)),
|
||||
labelPadding: EdgeInsets.only(left: 5, right: 5),
|
||||
tabs: getTabNames(),
|
||||
controller: tabController,
|
||||
onTap: (index) => bloc.activeDayIndex = index,
|
||||
@ -168,52 +178,57 @@ class _ExerciseTabs extends State<ExerciseTabs> with TickerProviderStateMixin {
|
||||
List<Tab> getTabNames() {
|
||||
List<Tab> tabs = [];
|
||||
widget.bloc.dayNames.forEach((element) {
|
||||
final Widget widget = RichText(
|
||||
text: TextSpan(
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)!.translate("Training Day") + ": \n",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
shadows: <Shadow>[
|
||||
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,
|
||||
),
|
||||
],
|
||||
)),
|
||||
TextSpan(
|
||||
text: element,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.yellow[400],
|
||||
shadows: <Shadow>[
|
||||
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,
|
||||
),
|
||||
],
|
||||
)),
|
||||
]));
|
||||
final Widget widget = Container(
|
||||
//height: 50,
|
||||
padding: EdgeInsets.only(top: 2, left: 5, right: 5, bottom: 2),
|
||||
color: Colors.white24,
|
||||
child: RichText(
|
||||
textScaleFactor: 0.8,
|
||||
text: TextSpan(
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)!.translate("Training Day") + ": \n",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
shadows: <Shadow>[
|
||||
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,
|
||||
),
|
||||
],
|
||||
)),
|
||||
TextSpan(
|
||||
text: element,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.yellow[400],
|
||||
shadows: <Shadow>[
|
||||
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,
|
||||
),
|
||||
],
|
||||
)),
|
||||
])));
|
||||
|
||||
tabs.add(Tab(child: widget));
|
||||
});
|
||||
@ -559,11 +574,17 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
setContext(context);
|
||||
print("detail ${widget.detail}");
|
||||
final ExercisePlanDetailState state = widget.detail.state;
|
||||
final bool done = state.equalsTo(ExercisePlanDetailState.finished) || state.equalsTo(ExercisePlanDetailState.skipped);
|
||||
final String countSerie = widget.detail.set.toString();
|
||||
final String step = (widget.detail.exercises.length).toString();
|
||||
String weight = widget.detail.weight != null ? widget.detail.weight!.toStringAsFixed(1) : "-";
|
||||
bool isDrop = false;
|
||||
if (widget.detail.weight == -3) {
|
||||
weight = t("DROP");
|
||||
isDrop = true;
|
||||
}
|
||||
String restingTime = widget.detail.restingTime == null ? "" : widget.detail.restingTime!.toStringAsFixed(0);
|
||||
bool isTest = false;
|
||||
if (widget.detail.weight != null && widget.detail.weight! == -1) {
|
||||
@ -574,6 +595,9 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
||||
if (widget.detail.repeats! == -1) {
|
||||
repeats = t("MAX");
|
||||
}
|
||||
final bool extraExercise = widget.detail.exerciseType!.name == "Warming Up" || widget.detail.exerciseType!.name == "Stretching";
|
||||
|
||||
bool buddyWarning = widget.detail.exerciseType == null ? false : widget.detail.exerciseType!.buddyWarning;
|
||||
setContext(context);
|
||||
return Container(
|
||||
color: Colors.transparent,
|
||||
@ -620,10 +644,58 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
||||
Container(
|
||||
width: 120,
|
||||
height: 80,
|
||||
child: MenuImage(
|
||||
imageName: widget.bloc.getActualImageName(widget.detail.exerciseType!.exerciseTypeId),
|
||||
workoutTreeId: widget.bloc.getActualWorkoutTreeId(widget.detail.exerciseType!.exerciseTypeId)!,
|
||||
),
|
||||
child: Badge(
|
||||
elevation: 0,
|
||||
padding: EdgeInsets.all(0),
|
||||
position: BadgePosition.bottomStart(start: -5),
|
||||
animationDuration: Duration(milliseconds: 500),
|
||||
animationType: BadgeAnimationType.slide,
|
||||
badgeColor: Colors.transparent,
|
||||
showBadge: true,
|
||||
badgeContent: IconButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return DialogHTML(
|
||||
title: widget.detail.exerciseType!.nameTranslation,
|
||||
htmlData: '<p>' + widget.detail.exerciseType!.descriptionTranslation + '</p>');
|
||||
}),
|
||||
icon: Icon(
|
||||
Icons.info_outline,
|
||||
color: Colors.yellow[200],
|
||||
)),
|
||||
child: Badge(
|
||||
elevation: 0,
|
||||
padding: EdgeInsets.all(0),
|
||||
position: BadgePosition.topEnd(end: -8),
|
||||
animationDuration: Duration(milliseconds: 500),
|
||||
animationType: BadgeAnimationType.slide,
|
||||
badgeColor: Colors.transparent,
|
||||
showBadge: buddyWarning,
|
||||
badgeContent: IconButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return DialogCommon(
|
||||
warning: true,
|
||||
text: "Warning",
|
||||
descriptions: t("Attention!"),
|
||||
description2: t("The safe and exact execution of this exercise you need a training buddy or a trainer"),
|
||||
description3: t("Execution at your own risk!"),
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
title: t('Training Buddy'),
|
||||
);
|
||||
}),
|
||||
icon: Icon(
|
||||
CustomIcon.exclamation_circle,
|
||||
color: Colors.red[800],
|
||||
)),
|
||||
child: MenuImage(
|
||||
imageName: widget.bloc.getActualImageName(widget.detail.exerciseType!.exerciseTypeId),
|
||||
workoutTreeId: widget.bloc.getActualWorkoutTreeId(widget.detail.exerciseType!.exerciseTypeId)!,
|
||||
radius: 12,
|
||||
))),
|
||||
),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
@ -656,18 +728,18 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
||||
),
|
||||
],
|
||||
)),
|
||||
widget.detail.exerciseType!.unitQuantityUnit != null
|
||||
widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise
|
||||
? TextSpan(
|
||||
text: "\n",
|
||||
)
|
||||
: TextSpan(),
|
||||
widget.detail.exerciseType!.unitQuantityUnit != null
|
||||
widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise
|
||||
? TextSpan(
|
||||
text: t(widget.detail.exerciseType!.unitQuantityUnit!) + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold))
|
||||
: TextSpan(),
|
||||
widget.detail.exerciseType!.unitQuantityUnit != null
|
||||
widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise
|
||||
? TextSpan(
|
||||
text: weight,
|
||||
style: GoogleFonts.inter(
|
||||
@ -677,37 +749,50 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
||||
TextSpan(
|
||||
text: "\n",
|
||||
),
|
||||
TextSpan(
|
||||
text: t(widget.detail.exerciseType!.unit) + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)),
|
||||
TextSpan(
|
||||
text: repeats,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
)),
|
||||
!extraExercise
|
||||
? TextSpan(
|
||||
text: t(widget.detail.exerciseType!.unit) + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold))
|
||||
: TextSpan(),
|
||||
!extraExercise
|
||||
? TextSpan(
|
||||
text: repeats,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
))
|
||||
: TextSpan(),
|
||||
TextSpan(
|
||||
text: "\n",
|
||||
),
|
||||
TextSpan(
|
||||
text: t("Set") + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)),
|
||||
TextSpan(
|
||||
text: step + "/" + countSerie,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
)),
|
||||
!extraExercise
|
||||
? TextSpan(
|
||||
text: t("Set") + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold))
|
||||
: TextSpan(),
|
||||
!extraExercise
|
||||
? TextSpan(
|
||||
text: step + "/" + countSerie,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
))
|
||||
: TextSpan(),
|
||||
TextSpan(
|
||||
text: "\n",
|
||||
),
|
||||
TextSpan(
|
||||
text: t("Resting time") + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)),
|
||||
TextSpan(
|
||||
text: restingTime + " " + t("min(s)"),
|
||||
style: GoogleFonts.inter(fontSize: 12, color: done ? Colors.grey[100] : Colors.white, fontWeight: FontWeight.bold)),
|
||||
!extraExercise
|
||||
? TextSpan(
|
||||
text: t("Resting time") + ": ",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold))
|
||||
: TextSpan(),
|
||||
!extraExercise
|
||||
? TextSpan(
|
||||
text: restingTime + " " + t("min(s)"),
|
||||
style:
|
||||
GoogleFonts.inter(fontSize: 12, color: done ? Colors.grey[100] : Colors.white, fontWeight: FontWeight.bold))
|
||||
: TextSpan(),
|
||||
]),
|
||||
)),
|
||||
isTest
|
||||
@ -740,7 +825,35 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
||||
)),
|
||||
]);
|
||||
})
|
||||
: Offstage()
|
||||
: isDrop
|
||||
? AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, snapshot) {
|
||||
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
GestureDetector(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return DialogCommon(
|
||||
warning: false,
|
||||
title: t("Drop set"),
|
||||
descriptions: t("Drop set"),
|
||||
description2: t("Recommended method:"),
|
||||
text: "OK",
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
onCancel: () => {
|
||||
Navigator.of(context).pop(),
|
||||
},
|
||||
);
|
||||
}),
|
||||
child: Icon(
|
||||
CustomIcon.question_circle,
|
||||
color: Colors.orange[200],
|
||||
size: 16,
|
||||
)),
|
||||
]);
|
||||
})
|
||||
: Offstage()
|
||||
]),
|
||||
),
|
||||
),
|
||||
|
@ -6,7 +6,6 @@ import 'package:aitrainer_app/model/customer_training_plan_details.dart';
|
||||
import 'package:aitrainer_app/util/trans.dart';
|
||||
import 'package:aitrainer_app/widgets/app_bar.dart';
|
||||
import 'package:aitrainer_app/widgets/exercise_save.dart';
|
||||
import 'package:aitrainer_app/widgets/number_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
@ -43,7 +42,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
|
||||
}, builder: (context, state) {
|
||||
return ModalProgressHUD(
|
||||
child: getExercises(bloc, detail),
|
||||
inAsyncCall: state is TrainingPlanLoading,
|
||||
inAsyncCall: state is TrainingPlanExerciseLoading,
|
||||
opacity: 0.5,
|
||||
color: Colors.black54,
|
||||
progressIndicator: CircularProgressIndicator(),
|
||||
@ -52,6 +51,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
|
||||
),
|
||||
floatingActionButton: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
|
||||
FloatingActionButton.extended(
|
||||
heroTag: "skipButton",
|
||||
onPressed: () => {
|
||||
Navigator.of(context).pop(),
|
||||
bloc.add(TrainingPlanSkipExercise(detail: detail)),
|
||||
@ -67,6 +67,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
|
||||
width: 20,
|
||||
),
|
||||
FloatingActionButton.extended(
|
||||
heroTag: "saveButton",
|
||||
onPressed: () => {
|
||||
Navigator.of(context).pop(),
|
||||
bloc.add(TrainingPlanSaveExercise(detail: detail)),
|
||||
@ -111,7 +112,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
|
||||
);
|
||||
}
|
||||
|
||||
Widget getExerciseForm(TrainingPlanBloc bloc, CustomerTrainingPlanDetails detail) {
|
||||
/* Widget getExerciseForm(TrainingPlanBloc bloc, CustomerTrainingPlanDetails detail) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 10, left: 25, right: 25),
|
||||
child: SingleChildScrollView(
|
||||
@ -224,5 +225,5 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: listWidgets,
|
||||
);
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ class MyTrainingPlans extends StatelessWidget with Trans, Logging {
|
||||
getTrainingPlan(t("Training Plans for Women"), "asset/menu/training_plans_q_woman.jpg", "for_woman"),
|
||||
getTrainingPlan(t("Training Plans of Celebrities"), "asset/menu/training_plans_q_celebrities.jpg", "celebrities"),
|
||||
getTrainingPlan(t("Training Plans for Gain Strength"), "asset/menu/training_plans_q_gain_strength.jpg", "gain_strength"),
|
||||
getTrainingPlan(t("Physical Prepare Program for Footgolfers"), "asset/menu/FG_2_edz.jpg", "footgolf"),
|
||||
]),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:aitrainer_app/library/image_cache.dart' as wt;
|
||||
import 'package:aitrainer_app/library/transparent_image.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class MenuImage extends StatelessWidget {
|
||||
final int? workoutTreeId;
|
||||
final String imageName;
|
||||
|
@ -25,10 +25,12 @@ class TreeviewParentWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget parentWidget = Text(
|
||||
this.text,
|
||||
style: GoogleFonts.archivoBlack(fontSize: fontSize, color: color ?? Colors.blue[800]!, backgroundColor: Colors.transparent),
|
||||
);
|
||||
Widget parentWidget = Text(this.text,
|
||||
style: GoogleFonts.archivoBlack(
|
||||
fontSize: fontSize,
|
||||
color: color ?? Colors.blue[800]!,
|
||||
backgroundColor: Colors.transparent,
|
||||
));
|
||||
|
||||
return Card(
|
||||
color: backgroundColor,
|
||||
|
50
pubspec.lock
50
pubspec.lock
@ -22,13 +22,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
apple_sign_in:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: apple_sign_in
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -50,6 +43,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.6.1"
|
||||
awesome_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: awesome_notifications
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.6+9"
|
||||
badges:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -442,13 +442,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.36.1"
|
||||
flurry:
|
||||
flurry_data:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flurry
|
||||
name: flurry_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.7"
|
||||
version: "0.0.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -517,20 +517,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
flutter_local_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0+4"
|
||||
flutter_local_notifications_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -1064,6 +1050,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
sign_in_with_apple:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sign_in_with_apple
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -1216,13 +1209,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timezone
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1435,4 +1421,4 @@ packages:
|
||||
version: "3.1.0"
|
||||
sdks:
|
||||
dart: ">=2.12.0 <3.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
flutter: ">=2.0.4"
|
||||
|
18
pubspec.yaml
18
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.19+89
|
||||
version: 1.1.19+90
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
@ -53,31 +53,28 @@ dependencies:
|
||||
confetti: ^0.6.0-nullsafety
|
||||
crypto: ^3.0.0
|
||||
carousel_slider: ^4.0.0-nullsafety.0
|
||||
#dropdown_search: ^0.5.0
|
||||
convex_bottom_bar: ^3.0.0
|
||||
flutter_app_badger: ^1.2.0
|
||||
#super_tooltip: ^1.0.1
|
||||
url_launcher: ^6.0.3
|
||||
extended_tabs: ^2.2.0
|
||||
upgrader: ^3.3.0
|
||||
|
||||
|
||||
firebase_core: ^1.2.0
|
||||
firebase_analytics: ^8.1.0
|
||||
firebase_messaging: ^10.0.0
|
||||
flutter_local_notifications: ^5.0.0
|
||||
firebase_auth: ^1.2.0
|
||||
firebase_remote_config: ^0.10.0
|
||||
awesome_notifications: ^0.0.6+9
|
||||
|
||||
syncfusion_flutter_gauges: ^19.1.63
|
||||
syncfusion_flutter_datagrid: ^19.1.63
|
||||
|
||||
flutter_facebook_auth: ^3.4.0
|
||||
google_sign_in: ^5.0.3
|
||||
apple_sign_in: ^0.1.0
|
||||
#sign_in_with_apple: ^3.0.0
|
||||
|
||||
sign_in_with_apple: ^3.0.0
|
||||
|
||||
#smartlook: ^1.0.7
|
||||
flurry: ^0.0.4
|
||||
flurry_data: ^0.0.1
|
||||
flutter_uxcam: ^2.0.0-beta.1
|
||||
|
||||
animated_widgets: ^1.0.6
|
||||
@ -249,6 +246,7 @@ flutter:
|
||||
- asset/menu/400m.jpg
|
||||
- asset/menu/FG_1_test.jpg
|
||||
- asset/menu/FG_1_training.jpg
|
||||
- asset/menu/FG_2_edz.jpg
|
||||
- asset/menu/alternate_dumbbell_presses.jpg
|
||||
- asset/menu/alternate_standing_shoulder_press.jpg
|
||||
- asset/menu/arnold_press.jpg
|
||||
@ -385,6 +383,7 @@ flutter:
|
||||
- asset/menu/standing_triceps_extension.jpg
|
||||
- asset/menu/stiff_legged_deadlift.jpg
|
||||
- asset/menu/straight-arm_rope_pull-down.jpg
|
||||
- asset/menu/stretching.jpg
|
||||
- asset/menu/t_bar_rows.jpg
|
||||
- asset/menu/test_center.jpg
|
||||
- asset/menu/test_on_machines.jpg
|
||||
@ -411,6 +410,7 @@ flutter:
|
||||
- asset/menu/upper_body.jpg
|
||||
- asset/menu/v_ups.jpg
|
||||
- asset/menu/wall_sit.jpg
|
||||
- asset/menu/warmup.jpg
|
||||
- asset/menu/warrior_stand.jpg
|
||||
- asset/menu/weight_free_test.jpg
|
||||
- asset/menu/weighted_bench_dip.jpg
|
||||
|
Loading…
Reference in New Issue
Block a user