1.1.22+3 corrections

This commit is contained in:
bossanyit 2021-09-05 07:47:15 +02:00
parent cebd83648b
commit e34add8185
57 changed files with 1577 additions and 1490 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -280,7 +280,10 @@
"Unleash your potential with WorkoutTest Premium!": "Unleash your potential with WorkoutTest Premium!",
"feature is reachable after you finished": "feature is reachable after you finished",
"100% test circles": "100% test rounds",
"Keep testing": "Keep Testing",
"week": "week",
"100% completed training": "100% completed training",
"after": "after",
"Keep testing": "Keep Training",
"Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.": "Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.",
"Please define your Exercise Plan": "Please define your Exercise Plan",
"Go to: 'Training Plan' - 'Edit My Custom Plan'": "Go to: 'Training Plan' - 'Edit My Custom Plan'",
@ -463,8 +466,6 @@
"because only that way can we show you your exercises, results and evaluations.": "because only that way can we show you your exercises, results and evaluations.",
"because only that way can we show you the personalized development diagrams and analysises": "because only that way can we show you the personalized development diagrams and analysises",
"because only in that way can you begin to execute a training plan": "because only in that way can you begin to execute a training plan",
"Please try to execute this exercise with exact weight and repeats what is suggested": "Please try to execute this exercise with exact weight and repeats what is suggested",
"Please try to execute this exercise with exact repeats what is suggested": "Please try to execute this exercise with exact repeats what is suggested",
"Please take a relative bigger weight and at least 12 times and do your best! MAXIMIZE it!": "Please take a relative bigger weight and at least 12 times and do your best! MAXIMIZE it!",
"During the exercise you should try your best:": "During the exercise you should try your best:",
"do it with the possible maximum repeats.": "do it with the possible maximum repeats.",
@ -509,5 +510,35 @@
"optimized training plans, customized to your fitness state and strength:": "optimized training plans, customized to your fitness state and strength:",
"Soon! Check back later for the plan details": "Soon! Check back later for the plan details",
"mins": "mins",
"set": "set"
"set": "set",
"Set / Reps / Weight": "Set / Reps / Weight",
"Weight_": "Weight",
"Got It": "Got It",
"Done!": "Done!",
"Show this tip no more": "Show this tip no more",
"Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions.": "Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions.",
"Please take a middle weight which you are able to do 8-20 times with.": "Please take a middle weight which you are able to do 8-20 times with.",
"Execute your MAXIMUM repeats with it!": "Execute your MAXIMUM repeats with it!",
"Type here your selected weight,": "Type here your selected weight,",
"then here, how many times could you repeat it!": "then here, how many times could you repeat it!",
"After you done, click to the OK button!": "After you done, click to the OK button!",
"If you executed less repeats than given, modify it, and click to OK!": "If you executed less repeats than given, modify it, and click to OK!",
"Please type in a real number!": "Please type in a real number!",
"Exception: Please type in a real number!": "Please type in a real number!",
"If you could do less, then modify and click to OK": "If you could do less, then modify and click to OK",
"Here is your tailored weight,": "Here is your tailored weight,",
"and executed with this number of repeats!": "and execute it with this number of repeats!",
"You can change the weight, if you could not set it in the training room": "You can change the weight, if you could not set it in the training room",
"Exception: Please type your repeats!": "Please type your maximum repeats!",
"For your optimal development we calculated a suitable weight and repeats": "For your optimal development we calculated a suitable weight and repeats",
"It is time to exhaust your muscles": "It is time to really exhaust your muscles",
"Next Exercise": "Next Exercise",
"and execute it with maximum repeats!": "and execute it with maximum repeats!",
"10 days Premium for free": "10 days Premium for free",
"Would you like to try all premium functions for 10 days, without any subscription or bank card data?": "Would you like to try all premium functions for 10 days, without any subscription or bank card data?",
"If you click to 'Yes', all premium functions will be available right now.": "If you click to 'Yes', all premium functions will be available right now.",
"If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.": "If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.",
"Based on your initial data, we will generate the personalized training plan for you.": "Based on your initial data, we will generate the personalized training plan for you.",
"No selected Training Plan": "No selected Training Plan",
"Min. 10 minutes": "Min. 10 minutes"
}

View File

@ -278,6 +278,9 @@
"Unleash your potential with WorkoutTest Premium!": "Bontakoztasd ki az erősségeidet WorkoutTest Prémiummal!",
"feature is reachable after you finished": "funkció elérhető számodra, miután teljesítetted",
"100% test circles": "100%-os teszt-köröd",
"week": "hét",
"100% completed training": "alatt 100%-osan végrehajtott edzést",
"after": "",
"Keep testing": "Folytasd a tesztelést",
"Enjoy also this premium feature to show all old evaluation data of your successful exercises.": "Élvezd ezt a prémium funkciót is, amely megjeleníti a korábbi gyakorlatok teljes kiértékelését",
"Please define your Exercise Plan": "Kérlek készíts edzéstervet!",
@ -507,5 +510,35 @@
"optimized training plans, customized to your fitness state and strength:": "optiomalizált edzés terveket, amelyeket a te erő- és fitnesz állapododra szabunk:",
"Soon! Check back later for the plan details": "Nemsokára! Nézz vissza később, amikor már aktiváltuk az edzésterv részleteit",
"mins": "perc",
"set": "sorozat"
"set": "sorozat",
"Set / Reps / Weight": "Sorozat / Ismétlés / Súly",
"Weight_": "Súly",
"Got It": "Értettem!",
"Done!": "Kész!",
"Show this tip no more": "Ne mutasd többet ezt a tippet",
"Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions.": "Ne felejtsd el, hogy a program csak akkor tud számodra megfelelő értéket adni, ha helyesen, és az utasítás szerint végzed el a feladatokat",
"Please take a middle weight which you are able to do 8-20 times with.": "Vegyél egy közepes súlyt, amivel 8-20 ismétlésre vagy képes.",
"Execute your MAXIMUM repeats with it!": "Végezz vele MAXIMUM ismétlést",
"Type here your selected weight,": "Írd be ide a kiválasztott súlyt,",
"then here, how many times could you repeat it!": "majd ide, hogy mennyi ismétlés sikerült belőle!",
"After you done, click to the OK button!": "Ha elvégezted a gyakorlatot, kattints az OK gombra!",
"If you executed less repeats than given, modify it, and click to OK!": "Ha kevesebb sikerült, akkor írd be és utána nyomj OK-t!",
"Please type in a real number!": "Kérlek írj be egy számot eredményként",
"Exception: Please type in a real number!": "Kérlek írj be egy számot eredményként",
"If you could do less, then modify and click to OK": "Ha kevesebb sikerült, írd be és kattints az OK-ra",
"Here is your tailored weight,": "Itt van a rádszabott súly,",
"and executed with this number of repeats!": "ennyi ismétlést végezz!",
"You can change the weight, if you could not set it in the training room": "Változtasd meg a súlyt, ha éppen nincs ennyi az edzőteremben",
"Exception: Please type your repeats!": "Kérlek írd be a maximum ismétlésed számát!",
"For your optimal development we calculated a suitable weight and repeats": "A fejlődésed érdekében meghatároztuk Neked a megfelelő súlyt és ismétlésszámot",
"It is time to exhaust your muscles": "Itt az idő, hogy igazán lemerítsd az izmaidat.",
"Next Exercise": "Következő",
"and execute it with maximum repeats!": "és hajtsd végre maximális ismétléssel!",
"10 days Premium for free": "10 nap Prémium előfizetés díjmentesen",
"Would you like to try all premium functions for 10 days, without any subscription or bank card data?": "Szeretnéd kipróbálni 10 napig az összes prémium funkciót, előfizetés és bankkártya-adatok megadása nélkül?",
"If you click to 'Yes', all premium functions will be available right now.": "Ha az 'Igen'-re kattintasz, azonnal eléred az összes prémium funkciót, edzést.",
"If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.": "Ha a 'Nem'-re kattintasz, használhatod az összes alapfunkciót, de elveszted a lehetőséget, hogy a prémium funkciókat ingyen kipróbáld.",
"Based on your initial data, we will generate the personalized training plan for you.": "A megadott adataid alapján most személyre szabott edzéstervet generálunk neked.",
"No selected Training Plan": "Nincs kiválasztott edzésterved",
"Min. 10 minutes": "Minimum 10 perc"
}

View File

@ -388,7 +388,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -531,7 +531,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -566,7 +566,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (

View File

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/sport.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/mautic_repository.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
@ -29,8 +30,8 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
weight = this.customerRepository.getWeight() == 0 ? 60 : this.customerRepository.getWeight();
height = this.customerRepository.getHeight() == 0 ? 170 : this.customerRepository.getHeight();
// selectedSport = customerRepository.getSport();
//print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString());
selectedFitnessItem = customerRepository.fitnessLevel;
selectedSport = customerRepository.getSport();
}
Sport? selectedSport;
@ -95,6 +96,7 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
} else if (event is CustomerSportChange) {
yield CustomerChangeLoading();
selectedSport = event.sport;
print("Selected Sport $selectedSport");
yield CustomerDataChanged();
} else if (event is CustomerSaveFitness) {
yield CustomerChangeLoading();
@ -137,6 +139,8 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
}
await customerRepository.saveCustomer();
MauticRepository mauticRepository = MauticRepository(customerRepository: customerRepository);
await mauticRepository.sendMauticDataChange();
Cache().initBadges();
yield CustomerSaveSuccess();
} else {
@ -151,10 +155,6 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
bool validation() {
if (customerRepository.customer == null) throw Exception("Customer object not defined");
return true;
/* return (emailValidation(customerRepository.customer!.email) == null) &&
(passwordValidation(customerRepository.customer!.password) == null) &&
(nameValidation(customerRepository.customer!.firstname) == null) &&
(nameValidation(customerRepository.customer!.name) == null); */
}
String? emailValidation(String? email) {

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:aitrainer_app/bloc/timer/timer_bloc.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
@ -26,7 +27,6 @@ class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlStat
late double initialRM;
late double unitQuantity;
late double quantity;
late double origQuantity;
late double oneRepQuantity;
late double oneRepUnitQuantity;
@ -36,6 +36,8 @@ class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlStat
double scrollOffset = 0;
late ExerciseTypeLocal exerciseTypeLocal;
List<Exercise> controlList = [];
@override
ExerciseControlBloc({required this.exerciseRepository, required this.readonly, required this.timerBloc})
: super(ExerciseControlInitial()) {
@ -53,29 +55,29 @@ class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlStat
if (oneRepQuantity > 25) {
quantity = 35;
}
origQuantity = quantity;
exerciseRepository.setUnitQuantity(unitQuantity);
exerciseRepository.setQuantity(quantity);
for (int i = 0; i < 3; i++) {
Exercise exercise = Exercise();
exercise.quantity = quantity;
exercise.unitQuantity = unitQuantity;
controlList.add(exercise);
}
timerBloc.add(TimerStart(duration: 300));
}
@override
Stream<ExerciseControlState> mapEventToState(ExerciseControlEvent event) async* {
try {
/* if (event is ExerciseControlLoad) {
yield ExerciseControlLoading();
step = 1;
yield ExerciseControlReady();
} else */
if (event is ExerciseControlQuantityChange) {
//yield ExerciseControlLoading();
if (event.step == step) {
exerciseRepository.setQuantity(event.quantity);
quantity = event.quantity;
controlList[step - 1].quantity = quantity;
}
//yield ExerciseControlReady();
} else if (event is ExerciseControlUnitQuantityChange) {
yield ExerciseControlLoading();
print("event step ${event.step} quantity ${event.quantity}");
@ -85,7 +87,8 @@ class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlStat
unitQuantity = event.quantity;
quantity = calculateQuantityByUnitQuantity().toDouble();
exerciseRepository.setQuantity(quantity);
origQuantity = quantity;
controlList[step - 1].unitQuantity = event.quantity;
controlList[step - 1].quantity = quantity;
}
yield ExerciseControlReady();
} else if (event is ExerciseControlSubmit) {
@ -94,13 +97,11 @@ class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlStat
step++;
scrollOffset = step * 400.0;
quantity = origQuantity;
if (exerciseRepository.exercise!.quantity == null) {
exerciseRepository.setQuantity(quantity);
}
exerciseRepository.end = DateTime.now();
await exerciseRepository.addExercise();
//exerciseRepository.initExercise();
step <= 3 ? timerBloc.add(TimerStart(duration: 300)) : timerBloc.add(TimerEnd(duration: 300));
}

View File

@ -25,8 +25,8 @@ class ExerciseNewBloc extends Bloc<ExerciseNewEvent, ExerciseNewState> with Logg
final CustomerRepository customerRepository;
final MenuBloc menuBloc;
late AnimationController bmiAnimationController;
double quantity = 12;
double unitQuantity = 30;
double quantity = -1;
double unitQuantity = -1;
double bmi = 0;
double bmr = 0;
double goalBMI = 0;
@ -148,6 +148,10 @@ class ExerciseNewBloc extends Bloc<ExerciseNewEvent, ExerciseNewState> with Logg
yield ExerciseNewReady();
} else if (event is ExerciseNewSubmit) {
yield ExerciseNewLoading();
if (quantity == -1 || unitQuantity == -1) {
yield ExerciseNewReady();
throw Exception("Please type in a real number");
}
exerciseRepository.end = DateTime.now();
await exerciseRepository.addExercise();
// exerciseRepository.initExercise();

View File

@ -2,8 +2,11 @@ import 'dart:async';
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/mautic_repository.dart';
import 'package:aitrainer_app/repository/split_test_respository.dart';
import 'package:aitrainer_app/repository/training_plan_repository.dart';
import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/util/enums.dart';
@ -19,7 +22,7 @@ part 'login_state.dart';
class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
final AccountBloc accountBloc;
final UserRepository userRepository;
final CustomerRepository customerRepository = CustomerRepository();
late CustomerRepository? customerRepository;
final SplitTestRepository splitTestRepository = SplitTestRepository();
final BuildContext context;
final bool isRegistration;
@ -30,7 +33,12 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
Color testColor = Colors.green[800]!;
bool emailCheckbox = true;
LoginBloc({required this.accountBloc, required this.userRepository, required this.context, required this.isRegistration})
LoginBloc(
{required this.accountBloc,
required this.userRepository,
required this.context,
required this.isRegistration,
this.customerRepository})
: super(LoginInitial()) {
String colorString = splitTestRepository.getSplitTestValue("registration_skip");
if (colorString == "red") {
@ -40,6 +48,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
if (emailCheckboxString == "0") {
emailCheckbox = false;
}
if (customerRepository == null) {
customerRepository = CustomerRepository();
}
}
@override
@ -87,56 +98,44 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
yield LoginSuccess();
} else if (event is RegistrationSubmit) {
yield LoginLoading();
/* if (!this.dataPolicyAllowed) {
throw Exception("Please accept our data policy");
} */
final String? validationError = validate();
if (validationError != null) {
yield LoginError(message: validationError);
} else {
await userRepository.addUser();
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await saveCustomer();
Track().track(TrackingEvent.registration, eventValue: "email");
customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await afterRegistration("email");
Cache().setLoginType(LoginType.email);
yield LoginSuccess();
}
} else if (event is RegistrationFB) {
yield LoginLoading();
/* if (!this.dataPolicyAllowed) {
throw Exception("Please accept our data policy");
} */
Cache().setLoginType(LoginType.fb);
await userRepository.addUserFB();
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await saveCustomer();
Track().track(TrackingEvent.registration, eventValue: "FB");
customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await afterRegistration("FB");
yield LoginSuccess();
} else if (event is RegistrationGoogle) {
yield LoginLoading();
/* if (!this.dataPolicyAllowed) {
throw Exception("Please accept our data policy");
} */
Cache().setLoginType(LoginType.google);
await userRepository.addUserGoogle();
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await saveCustomer();
Track().track(TrackingEvent.registration, eventValue: "Google");
customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await afterRegistration("Google");
yield LoginSuccess();
} else if (event is RegistrationApple) {
yield LoginLoading();
/* if (!this.dataPolicyAllowed) {
throw Exception("Please accept our data policy");
} */
Cache().setLoginType(LoginType.apple);
await userRepository.addUserApple();
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await saveCustomer();
Track().track(TrackingEvent.registration, eventValue: "Apple");
customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0;
await afterRegistration("Apple");
yield LoginSuccess();
} else if (event is DataProtectionClicked) {
@ -162,10 +161,23 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
}
}
Future<void> saveCustomer() async {
customerRepository.customer = Cache().userLoggedIn!;
customerRepository.customer!.dataPolicyAllowed = 1;
await customerRepository.saveCustomer();
Future<void> afterRegistration(String event) async {
Customer tempCustomer = customerRepository!.customer!;
customerRepository!.customer = Cache().userLoggedIn!;
customerRepository!.customer!.fitnessLevel = tempCustomer.fitnessLevel;
customerRepository!.customer!.goal = tempCustomer.goal;
customerRepository!.customer!.sex = tempCustomer.sex;
customerRepository!.customer!.dataPolicyAllowed = 1;
customerRepository!.customer!.emailSubscription = 1;
customerRepository!.customer!.syncedDate = DateTime.now();
customerRepository!.setCustomerProperty("Weight", tempCustomer.getProperty("Weight"));
customerRepository!.setCustomerProperty("Height", tempCustomer.getProperty("Height"));
await customerRepository!.saveCustomer();
MauticRepository mauticRepository = MauticRepository(customerRepository: customerRepository!);
await mauticRepository.sendMauticSubscription();
TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository();
trainingPlanRepository.generateTrainingPlan();
Track().track(TrackingEvent.registration, eventValue: event);
}
String? emailValidation(String? email) {

View File

@ -7,6 +7,8 @@ import 'package:aitrainer_app/model/customer_training_plan_details.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/model/training_plan.dart';
import 'package:aitrainer_app/model/training_plan_detail.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/training_plan_repository.dart';
import 'package:aitrainer_app/service/exercise_service.dart';
@ -29,6 +31,9 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
CustomerTrainingPlan? _myPlan;
CustomerTrainingPlanDetails? _myDetail;
TrainingPlan? _myTrainingPlan;
final List<String> trainingPlanDayNames = [];
bool started = false;
final List<String> dayNames = [];
bool restarting = false;
@ -38,6 +43,9 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
CustomerTrainingPlan? getMyPlan() => this._myPlan;
setMyPlan(CustomerTrainingPlan? myPlan) => this._myPlan = myPlan;
TrainingPlan? getMyTrainingPlan() => this._myTrainingPlan;
setMyTrainingPlan(TrainingPlan? myTrainingPlan) => this._myTrainingPlan = myTrainingPlan;
CustomerTrainingPlanDetails? getMyDetail() => this._myDetail;
setMyDetail(CustomerTrainingPlanDetails? value) => this._myDetail = value;
@ -88,6 +96,10 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
yield TrainingPlanReady();
} else if (event is TrainingPlanSaveExercise) {
yield TrainingPlanLoading();
if (event.detail.repeats == -1) {
yield TrainingPlanReady();
throw Exception("Please type your repeats!");
}
if (event.detail.weight == -3) {
print("DropSet");
event.detail.state = ExercisePlanDetailState.finished;
@ -293,6 +305,83 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
getActiveDayIndex();
}
void activateTrainingPlanDays() {
if (_myTrainingPlan == null || _myTrainingPlan!.details == null) {
return;
}
trainingPlanDayNames.clear();
String dayName = ".";
_myTrainingPlan!.details!.forEach((element) {
if (element.day != null && element.day != dayName) {
trainingPlanDayNames.add(element.day!);
dayName = element.day!;
}
});
if (trainingPlanDayNames.length == 0) {
dayName = "";
trainingPlanDayNames.add(dayName);
}
}
List<TrainingPlanDetail> trainingPlanDetailSummary(TrainingPlan plan, String dayName) {
List<TrainingPlanDetail> details = [];
TrainingPlanDetail? prev;
plan.details!.forEach((element) {
if (prev == null || element.exerciseTypeId != prev!.exerciseTypeId) {
if (element.day! == dayName) {
element.summary = getSummary(element);
details.add(element);
}
prev = element;
}
});
return details;
}
List<TrainingPlanDetail> getAllTrainingPlanDetailsSameExercise(TrainingPlanDetail detail) {
List<TrainingPlanDetail> list = [];
getMyTrainingPlan()!.details!.forEach((element) {
if (detail.exerciseTypeId == element.exerciseTypeId) {
list.add(element);
}
});
return list;
}
String getSummary(TrainingPlanDetail detail) {
String summary = "";
String set = "1";
set = detail.set.toString() + "/ ";
List<TrainingPlanDetail> details = getAllTrainingPlanDetailsSameExercise(detail);
int index = 0;
String quantities = "";
details.forEach((element) {
String delimiter = ", ";
if (index == 0) {
delimiter = "";
}
if (element.repeats == -1) {
quantities += delimiter + " MAX ";
} else {
quantities += delimiter + "${element.repeats}";
}
index++;
});
//quantities += " / ? kg";
summary = quantities;
return summary;
}
void addExtraExerciseType(String name, String dayName) {
if (Cache().getExerciseTypes() == null) {
return;

View File

@ -307,6 +307,60 @@ class SuperTooltip {
isOpen = true;
}
void showBox(BuildContext targetContext) {
final renderBox = targetContext.findRenderObject() as RenderBox;
var size = renderBox.size;
print("Size $size");
if (containsBackgroundOverlay) {
_backGroundOverlay = OverlayEntry(
builder: (context) => _AnimationWrapper(
builder: (context, opacity) => AnimatedOpacity(
opacity: opacity,
duration: const Duration(milliseconds: 600),
child: GestureDetector(
onTap: () {
if (dismissOnTapOutside) {
close();
}
},
child: Container(
decoration: ShapeDecoration(
shape: _ShapeOverlay(
touchThrougArea, touchThroughAreaShape, touchThroughAreaCornerRadius, outsideBackgroundColor))),
),
),
));
}
_ballonOverlay = OverlayEntry(
builder: (context) => _AnimationWrapper(
builder: (context, opacity) => Positioned(
left: left, //offset.dx,
top: top,
width: size.width > maxWidth! ? maxWidth : size.width,
child: AnimatedOpacity(
duration: Duration(
milliseconds: 300,
),
opacity: opacity,
child: Stack(
fit: StackFit.passthrough,
children: [_buildPopUp(), _buildCloseButton()],
),
)),
));
var overlays = <OverlayEntry>[];
if (containsBackgroundOverlay) {
overlays.add(_backGroundOverlay!);
}
overlays.add(_ballonOverlay!);
Overlay.of(targetContext)!.insertAll(overlays);
isOpen = true;
}
Widget _buildPopUp() {
return Positioned(
child: Container(
@ -675,6 +729,122 @@ class _PopupBallonLayoutDelegate extends SingleChildLayoutDelegate {
}
}
class _PopupBallonLayoutDelegateBox extends SingleChildLayoutDelegate {
final double? _minWidth;
final double? _maxWidth;
final double? _minHeight;
final double? _maxHeight;
final double? _top;
final double? _bottom;
final double? _left;
final double? _right;
final double? _outSidePadding;
_PopupBallonLayoutDelegateBox({
double? minWidth,
double? maxWidth,
double? minHeight,
double? maxHeight,
double? outSidePadding,
double? top,
double? bottom,
double? left,
double? right,
}) : _minWidth = minWidth,
_maxWidth = maxWidth,
_minHeight = minHeight,
_maxHeight = maxHeight,
_top = top,
_bottom = bottom,
_left = left,
_right = right,
_outSidePadding = outSidePadding;
@override
Offset getPositionForChild(Size size, Size childSize) {
double? calcLeftMostXtoTarget() {
double? leftMostXtoTarget;
if (_left != null) {
leftMostXtoTarget = _left;
} else if (_right != null) {
leftMostXtoTarget = max(
size.topLeft(Offset.zero).dx + _outSidePadding!, size.topRight(Offset.zero).dx - _outSidePadding! - childSize.width - _right!);
}
return leftMostXtoTarget;
}
double? calcTopMostYtoTarget() {
double? topmostYtoTarget;
if (_top != null) {
topmostYtoTarget = _top!;
} else if (_bottom != null) {
topmostYtoTarget = max(size.topLeft(Offset.zero).dy + _outSidePadding!,
size.bottomRight(Offset.zero).dy - _outSidePadding! - childSize.height - _bottom!);
}
return topmostYtoTarget;
}
return new Offset(calcLeftMostXtoTarget()!, _top!);
}
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// print("ParentConstraints: $constraints");
var calcMinWidth = _minWidth ?? 0.0;
var calcMaxWidth = _maxWidth ?? double.infinity;
var calcMinHeight = _minHeight ?? 0.0;
var calcMaxHeight = _maxHeight ?? double.infinity;
void calcMinMaxWidth() {
if (_left != null && _right != null) {
calcMaxWidth = constraints.maxWidth - (_left! + _right!);
} else if ((_left != null && _right == null) || (_left == null && _right != null)) {
// make sure that the sum of left, right + maxwidth isn't bigger than the screen width.
var sideDelta = (_left ?? 0.0) + (_right ?? 0.0) + _outSidePadding!;
if (calcMaxWidth > constraints.maxWidth - sideDelta) {
calcMaxWidth = constraints.maxWidth - sideDelta;
}
} else {
if (calcMaxWidth > constraints.maxWidth - 2 * _outSidePadding!) {
calcMaxWidth = constraints.maxWidth - 2 * _outSidePadding!;
}
}
}
void calcMinMaxHeight() {
if (_top != null && _bottom != null) {
calcMaxHeight = constraints.maxHeight - (_top! + _bottom!);
} else if ((_top != null && _bottom == null) || (_top == null && _bottom != null)) {
// make sure that the sum of top, bottom + maxHeight isn't bigger than the screen Height.
var sideDelta = (_top ?? 0.0) + (_bottom ?? 0.0) + _outSidePadding!;
if (calcMaxHeight > constraints.maxHeight - sideDelta) {
calcMaxHeight = constraints.maxHeight - sideDelta;
}
} else {
if (calcMaxHeight > constraints.maxHeight - 2 * _outSidePadding!) {
calcMaxHeight = constraints.maxHeight - 2 * _outSidePadding!;
}
}
}
var childConstraints = new BoxConstraints(
minWidth: calcMinWidth > calcMaxWidth ? calcMaxWidth : calcMinWidth,
maxWidth: calcMaxWidth,
minHeight: calcMinHeight > calcMaxHeight ? calcMaxHeight : calcMinHeight,
maxHeight: calcMaxHeight);
// print("Child constraints: $childConstraints");
return childConstraints;
}
@override
bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
return false;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
class _BubbleShape extends ShapeBorder {

View File

@ -41,7 +41,6 @@ import 'package:aitrainer_app/view/test_set_edit.dart';
import 'package:aitrainer_app/view/test_set_execute.dart';
import 'package:aitrainer_app/view/test_set_new.dart';
import 'package:aitrainer_app/view/training_plan_activate_page.dart';
import 'package:aitrainer_app/view/training_plan_execute_page.dart';
import 'package:aitrainer_app/view/training_plan_exercise.dart';
import 'package:aitrainer_app/widgets/home.dart';
import 'package:aitrainer_app/library/facebook_app_events/facebook_app_events.dart';
@ -180,8 +179,8 @@ Future<Null> main() async {
BlocProvider<TestSetExecuteBloc>(
create: (BuildContext context) => TestSetExecuteBloc(),
),
/* BlocProvider<TutorialBloc>(
create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())), */
BlocProvider<TutorialBloc>(
create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())),
BlocProvider<TrainingPlanBloc>(create: (context) {
final MenuBloc menuBloc = BlocProvider.of<MenuBloc>(context);
return TrainingPlanBloc(menuBloc: menuBloc, trainingPlanRepository: TrainingPlanRepository());
@ -283,7 +282,6 @@ class WorkoutTestApp extends StatelessWidget {
'myTrainingPlanCustom': (context) => TrainingPlanCustomPage(),
'myTrainingPlanCustomAdd': (context) => TrainingPlanCustomAddPage(),
'myTrainingPlanActivate': (context) => TrainingPlanActivatePage(),
'myTrainingPlanExecute2': (context) => TrainingPlanExecutePage(),
'myTrainingPlanExecute': (context) => TrainingPlanExecute(),
'myTrainingPlanExercise': (context) => TrainingPlanExercise(),
'myTrainingEvaluation': (context) => TrainingEvaluationPage(),

View File

@ -71,7 +71,11 @@ enum ActivityDone {
tutorialBasicLegPress,
tutorialDevelopment,
isExerciseLogSeen,
isMuscleDevelopmentSeen
isMuscleDevelopmentSeen,
isBodyTypeSeen,
exerciseSaveTestTip,
exerciseSaveTrainingTip,
exerciseSaveTestsetTip
}
extension ActivityDoneExt on ActivityDone {
@ -641,8 +645,8 @@ class Cache with Logging {
}
if (this._exercises == null || this._exercises!.isEmpty) {
setBadge("home", true);
setBadge("Muscle Build / Shape Toning", true);
setBadge("Cardio", true);
setBadge("Custom Tests", true);
setBadge("Start Training", true);
}
if (customerRepository.getHeight() == 0) {
setBadge("BMI", true);
@ -726,6 +730,8 @@ class Cache with Logging {
sharedPreferences.setBool(activity.toStr(), true);
}
bool isActivityDone(ActivityDone activity) => activitiesDone[activity.toStr()] == true;
List<Evaluation>? get evaluations => this._evaluations;
set evaluations(List<Evaluation>? value) => this._evaluations = value;

View File

@ -24,6 +24,7 @@ class Customer {
DateTime? dateChange;
int? emailSubscription;
int? sportId;
DateTime? syncedDate;
DateTime? trialDate;
LinkedHashMap<String, CustomerProperty> properties = LinkedHashMap();
@ -71,6 +72,7 @@ class Customer {
this.dataPolicyAllowed = json['dataPolicyAllowed'];
this.emailSubscription = json['emailSubscription'];
this.sportId = json['sportId'];
this.syncedDate = json['syncedDate'] == null ? null : DateTime.parse(json['syncedDate']);
this.trialDate = json['trialDate'] == null ? null : DateTime.parse(json['trialDate']);
this.dateAdd = json['dateAdd'] == null ? DateTime.parse("0000-00-00") : DateTime.parse(json['dateAdd']);
@ -96,9 +98,13 @@ class Customer {
"dateChange": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateChange!),
"emailSubscription": this.emailSubscription,
"sportId": this.sportId,
"trialDate": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.trialDate!),
"syncedDate": this.syncedDate == null ? null : DateFormat('yyyy-MM-dd HH:mm:ss').format(this.syncedDate!),
"trialDate": this.trialDate == null ? null : DateFormat('yyyy-MM-dd HH:mm:ss').format(this.trialDate!),
};
@override
String toString() => this.toJson().toString();
double getProperty(String propertyName) {
if (this.properties[propertyName] == null) {
return 0;

View File

@ -59,7 +59,7 @@ class CustomerTrainingPlanDetails {
this.exerciseTypeId = json['exerciseTypeId'];
this.set = json['set'];
this.repeats = json['repeats'] == "null" ? -1 : json['repeats'];
this.weight = json['weight'];
this.weight = json['weight'] == "null" ? 0 : json['weight'];
this.restingTime = json['restingTime'];
this.parallel = json['parallel'] == "false"
? false

View File

@ -1,7 +1,5 @@
import 'dart:collection';
import 'package:aitrainer_app/util/app_language.dart';
class Faq {
late int faqId;
late String name;

40
lib/model/mautic.dart Normal file
View File

@ -0,0 +1,40 @@
class Mautic {
late int formId;
String? firstname;
String? lastname;
String? email;
String? fitnessLevel;
String? goal;
int? databaseId;
String? subscriptionDate;
String? language;
Map<String, dynamic> toJson() => {
"formId": this.formId,
"firstname": this.firstname,
"lastname": this.lastname,
"email": this.email,
"fitnessLevel": this.fitnessLevel,
"goal": this.goal,
"databaseId": this.databaseId,
"subscriptionDate": this.subscriptionDate,
"language": this.language
};
String toForm() {
String form = "mauticform[formId]=${this.formId}";
form += this.email == null ? "" : "&mauticform[email]=${this.email}";
form += this.lastname == null ? "" : "&mauticform[f_name]=${this.lastname}";
form += this.firstname == null ? "" : "&mauticform[firstname]=${this.firstname}";
form += this.fitnessLevel == null ? "" : "&mauticform[fitness_level]=${this.fitnessLevel}";
form += this.goal == null ? "" : "&mauticform[goal]=${this.goal}";
form += this.subscriptionDate == null ? "" : "&mauticform[subscribed]=${this.subscriptionDate}";
form += this.databaseId == null ? "" : "&mauticform[database_id]=${this.databaseId}";
form += this.language == null ? "" : "&mauticform[language]=${this.language}";
return form;
}
@override
String toString() => this.toJson().toString();
}

View File

@ -1,13 +1,21 @@
import 'dart:collection';
class Sport {
late int sportId;
late String name;
late String sportNameTranslation;
HashMap<String, String> nameTranslations = HashMap();
Sport.fromJson(Map json) {
this.sportId = json['sportId'];
this.name = json['name'];
this.sportNameTranslation =
json['translations'] != null && (json['translations']).length > 0 ? json['translations'][0]['sportName'] : this.name;
nameTranslations['en'] = name;
if (json['translations'] != null && json['translations'].length > 0) {
json['translations'].forEach((translation) {
nameTranslations[translation['languageCode']] = translation['sportName'];
});
}
}
Map<String, dynamic> toJson() => {

View File

@ -10,6 +10,7 @@ class TrainingPlanDetail {
bool? parallel;
int? dayId;
String? day;
String? summary;
TrainingPlanDetail.fromJson(Map<String, dynamic> json) {
this.trainingPlanDetailId = json['trainingPlanDetailId'];
@ -35,6 +36,7 @@ class TrainingPlanDetail {
"parallel": this.parallel,
"dayId": this.dayId,
"day": this.day,
"summary": this.summary
};
@override

View File

@ -0,0 +1,49 @@
import 'package:intl/intl.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/mautic.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/service/mautic.dart';
import 'package:aitrainer_app/util/app_language.dart';
class MauticRepository {
final CustomerRepository customerRepository;
const MauticRepository({required this.customerRepository});
Future<void> sendMauticSubscription() async {
Mautic mautic = Mautic();
mautic.formId = 2;
mautic.databaseId = Cache().userLoggedIn!.customerId!;
mautic.firstname = customerRepository.customer!.firstname == null ? "" : customerRepository.customer!.firstname!;
mautic.lastname = customerRepository.customer!.name == null ? "" : customerRepository.customer!.name!;
mautic.email = customerRepository.customer!.email == null ? "" : customerRepository.customer!.email!;
if (mautic.email == null || mautic.email!.contains("privaterelay.appleid.com")) {
return;
}
mautic.fitnessLevel = customerRepository.customer!.fitnessLevel == null ? "" : customerRepository.customer!.fitnessLevel!;
mautic.goal = customerRepository.customer!.goal == null ? "" : customerRepository.customer!.goal!;
mautic.subscriptionDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now());
mautic.language = AppLanguage().appLocal.languageCode;
await MauticApi().sendMauticForm(mautic);
customerRepository.customer!.syncedDate = DateTime.now();
await customerRepository.saveCustomer();
}
Future<void> sendMauticDataChange() async {
Mautic mautic = Mautic();
mautic.formId = 3;
mautic.databaseId = Cache().userLoggedIn!.customerId!;
mautic.firstname = customerRepository.customer!.firstname == null ? "" : customerRepository.customer!.firstname!;
mautic.lastname = customerRepository.customer!.name == null ? "" : customerRepository.customer!.name!;
mautic.email = customerRepository.customer!.email == null ? "" : customerRepository.customer!.email!;
if (mautic.email == null || mautic.email!.contains("privaterelay.appleid.com")) {
return;
}
mautic.fitnessLevel = customerRepository.customer!.fitnessLevel == null ? "" : customerRepository.customer!.fitnessLevel!;
mautic.goal = customerRepository.customer!.goal == null ? "" : customerRepository.customer!.goal!;
await MauticApi().sendMauticForm(mautic);
}
}

View File

@ -63,6 +63,7 @@ class TrainingPlanRepository {
TrainingPlan? trainingPlan = this.getTrainingPlanById(trainingPlanId);
if (trainingPlan == null || trainingPlan.details == null) {
print("trainingPlan null");
return null;
}
@ -274,14 +275,26 @@ class TrainingPlanRepository {
bool isWoman = Cache().userLoggedIn!.sex == "w";
if (Cache().userLoggedIn!.goal == "shape_forming") {
if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("beginner_man");
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("man_routine1");
} else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("beginner_split");
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("man_routine3");
} else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("man_routine4");
} else {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("5day") : getTrainingPlanByInternalName("5day");
trainingPlanId = isWoman ? getTrainingPlanByInternalName("man_routine2") : getTrainingPlanByInternalName("man_routine2");
}
} else {
if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("beginner_man");
} else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("man_foundation");
} else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("basic_mass_building");
} else {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("man_routine2") : getTrainingPlanByInternalName("mass_building");
}
}
print("Generated plan $trainingPlanId fitness ${Cache().userLoggedIn!.fitnessLevel} - ${FitnessState.beginner}");
@ -290,6 +303,7 @@ class TrainingPlanRepository {
CustomerTrainingPlan? customerTrainingPlan = activateTrainingPlan(trainingPlanId);
if (customerTrainingPlan != null) {
Cache().myTrainingPlan = customerTrainingPlan;
Cache().saveMyTrainingPlan();
}
}
}

29
lib/service/mautic.dart Normal file
View File

@ -0,0 +1,29 @@
import 'dart:io';
import 'package:aitrainer_app/model/mautic.dart';
import 'package:aitrainer_app/service/logging.dart';
class MauticApi with Logging {
final String mauticUrl = "https://mautic.aitrainer.app/form/submit?formId=";
Future<void> sendMauticForm(Mautic model) async {
final String body = model.toForm();
log(" ===== mautic subscription:" + body);
HttpClient client = new HttpClient();
String url = mauticUrl + model.formId.toString();
var uri = Uri.parse(url);
final HttpClientRequest request = await client.postUrl(uri);
request.headers.set('Content-Type', 'application/x-www-form-urlencoded');
request.headers.set('cache-control', 'no-cache');
request.write(body);
HttpClientResponse result = await request.close();
client.close();
if (!(result.statusCode == 200 || result.statusCode == 302)) {
trace("mautic response: ${result.statusCode}");
throw Exception("Network error, try again later!");
}
}
}

View File

@ -57,7 +57,8 @@ enum TrackingEvent {
training_plan_execute,
training_plan_finished,
training_plan_custom,
trial
trial,
feedback_email
}
T enumFromString<T>(Iterable<T> values, String value) {
@ -103,3 +104,11 @@ extension ExerciseTypeTrainingPlanStateExt on ExerciseTypeTrainingPlanState {
bool equalsTo(ExerciseTypeTrainingPlanState state) => this.toString() == state.toString();
bool equalsStringTo(String state) => this.toStr() == state;
}
enum ExerciseSaveType { test, training, test_set }
extension ExerciseSaveTypeExt on ExerciseSaveType {
String toStr() => this.toString().split(".").last;
bool equalsTo(ExerciseSaveType type) => this.toString() == type.toString();
bool equalsStringTo(String type) => this.toStr() == type;
}

View File

@ -5,6 +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:firebase_messaging/firebase_messaging.dart';
import 'package:flurry_data/flurry_data.dart';
import 'package:flutter_uxcam/flutter_uxcam.dart';
@ -21,7 +22,7 @@ class Track with Logging {
void track(TrackingEvent event, {String eventValue = ""}) {
analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue});
if (!isInDebugMode) {
FlurryData.logEvent(event.toString());
FlurryData.logEvent(event.enumToString());
// Smartlook.setGlobalEventProperty(event.toString(), eventValue, false);
FlutterUxcam.logEventWithProperties(event.enumToString(), {"value": eventValue});
model.Tracking tracking = model.Tracking();
@ -32,6 +33,7 @@ class Track with Logging {
tracking.eventValue = eventValue;
}
tracking.dateAdd = DateTime.now();
FirebaseMessaging.instance.subscribeToTopic(event.enumToString());
TrackingApi().saveTracking(tracking);
}
}

View File

@ -65,7 +65,7 @@ class AccountPage extends StatelessWidget with Trans {
return ListView(padding: EdgeInsets.only(top: 35), children: <Widget>[
ListTile(
leading: Common.badgedIcon(Colors.grey, Icons.perm_identity, "personalData"), //Icon(Icons.perm_identity),
subtitle: Text(t("Profile")),
subtitle: Text(t("Profile") + " " + t("and") + " " + t("Sport")),
title: TextButton(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(customerName, style: TextStyle(color: Colors.blue)),
@ -108,7 +108,7 @@ class AccountPage extends StatelessWidget with Trans {
),
ListTile(
leading: Common.badgedIcon(Colors.grey, Icons.perm_contact_cal, "FitnessLevel"), //Icon(Icons.perm_contact_cal),
subtitle: Text(t("Activity") + " " + t("and") + " " + t("Sport")),
subtitle: Text(t("Activity")),
title: TextButton(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(fitnessLevel, style: TextStyle(color: Colors.blue)),

View File

@ -7,6 +7,7 @@ import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
import 'package:aitrainer_app/widgets/dialog_html.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -34,9 +35,13 @@ class _CustomerBodyTypeAnimationPageState extends State<CustomerBodyTypeAnimatio
customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
}
PreferredSizeWidget _bar = AppBarMin(
back: true,
);
setContext(context);
return Scaffold(
appBar: AppBarNav(depth: 0),
appBar: _bar,
body: Container(
height: double.infinity,
width: double.infinity,

View File

@ -2,8 +2,6 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/sport.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/model/fitness_state.dart';
@ -18,7 +16,6 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
import '../library/dropdown_search/dropdown_search.dart';
// ignore: must_be_immutable
class CustomerFitnessPage extends StatefulWidget {
@ -76,8 +73,11 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
if (fulldata) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("customerSexPage", arguments: changeBloc.customerRepository);
} else {
Navigator.of(context).popAndPushNamed("customerSexPage", arguments: changeBloc.customerRepository);
}
}
},
builder: (context, state) {
@ -103,15 +103,15 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
changeBloc.add(CustomerSave()),
}
},
backgroundColor: Color(0xffb4f500),
backgroundColor: Colors.orange[600],
icon: Icon(
CustomIcon.save,
color: Colors.black,
color: Colors.white,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white),
),
),
);
@ -141,46 +141,19 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
SizedBox(
height: h,
),
getButton("Beginner", "I am beginner", FitnessState.beginner),
SizedBox(
height: h,
),
getButton("Intermediate", "I am intermediate", FitnessState.intermediate),
SizedBox(
height: h,
),
getButton("Advanced", "I am advanced", FitnessState.advanced),
SizedBox(
height: h,
),
getButton("Professional", "I am professional", FitnessState.professional),
/* Divider(),
Text(
t("Your Primary Sport") + ":",
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.orange,
fontSize: 20,
),
), */
//getSport(changeBloc),
/* Divider(),
ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
primary: Colors.orange,
),
child: Text(fulldata ? t("Save") : t("Next")),
onPressed: () => {
changeBloc.add(CustomerSave()),
Navigator.of(context).pop(),
if (!fulldata) {Navigator.of(context).pushNamed("customerBodyTypePage", arguments: customerRepository)}
},
) */
],
),
);
@ -233,98 +206,6 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
side: BorderSide(width: 4, color: Colors.white24),
borderRadius: BorderRadius.circular(12),
);
//return
return returnCode;
}
Widget getSport(CustomerChangeBloc bloc) {
Sport? selected = bloc.selectedSport;
return Container(
padding: EdgeInsets.only(left: 65, right: 65),
child: DropdownSearch<Sport>(
dropdownSearchDecoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 15, top: 5, bottom: 5),
labelText: t("Sport"),
labelStyle: GoogleFonts.inter(fontSize: 16, color: Colors.indigo),
//fillColor: Colors.black38,
filled: false,
border: OutlineInputBorder(
gapPadding: 2.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.blue, width: 0.4),
),
),
mode: Mode.MENU,
compareFn: (Sport? i, Sport? s) {
if (i == null || s == null) {
return false;
} else {
return i.sportId == s.sportId;
}
},
showSelectedItem: true,
selectedItem: selected,
itemAsString: (data) => t(data!.sportNameTranslation),
onChanged: (data) {
bloc.add(CustomerSportChange(sport: data!));
},
dropdownBuilder: _customDropDownItem,
popupItemBuilder: _customMenuBuilder,
popupBarrierColor: Colors.white10,
//popupBackgroundColor: Colors.yellow,
items: Cache().getSports(),
dropDownButton: Icon(
Icons.arrow_drop_down,
color: Colors.indigo,
),
));
//items: FitnessItem().toList()));
}
Widget _customMenuBuilder(BuildContext context, Sport? sport, bool isSelected) {
return Container(
decoration: !isSelected
? BoxDecoration(color: Colors.grey[300])
: BoxDecoration(
border: Border.all(color: Colors.blue),
borderRadius: BorderRadius.circular(12),
color: Colors.grey[100],
),
child: ListTile(
selected: isSelected,
title: Text(
t(sport!.sportNameTranslation),
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]),
),
subtitle: Text(
t(sport.name),
style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]),
),
),
);
}
Widget _customDropDownItem(BuildContext context, Sport? item, String itemDesignation) {
return Container(
child: (item == null)
? ListTile(
contentPadding: EdgeInsets.all(0),
title: Text(
t("No item selected"),
style: GoogleFonts.inter(fontSize: 14, color: Colors.blue[600]),
),
)
: ListTile(
contentPadding: EdgeInsets.all(0),
title: Text(
t(item.sportNameTranslation),
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]),
),
subtitle: Text(
t(item.name),
style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]),
),
),
);
}
}

View File

@ -81,7 +81,7 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
customerRepository = CustomerRepository();
}
PreferredSizeWidget _bar = AppBarMin(
back: false,
back: true,
);
if (!fulldata) {
_bar = AppBarProgress(max: 14, min: 0);
@ -107,7 +107,11 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
Navigator.of(context).pushNamed("customerFitnessPage", arguments: changeBloc.customerRepository);
if (fulldata) {
Navigator.of(context).pop();
} else {
Navigator.of(context).popAndPushNamed("customerFitnessPage", arguments: changeBloc.customerRepository);
}
}
},
builder: (context, state) {
@ -136,15 +140,15 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
changeBloc.add(CustomerSave()),
}
},
backgroundColor: Color(0xffb4f500),
backgroundColor: Colors.orange[600],
icon: Icon(
CustomIcon.save,
color: Colors.black,
color: Colors.white,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white),
),
),
);

View File

@ -64,8 +64,11 @@ class _CustomerHeightPageState extends State<CustomerHeightPage> with Trans {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
if (fulldata) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("registration", arguments: changeBloc.customerRepository);
} else {
Navigator.of(context).popAndPushNamed("registration", arguments: changeBloc.customerRepository);
}
}
},
builder: (context, state) {
@ -76,15 +79,15 @@ class _CustomerHeightPageState extends State<CustomerHeightPage> with Trans {
)),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => changeBloc.add(CustomerSaveHeight()),
backgroundColor: Color(0xffb4f500),
backgroundColor: Colors.orange[600],
icon: Icon(
CustomIcon.save,
color: Colors.black,
color: Colors.white,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Finish"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white),
),
),
);

View File

@ -3,7 +3,10 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/library/dropdown_search/dropdown_search.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/sport.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
@ -385,10 +388,119 @@ class CustomerModifyPage extends StatelessWidget with Trans {
},
),
Divider(),
Divider(),
Text(
t("Your Primary Sport") + ":",
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.orange,
fontSize: 20,
),
),
getSport(customerBloc),
Divider(),
],
),
),
),
);
}
Widget getSport(CustomerChangeBloc bloc) {
Sport? selected = bloc.selectedSport;
return Container(
padding: EdgeInsets.only(left: 65, right: 65),
child: DropdownSearch<Sport>(
dropdownSearchDecoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 15, top: 5, bottom: 5),
labelText: t("Sport"),
labelStyle: GoogleFonts.inter(fontSize: 16, color: Colors.indigo),
//fillColor: Colors.black38,
filled: false,
border: OutlineInputBorder(
gapPadding: 2.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.blue, width: 0.4),
),
),
mode: Mode.MENU,
compareFn: (Sport? i, Sport? s) {
if (i == null || s == null) {
return false;
} else {
return i.sportId == s.sportId;
}
},
showSelectedItem: true,
selectedItem: selected,
itemAsString: (data) => data!.nameTranslations[AppLanguage().appLocal.toString()] != null
? data.nameTranslations[AppLanguage().appLocal.toString()]!
: data.name,
onChanged: (data) {
bloc.add(CustomerSportChange(sport: data!));
},
dropdownBuilder: _customDropDownItem,
popupItemBuilder: _customMenuBuilder,
popupBarrierColor: Colors.white10,
//popupBackgroundColor: Colors.yellow,
items: Cache().getSports(),
dropDownButton: Icon(
Icons.arrow_drop_down,
color: Colors.indigo,
),
));
//items: FitnessItem().toList()));
}
Widget _customMenuBuilder(BuildContext context, Sport? sport, bool isSelected) {
return Container(
decoration: !isSelected
? BoxDecoration(color: Colors.grey[300])
: BoxDecoration(
border: Border.all(color: Colors.blue),
borderRadius: BorderRadius.circular(12),
color: Colors.grey[100],
),
child: ListTile(
selected: isSelected,
title: Text(
sport!.nameTranslations[AppLanguage().appLocal.toString()] != null
? sport.nameTranslations[AppLanguage().appLocal.toString()]!
: sport.name,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]),
),
subtitle: Text(
sport.name,
style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]),
),
),
);
}
Widget _customDropDownItem(BuildContext context, Sport? item, String itemDesignation) {
return Container(
child: (item == null)
? ListTile(
contentPadding: EdgeInsets.all(0),
title: Text(
t("No item selected"),
style: GoogleFonts.inter(fontSize: 14, color: Colors.blue[600]),
),
)
: ListTile(
contentPadding: EdgeInsets.all(0),
title: Text(
item.nameTranslations[AppLanguage().appLocal.toString()] != null
? item.nameTranslations[AppLanguage().appLocal.toString()]!
: item.name,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]),
),
subtitle: Text(
item.name,
style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]),
),
),
);
}
}

View File

@ -64,8 +64,11 @@ class _CustomerSexPageState extends State<CustomerSexPage> with Trans {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
if (fulldata) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("customerWeightPage", arguments: changeBloc.customerRepository);
} else {
Navigator.of(context).popAndPushNamed("customerWeightPage", arguments: changeBloc.customerRepository);
}
}
},
builder: (context, state) {
@ -84,15 +87,15 @@ class _CustomerSexPageState extends State<CustomerSexPage> with Trans {
onPressed: () => {
changeBloc.add(CustomerSaveSex()),
},
backgroundColor: Color(0xffb4f500),
backgroundColor: Colors.orange[600],
icon: Icon(
CustomIcon.save,
color: Colors.black,
color: Colors.white,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white),
),
),
);

View File

@ -1,7 +1,6 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
@ -65,8 +64,11 @@ class _CustomerWeightPageState extends State<CustomerWeightPage> with Trans {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
if (fulldata) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("customerHeightPage", arguments: changeBloc.customerRepository);
} else {
Navigator.of(context).popAndPushNamed("customerHeightPage", arguments: changeBloc.customerRepository);
}
}
},
builder: (context, state) {
@ -85,15 +87,15 @@ class _CustomerWeightPageState extends State<CustomerWeightPage> with Trans {
onPressed: () => {
changeBloc.add(CustomerSaveWeight()),
},
backgroundColor: Color(0xffb4f500),
backgroundColor: Colors.orange[600],
icon: Icon(
CustomIcon.save,
color: Colors.black,
color: Colors.white,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white),
),
),
);

View File

@ -1,4 +1,3 @@
import 'package:aitrainer_app/library/button_animations.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
import 'package:flutter/material.dart';

View File

@ -231,7 +231,7 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
Divider(),
numberPickForm(exerciseBloc, 3),
SizedBox(
height: 80,
height: 120,
)
]),
)),
@ -263,12 +263,10 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
}
Widget numberPickForm(ExerciseControlBloc exerciseBloc, int step) {
final String strTimes = step == 2 ? exerciseBloc.origQuantity.toStringAsFixed(0) : "max.";
//print("step $step, quantity ${exerciseBloc.origQuantity}");
final String strTimes = step == 2 ? exerciseBloc.controlList[step - 1].quantity!.toStringAsFixed(0) : "max.";
String title = (step + 1).toString() + "/4 " + t("Control Exercise:");
LinkedHashMap args = LinkedHashMap();
final TutorialBloc tutorialBloc = BlocProvider.of<TutorialBloc>(context);
List<Widget> listWidgets = [
Text(
@ -294,7 +292,7 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
children: [
TextSpan(text: t("Please repeat with ")),
TextSpan(
text: exerciseBloc.unitQuantity.toStringAsFixed(0) +
text: exerciseBloc.controlList[step - 1].unitQuantity!.toStringAsFixed(0) +
" " +
exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!,
style: GoogleFonts.inter(
@ -324,7 +322,7 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
NumberPickerWidget(
minValue: 0,
maxValue: 200,
initalValue: exerciseBloc.origQuantity.round(),
initalValue: exerciseBloc.controlList[step - 1].quantity!.round(),
unit: t("reps"),
color: Colors.yellow[50]!,
onChange: (value) => {exerciseBloc.add(ExerciseControlQuantityChange(quantity: value.toDouble(), step: step))}),
@ -335,12 +333,6 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
onSurface: Colors.blueAccent,
),
onPressed: () {
if (tutorialBloc.isActive) {
if (!tutorialBloc.checkAction("Save")) {
return;
}
}
exerciseBloc.add(ExerciseControlSubmit(step: step));
if (step == 3) {
Navigator.of(context).pop();
@ -354,7 +346,7 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
children: [
Image.asset('asset/icon/gomb_orange_c.png', width: 140, height: 60),
Text(
t("Save"),
t("Done!"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
@ -436,9 +428,9 @@ class _UnitQuantityControlState extends State<UnitQuantityControl> with Trans {
height: 20,
),
NumberPickerWidget(
minValue: (widget.exerciseBloc.unitQuantity - 30).round(),
maxValue: (widget.exerciseBloc.unitQuantity + 30).round(),
initalValue: widget.exerciseBloc.unitQuantity.round(),
minValue: (widget.exerciseBloc.controlList[widget.step - 1].unitQuantity! - 30).round(),
maxValue: (widget.exerciseBloc.controlList[widget.step - 1].unitQuantity! + 30).round(),
initalValue: widget.exerciseBloc.controlList[widget.step - 1].unitQuantity!.round(),
unit: t("kg"),
color: Colors.yellow[50]!,
onChange: (value) => {
@ -452,9 +444,9 @@ class _UnitQuantityControlState extends State<UnitQuantityControl> with Trans {
onTap: () => {
if (changedValue == null)
{
changedValue = widget.exerciseBloc.unitQuantity,
changedValue = widget.exerciseBloc.controlList[widget.step - 1].unitQuantity,
},
//print("Changed new value $changedValue"),
print("Changed new value $changedValue, step ${widget.step}"),
widget.exerciseBloc.add(ExerciseControlUnitQuantityChange(quantity: changedValue!.toDouble(), step: widget.step)),
Navigator.of(context).pop(),
},

View File

@ -8,6 +8,7 @@ import 'package:aitrainer_app/library/custom_icon_icons.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/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/service/logging.dart';
@ -17,6 +18,7 @@ import 'package:aitrainer_app/widgets/bmi_widget.dart';
import 'package:aitrainer_app/widgets/bmr_widget.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/exercise_save.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:aitrainer_app/widgets/size_widget.dart';
import 'package:aitrainer_app/widgets/tutorial_widget.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -55,7 +57,9 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
title: t("Warning"),
descriptions: t(state.message),
text: "OK",
onTap: () => Navigator.of(context).pushNamed("login"),
onTap: () => {
Navigator.of(context).pop(),
},
onCancel: () => {
Navigator.of(context).pop(),
},
@ -114,10 +118,12 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
return SizeWidget(exerciseBloc: exerciseBloc);
}
WorkoutMenuTree? workoutTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseType.exerciseTypeId);
return Scaffold(
appBar: AppBarNav(depth: 1),
body: Container(
padding: EdgeInsets.only(top: 10, left: 20, right: 20),
padding: EdgeInsets.only(),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_black_background.jpg'),
@ -140,23 +146,24 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
onUnitQuantityChanged: (value) => exerciseBloc.add(ExerciseNewQuantityUnitChange(quantity: value)),
//onSubmit: () => confirmationDialog(exerciseBloc, menuBloc),
exerciseTypeId: exerciseType.exerciseTypeId,
)),
tip: ActivityDone.exerciseSaveTestTip,
menuImage: MenuImage(
imageName: workoutTree!.imageName,
workoutTreeId: workoutTree.id,
radius: 0,
))),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => save(exerciseBloc, menuBloc),
backgroundColor: Colors.orange[800],
backgroundColor: Colors.orange[600],
icon: Icon(
CustomIcon.save,
CustomIcon.ok_circled,
size: 20,
),
label: Text(
t("Save"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 12),
t("Done!"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16),
),
),
/* bottomNavigationBar: BottomBarMultipleExercises(
isSet: false,
exerciseTypeId: exerciseType.exerciseTypeId,
), */
);
}
@ -195,6 +202,11 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
return;
}
if (bloc.quantity == -1 || bloc.unitQuantity == -1) {
bloc.add(ExerciseNewAddError(message: "Please type in a real number!"));
return;
}
String quantity = bloc.exerciseRepository.exercise!.quantity! % 1 == 0
? bloc.exerciseRepository.exercise!.quantity!.round().toString()
: bloc.exerciseRepository.exercise!.quantity!.toString();
@ -209,9 +221,9 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
// ignore: close_sinks
final TestSetExecuteBloc executeBloc = BlocProvider.of<TestSetExecuteBloc>(context);
final question = bloc.exerciseRepository.exercise!.quantity! == 12.0
? "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?"
: "Do you save this exercise with these parameters?";
/* final question = bloc.exerciseRepository.exercise!.quantity! == 12.0
? "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?" */
final question = "Do you save this exercise with these parameters?";
showCupertinoDialog(
useRootNavigator: true,

View File

@ -37,7 +37,7 @@ class _MyDevelopmentBodyPage extends State<MyDevelopmentBodyPage> with Trans, Co
setContext(context);
return DialogPremium(
unlocked: Cache().hasPurchased,
unlockRound: 2,
unlockRound: 4,
function: "My Whole Body Development",
unlockedText: null,
onTap: () => {Navigator.of(context).pop(), Navigator.of(context).pop()},

View File

@ -163,7 +163,7 @@ class _MyDevelopmentPage extends State<MyDevelopmentPage> with Trans {
builder: (BuildContext context) {
return DialogPremium(
unlocked: Cache().hasPurchased,
unlockRound: 3,
unlockRound: 12,
function: "Predictions",
unlockedText: null,
onTap: () => {Navigator.of(context).pop()},

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/bloc/login/login_bloc.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/training_plan_repository.dart';
import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
@ -23,12 +24,23 @@ class RegistrationPage extends StatelessWidget with Trans {
@override
Widget build(BuildContext context) {
final accountBloc = BlocProvider.of<AccountBloc>(context);
CustomerRepository? customerRepository;
if (ModalRoute.of(context) != null && ModalRoute.of(context)!.settings.arguments != null) {
customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
print("CustomerFitness reg ${customerRepository.customer!.toJson()}");
}
setContext(context);
return Scaffold(
appBar: AppBarMin(),
body: BlocProvider(
create: (context) =>
LoginBloc(userRepository: UserRepository(), accountBloc: accountBloc, context: context, isRegistration: true),
create: (context) => LoginBloc(
userRepository: UserRepository(),
accountBloc: accountBloc,
context: context,
isRegistration: true,
customerRepository: customerRepository),
child: BlocConsumer<LoginBloc, LoginState>(listener: (context, state) {
if (state is LoginError) {
ScaffoldMessenger.of(context).showSnackBar(
@ -67,7 +79,9 @@ class RegistrationPage extends StatelessWidget with Trans {
}
}, builder: (context, state) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
if (customerRepository != null) {
print("Customer data ${customerRepository.customer!.toJson()}");
}
return ModalProgressHUD(
child: loadForm(loginBloc, accountBloc),
inAsyncCall: state is LoginLoading,

View File

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/bloc/settings/settings_bloc.dart';
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
@ -19,8 +17,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:toggle_switch/toggle_switch.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:mailto/mailto.dart';
import 'package:url_launcher/url_launcher.dart';
// ignore: must_be_immutable
class SettingsPage extends StatelessWidget with Trans {
@ -87,7 +85,7 @@ class SettingsPage extends StatelessWidget with Trans {
//getTuturialBasic(settingsBloc),
getTermsOfUse(),
getPrivacy(),
getFaq(),
mailTo(),
getVersion(),
//getDevice(settingsBloc),
]);
@ -264,25 +262,40 @@ class SettingsPage extends StatelessWidget with Trans {
);
}
Future<void> _printAndCopy(String cmd) async {
print(cmd);
await Clipboard.setData(ClipboardData(text: cmd));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Copied to Clipboard')),
ListTile mailTo() {
return ListTile(
leading: Icon(CustomIcon.mail_1),
title: TextButton(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text("Feedback email", style: TextStyle(color: Colors.indigo)),
Icon(
Icons.arrow_forward_ios,
color: Colors.indigo,
),
]),
style: TextButton.styleFrom(
backgroundColor: Colors.white70,
onSurface: Colors.grey,
),
onPressed: () => {
launchMailto(),
Track().track(TrackingEvent.feedback_email),
},
),
);
}
void link1() {
String? cmd;
String cmdSuffix;
if (Platform.isIOS) {
cmd = '/usr/bin/xcrun simctl openurl booted';
} else if (Platform.isAndroid) {
cmd = '\$ANDROID_HOME/platform-tools/adb shell \'am start'
' -a android.intent.action.VIEW'
' -c android.intent.category.BROWSABLE -d';
cmdSuffix = "'";
}
launchMailto() async {
String from = Cache().userLoggedIn == null ? "" : Cache().userLoggedIn!.customerId!.toStringAsFixed(0);
final mailtoLink = Mailto(
to: ['service@workouttest.com'],
subject: 'Feedback from app: $from',
body: '',
);
// Convert the Mailto instance into a string.
// Use either Dart's string interpolation
// or the toString() method.
print("Mailto: $mailtoLink");
await launch('$mailtoLink');
}
}

View File

@ -3,13 +3,16 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart';
import 'package:aitrainer_app/bloc/test_set_new/test_set_new_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_menu_tree.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_bar_multiple_exercises.dart';
import 'package:aitrainer_app/widgets/exercise_save.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
@ -29,7 +32,6 @@ class TestSetNew extends StatelessWidget with Trans {
return Scaffold(
appBar: AppBarNav(depth: 1),
body: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_black_background.jpg'),
@ -77,7 +79,7 @@ class TestSetNew extends StatelessWidget with Trans {
backgroundColor: Colors.orange[800],
icon: Icon(CustomIcon.save),
label: Text(
t("Save"),
t("Done"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16),
),
),
@ -89,6 +91,8 @@ class TestSetNew extends StatelessWidget with Trans {
}
Widget getExercises(TestSetNewBloc bloc) {
WorkoutMenuTree? workoutTree =
bloc.executeBloc.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(bloc.exerciseType.exerciseTypeId);
return ExerciseSave(
exerciseName: bloc.exerciseType.nameTranslation,
exerciseDescription: bloc.exerciseType.descriptionTranslation,
@ -103,6 +107,12 @@ class TestSetNew extends StatelessWidget with Trans {
},
onUnitQuantityChanged: (value) => bloc.add(TestSetNewChangeQuantityUnit(quantity: value)),
exerciseTypeId: bloc.exerciseType.exerciseTypeId,
tip: ActivityDone.exerciseSaveTestsetTip,
menuImage: MenuImage(
imageName: workoutTree!.imageName,
workoutTreeId: workoutTree.id,
radius: 0,
),
/* onSubmit: () {
Navigator.of(context).pop();
bloc.add(TestSetNewSubmit());

View File

@ -6,6 +6,7 @@ import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/library/tree_view.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/training_plan.dart';
import 'package:aitrainer_app/model/training_plan_detail.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/util/trans.dart';
@ -58,7 +59,7 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
title: t("Warning"),
descriptions: t(state.message),
text: "OK",
onTap: () => Navigator.of(context).pushNamed("login"),
onTap: () => Navigator.of(context).pop(),
onCancel: () => {
Navigator.of(context).pop(),
},
@ -197,21 +198,45 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
icon: Icon(Icons.list_sharp),
color: Colors.blue[800],
),
children: _getChildList(element, bloc),
children: _getDays(element, bloc),
)));
});
return listWidget;
}
List<Widget> _getChildList(TrainingPlan plan, TrainingPlanBloc bloc) {
List<Widget> _getDays(TrainingPlan plan, TrainingPlanBloc bloc) {
List<Widget> listWidget = [];
bloc.setMyTrainingPlan(plan);
bloc.activateTrainingPlanDays();
bloc.trainingPlanDayNames.forEach((value) {
listWidget.add(Container(
margin: const EdgeInsets.only(left: 4.0),
child: TreeViewChild(
startExpanded: true,
parent: TreeviewParentWidget(
text: " * " + t("Training Day") + ": " + value,
fontSize: 16,
color: Colors.white,
icon: Icon(Icons.view_day),
backgroundColor: Colors.white24,
),
children: _getChildList(plan, bloc, value),
)));
});
return listWidget;
}
List<Widget> _getChildList(TrainingPlan plan, TrainingPlanBloc bloc, String dayName) {
List<Widget> list = [];
bool restricted = (!plan.free && !Cache().hasPurchased);
list.add(Card(
margin: EdgeInsets.only(left: 10, top: 5),
color: Colors.white60,
color: Colors.white24,
child: Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Column(children: [
@ -262,7 +287,7 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
),
))
: Offstage(),
getPlanDetails(plan, bloc),
getPlanDetails(plan, bloc, dayName),
ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
@ -371,13 +396,17 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
}
}
Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc) {
Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc, String dayName) {
return SfDataGrid(
headerRowHeight: 30,
rowHeight: 60,
rowHeight: 70,
columnWidthMode: ColumnWidthMode.lastColumnFill,
defaultColumnWidth: 50,
source: TrainingPlanDetailSource(
plan: plan,
menuBloc: bloc.menuBloc,
bloc: bloc,
dayName: dayName,
onWeightTap: () => {
showDialog(
context: context,
@ -438,18 +467,23 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
columns: [
GridTextColumn(
columnWidthMode: ColumnWidthMode.lastColumnFill,
maximumWidth: 160,
maximumWidth: 130,
columnName: 'exerciseImage',
label: Container(
color: Colors.green[50],
color: Colors.black38,
padding: EdgeInsets.only(left: 8.0),
alignment: Alignment.centerLeft,
child: Text(
t('Exercise'),
style: GoogleFonts.inter(
color: Colors.white,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 0,
visible: false,
columnName: 'exerciseName',
label: Container(
@ -462,47 +496,40 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 40,
columnName: 'set',
maximumWidth: 60,
columnName: 'Set',
label: Container(
color: Colors.green[50],
color: Colors.black38,
padding: EdgeInsets.symmetric(horizontal: 2.0),
alignment: Alignment.centerLeft,
child: Text(
t('Set'),
style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 50,
columnName: 'repeats',
maximumWidth: 100,
columnWidthMode: ColumnWidthMode.fill,
columnName: 'Repeats',
label: Container(
color: Colors.green[50],
color: Colors.black38,
padding: EdgeInsets.symmetric(horizontal: 2.0),
alignment: Alignment.centerLeft,
child: Text(
t('Reps'),
t('Repeats'),
style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 60,
columnName: 'weight',
columnName: 'Weight',
label: Container(
color: Colors.green[50],
color: Colors.black38,
padding: EdgeInsets.symmetric(horizontal: 2.0),
alignment: Alignment.centerLeft,
child: Text(
t('Weight'),
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 50,
columnName: 'day',
label: Container(
color: Colors.green[50],
padding: EdgeInsets.symmetric(horizontal: 8.0),
alignment: Alignment.centerLeft,
child: Text(
t('Day'),
t('Weight_'),
style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
))),
],
@ -512,19 +539,25 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
class TrainingPlanDetailSource extends DataGridSource {
final TrainingPlan plan;
final TrainingPlanBloc bloc;
final MenuBloc menuBloc;
final dayName;
final VoidCallback onDropsetTap;
final VoidCallback onWeightTap;
final VoidCallback onRepeatTap;
TrainingPlanDetailSource({
required this.plan,
required this.menuBloc,
required this.bloc,
required this.dayName,
required this.onDropsetTap,
required this.onWeightTap,
required this.onRepeatTap,
}) {
if (plan.details != null) {
dataGridRows = plan.details!.map((dataGridRow) {
List<TrainingPlanDetail> details = bloc.trainingPlanDetailSummary(plan, dayName);
dataGridRows = details.map((dataGridRow) {
WorkoutMenuTree? menuTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(dataGridRow.exerciseTypeId);
if (menuTree == null) {
return DataGridRow(cells: []);
@ -539,9 +572,8 @@ class TrainingPlanDetailSource extends DataGridSource {
)),
DataGridCell<String>(columnName: 'exerciseName', value: menuTree.name),
DataGridCell<int>(columnName: 'set', value: dataGridRow.set),
DataGridCell<int>(columnName: 'reps', value: dataGridRow.repeats),
DataGridCell<String>(columnName: 'reps', value: dataGridRow.summary!),
DataGridCell<double>(columnName: 'weight', value: dataGridRow.weight),
DataGridCell<String>(columnName: 'day', value: dataGridRow.day),
]);
}).toList();
}
@ -557,9 +589,9 @@ class TrainingPlanDetailSource extends DataGridSource {
if (row.getCells().isEmpty) {
return null;
}
String name = row.getCells()[1].value;
String name = row.getCells()[1].value.toString();
return DataGridRowAdapter(
color: Colors.white60,
color: Colors.white12,
cells: row.getCells().map<Widget>((dataGridCell) {
return Container(
alignment: dataGridCell.columnName == "exerciseImage" ? Alignment.centerLeft : Alignment.centerLeft,
@ -573,8 +605,7 @@ class TrainingPlanDetailSource extends DataGridSource {
name,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style:
GoogleFonts.inter(fontSize: 10, color: Colors.yellow[600], fontWeight: FontWeight.bold, shadows: <Shadow>[
style: GoogleFonts.inter(fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
@ -590,7 +621,7 @@ class TrainingPlanDetailSource extends DataGridSource {
},
child: Icon(
CustomIcon.question_circle,
color: Colors.indigo[300],
color: Colors.white,
))
: dataGridCell.columnName == "weight" && dataGridCell.value == -3
? GestureDetector(
@ -608,12 +639,12 @@ class TrainingPlanDetailSource extends DataGridSource {
},
child: Icon(
CustomIcon.question_circle,
color: Colors.indigo[600],
color: Colors.white,
))
: Text(dataGridCell.value.toString(),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.indigo,
color: Colors.white,
fontWeight: FontWeight.bold,
)));
}).toList());

View File

@ -45,8 +45,20 @@ class _TrainingPlanExecuteState extends State<TrainingPlanExecute> with Trans {
),
child: BlocConsumer<TrainingPlanBloc, TrainingPlanState>(listener: (context, state) {
if (state is TrainingPlanError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
warning: true,
title: t("Warning"),
descriptions: t(state.message),
text: "OK",
onTap: () => Navigator.of(context).pop(),
onCancel: () => {
Navigator.of(context).pop(),
},
);
});
} else if (state is TrainingPlanDayFinished) {
bloc.celebrating = false;
final HashMap args = HashMap();
@ -368,7 +380,7 @@ class _ExerciseListState extends State<ExerciseList> with Trans {
bloc.getMyPlan()!.days[widget.dayName] != null &&
bloc.getMyPlan()!.days[widget.dayName]!.isNotEmpty) {
bloc.getMyPlan()!.days[widget.dayName]!.forEach((element) {
if (prev != null && prev!.exerciseTypeId != element.exerciseTypeId) {
if (prev == null || (prev != null && prev!.exerciseTypeId != element.exerciseTypeId)) {
tiles.add(GestureDetector(
onTap: () =>
bloc.getNext() != null ? executeExercise(bloc, bloc.getNext()!, context) : Navigator.of(context).pushNamed('home'),

View File

@ -1,914 +0,0 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/customer_training_plan_details.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
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/weight_control.dart';
import 'package:badges/badges.dart';
import 'package:extended_tabs/extended_tabs.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:timeline_tile/timeline_tile.dart';
// ignore: must_be_immutable
class TrainingPlanExecutePage extends StatefulWidget {
@override
_TrainingPlanExecutePageState createState() => _TrainingPlanExecutePageState();
}
class _TrainingPlanExecutePageState extends State<TrainingPlanExecutePage> with Trans {
final scrollController = ScrollController();
TrainingPlanBloc? bloc;
@override
Widget build(BuildContext context) {
final HashMap args = HashMap();
bloc = BlocProvider.of<TrainingPlanBloc>(context);
bloc!.activateDays();
setContext(context);
return Scaffold(
appBar: AppBarNav(depth: 0),
body: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_black_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: BlocConsumer<TrainingPlanBloc, TrainingPlanState>(listener: (context, state) {
if (state is TrainingPlanError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is TrainingPlanDayFinished) {
bloc!.celebrating = false;
args["bloc"] = bloc;
args["day"] = bloc!.dayNames[bloc!.activeDayIndex];
Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args);
} else if (state is TrainingPlanDayReadyToRestart) {
if (!bloc!.celebrating) {
showCupertinoDialog(
useRootNavigator: true,
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text(t("The training is finished")),
content: Column(children: [Divider(), Text(t("Do you want to restart, or select a new Training Plan?"))]),
actions: [
TextButton(
child: Text(t("New Training Plan"), textAlign: TextAlign.center),
onPressed: () => {
Navigator.pop(context),
Navigator.of(context).popAndPushNamed('myTrainingPlans'),
bloc!.restarting = false,
}),
TextButton(
child: Text(t("Restart")),
onPressed: () {
bloc!.restart();
Navigator.pop(context);
Navigator.of(context).popAndPushNamed('home');
},
)
],
));
}
}
}, builder: (context, state) {
return ModalProgressHUD(
child: ExerciseTabs(bloc: bloc!),
inAsyncCall: state is TrainingPlanLoading,
opacity: 0.5,
color: Colors.black54,
progressIndicator: CircularProgressIndicator(),
);
}),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => {
args["bloc"] = bloc,
args["day"] = bloc!.dayNames[bloc!.activeDayIndex],
bloc!.getNext() != null
? _ExerciseListState.executeExercise(bloc!, bloc!.getNext()!, context)
: Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args),
},
backgroundColor: Colors.orange[800],
icon: Icon(CustomIcon.weight_hanging),
label: Text(
t("Training") + "!",
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16),
),
),
);
}
}
class ExerciseTabs extends StatefulWidget {
final TrainingPlanBloc bloc;
ExerciseTabs({required this.bloc});
@override
_ExerciseTabs createState() => _ExerciseTabs();
}
class _ExerciseTabs extends State<ExerciseTabs> with TickerProviderStateMixin {
late TabController tabController;
@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));
}
@override
void dispose() {
tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return getTabs(widget.bloc);
}
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,
),
Expanded(
child: ExtendedTabBarView(
children: getExerciseLists(),
controller: tabController,
/// if link is true and current tabbarview over scroll,
/// it will check and scroll ancestor or child tabbarView.
link: true,
/// cache page count
/// default is 0.
/// if cacheExtent is 1, it has two pages in cache
/// null is infinity, it will cache all pages
cacheExtent: 0,
)),
]);
}
List<Tab> getTabNames() {
List<Tab> tabs = [];
widget.bloc.dayNames.forEach((element) {
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));
});
return tabs;
}
List<Widget> getExerciseLists() {
List<Widget> list = [];
widget.bloc.dayNames.forEach((element) {
list.add(ExerciseList(bloc: widget.bloc, dayName: element));
});
return list;
}
}
class ExerciseList extends StatefulWidget {
final TrainingPlanBloc bloc;
final String dayName;
ExerciseList({required this.bloc, required this.dayName});
@override
_ExerciseListState createState() => _ExerciseListState();
}
class _ExerciseListState extends State<ExerciseList> with Trans {
final scrollController = ScrollController();
double offset = 5;
@override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) {
animate();
});
super.initState();
}
@override
void didUpdateWidget(ExerciseList page) {
super.didUpdateWidget(page);
WidgetsBinding.instance!.addPostFrameCallback((_) {
animate();
});
}
void animate() {
offset = widget.bloc.getOffset();
if (scrollController.hasClients) {
scrollController.animateTo(offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
}
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
setContext(context);
return CustomScrollView(controller: scrollController, slivers: [
SliverList(delegate: SliverChildListDelegate(getTiles(widget.bloc))),
]);
}
List<Widget> getTiles(TrainingPlanBloc bloc) {
List<Widget> tiles = [];
tiles.add(getStartTile(bloc));
tiles.addAll(getExerciseTiles(bloc, context));
if (bloc.getMyPlan() != null) tiles.add(getEndTile());
return tiles;
}
Widget getStartTile(TrainingPlanBloc bloc) {
String startText = "";
String explainingText = "";
if (null == bloc.getMyPlan()) {
startText = t("No Active Training Plan");
explainingText = t("Please select one in the Training menu, or create your custom plan");
} else {
startText = bloc.isStarted() ? t("Continue your training") : t("Start your training");
explainingText = bloc.getMyPlan()!.name != null ? bloc.getMyPlan()!.name! : "";
}
return TimelineTile(
alignment: TimelineAlign.manual,
lineXY: 0.1,
isFirst: true,
afterLineStyle: const LineStyle(
color: Colors.orange,
thickness: 6,
),
indicatorStyle: IndicatorStyle(
width: 40,
color: Colors.orange,
padding: const EdgeInsets.all(8),
iconStyle: IconStyle(
color: Colors.white,
iconData: Icons.emoji_flags_rounded,
),
),
endChild: Container(
padding: EdgeInsets.only(top: 30),
constraints: const BoxConstraints(
minHeight: 120,
),
color: Colors.transparent,
child: RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
children: [
TextSpan(
text: startText,
style: GoogleFonts.inter(
fontSize: 20,
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,
),
],
)),
TextSpan(
text: "\n",
style: GoogleFonts.inter(
fontSize: 16,
color: Colors.white,
)),
TextSpan(
text: explainingText,
style: GoogleFonts.inter(
fontSize: 16,
color: Colors.white,
)),
])),
),
);
}
Widget getEndTile() {
return Container(
color: Colors.transparent,
child: TimelineTile(
alignment: TimelineAlign.manual,
lineXY: 0.1,
isLast: true,
beforeLineStyle: const LineStyle(
color: Colors.orange,
thickness: 6,
),
indicatorStyle: IndicatorStyle(
width: 40,
color: Colors.orange,
padding: const EdgeInsets.all(8),
iconStyle: IconStyle(
color: Colors.white,
iconData: Icons.thumb_up,
),
),
endChild: Container(
padding: EdgeInsets.only(top: 50),
constraints: const BoxConstraints(
minHeight: 120,
),
color: Colors.transparent,
child: RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
children: [
TextSpan(
text: "Finish!",
style: GoogleFonts.inter(
fontSize: 20,
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,
),
],
)),
])),
),
),
);
}
List<Widget> getExerciseTiles(TrainingPlanBloc bloc, BuildContext context) {
List<Widget> tiles = [];
if (bloc.getMyPlan() != null &&
bloc.getMyPlan()!.details.isNotEmpty &&
bloc.getMyPlan()!.days[widget.dayName] != null &&
bloc.getMyPlan()!.days[widget.dayName]!.isNotEmpty) {
bloc.getMyPlan()!.days[widget.dayName]!.forEach((element) {
tiles.add(
/* GestureDetector(
onTap: () => bloc.getNext() != null ? executeExercise(bloc, bloc.getNext()!, context) : Navigator.of(context).pushNamed('home'),
child: */
ExerciseTile(
bloc: bloc,
detail: element,
));
});
}
return tiles;
}
static void executeExercise(TrainingPlanBloc bloc, CustomerTrainingPlanDetails detail, BuildContext context) {
CustomerTrainingPlanDetails? next = bloc.getNext();
if (next != null) {
String title = "";
String description = "";
String description2 = "";
if (next.exerciseTypeId != detail.exerciseTypeId) {
title = AppLocalizations.of(context)!.translate("Stop!");
description = AppLocalizations.of(context)!.translate("Please continue with the next exercise in the queue:") +
next.exerciseType!.nameTranslation;
} else {
final HashMap args = HashMap();
args['exerciseType'] = next.exerciseType;
args['customerTrainingPlanDetails'] = detail;
Navigator.of(context).pushNamed('myTrainingPlanExercise', arguments: args);
return;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DialogCommon(
title: title,
descriptions: description,
description2: description2,
text: "OK",
onTap: () => {Navigator.of(context).pop()},
onCancel: () => {Navigator.of(context).pop()},
);
});
} else {
Navigator.of(context).pushNamed('home');
}
}
}
class ExerciseTile extends StatefulWidget {
final TrainingPlanBloc bloc;
final CustomerTrainingPlanDetails detail;
ExerciseTile({required this.bloc, required this.detail});
@override
_ExerciseTileState createState() => _ExerciseTileState();
}
class _ExerciseTileState extends State<ExerciseTile> with Trans {
GestureRecognizer? _tapRecognizer;
@override
void initState() {
_tapRecognizer = TapGestureRecognizer()..onTap = _onPlusMinusWeight;
super.initState();
}
@override
void dispose() {
if (_tapRecognizer != null) {
_tapRecognizer!.dispose();
}
super.dispose();
}
void _onPlusMinusWeight() {
showDialog(
context: context,
builder: (BuildContext context) {
return WeightControl(
initialValue: widget.detail.weight != null ? widget.detail.weight! : 30,
onTap: (value) => widget.bloc.add(TrainingPlanWeightChangeRecalculate(detail: widget.detail, weight: value)),
);
});
}
Widget getIndicator(ExercisePlanDetailState state) {
CustomerTrainingPlanDetails? next = widget.bloc.getNext();
bool actual = false;
if (next != null) {
if (next.exerciseTypeId == widget.detail.exerciseTypeId) {
actual = true;
}
}
if (state.equalsTo(ExercisePlanDetailState.inProgress)) {
return ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: actual ? Colors.green : Colors.orange,
child: Icon(
CustomIcon.calendar_2,
size: 28,
color: Colors.white,
)));
} else if (state.equalsTo(ExercisePlanDetailState.finished)) {
return ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: Colors.white,
child: Icon(
CustomIcon.ok_circled,
size: 40,
color: Colors.green,
)));
} else if (state.equalsTo(ExercisePlanDetailState.skipped)) {
return ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: Colors.white,
child: Icon(
CustomIcon.stop_1,
size: 40,
color: Colors.grey,
)));
} else if (state.equalsTo(ExercisePlanDetailState.extra)) {
return ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: Colors.white,
child: Icon(
CustomIcon.stopwatch_20,
size: 40,
color: Colors.blue[800],
)));
} else {
return Image.asset(
"asset/image/pict_reps_volumen_db.png",
);
}
}
@override
Widget build(BuildContext context) {
setContext(context);
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 = "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) {
weight = "TEST";
isTest = true;
}
String repeats = widget.detail.repeats!.toString();
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,
child: TimelineTile(
alignment: TimelineAlign.manual,
lineXY: 0.1,
beforeLineStyle: const LineStyle(
color: Color(0xffb4f500),
thickness: 6,
),
afterLineStyle: const LineStyle(
color: Color(0xffb4f500),
thickness: 6,
),
indicatorStyle: IndicatorStyle(
width: 40,
height: 40,
indicator: getIndicator(state),
),
startChild: Container(
child: Column(children: [
SizedBox(
height: 1,
),
SizedBox(
height: 55,
),
done
? Offstage()
: IconButton(
padding: EdgeInsets.zero,
alignment: Alignment.centerLeft,
icon: Icon(
Icons.skip_next_sharp,
size: 30,
color: Colors.orange[300],
),
onPressed: () => skip()),
]),
),
endChild: Container(
padding: EdgeInsets.only(left: 10),
child: Row(children: [
Container(
width: 120,
height: 80,
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,
),
Expanded(
child: RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
color: done ? Colors.grey[400] : Colors.white,
),
children: [
TextSpan(
text: widget.detail.exerciseType!.nameTranslation,
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
color: done ? Colors.grey[400] : Colors.orange[500],
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,
),
],
)),
widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise
? TextSpan(
text: "\n",
)
: TextSpan(),
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 && !extraExercise
? TextSpan(
text: t(weight),
style: GoogleFonts.inter(
fontSize: 12,
))
: TextSpan(),
widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise && weight != "TEST" && weight != "DROP"
? TextSpan(
text: " - +",
style: GoogleFonts.archivoBlack(
color: Colors.blue,
fontSize: 16,
),
recognizer: _tapRecognizer,
mouseCursor: SystemMouseCursors.precise,
)
: TextSpan(),
TextSpan(
text: "\n",
),
!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",
),
!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",
),
!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
? Container(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
GestureDetector(
onTap: () => showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
warning: false,
title: t("Why Test?"),
descriptions: t("This is your first exercise after at least 3 weeks."),
description2:
t("The first exercise will be a test. The following sets will be recalculated base on your test."),
description3: t("This is the most optimal way for your development"),
text: "OK",
onTap: () => Navigator.of(context).pop(),
onCancel: () => {
Navigator.of(context).pop(),
},
);
}),
child: Icon(
CustomIcon.question_circle,
color: Colors.yellowAccent[700],
size: 16,
)),
]))
: isDrop
? Container(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
GestureDetector(
onTap: () => showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
warning: false,
title: t("Drop Set"),
descriptions: t(
"Execute at least 3 sets with maximum repeats, without resting time, with decreasing the weight."),
description2:
t("The goal is to completly exhaust your muscle without lifting a ridiculous weight end the end."),
text: "OK",
onTap: () => Navigator.of(context).pop(),
onCancel: () => {
Navigator.of(context).pop(),
},
);
}),
child: Icon(
CustomIcon.question_circle,
color: Colors.orange[200],
size: 16,
)),
]))
: Offstage()
]),
),
),
);
}
void skip() {
showCupertinoDialog(
useRootNavigator: true,
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text(t("You want to skip really this exercise?")),
content: Column(children: [
Divider(),
]),
actions: [
TextButton(
child: Text(t("No")),
onPressed: () => {
Navigator.pop(context),
}),
TextButton(
child: Text(t("Yes")),
onPressed: () {
Navigator.pop(context);
widget.bloc.add(TrainingPlanSkipExercise(detail: widget.detail));
},
)
],
));
}
}

View File

@ -2,10 +2,12 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/cache.dart';
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/menu_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
@ -26,7 +28,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
body: Container(
height: double.infinity,
width: double.infinity,
padding: EdgeInsets.all(20),
//padding: EdgeInsets.all(20),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("asset/image/WT_black_background.jpg"),
@ -75,7 +77,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
backgroundColor: Colors.orange[800],
icon: Icon(CustomIcon.save),
label: Text(
isDropSet ? t("Done") : t("Save"),
t("Done"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16),
),
),
@ -92,7 +94,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
unit: detail.exerciseType!.unit,
unitQuantityUnit: detail.exerciseType!.unitQuantityUnit,
hasUnitQuantity: detail.exerciseType!.unitQuantityUnit != null,
weight: detail.weight == -1 ? 0 : detail.weight,
weight: detail.weight,
repeats: detail.repeats == -1 ? 99 : detail.repeats,
set: detail.set,
exerciseNr: detail.exercises.length + 1,
@ -100,6 +102,13 @@ class TrainingPlanExercise extends StatelessWidget with Trans {
onQuantityChanged: (value) => bloc.add(TrainingPlanRepeatsChange(repeats: value.toInt(), detail: detail)),
exerciseTypeId: detail.exerciseType!.exerciseTypeId,
originalQuantity: originalQuantity,
tip: ActivityDone.exerciseSaveTrainingTip,
menuImage: MenuImage(
imageName: bloc.getActualImageName(detail.exerciseType!.exerciseTypeId),
workoutTreeId: bloc.getActualWorkoutTreeId(detail.exerciseType!.exerciseTypeId)!,
radius: 0,
filter: false,
),
);
} else {
return getDropSet(bloc, detail);

View File

@ -44,7 +44,7 @@ class MyTrainingPlans extends StatelessWidget with Trans, Logging {
title: t("Warning"),
descriptions: t(state.message),
text: "OK",
onTap: () => Navigator.of(context).pushNamed("login"),
onTap: () => Navigator.of(context).pop(),
onCancel: () => {
Navigator.of(context).pop(),
},

View File

@ -286,7 +286,7 @@ class _BMRState extends State<BMR> with Trans {
),
),
mode: Mode.MENU,
compareFn: (FitnessState i, FitnessState s) => i.isEqual(s),
compareFn: (FitnessState? i, FitnessState? s) => i!.isEqual(s),
showSelectedItem: true,
selectedItem: FitnessItem().getItem(fitnessLevel),
itemAsString: (data) => t(data!.stateText),
@ -301,7 +301,6 @@ class _BMRState extends State<BMR> with Trans {
color: Colors.yellow[200],
),
));
//items: FitnessItem().toList()));
}
Widget _customMenuBuilder(BuildContext context, FitnessState? item, bool isSelected) {

View File

@ -88,7 +88,7 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
alignment: AlignmentDirectional.topEnd,
children: [
Text(
widget.unlocked ? t("Keep testing") : t("Go Premium") + " ",
widget.unlocked ? t("Keep training") : t("Go Premium") + " ",
style: GoogleFonts.archivoBlack(
fontSize: widget.unlocked ? 20 : 24,
color: Colors.yellow[400],
@ -257,11 +257,7 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
list.add(TextSpan(text: t(" ")));
list.add(
TextSpan(
text: widget.unlockRound == 1
? t("the first")
: widget.unlockRound == 2
? t("the second")
: t("the third"),
text: widget.unlockRound.toString() + " " + t("week"),
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
@ -282,7 +278,7 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
),
);
list.add(TextSpan(text: t(" ")));
list.add(TextSpan(text: t("100% test circles")));
list.add(TextSpan(text: t("100% completed training")));
return list;
}

View File

@ -120,7 +120,7 @@ class _DialogTrialWidgetState extends State<DialogTrialWidget> with Trans {
children: [
Image.asset('asset/icon/gomb_lila_b.png', width: 100, height: 45),
Text(
t("Nem"),
t("No"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
@ -132,7 +132,7 @@ class _DialogTrialWidgetState extends State<DialogTrialWidget> with Trans {
children: [
Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45),
Text(
t("Igen"),
t("Yes"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],

View File

@ -1,21 +1,113 @@
import 'dart:async';
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:aitrainer_app/widgets/time_picker.dart';
import 'package:aitrainer_app/widgets/tutorial_widget.dart';
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:keyboard_actions/keyboard_actions_config.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';
import 'package:wakelock/wakelock.dart';
import 'dialog_common.dart';
import 'dialog_html.dart';
enum Explanations { intro, introBold, explanationWeight, explanationRepeats, explanationButton, explanationButtonExt }
extension ExplanationsExt on Explanations {
String toStr() => this.toString().split(".").last;
bool equalsTo(Explanations type) => this.toString() == type.toString();
bool equalsStringTo(String type) => this.toStr() == type;
}
class ExplanationExt {
final ActivityDone tip;
final String? unitQuantityUnit;
final double? weight;
final int? repeats;
ExplanationExt({
required this.tip,
this.unitQuantityUnit,
required this.weight,
required this.repeats,
});
String getExplanation(Explanations explanation) {
String expl = "";
if (this.tip.equalsTo(ActivityDone.exerciseSaveTestTip)) {
if (explanation.equalsTo(Explanations.intro)) {
expl = "Please take a middle weight which you are able to do 8-20 times with.";
} else if (explanation.equalsTo(Explanations.introBold)) {
expl = "Execute your MAXIMUM repeats with it!";
} else if (explanation.equalsTo(Explanations.explanationWeight)) {
expl = "Type here your selected weight,";
} else if (explanation.equalsTo(Explanations.explanationRepeats)) {
expl = "then here, how many times could you repeat it!";
} else if (explanation.equalsTo(Explanations.explanationButton)) {
expl = "After you done, click to the OK button!";
} else if (explanation.equalsTo(Explanations.explanationButtonExt)) {
expl = "";
}
} else if (this.tip.equalsTo(ActivityDone.exerciseSaveTrainingTip)) {
if (unitQuantityUnit != null) {
if (weight == -1) {
if (explanation.equalsTo(Explanations.intro)) {
expl = "Please take a middle weight which you are able to do 8-20 times with.";
} else if (explanation.equalsTo(Explanations.introBold)) {
expl = "Execute your MAXIMUM repeats with it!";
}
} else if (repeats == 99) {
if (explanation.equalsTo(Explanations.intro)) {
expl = "It is time to exhaust your muscles";
}
} else {
if (explanation.equalsTo(Explanations.intro)) {
expl = "For your optimal development we calculated a suitable weight and repeats";
} else if (explanation.equalsTo(Explanations.introBold)) {
expl = "You can change the weight, if you could not set it in the training room";
}
}
if (explanation.equalsTo(Explanations.explanationButtonExt)) {
if (repeats != 99) {
expl = "If you could do less, then modify and click to OK";
}
} else if (explanation.equalsTo(Explanations.explanationWeight)) {
if (weight == -1 || weight == -2) {
expl = "Type here your selected weight,";
} else {
expl = "Here is your tailored weight,";
}
} else if (explanation.equalsTo(Explanations.explanationRepeats)) {
if (repeats == 99) {
expl = "and execute it with maximum repeats!";
} else {
expl = "and executed with this number of repeats!";
}
}
} else {
if (repeats == 99) {
if (explanation.equalsTo(Explanations.intro)) {
expl = "Please repeat as much times as you can! MAXIMIZE it!";
}
} else {
if (explanation.equalsTo(Explanations.intro)) {
expl = "Please try to execute this exercise with exact repeats what is suggested";
}
}
}
if (explanation.equalsTo(Explanations.explanationButton)) {
expl = "After you done, click to the OK button!";
}
} else if (this.tip.equalsTo(ActivityDone.exerciseSaveTestsetTip)) {}
return expl;
}
}
// ignore: must_be_immutable
class ExerciseSave extends StatefulWidget {
final ValueChanged<double> onQuantityChanged;
@ -33,6 +125,8 @@ class ExerciseSave extends StatefulWidget {
final int? set;
final int? exerciseNr;
final int? originalQuantity;
final MenuImage menuImage;
final ActivityDone? tip;
ExerciseSave(
{required this.onQuantityChanged,
@ -49,7 +143,9 @@ class ExerciseSave extends StatefulWidget {
this.repeats,
this.set,
this.exerciseNr,
this.originalQuantity});
this.originalQuantity,
required this.menuImage,
this.tip});
@override
_ExerciseSaveState createState() => _ExerciseSaveState();
}
@ -72,12 +168,6 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
@override
initState() {
super.initState();
_controller1.text = widget.weight == null
? "0"
: widget.weight! % widget.weight!.round() == 0
? widget.weight!.toStringAsFixed(0)
: widget.weight!.toStringAsFixed(1);
_controller2.text = widget.repeats == null ? "12" : widget.repeats!.toStringAsFixed(0);
_nodeText1.addListener(() {
if (_nodeText1.hasFocus) {
_controller1.selection = TextSelection(baseOffset: 0, extentOffset: _controller1.text.length);
@ -98,25 +188,31 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
}
SchedulerBinding.instance!.addPostFrameCallback((_) {
/* //final TutorialBloc bloc = BlocProvider.of<TutorialBloc>(context);
if (bloc.actualCheck == "directTest") {
_controller1.text = widget.weight == null || widget.weight == -1
? "--"
: widget.weight! % widget.weight!.round() == 0
? widget.weight!.toStringAsFixed(0)
: widget.weight!.toStringAsFixed(1);
_controller2.text = widget.repeats == null
? "--"
: widget.repeats! == 99
? "MAX"
: widget.repeats!.toStringAsFixed(0);
if (widget.unitQuantityUnit != null && widget.tip != null && Cache().isActivityDone(widget.tip!) == false) {
Timer(
Duration(milliseconds: 2000),
() => {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DialogCommon(
title: t("Attention"),
descriptions: t(widget.exerciseTask),
text: "OK",
onTap: () => Navigator.of(context).pop(),
onCancel: () => Navigator.of(context).pop(),
);
})
TutorialWidget().explanation(
context,
ExplanationWidget(
unitQuantityUnit: widget.unitQuantityUnit,
unit: widget.unit,
tip: widget.tip,
weight: widget.weight,
repeats: widget.repeats,
)),
});
} */
}
});
}
@ -189,13 +285,44 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
}
Widget getExerciseWidget() {
ExplanationExt expl = ExplanationExt(
tip: widget.tip!,
weight: widget.weight,
repeats: widget.repeats,
unitQuantityUnit: widget.unitQuantityUnit,
);
return KeyboardActions(
config: _buildConfig(context),
child: Container(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[
Text(
Stack(alignment: Alignment.bottomLeft, children: [
Badge(
elevation: 0,
padding: EdgeInsets.all(0),
position: BadgePosition.topEnd(top: 5, end: 5),
animationDuration: Duration(milliseconds: 1500),
animationType: BadgeAnimationType.fade,
badgeColor: Colors.transparent,
showBadge: true,
badgeContent: IconButton(
iconSize: 30,
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) {
return DialogHTML(title: widget.exerciseName, htmlData: '<p>' + widget.exerciseDescription + '</p>');
}),
icon: Icon(
CustomIcon.info_circle,
color: Colors.white,
)),
child: widget.menuImage,
),
Container(
padding: EdgeInsets.only(left: 10, bottom: 10, right: 10),
child: Text(
widget.exerciseName,
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
@ -217,29 +344,57 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
overflow: TextOverflow.fade,
maxLines: 4,
softWrap: true,
textAlign: TextAlign.center,
),
SizedBox(
height: 15,
),
InkWell(
child: Text(
t("Exercise descripton") + " »",
style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[200]),
),
onTap: () => {
showDialog(
context: context,
builder: (BuildContext context) {
return DialogHTML(title: widget.exerciseName, htmlData: '<p>' + widget.exerciseDescription + '</p>');
})
))
]),
ListTile(
leading: IconButton(
iconSize: 30,
onPressed: () => {
if (widget.unitQuantityUnit != null)
{
TutorialWidget().explanation(
context,
ExplanationWidget(
unitQuantityUnit: widget.unitQuantityUnit,
unit: widget.unit,
tip: widget.tip,
weight: widget.weight,
repeats: widget.repeats,
))
}
},
icon: Icon(
CustomIcon.info_circle,
color: Colors.orange[100],
)),
subtitle: Text(
t(expl.getExplanation(Explanations.intro)),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.bold,
shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black54,
),
Divider(
color: Colors.transparent,
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 12.0,
color: Colors.black54,
),
Text(
t(widget.exerciseTask),
],
),
maxLines: 3,
textAlign: TextAlign.left,
overflow: TextOverflow.fade,
softWrap: true,
),
),
expl.getExplanation(Explanations.introBold).length > 0
? Text(
t(expl.getExplanation(Explanations.introBold)),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.orange,
@ -261,10 +416,8 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
),
Divider(
color: Colors.transparent,
),
)
: Offstage(),
widget.unit == "second"
? Text(
getTimeGoal(widget.originalQuantity),
@ -289,29 +442,60 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
)
: Offstage(),
columnQuantityUnit(),
Divider(
color: Colors.transparent,
),
columnQuantity(),
Divider(
color: Colors.transparent,
),
widget.hasUnitQuantity
? Text(
widget.set == null || widget.exerciseNr == null
? t("Step") + ": " + "1/4"
: t("Step") + ": " + "${widget.exerciseNr}/${widget.set}",
Text(
t(expl.getExplanation(Explanations.explanationButton)),
style: GoogleFonts.inter(
fontSize: 22,
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.bold,
shadows: <Shadow>[
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,
),
],
),
maxLines: 3,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
)
: Offstage(),
),
Padding(
padding: const EdgeInsets.only(left: 35, right: 35),
child: Text(
t(expl.getExplanation(Explanations.explanationButtonExt)),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.orange,
fontWeight: FontWeight.bold,
shadows: <Shadow>[
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,
),
],
),
maxLines: 3,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
)),
Divider(
color: Colors.transparent,
),
@ -476,3 +660,168 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
return row;
}
}
class ExplanationWidget extends StatefulWidget {
final String? unitQuantityUnit;
final String unit;
final ActivityDone? tip;
final double? weight;
final int? repeats;
const ExplanationWidget({
Key? key,
this.unitQuantityUnit,
required this.unit,
this.tip,
this.weight,
this.repeats,
}) : super(key: key);
@override
_ExplanationWidgetState createState() => _ExplanationWidgetState();
}
class _ExplanationWidgetState extends State<ExplanationWidget> with Trans {
bool _selected = false;
@override
Widget build(BuildContext context) {
ExplanationExt expl = ExplanationExt(
tip: widget.tip!,
weight: widget.weight,
repeats: widget.repeats,
unitQuantityUnit: widget.unitQuantityUnit,
);
setContext(context);
return Material(
color: Colors.transparent,
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
child: Text(t(expl.getExplanation(Explanations.intro)),
maxLines: 5,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
))),
Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
child: Text(t(expl.getExplanation(Explanations.introBold)),
maxLines: 5,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
color: Colors.orange,
fontWeight: FontWeight.bold,
fontSize: 16,
))),
Stack(children: [
Padding(
padding: const EdgeInsets.only(top: 13, left: 18, right: 18),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
TextFormField(
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5),
labelText: t(widget.unitQuantityUnit!),
labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]),
fillColor: Colors.black38,
filled: true,
border: OutlineInputBorder(
gapPadding: 8.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
initialValue: ".",
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.transparent),
onChanged: (value) {}),
])),
Container(
padding: EdgeInsets.only(top: 35, left: 35, right: 35),
child: Text(
t(expl.getExplanation(Explanations.explanationWeight)),
style: GoogleFonts.archivoBlack(fontSize: 23, color: Colors.yellow[300]),
)),
]),
Stack(children: [
Padding(
padding: const EdgeInsets.only(top: 10, left: 15, right: 15),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
TextFormField(
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5),
labelText: t(widget.unit),
labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]),
fillColor: Colors.black38,
filled: true,
border: OutlineInputBorder(
gapPadding: 8.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
initialValue: ".",
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.transparent),
onChanged: (value) {}),
])),
Container(
padding: EdgeInsets.only(top: 25, left: 35, right: 35),
child: Text(
t(expl.getExplanation(Explanations.explanationRepeats)),
style: GoogleFonts.archivoBlack(fontSize: 23, color: Colors.yellow[300]),
)),
]),
Padding(
padding: const EdgeInsets.only(top: 10, left: 5, right: 5),
child: Text(
t("Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions."),
maxLines: 5,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
color: Colors.white,
fontSize: 14,
))),
Divider(),
GestureDetector(
onTap: () => {
TutorialWidget().close(),
if (_selected && widget.tip != null)
{
Cache().setActivityDonePrefs(widget.tip!),
}
},
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45),
Text(
t("Got It"),
style: TextStyle(fontSize: 20, color: Colors.white),
),
],
)),
Theme(
data: ThemeData(unselectedWidgetColor: Colors.white38),
child: CheckboxListTile(
value: _selected,
onChanged: (bool? checked) {
setState(() {
_selected = checked!;
});
},
checkColor: Colors.white,
activeColor: Colors.orange[600],
controlAffinity: ListTileControlAffinity.leading,
title: Text(
t("Show this tip no more"),
style: GoogleFonts.inter(color: Colors.grey),
)))
],
));
}
}

View File

@ -83,16 +83,16 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
context: context,
builder: (BuildContext context) {
return DialogTrialWidget(
title: "10 days Premium for free",
description: "Would you like to try all premium functions for 10 days, without any subscription or bank card data?",
title: t("10 days Premium for free"),
description: t("Would you like to try all premium functions for 10 days, without any subscription or bank card data?"),
widget: Column(children: [
Text(
"If you click to 'Yes', all premium functions will be available right now.",
t("If you click to 'Yes', all premium functions will be available right now."),
style: GoogleFonts.inter(color: Colors.white),
),
Divider(),
Text(
"If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.",
t("If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free."),
style: GoogleFonts.inter(color: Colors.white),
),
]),

View File

@ -25,6 +25,46 @@ class TutorialWidget with Trans, Logging {
}
}
Widget explanationFrame(Widget widget) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [BoxShadow(color: Colors.black, offset: Offset(0, 10), blurRadius: 10)],
image: DecorationImage(
image: AssetImage('asset/image/WT_plainblack_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: widget,
);
}
void explanation(BuildContext context, Widget widget) {
tooltip = SuperTooltip(
top: 120,
left: 20,
right: 20,
backgroundColor: Colors.black87,
popupDirection: TooltipDirection.up,
maxWidth: 330,
borderColor: Colors.transparent,
borderWidth: 1.0,
minimumOutSidePadding: 20,
//snapsFarAwayVertically: false,
showCloseButton: ShowCloseButton.outside,
closeButtonColor: Colors.grey,
dismissOnTapOutside: true,
outsideBackgroundColor: Colors.black.withOpacity(0.6),
hasShadow: true,
touchThrougArea: null,
//onClose: () => bloc.add(TutorialFinished()),
custom: true,
content: explanationFrame(widget));
tooltip!.showBox(context);
}
void tip(BuildContext context) {
final TutorialBloc bloc = BlocProvider.of<TutorialBloc>(context);
if (bloc.action == null) {

View File

@ -707,6 +707,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
mailto:
dependency: "direct main"
description:
name: mailto
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
matcher:
dependency: transitive
description:
@ -1294,12 +1301,12 @@ packages:
source: hosted
version: "3.5.1"
url_launcher:
dependency: transitive
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.3"
version: "6.0.9"
url_launcher_linux:
dependency: transitive
description:
@ -1320,7 +1327,7 @@ packages:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.0.4"
url_launcher_web:
dependency: transitive
description:

View File

@ -58,6 +58,8 @@ dependencies:
upgrader: ^3.5.1
web_browser: ^0.5.0
flutter_fadein: ^2.0.0
mailto: ^2.0.0
url_launcher: ^6.0.9
firebase_core: ^1.5.0
firebase_analytics: ^8.1.0
@ -280,6 +282,7 @@ flutter:
- asset/menu/close_grip_front_lat_pulldown.jpg
- asset/menu/close_grip_pull_ups.jpg
- asset/menu/close_reverse_grip_lat_pulldown.jpg
- asset/menu/close_reverse_grip_pull_ups.jpg
- asset/menu/concentration.jpg
- asset/menu/cooper.jpg
- asset/menu/crisscross.jpg
@ -294,6 +297,7 @@ flutter:
- asset/menu/donkey_calf_raises.jpg
- asset/menu/dumbbell_alternate_bicep_curl.jpg
- asset/menu/dumbell_bench_presses.jpg
- asset/menu/dumbbell_presses.jpg
- asset/menu/ez_bar_burl.jpg
- asset/menu/flyes.jpg
- asset/menu/forward_raise.jpg
@ -358,6 +362,7 @@ flutter:
- asset/menu/seated_dumbbell_curl.jpg
- asset/menu/seated_dumbbell_shoulder_press.jpg
- asset/menu/seated_lateral_raises.jpg
- asset/menu/seated_leg_curls.jpg
- asset/menu/seated_triceps_extension.jpg
- asset/menu/shrugs.jpg
- asset/menu/side_plank.jpg
@ -372,6 +377,7 @@ flutter:
- asset/menu/sizes.jpg
- asset/menu/smith_machine_chest_press.jpg
- asset/menu/smith_machine_squats.jpg
- asset/menu/smith_machine_front_press.jpg
- asset/menu/squats_with_kettlebell.jpg
- asset/menu/squat_jump_weight.jpg
- asset/menu/squat_jump.jpg
@ -388,6 +394,7 @@ flutter:
- asset/menu/standing_single_arm_lateral_raises.jpg
- asset/menu/standing_triceps_extension.jpg
- asset/menu/stiff_legged_deadlift.jpg
- asset/menu/straight_arm_pulldown.jpg
- asset/menu/straight-arm_rope_pull-down.jpg
- asset/menu/stretching.jpg
- asset/menu/t_bar_rows.jpg