WT 1.1.11+3 Evaluation, null-safe fixes

This commit is contained in:
bossanyit 2021-04-12 00:51:09 +02:00
parent 01148c6e39
commit 10c8a0a2d8
73 changed files with 3256 additions and 676 deletions

View File

@ -8,7 +8,8 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="io.flutter.app.FlutterApplication"
android:label="WorkoutTest"
android:label="WorkoutTest"
android:usesCleartextTraffic="true"
android:icon="@mipmap/launcher_icon">
<activity
android:name="com.aitrainer.aitrainer_app.MainActivity"

BIN
asset/image/kupa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
asset/menu/FG_1_test.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@ -32,7 +32,9 @@
"Exception: Customer does not exist or the password is wrong": "The email does not exist or the password is wrong",
"Exception: Facebook signup was not successful. Please try another method": "Facebook signup was not successful. Please try another method",
"Exception: Customer exists": "The email address has been registered already",
"Exception: Exception: Facebook login was not successful": "Facebook signin was not successful",
"Exception: The email address has been registered already":"The email address has been registered already",
"Exception: Exception: The email address has been registered already":"The email address has been registered already",
"Exception: Please type an email address": "Please type an email address",
"Exception: Password too short": "Password too short (at least 9 characters)",
"Exception: Google Sign In failed":"Google Sign In failed",
@ -83,6 +85,7 @@
"No": "No",
"with": "with",
"Do you save this exercise with these parameters?":"Do you save this exercise with these parameters?",
"Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?":"Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?",
"The number of the exercise": "The number of the exercise",
"The number of the exercise done with": "The number of the exercise done with",
"Please repeat with": "Please repeat with",
@ -226,6 +229,7 @@
"Almost!": "Almost done!",
"You have only 1-2 exercise left to finish!": "You have only 1-2 exercise left to finish!",
"exercise!": "exercise!",
"exercise": "exercise",
"Chest": "Chest",
"Back": "Back",
"Thigh": "Thigh",
@ -409,6 +413,29 @@
"Compact Test":"Compact Test",
"Custom Test":"Custom Test",
"Set": "Set",
"Add this exercise to execute it paralell":"Add this exercise to execute it paralell"
"Add this exercise to execute it paralell":"Add this exercise to execute it paralell",
"very_poor": "Very Poor",
"poor": "Poor",
"below_average": "Below Average",
"average": "Average",
"above_verage": "Above Average",
"good": "Good",
"excellent": "Excellent",
"elite": "Elite",
"Training":"Training",
"Growth":"Growth",
"Compared with...":"Compared with...",
"your best":"your best",
"your last":"your last",
"best":"best",
"last":"last",
"volumen":"volumen",
"Get the Fastlane to your":"Get the Fastlane to your",
"Development":"Development",
"How can serve you this result?":"How can serve you this result?",
"Go":"Go",
"Result":"Result"
}

View File

@ -33,6 +33,7 @@
"Customer does not exist or the password is wrong": "A felhasználó nem létezik vagy a jelszó rossz.",
"The email does not exist or the password is wrong": "A felhasználó nem létezik vagy a jelszó rossz.",
"Exception: Facebook login was not successful": "Facebook bejelentkezés sikertelen",
"Exception: Exception: Facebook login was not successful": "Facebook bejelentkezés sikertelen",
"Exception: Facebook signup was not successful. Please try another method": "Facebook regisztráció sikertelen. Kérlek próbálj mással regisztrálni",
"Exception: Please type an email address": "Kérlek írj be egy email címet",
"Exception: Password too short": "A jelszó min. 9 karakterből álljon",
@ -40,6 +41,7 @@
"Exception: Facebook login cancelled":"Facebook bejelentkezés megszakítva ",
"Exception: Facebook login failed":"Facebook bejelentkezés sikertelen",
"Exception: The email address has been registered already":"Ezzel email címmel már regisztráltak",
"Exception: Exception: The email address has been registered already":"Ezzel email címmel már regisztráltak",
"Exception: Google Sign In failed":"Google bejelentkezés sikertelen",
"Exception: Apple Sign-In failed":"Apple bejelentkezés sikertelen",
"Exception: Apple Sign-In cancelled":"Apple bejelentkezés megszakítva",
@ -94,6 +96,7 @@
"No": "Nem",
"with": "",
"Do you save this exercise with these parameters?":"Elmented a gyakorlatot?",
"Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?":"Mindent beleadtál, ez a MAXIMUM, amit végre tudtál hajtani? Biztos vagy benne, hogy CSAK 12 ismétléssel mentsük el a gyakorlatot?",
"repeat": "ismétlés",
@ -224,6 +227,7 @@
"Almost!": "Majdnem a végén vagy!",
"You have only 1-2 exercise left to finish!": "Már csak 1-2 gyakorlat van a hátra!",
"exercise!": "gyakorlattal!",
"exercise": "gyakorlat",
"Chest": "mell",
"Back": "hát",
"Thigh": "comb",
@ -270,7 +274,7 @@
"Available Equipments":"Elérhető eszközök",
"select your places by tapping":"kattints az edzéshelyszínre",
"Available Training Places":"Elérhető edzéshelyszínek",
"Please take a relative bigger weight and repeat 12-20 times":"Válassz egy relatív nagyobb súlyt, amivel maximum 12-20 közötti ismétlésre vagy képes",
"Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!":"Válassz egy relatív nagyobb súlyt, amivel 12-20 közötti ismétlésre vagy képes, és adj bele mindent! MAXOLD KI!",
"Please take a medium weight and repeat 20-30 times": "Válassz egy közepes súlyt, amivel maximum 20-30 közötti ismétlésre vagy képes",
"Equipment Filter":"Eszköz szűrő",
"Live-Server":"Live-Server",
@ -405,7 +409,28 @@
"Compact Test":"Kompakt teszt",
"Custom Test":"Egyedi teszt",
"Set": "Széria",
"Add this exercise to execute it paralell":"Adj hozzá egy gyakorlatot a párhuzamos végrehajtáshoz"
"Add this exercise to execute it paralell":"Adj hozzá egy gyakorlatot a párhuzamos végrehajtáshoz",
"very_poor": "Nagyon gyenge",
"poor": "Gyenge",
"below_average": "Átlag alatti",
"average": "Átlagos",
"above_verage": "Átlag feletti",
"good": "Jó",
"excellent": "Kitűnő",
"elite": "Elit kategória",
"Training":"Tréning",
"Growth":"Fejlődésem",
"Compared with...":"Összehasonlítva...",
"your best":"a legjobb",
"your last":"az utolsó",
"best":"a legjobb",
"last":"az utolsó",
"volumen":"volumennel",
"How can serve you this result?":"Hogyan segít ez az eredmény téged?",
"Get the Fastlane to your":"Kapd el a gyorsítósávot a optimális",
"Development":"Fejlődésedhez",
"Go":"Érdekel",
"Result":"Értékelés"
}

View File

@ -31,18 +31,18 @@ PODS:
- Firebase/Messaging (7.3.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 7.3.0)
- firebase_analytics (8.0.0-dev.0):
- firebase_analytics (8.0.0-dev.2):
- Firebase/Analytics (= 7.3.0)
- firebase_core
- Flutter
- firebase_auth (1.0.1):
- firebase_auth (1.0.3):
- Firebase/Auth (= 7.3.0)
- firebase_core
- Flutter
- firebase_core (1.0.2):
- firebase_core (1.0.3):
- Firebase/CoreOnly (= 7.3.0)
- Flutter
- firebase_messaging (9.1.0):
- firebase_messaging (9.1.1):
- Firebase/Messaging (= 7.3.0)
- firebase_core
- Flutter
@ -86,6 +86,10 @@ PODS:
- GoogleUtilities/Environment (~> 7.0)
- GoogleUtilities/Reachability (~> 7.0)
- GoogleUtilities/UserDefaults (~> 7.0)
- flurry (0.0.4):
- Flurry-iOS-SDK/FlurrySDK
- Flutter
- Flurry-iOS-SDK/FlurrySDK (11.2.0)
- Flutter (1.0.0)
- flutter_facebook_auth (2.0.0):
- FBSDKCoreKit (~> 9.1.0)
@ -181,6 +185,7 @@ DEPENDENCIES:
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- flurry (from `.symlinks/plugins/flurry/ios`)
- Flutter (from `Flutter`)
- flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
@ -210,6 +215,7 @@ SPEC REPOS:
- FirebaseInstallations
- FirebaseInstanceID
- FirebaseMessaging
- Flurry-iOS-SDK
- FMDB
- GoogleAppMeasurement
- GoogleDataTransport
@ -236,6 +242,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
flurry:
:path: ".symlinks/plugins/flurry/ios"
Flutter:
:path: Flutter
flutter_facebook_auth:
@ -274,10 +282,10 @@ SPEC CHECKSUMS:
FBSDKCoreKit: a00fe2efd780c195a5e09201bf51c56106245b40
FBSDKLoginKit: d98498c598ec09de657385a9349a1f21119b7f86
Firebase: 26223c695fe322633274198cb19dca8cb7e54416
firebase_analytics: c9b8ddc8e864e45cd70761c5d972bd11c83574ab
firebase_auth: 9f6491ea8e44570323361ae713a2ae3175b3f21a
firebase_core: e6cbb0d1f7091edfcae31559e58224bfc1e455dc
firebase_messaging: 9c746d6c52bb05764e73bbe745d0d698e5afb695
firebase_analytics: 4c032e04324c47ee6dd7a28bfff831e0ac0d09b6
firebase_auth: c2c27e1081671b02f90981e70dad54722198491f
firebase_core: b5d81dfd4fb2d6f700e67de34d9a633ae325c4e9
firebase_messaging: 7547c99f31466371f9cfcb733d5a1bf32a819872
FirebaseAnalytics: 2580c2d62535ae7b644143d48941fcc239ea897a
FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2
FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972
@ -285,6 +293,8 @@ SPEC CHECKSUMS:
FirebaseInstallations: 5e777e6640fa060405cc7632447b6c5ca5af4742
FirebaseInstanceID: 53140c03b9f6136f890d7901399f85a4c90ab2d0
FirebaseMessaging: 68d1bcb14880189558a8ae57167abe0b7e417232
flurry: 15b01f664ab1367c62b50291541ea7f78ca85aad
Flurry-iOS-SDK: 6636d30c30f12010e7c7c71d84b443416a168efc
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_facebook_auth: 4b170c07b7fce791497093fcc3f134fb215f3f07
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
@ -307,7 +317,7 @@ SPEC CHECKSUMS:
PurchasesCoreSwift: 31c2a3d7394432abbe64d46f0933835de0b33033
PurchasesHybridCommon: 013c8072b73e752a206779747e88c068fbf999ec
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
smartlook: bda0b1561935a02ef0fea5448258d5ac75027859
smartlook: bbc5c73a85c752a31dabf100c8930838c646342e
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
wakelock: b0843b2479edbf6504d8d262c2959446f35373aa

View File

@ -388,7 +388,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -405,7 +405,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.10;
MARKETING_VERSION = 1.1.11;
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -531,7 +531,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -548,7 +548,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.10;
MARKETING_VERSION = 1.1.11;
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -566,7 +566,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -583,7 +583,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.10;
MARKETING_VERSION = 1.1.11;
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View File

@ -74,7 +74,7 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
yield AccountLoggedIn();
} else if (event is AccountLogout) {
await Cache().logout();
//customerRepository.customer = null;
customerRepository.customer = null;
customerRepository.emptyTrainees();
loggedIn = false;
yield AccountLoggedOut();

View File

@ -9,6 +9,8 @@ import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import '../../model/fitness_state.dart';
part 'customer_change_event.dart';
part 'customer_change_state.dart';
@ -19,14 +21,40 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
double weight = 60;
double height = 170;
CustomerChangeBloc({required this.customerRepository}) : super(CustomerChangeInitial()) {
year = this.customerRepository.customer.birthYear;
if (this.customerRepository.customer == null) {
this.customerRepository.createNew();
}
year = this.customerRepository.customer!.birthYear;
if (year == null || year == 0) {
year = 1990;
}
weight = this.customerRepository.getWeight() == 0 ? 60 : this.customerRepository.getWeight();
height = this.customerRepository.getHeight() == 0 ? 170 : this.customerRepository.getHeight();
print("fitnesslevel " + customerRepository.customer!.fitnessLevel.toString());
if (customerRepository.customer!.fitnessLevel != null) {
if (!FitnessItem().elements.contains(customerRepository.customer!.fitnessLevel)) {
Sport.values.forEach((element) {
print(" .. ${element.toStr()}");
if (element.equalsStringTo(customerRepository.customer!.fitnessLevel!)) {
selectedSport = element;
selectedFitnessItem = FitnessState.professional;
}
});
if (selectedSport == null) {
selectedFitnessItem = customerRepository.customer!.fitnessLevel;
}
} else {
selectedFitnessItem = customerRepository.customer!.fitnessLevel;
}
}
print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString());
}
Sport? selectedSport;
String? selectedFitnessItem;
@override
Stream<CustomerChangeState> mapEventToState(
CustomerChangeEvent event,
@ -42,7 +70,8 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
visiblePassword = !visiblePassword;
yield CustomerDataChanged();
} else if (event is CustomerFitnessChange) {
customerRepository.setFitnessLevel(event.fitness);
//customerRepository.setFitnessLevel(event.fitness);
selectedFitnessItem = event.fitness;
yield CustomerDataChanged();
} else if (event is CustomerBirthYearChange) {
yield CustomerChangeLoading();
@ -77,9 +106,25 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
} else if (event is CustomerGenderChange) {
customerRepository.setSex(event.gender == 0 ? "m" : "w");
yield CustomerDataChanged();
} else if (event is CustomerSportChange) {
yield CustomerChangeLoading();
selectedSport = event.sport;
//customerRepository.setFitnessLevel(event.sport.toStr());
yield CustomerDataChanged();
} else if (event is CustomerSave) {
yield CustomerSaving();
if (validation()) {
if (selectedFitnessItem != null) {
if (selectedFitnessItem == FitnessState.professional) {
if (selectedSport != null) {
customerRepository.setFitnessLevel(selectedSport!.toStr());
} else {
customerRepository.setFitnessLevel(FitnessState.professional);
}
} else {
customerRepository.setFitnessLevel(selectedFitnessItem!);
}
}
await customerRepository.saveCustomer();
Cache().initBadges();
yield CustomerSaveSuccess();
@ -123,4 +168,7 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
}
return null;
}
Sport? get getSelectedSport => this.selectedSport;
set setSelectedSport(Sport? selectedSport) => this.selectedSport = selectedSport;
}

View File

@ -95,6 +95,14 @@ class CustomerPasswordChange extends CustomerChangeEvent {
List<Object> get props => [password];
}
class CustomerSportChange extends CustomerChangeEvent {
final Sport sport;
const CustomerSportChange({required this.sport});
@override
List<Object> get props => [sport];
}
class CustomerChangePasswordObscure extends CustomerChangeEvent {
const CustomerChangePasswordObscure();
}

View File

@ -26,8 +26,8 @@ class ExerciseExecutePlanAddBloc extends Bloc<ExerciseExecutePlanAddEvent, Exerc
int step = 1;
int countSteps = 1;
late double quantity;
late double unitQuantity;
double? quantity;
double? unitQuantity;
double scrollOffset = 0;
@ -38,7 +38,9 @@ class ExerciseExecutePlanAddBloc extends Bloc<ExerciseExecutePlanAddEvent, Exerc
required this.customerId,
required this.workoutTree,
required this.planBloc})
: super(ExerciseExecutePlanAddInitial()) {
: super(ExerciseExecutePlanAddInitial());
void init() {
exerciseRepository.exerciseType = workoutTree.exerciseType;
if (Cache().userLoggedIn!.customerId == customerId) {
customer = Cache().userLoggedIn!;
@ -48,12 +50,15 @@ class ExerciseExecutePlanAddBloc extends Bloc<ExerciseExecutePlanAddEvent, Exerc
exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType!);
exerciseRepository.customer = customer;
countSteps = exercisePlanRepository.getActualPlanDetail()!.serie!;
unitQuantity = double.parse(exercisePlanRepository.getActualPlanDetail()!.weightEquation!);
if (exercisePlanRepository.getActualPlanDetail()!.weightEquation == null) {
unitQuantity = 0.0;
} else {
unitQuantity = double.parse(exercisePlanRepository.getActualPlanDetail()!.weightEquation!);
}
quantity = exercisePlanRepository.getActualPlanDetail()!.repeats!.toDouble();
exerciseRepository.setQuantity(quantity);
exerciseRepository.setUnitQuantity(unitQuantity);
exerciseRepository.setQuantity(quantity!);
exerciseRepository.setUnitQuantity(unitQuantity!);
}
@override
@ -61,17 +66,18 @@ class ExerciseExecutePlanAddBloc extends Bloc<ExerciseExecutePlanAddEvent, Exerc
try {
if (event is ExerciseExecutePlanAddLoad) {
yield ExerciseExecutePlanAddLoading();
init();
Track().track(TrackingEvent.my_exercise_plan_execute_open);
yield ExerciseExecutePlanAddReady();
} else if (event is ExerciseExecutePlanAddChangeQuantity) {
yield ExerciseExecutePlanAddLoading();
quantity = event.quantity;
exerciseRepository.setQuantity(quantity);
exerciseRepository.setQuantity(quantity!);
yield ExerciseExecutePlanAddReady();
} else if (event is ExerciseExecutePlanAddChangeUnitQuantity) {
yield ExerciseExecutePlanAddLoading();
unitQuantity = event.quantity;
exerciseRepository.setUnitQuantity(unitQuantity);
exerciseRepository.setUnitQuantity(unitQuantity!);
yield ExerciseExecutePlanAddReady();
} else if (event is ExerciseExecutePlanAddSubmit) {
yield ExerciseExecutePlanAddLoading();

View File

@ -68,10 +68,10 @@ class ExerciseNewBloc extends Bloc<ExerciseNewEvent, ExerciseNewState> with Logg
exerciseRepository.start = DateTime.now();
if (Cache().userLoggedIn != null) {
customerRepository.customer = Cache().userLoggedIn!;
weight = customerRepository.customer.getProperty("Weight");
height = customerRepository.customer.getProperty("Height");
birthYear = customerRepository.customer.birthYear!;
fitnessLevel = customerRepository.customer.fitnessLevel!;
weight = customerRepository.customer!.getProperty("Weight");
height = customerRepository.customer!.getProperty("Height");
birthYear = customerRepository.customer!.birthYear!;
fitnessLevel = customerRepository.customer!.fitnessLevel!;
}
if (exerciseType.unit == "second") {
stopWatchTimer.rawTime.listen((value) => {timerValue = value, this.setQuantity((value / 1000).toDouble())});
@ -183,22 +183,22 @@ class ExerciseNewBloc extends Bloc<ExerciseNewEvent, ExerciseNewState> with Logg
int year = int.parse(DateFormat(DateFormat.YEAR).format(date));
if (customerRepository.customer.sex == "m") {
if (customerRepository.customer!.sex == "m") {
//66.47 + ( 13.75 × tömeg kg-ban ) + ( 5.003 × magasság cm-ben ) ( 6.755 × életkor évben kifejezve )
bmr = 66.47 + (13.75 * weight) + (5.003 * height) - (6.755 * (year - customerRepository.customer.birthYear!));
bmr = 66.47 + (13.75 * weight) + (5.003 * height) - (6.755 * (year - customerRepository.customer!.birthYear!));
} else {
//BMR = 655.1 + ( 9.563 × ömeg kg-ban ) + ( 1.85 × magasság cm-ben) ( 4.676 × életkor évben kifejezve )
bmr = 655.1 + (9.563 * weight) + (1.85 * height) - (4.676 * (year - customerRepository.customer.birthYear!));
bmr = 655.1 + (9.563 * weight) + (1.85 * height) - (4.676 * (year - customerRepository.customer!.birthYear!));
}
bmrEnergy = bmr;
if (customerRepository.customer.fitnessLevel == FitnessState.beginner) {
if (customerRepository.customer!.fitnessLevel == FitnessState.beginner) {
bmr *= 1.2;
} else if (customerRepository.customer.fitnessLevel == FitnessState.intermediate) {
} else if (customerRepository.customer!.fitnessLevel == FitnessState.intermediate) {
bmr *= 1.375;
} else if (customerRepository.customer.fitnessLevel == FitnessState.advanced) {
} else if (customerRepository.customer!.fitnessLevel == FitnessState.advanced) {
bmr *= 1.55;
} else if (customerRepository.customer.fitnessLevel == FitnessState.professional) {
} else if (customerRepository.customer!.fitnessLevel == FitnessState.professional) {
bmr *= 1.9;
}
return bmr;

View File

@ -75,12 +75,17 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
if (!this.dataPolicyAllowed) {
throw Exception("Please accept our data policy");
}
await userRepository.addUser();
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
await saveCustomer();
Track().track(TrackingEvent.registration, eventValue: "email");
Cache().setLoginType(LoginType.email);
yield LoginSuccess();
final String? validationError = validate();
if (validationError != null) {
yield LoginError(message: validationError);
} else {
await userRepository.addUser();
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!));
await saveCustomer();
Track().track(TrackingEvent.registration, eventValue: "email");
Cache().setLoginType(LoginType.email);
yield LoginSuccess();
}
} else if (event is RegistrationFB) {
yield LoginLoading();
if (!this.dataPolicyAllowed) {
@ -131,20 +136,36 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
Future<void> saveCustomer() async {
customerRepository.customer = Cache().userLoggedIn!;
customerRepository.customer.dataPolicyAllowed = 1;
customerRepository.customer!.dataPolicyAllowed = 1;
await customerRepository.saveCustomer();
}
String? emailValidation(String email) {
String? emailValidation(String? email) {
String? message = Common.emailValidation(email);
return message;
}
String? passwordValidation(String value) {
String? passwordValidation(String? value) {
String? message = Common.passwordValidation(value);
if (message != null) {
message = t(message);
}
return message;
}
String? validate() {
String? error;
error = emailValidation(userRepository.user.email);
if (error != null) {
return error;
}
error = passwordValidation(userRepository.user.password);
if (error != null) {
return error;
}
return error;
}
}

View File

@ -98,7 +98,9 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> with Trans, Logging {
//await menuTreeRepository.createTree();
//menuTreeRepository.getBranch(this.parent);
//setMenuInfo();
exerciseDeviceRepository.setDevices(Cache().getDevices()!);
if (Cache().getDevices() != null) {
exerciseDeviceRepository.setDevices(Cache().getDevices()!);
}
yield MenuReady();
} else if (event is MenuRecreateTree) {
yield MenuLoading();

View File

@ -25,7 +25,7 @@ class PasswordResetBloc extends Bloc<PasswordResetEvent, PasswordResetState> {
} else if (event is PasswordResetSubmit) {
yield PasswordResetLoading();
await userRepository.resetPassword();
yield PasswordResetReady();
yield PasswordResetFinished();
}
} on Exception catch (e) {
yield PasswordResetError(message: e.toString());

View File

@ -26,3 +26,7 @@ class PasswordResetError extends PasswordResetState {
class PasswordResetReady extends PasswordResetState {
const PasswordResetReady();
}
class PasswordResetFinished extends PasswordResetState {
const PasswordResetFinished();
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:aitrainer_app/model/result.dart';
import 'package:aitrainer_app/repository/evaluation_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/exercise_result_repository.dart';
import 'package:aitrainer_app/service/logging.dart';
@ -17,6 +18,7 @@ part 'result_state.dart';
class ResultBloc extends Bloc<ResultEvent, ResultState> with Logging, Trans {
final ExerciseResultRepository resultRepository;
final ExerciseRepository exerciseRepository;
final EvaluationRepository evaluationRepository = EvaluationRepository();
final BuildContext context;
//List<HealthDataPoint> _healthDataList = [];
DateTime? startTime;
@ -53,7 +55,7 @@ class ResultBloc extends Bloc<ResultEvent, ResultState> with Logging, Trans {
//await _fetchHealthData();
_matchExerciseData();
await resultRepository.saveExerciseResults();
//await resultRepository.saveExerciseResults();
yield ResultReady();
}
} on Exception catch (ex) {

View File

@ -31,7 +31,7 @@ class TestSetControlBloc extends Bloc<TestSetControlEvent, TestSetControlState>
}
void initBloc() {
oneRepMax = executeBloc.calculate1RM(exercisePlanDetail.exercises!.last.unitQuantity, exercisePlanDetail.exercises!.last.quantity);
oneRepMax = executeBloc.calculate1RM(exercisePlanDetail.exercises!.last.unitQuantity!, exercisePlanDetail.exercises!.last.quantity!);
initQuantity = 12;
quantity = initQuantity;
initUnitQuantity = oneRepMax * 0.75;

View File

@ -136,7 +136,9 @@ class TestSetExecuteBloc extends Bloc<TestSetExecuteEvent, TestSetExecuteState>
if (!this.existsInPlanDetails(event.exerciseTypeId)) {
ExercisePlanDetail exercisePlanDetail = ExercisePlanDetail(event.exerciseTypeId);
exercisePlanDetail.exercisePlanId = exercisePlan!.exercisePlanId!;
if (exercisePlan!.exercisePlanId != null) {
exercisePlanDetail.exercisePlanId = exercisePlan!.exercisePlanId!;
}
final ExerciseType exerciseType = Cache().getExerciseTypeById(event.exerciseTypeId)!;
exercisePlanDetail.serie = exerciseType.unitQuantityUnit == null ? 1 : 4;
@ -316,8 +318,9 @@ class TestSetExecuteBloc extends Bloc<TestSetExecuteEvent, TestSetExecuteState>
ExercisePlanDetail? getNext() {
ExercisePlanDetail? nextExercisePlanDetail;
int minStep = 99;
if (this.exercisePlanDetails == null) {
if (this.exercisePlanDetails != null) {
for (final detail in this.exercisePlanDetails!) {
if (!detail.state.equalsTo(ExercisePlanDetailState.finished)) {
if (detail.exercises == null) {
@ -369,9 +372,9 @@ class TestSetExecuteBloc extends Bloc<TestSetExecuteEvent, TestSetExecuteState>
if (!hasBegun() || exercisePlanDetail.exercises!.length < 2) {
return text;
}
final double unitQuantity = exercisePlanDetail.exercises!.last.unitQuantity;
final double quantity = exercisePlanDetail.exercises!.last.quantity;
double oneRepMax = this.calculate1RM(quantity, unitQuantity);
final double? unitQuantity = exercisePlanDetail.exercises!.last.unitQuantity!;
final double? quantity = exercisePlanDetail.exercises!.last.quantity!;
double oneRepMax = this.calculate1RM(quantity!, unitQuantity!);
text = (oneRepMax * 0.75).round().toStringAsFixed(0) + " " + exercisePlanDetail.exerciseType!.unitQuantityUnit!;
return text;
}

View File

@ -0,0 +1,541 @@
library dropdown_search;
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'popup_menu.dart';
import 'select_dialog.dart';
typedef Future<List<T>> DropdownSearchOnFind<T>(String text);
typedef String DropdownSearchItemAsString<T>(T item);
typedef bool DropdownSearchFilterFn<T>(T item, String filter);
typedef bool DropdownSearchCompareFn<T>(T item, T selectedItem);
typedef Widget DropdownSearchBuilder<T>(BuildContext context, T selectedItem, String itemAsString);
typedef Widget DropdownSearchPopupItemBuilder<T>(
BuildContext context,
T item,
bool isSelected,
);
typedef bool DropdownSearchPopupItemEnabled<T>(T item);
typedef Widget ErrorBuilder<T>(BuildContext context, String? searchEntry, dynamic exception);
typedef Widget EmptyBuilder<T>(BuildContext context, String? searchEntry);
typedef Widget LoadingBuilder<T>(BuildContext context, String? searchEntry);
typedef Widget IconButtonBuilder(BuildContext context);
typedef Future<bool> BeforeChange<T>(T prevItem, T nextItem);
typedef Widget FavoriteItemsBuilder<T>(BuildContext context, T item);
///[items] are the original item from [items] or/and [onFind]
typedef List<T> FavoriteItems<T>(List<T> items);
enum Mode { DIALOG, BOTTOM_SHEET, MENU }
class DropdownSearch<T> extends StatefulWidget {
///DropDownSearch label
final String? label;
///DropDownSearch hint
final String? hint;
///show/hide the search box
final bool showSearchBox;
///true if the filter on items is applied onlie (via API)
final bool isFilteredOnline;
///show/hide clear selected item
final bool showClearButton;
///offline items list
final List<T>? items;
///selected item
final T? selectedItem;
///function that returns item from API
final DropdownSearchOnFind<T>? onFind;
///called when a new item is selected
final ValueChanged<T>? onChanged;
///to customize list of items UI
final DropdownSearchBuilder<T>? dropdownBuilder;
///to customize selected item
final DropdownSearchPopupItemBuilder<T>? popupItemBuilder;
///decoration for search box
final InputDecoration? searchBoxDecoration;
///the title for dialog/menu/bottomSheet
final Color? popupBackgroundColor;
///custom widget for the popup title
final Widget? popupTitle;
///customize the fields the be shown
final DropdownSearchItemAsString<T>? itemAsString;
/// custom filter function
final DropdownSearchFilterFn<T>? filterFn;
///enable/disable dropdownSearch
final bool enabled;
///MENU / DIALOG/ BOTTOM_SHEET
final Mode mode;
///the max height for dialog/bottomSheet/Menu
final double? maxHeight;
///the max width for the dialog
final double? dialogMaxWidth;
///select the selected item in the menu/dialog/bottomSheet of items
final bool showSelectedItem;
///function that compares two object with the same type to detected if it's the selected item or not
final DropdownSearchCompareFn<T>? compareFn;
///dropdownSearch input decoration
final InputDecoration? dropdownSearchDecoration;
///custom layout for empty results
final EmptyBuilder? emptyBuilder;
///custom layout for loading items
final LoadingBuilder? loadingBuilder;
///custom layout for error
final ErrorBuilder? errorBuilder;
///the search box will be focused if true
final bool autoFocusSearchBox;
///custom shape for the popup
final ShapeBorder? popupShape;
final AutovalidateMode autoValidateMode;
/// An optional method to call with the final value when the form is saved via
final FormFieldSetter<T>? onSaved;
/// An optional method that validates an input. Returns an error string to
/// display if the input is invalid, or null otherwise.
final FormFieldValidator<T>? validator;
///custom dropdown clear button icon widget
final Widget? clearButton;
///custom clear button widget builder
final IconButtonBuilder? clearButtonBuilder;
///custom dropdown icon button widget
final Widget? dropDownButton;
///custom dropdown button widget builder
final IconButtonBuilder? dropdownButtonBuilder;
///whether to manage the clear and dropdown icons via InputDecoration suffixIcon
final bool showAsSuffixIcons;
///If true, the dropdownBuilder will continue the uses of material behavior
///This will be useful if you want to handle a custom UI only if the item !=null
final bool dropdownBuilderSupportsNullItem;
///defines if an item of the popup is enabled or not, if the item is disabled,
///it cannot be clicked
final DropdownSearchPopupItemEnabled<T>? popupItemDisabled;
///set a custom color for the popup barrier
final Color? popupBarrierColor;
///text controller to set default search word for example
final TextEditingController? searchBoxController;
///called when popup is dismissed
final VoidCallback? onPopupDismissed;
/// callback executed before applying value change
///delay before searching, change it to Duration(milliseconds: 0)
///if you do not use online search
final Duration? searchDelay;
/// callback executed before applying value change
final BeforeChange<T>? onBeforeChange;
///show or hide favorites items
final bool showFavoriteItems;
///to customize favorites chips
final FavoriteItemsBuilder<T>? favoriteItemBuilder;
///favorites items list
final FavoriteItems<T>? favoriteItems;
///favorite items alignment
final MainAxisAlignment? favoriteItemsAlignment;
DropdownSearch({
Key? key,
this.onSaved,
this.validator,
this.autoValidateMode = AutovalidateMode.disabled,
this.onChanged,
this.mode = Mode.DIALOG,
this.label,
this.hint,
this.isFilteredOnline = false,
this.popupTitle,
this.items,
this.selectedItem,
this.onFind,
this.dropdownBuilder,
this.popupItemBuilder,
this.showSearchBox = false,
this.showClearButton = false,
this.searchBoxDecoration,
this.popupBackgroundColor,
this.enabled = true,
this.maxHeight,
this.filterFn,
this.itemAsString,
this.showSelectedItem = false,
this.compareFn,
this.dropdownSearchDecoration,
this.emptyBuilder,
this.loadingBuilder,
this.errorBuilder,
this.autoFocusSearchBox = false,
this.dialogMaxWidth,
this.clearButton,
this.clearButtonBuilder,
this.dropDownButton,
this.dropdownButtonBuilder,
this.showAsSuffixIcons = false,
this.dropdownBuilderSupportsNullItem = false,
this.popupShape,
this.popupItemDisabled,
this.popupBarrierColor,
this.onPopupDismissed,
this.searchBoxController,
this.searchDelay,
this.onBeforeChange,
this.favoriteItemBuilder,
this.favoriteItems,
this.showFavoriteItems = false,
this.favoriteItemsAlignment = MainAxisAlignment.start,
}) : assert(!showSelectedItem || T == String || compareFn != null),
super(key: key);
@override
DropdownSearchState<T> createState() => DropdownSearchState<T>();
}
class DropdownSearchState<T> extends State<DropdownSearch<T?>> {
final ValueNotifier<T?> _selectedItemNotifier = ValueNotifier(null);
final ValueNotifier<bool> _isFocused = ValueNotifier(false);
@override
void initState() {
super.initState();
_selectedItemNotifier.value = widget.selectedItem;
}
@override
void didUpdateWidget(DropdownSearch<T?> oldWidget) {
final oldSelectedItem = oldWidget.selectedItem;
final newSelectedItem = widget.selectedItem;
if (oldSelectedItem != newSelectedItem) {
_selectedItemNotifier.value = newSelectedItem;
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<T?>(
valueListenable: _selectedItemNotifier,
builder: (context, T? data, wt) {
return IgnorePointer(
ignoring: !widget.enabled,
child: GestureDetector(
onTap: () => _selectSearchMode(data),
child: _formField(data),
),
);
},
);
}
Widget _defaultSelectItemWidget(T? data) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: widget.dropdownBuilder != null
? widget.dropdownBuilder!(
context,
data,
_selectedItemAsString(data),
)
: Text(_selectedItemAsString(data), style: Theme.of(context).textTheme.subtitle1),
),
if (!widget.showAsSuffixIcons) _manageTrailingIcons(data),
],
);
}
Widget _formField(T? value) {
return FormField(
enabled: widget.enabled,
onSaved: widget.onSaved,
validator: widget.validator,
autovalidateMode: widget.autoValidateMode,
initialValue: widget.selectedItem,
builder: (FormFieldState<T> state) {
if (state.value != value) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
state.didChange(value);
});
}
return ValueListenableBuilder(
valueListenable: _isFocused,
builder: (context, bool isFocused, w) {
return InputDecorator(
isEmpty: value == null && (widget.dropdownBuilder == null || widget.dropdownBuilderSupportsNullItem),
isFocused: isFocused,
decoration: _manageDropdownDecoration(state, value),
child: _defaultSelectItemWidget(value),
);
});
},
);
}
///manage dropdownSearch field decoration
InputDecoration _manageDropdownDecoration(FormFieldState state, T? data) {
return (widget.dropdownSearchDecoration ??
InputDecoration(contentPadding: EdgeInsets.fromLTRB(12, 12, 0, 0), border: OutlineInputBorder()))
.applyDefaults(Theme.of(state.context).inputDecorationTheme)
.copyWith(
enabled: widget.enabled,
labelText: widget.label,
hintText: widget.hint,
suffixIcon: widget.showAsSuffixIcons ? _manageTrailingIcons(data) : null,
errorText: state.errorText);
}
///function that return the String value of an object
String _selectedItemAsString(T? data) {
if (data == null) {
return "";
} else if (widget.itemAsString != null) {
return widget.itemAsString!(data);
} else {
return data.toString();
}
}
///function that manage Trailing icons(close, dropDown)
Widget _manageTrailingIcons(T? data) {
final clearButtonPressed = () => _handleOnChangeSelectedItem(null);
final dropdownButtonPressed = () => _selectSearchMode(data);
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
if (data != null && widget.showClearButton == true)
widget.clearButtonBuilder != null
? GestureDetector(
onTap: clearButtonPressed,
child: widget.clearButtonBuilder!(context),
)
: IconButton(
icon: widget.clearButton ?? const Icon(Icons.clear, size: 24),
onPressed: clearButtonPressed,
),
widget.dropdownButtonBuilder != null
? GestureDetector(
onTap: dropdownButtonPressed,
child: widget.dropdownButtonBuilder!(context),
)
: IconButton(
icon: widget.dropDownButton ?? const Icon(Icons.arrow_drop_down, size: 24),
onPressed: dropdownButtonPressed,
),
],
);
}
///open dialog
Future<T?> _openSelectDialog(T? data) {
return showGeneralDialog(
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
transitionDuration: const Duration(milliseconds: 400),
barrierColor: widget.popupBarrierColor ?? const Color(0x80000000),
context: context,
pageBuilder: (context, animation, secondaryAnimation) {
return AlertDialog(
contentPadding: EdgeInsets.all(0),
shape: widget.popupShape,
backgroundColor: widget.popupBackgroundColor,
content: _selectDialogInstance(data),
);
},
);
}
///open BottomSheet (Dialog mode)
Future<T?> _openBottomSheet(T? data) {
return showModalBottomSheet<T>(
barrierColor: widget.popupBarrierColor,
backgroundColor: widget.popupBackgroundColor,
isScrollControlled: true,
shape: widget.popupShape,
context: context,
builder: (ctx) {
return _selectDialogInstance(data, defaultHeight: 350);
});
}
///openMenu
Future<T?> _openMenu(T? data) {
// Here we get the render object of our physical button, later to get its size & position
final RenderBox popupButtonObject = context.findRenderObject() as RenderBox;
// Get the render object of the overlay used in `Navigator` / `MaterialApp`, i.e. screen size reference
final RenderBox overlay = Overlay.of(context)!.context.findRenderObject() as RenderBox;
// Calculate the show-up area for the dropdown using button's size & position based on the `overlay` used as the coordinate space.
final RelativeRect position = RelativeRect.fromSize(
Rect.fromPoints(
popupButtonObject.localToGlobal(popupButtonObject.size.bottomLeft(Offset.zero), ancestor: overlay),
popupButtonObject.localToGlobal(popupButtonObject.size.bottomRight(Offset.zero), ancestor: overlay),
),
Size(overlay.size.width, overlay.size.height),
);
return customShowMenu<T>(
barrierColor: widget.popupBarrierColor,
shape: widget.popupShape,
color: widget.popupBackgroundColor,
context: context,
position: position,
elevation: 8,
items: [
CustomPopupMenuItem(
enabled: false,
child: Container(
width: popupButtonObject.size.width,
child: _selectDialogInstance(data, defaultHeight: 224),
),
),
]);
}
SelectDialog<T?> _selectDialogInstance(T? data, {double? defaultHeight}) {
return SelectDialog<T?>(
popupTitle: widget.popupTitle,
maxHeight: widget.maxHeight ?? defaultHeight,
isFilteredOnline: widget.isFilteredOnline,
itemAsString: widget.itemAsString,
filterFn: widget.filterFn,
items: widget.items,
onFind: widget.onFind,
showSearchBox: widget.showSearchBox,
itemBuilder: widget.popupItemBuilder,
selectedValue: data,
searchBoxDecoration: widget.searchBoxDecoration,
onChanged: _handleOnChangeSelectedItem,
showSelectedItem: widget.showSelectedItem,
compareFn: widget.compareFn,
emptyBuilder: widget.emptyBuilder,
loadingBuilder: widget.loadingBuilder,
errorBuilder: widget.errorBuilder,
autoFocusSearchBox: widget.autoFocusSearchBox,
dialogMaxWidth: widget.dialogMaxWidth,
itemDisabled: widget.popupItemDisabled,
searchBoxController: widget.searchBoxController ?? TextEditingController(),
searchDelay: widget.searchDelay,
showFavoriteItems: widget.showFavoriteItems,
favoriteItems: widget.favoriteItems,
favoriteItemBuilder: widget.favoriteItemBuilder,
favoriteItemsAlignment: widget.favoriteItemsAlignment,
);
}
///Function that manage focus listener
///set true only if the widget already not focused to prevent unnecessary build
///same thing for clear focus,
void _handleFocus(bool isFocused) {
if (isFocused && !_isFocused.value) {
FocusScope.of(context).unfocus();
_isFocused.value = true;
} else if (!isFocused && _isFocused.value) _isFocused.value = false;
}
///handle on change value , if the validation is active , we validate the new selected item
void _handleOnChangeSelectedItem(T? selectedItem) {
final changeItem = () {
_selectedItemNotifier.value = selectedItem;
if (widget.onChanged != null) widget.onChanged!(selectedItem);
};
if (widget.onBeforeChange != null) {
widget.onBeforeChange!(_selectedItemNotifier.value, selectedItem).then((value) {
if (value == true) {
changeItem();
}
});
} else {
changeItem();
}
_handleFocus(false);
}
///Function that return then UI based on searchMode
///[data] selected item to be passed to the UI
///If we close the popup , or maybe we just selected
///another widget we should clear the focus
Future<T?> _selectSearchMode(T? data) async {
_handleFocus(true);
T? selectedItem;
if (widget.mode == Mode.MENU) {
selectedItem = await _openMenu(data);
} else if (widget.mode == Mode.BOTTOM_SHEET) {
selectedItem = await _openBottomSheet(data);
} else {
selectedItem = await _openSelectDialog(data);
}
_handleFocus(false);
widget.onPopupDismissed?.call();
return selectedItem;
}
///Public Function that return then UI based on searchMode
///[data] selected item to be passed to the UI
///If we close the popup , or maybe we just selected
///another widget we should clear the focus
///THIS USED FOR OPEN DROPDOWN_SEARCH PROGRAMMATICALLY,
///otherwise you can you [_selectSearchMode]
Future<T?> openDropDownSearch() => _selectSearchMode(_selectedItemNotifier.value);
///Change selected Value; this function is public USED to change the selected
///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem]
void changeSelectedItem(T selectedItem) => _handleOnChangeSelectedItem(selectedItem);
///Change selected Value; this function is public USED to clear selected
///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem]
void clear() => _handleOnChangeSelectedItem(null);
///get selected value programmatically
T? get getSelectedItem => _selectedItemNotifier.value;
///check if the dropdownSearch is focused
bool get isFocused => _isFocused.value;
}

View File

@ -1,39 +0,0 @@
import 'dart:async';
import 'package:flutter/services.dart';
class Flurry {
static const MethodChannel _channel = const MethodChannel('flurry');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static Future<Null> initialize({String androidKey = "", String iosKey = "", bool enableLog = true}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("api_key_android", () => androidKey);
args.putIfAbsent("api_key_ios", () => iosKey);
args.putIfAbsent("is_log_enabled", () => enableLog);
await _channel.invokeMethod('initialize', args);
return null;
}
static Future<Null> logEvent(String message) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("message", () => message);
await _channel.invokeMethod('logEvent', args);
return null;
}
static Future<Null> setUserId(String userId) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("userId", () => userId);
await _channel.invokeMethod('userId', args);
return null;
}
}

589
lib/library/popup_menu.dart Normal file
View File

@ -0,0 +1,589 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
const Duration _kMenuDuration = Duration(milliseconds: 300);
const double _kMenuCloseIntervalEnd = 2.0 / 3.0;
const double _kMenuHorizontalPadding = 0.0;
const double _kMenuMinWidth = 2.0 * _kMenuWidthStep;
const double _kMenuVerticalPadding = 0.0;
const double _kMenuWidthStep = 1.0;
const double _kMenuScreenPadding = 0.0;
// This widget only exists to enable _PopupMenuRoute to save the sizes of
// each menu item. The sizes are used by _PopupMenuRouteLayout to compute the
// y coordinate of the menu's origin so that the center of selected menu
// item lines up with the center of its PopupMenuButton.
class _MenuItem extends SingleChildRenderObjectWidget {
const _MenuItem({
Key? key,
required this.onLayout,
Widget? child,
}) : super(key: key, child: child);
final ValueChanged<Size> onLayout;
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderMenuItem(onLayout);
}
@override
void updateRenderObject(BuildContext context, covariant _RenderMenuItem renderObject) {
renderObject.onLayout = onLayout;
}
}
class _RenderMenuItem extends RenderShiftedBox {
_RenderMenuItem(this.onLayout, [RenderBox? child]) : super(child);
ValueChanged<Size> onLayout;
@override
void performLayout() {
if (child == null) {
size = Size.zero;
} else {
child!.layout(constraints, parentUsesSize: true);
size = constraints.constrain(child!.size);
}
final BoxParentData childParentData = child!.parentData as BoxParentData;
childParentData.offset = Offset.zero;
onLayout(size);
}
}
/// An item in a material design popup menu.
///
/// To show a popup menu, use the [customShowMenu] function. To create a button that
/// shows a popup menu, consider using [PopupMenuButton].
///
/// To show a checkmark next to a popup menu item, consider using
/// [CheckedPopupMenuItem].
///
/// Typically the [child] of a [CustomPopupMenuItem] is a [Text] widget. More
/// elaborate menus with icons can use a [ListTile]. By default, a
/// [CustomPopupMenuItem] is kMinInteractiveDimension pixels high. If you use a widget
/// with a different height, it must be specified in the [height] property.
///
/// {@tool sample}
///
/// Here, a [Text] widget is used with a popup menu item. The `WhyFarther` type
/// is an enum, not shown here.
///
/// ```dart
/// const CustomPopupMenuItem<WhyFarther>(
/// value: WhyFarther.harder,
/// child: Text('Working a lot harder'),
/// )
/// ```
/// {@end-tool}
///
/// See the example at [PopupMenuButton] for how this example could be used in a
/// complete menu, and see the example at [CheckedPopupMenuItem] for one way to
/// keep the text of [CustomPopupMenuItem]s that use [Text] widgets in their [child]
/// slot aligned with the text of [CheckedPopupMenuItem]s or of [CustomPopupMenuItem]
/// that use a [ListTile] in their [child] slot.
///
/// See also:
///
/// * [PopupMenuDivider], which can be used to divide items from each other.
/// * [CheckedPopupMenuItem], a variant of [CustomPopupMenuItem] with a checkmark.
/// * [customShowMenu], a method to dynamically show a popup menu at a given location.
/// * [PopupMenuButton], an [IconButton] that automatically shows a menu when
/// it is tapped.
class CustomPopupMenuItem<T> extends PopupMenuEntry<T> {
/// Creates an item for a popup menu.
///
/// By default, the item is [enabled].
///
/// The `enabled` and `height` arguments must not be null.
const CustomPopupMenuItem({
Key? key,
this.value,
this.enabled = true,
this.height = kMinInteractiveDimension,
this.textStyle,
required this.child,
}) : super(key: key);
/// The value that will be returned by [customShowMenu] if this entry is selected.
final T? value;
/// Whether the user is permitted to select this item.
///
/// Defaults to true. If this is false, then the item will not react to
/// touches.
final bool enabled;
/// The minimum height height of the menu item.
///
/// Defaults to [kMinInteractiveDimension] pixels.
@override
final double height;
/// The text style of the popup menu item.
///
/// If this property is null, then [PopupMenuThemeData.textStyle] is used.
/// If [PopupMenuThemeData.textStyle] is also null, then [ThemeData.textTheme.subhead] is used.
final TextStyle? textStyle;
/// The widget below this widget in the tree.
///
/// Typically a single-line [ListTile] (for menus with icons) or a [Text]. An
/// appropriate [DefaultTextStyle] is put in scope for the child. In either
/// case, the text should be short enough that it won't wrap.
final Widget child;
@override
bool represents(T? value) => value == this.value;
@override
PopupMenuItemState<T, CustomPopupMenuItem<T>> createState() => PopupMenuItemState<T, CustomPopupMenuItem<T>>();
}
/// The [State] for [CustomPopupMenuItem] subclasses.
///
/// By default this implements the basic styling and layout of Material Design
/// popup menu items.
///
/// The [buildChild] method can be overridden to adjust exactly what gets placed
/// in the menu. By default it returns [CustomPopupMenuItem.child].
///
/// The [handleTap] method can be overridden to adjust exactly what happens when
/// the item is tapped. By default, it uses [Navigator.pop] to return the
/// [CustomPopupMenuItem.value] from the menu route.
///
/// This class takes two type arguments. The second, `W`, is the exact type of
/// the [Widget] that is using this [State]. It must be a subclass of
/// [CustomPopupMenuItem]. The first, `T`, must match the type argument of that widget
/// class, and is the type of values returned from this menu.
class PopupMenuItemState<T, W extends CustomPopupMenuItem<T>> extends State<W> {
/// The menu item contents.
///
/// Used by the [build] method.
///
/// By default, this returns [CustomPopupMenuItem.child]. Override this to put
/// something else in the menu entry.
@protected
Widget buildChild() => widget.child;
/// The handler for when the user selects the menu item.
///
/// Used by the [InkWell] inserted by the [build] method.
///
/// By default, uses [Navigator.pop] to return the [CustomPopupMenuItem.value] from
/// the menu route.
@protected
void handleTap() {
Navigator.pop<T>(context, widget.value);
}
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
TextStyle? style = widget.textStyle ?? popupMenuTheme.textStyle ?? theme.textTheme.subtitle1;
if (!widget.enabled) style = style!.copyWith(color: theme.disabledColor);
Widget item = AnimatedDefaultTextStyle(
style: style!,
duration: kThemeChangeDuration,
child: Container(
alignment: AlignmentDirectional.centerStart,
constraints: BoxConstraints(minHeight: widget.height),
padding: const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding),
child: buildChild(),
),
);
if (!widget.enabled) {
final bool isDark = theme.brightness == Brightness.dark;
item = IconTheme.merge(
data: IconThemeData(opacity: isDark ? 0.5 : 0.38),
child: item,
);
}
return InkWell(
onTap: widget.enabled ? handleTap : null,
canRequestFocus: widget.enabled,
child: item,
);
}
}
class _PopupMenu<T> extends StatelessWidget {
const _PopupMenu({
Key? key,
this.route,
this.semanticLabel,
}) : super(key: key);
final _PopupMenuRoute<T>? route;
final String? semanticLabel;
@override
Widget build(BuildContext context) {
final double unit = 1.0 / (route!.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
final List<Widget> children = <Widget>[];
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
for (int i = 0; i < route!.items.length; i += 1) {
final double start = (i + 1) * unit;
final double end = (start + 1.5 * unit).clamp(0.0, 1.0);
final CurvedAnimation opacity = CurvedAnimation(
parent: route!.animation!,
curve: Interval(start, end),
);
Widget item = route!.items[i];
if (route!.initialValue != null && route!.items[i].represents(route!.initialValue)) {
item = Container(
color: Theme.of(context).highlightColor,
child: item,
);
}
children.add(
_MenuItem(
onLayout: (Size size) {
route!.itemSizes[i] = size;
},
child: FadeTransition(
opacity: opacity,
child: item,
),
),
);
}
final CurveTween opacity = CurveTween(curve: const Interval(0.0, 1.0 / 3.0));
final CurveTween width = CurveTween(curve: Interval(0.0, unit));
final CurveTween height = CurveTween(curve: Interval(0.0, unit * route!.items.length));
final Widget child = ConstrainedBox(
constraints: const BoxConstraints(minWidth: _kMenuMinWidth),
child: IntrinsicWidth(
stepWidth: _kMenuWidthStep,
child: Semantics(
scopesRoute: true,
namesRoute: true,
explicitChildNodes: true,
label: semanticLabel,
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: _kMenuVerticalPadding),
child: ListBody(children: children),
),
),
),
);
return AnimatedBuilder(
animation: route!.animation!,
builder: (BuildContext context, Widget? child) {
return Opacity(
opacity: opacity.evaluate(route!.animation!),
child: Material(
shape: route!.shape ?? popupMenuTheme.shape,
color: route!.color ?? popupMenuTheme.color,
type: MaterialType.card,
elevation: route!.elevation ?? popupMenuTheme.elevation ?? 8.0,
child: Align(
alignment: AlignmentDirectional.topEnd,
widthFactor: width.evaluate(route!.animation!),
heightFactor: height.evaluate(route!.animation!),
child: child,
),
),
);
},
child: child,
);
}
}
// Positioning of the menu on the screen.
class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
_PopupMenuRouteLayout(this.position, this.itemSizes, this.selectedItemIndex, this.textDirection);
// Rectangle of underlying button, relative to the overlay's dimensions.
final RelativeRect? position;
// The sizes of each item are computed when the menu is laid out, and before
// the route is laid out.
List<Size?> itemSizes;
// The index of the selected item, or null if PopupMenuButton.initialValue
// was not specified.
final int? selectedItemIndex;
// Whether to prefer going to the left or to the right.
final TextDirection textDirection;
// We put the child wherever position specifies, so long as it will fit within
// the specified parent size padded (inset) by 8. If necessary, we adjust the
// child's position so that it fits.
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// The menu can be at most the size of the overlay minus 8.0 pixels in each
// direction.
return BoxConstraints.loose(constraints.biggest - const Offset(_kMenuScreenPadding * 2.0, _kMenuScreenPadding * 2.0) as Size);
}
@override
Offset getPositionForChild(Size size, Size childSize) {
// size: The size of the overlay.
// childSize: The size of the menu, when fully open, as determined by
// getConstraintsForChild.
// Find the ideal vertical position.
double y = position!.top;
if (selectedItemIndex != null) {
double selectedItemOffset = _kMenuVerticalPadding;
for (int index = 0; index < selectedItemIndex!; index += 1) selectedItemOffset += itemSizes[index]!.height;
selectedItemOffset += itemSizes[selectedItemIndex!]!.height / 2;
y = position!.top + (size.height - position!.top - position!.bottom) / 2.0 - selectedItemOffset;
}
// Find the ideal horizontal position.
late double x;
if (position!.left > position!.right) {
// Menu button is closer to the right edge, so grow to the left, aligned to the right edge.
x = size.width - position!.right - childSize.width;
} else if (position!.left < position!.right) {
// Menu button is closer to the left edge, so grow to the right, aligned to the left edge.
x = position!.left;
} else {
// Menu button is equidistant from both edges, so grow in reading direction.
switch (textDirection) {
case TextDirection.rtl:
x = size.width - position!.right - childSize.width;
break;
case TextDirection.ltr:
x = position!.left;
break;
}
}
// Avoid going outside an area defined as the rectangle 8.0 pixels from the
// edge of the screen in every direction.
if (x < _kMenuScreenPadding)
x = _kMenuScreenPadding;
else if (x + childSize.width > size.width - _kMenuScreenPadding) x = size.width - childSize.width - _kMenuScreenPadding;
if (y < _kMenuScreenPadding)
y = _kMenuScreenPadding;
else if (y + childSize.height > size.height - _kMenuScreenPadding) y = size.height - childSize.height - _kMenuScreenPadding;
return Offset(x, y);
}
@override
bool shouldRelayout(_PopupMenuRouteLayout oldDelegate) {
// If called when the old and new itemSizes have been initialized then
// we expect them to have the same length because there's no practical
// way to change length of the items list once the menu has been shown.
assert(itemSizes.length == oldDelegate.itemSizes.length);
return position != oldDelegate.position ||
selectedItemIndex != oldDelegate.selectedItemIndex ||
textDirection != oldDelegate.textDirection ||
!listEquals(itemSizes, oldDelegate.itemSizes);
}
}
class _PopupMenuRoute<T> extends PopupRoute<T> {
_PopupMenuRoute({
this.position,
required this.items,
this.initialValue,
this.elevation,
this.theme,
this.popupMenuTheme,
this.barrierLabel,
this.semanticLabel,
this.shape,
this.color,
this.showMenuContext,
this.captureInheritedThemes,
this.barrierColor,
}) : itemSizes = List<Size?>.filled(items.length, null, growable: false);
final RelativeRect? position;
final List<PopupMenuEntry<T>> items;
final List<Size?> itemSizes;
final dynamic initialValue;
final double? elevation;
final ThemeData? theme;
final String? semanticLabel;
final ShapeBorder? shape;
final Color? color;
final PopupMenuThemeData? popupMenuTheme;
final BuildContext? showMenuContext;
final bool? captureInheritedThemes;
final Color? barrierColor;
@override
Animation<double> createAnimation() {
return CurvedAnimation(
parent: super.createAnimation(),
curve: Curves.linear,
reverseCurve: const Interval(0.0, _kMenuCloseIntervalEnd),
);
}
@override
Duration get transitionDuration => _kMenuDuration;
@override
bool get barrierDismissible => true;
@override
final String? barrierLabel;
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
int? selectedItemIndex;
if (initialValue != null) {
for (int index = 0; selectedItemIndex == null && index < items.length; index += 1) {
if (items[index].represents(initialValue)) selectedItemIndex = index;
}
}
Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel);
if (captureInheritedThemes!) {
menu = InheritedTheme.captureAll(showMenuContext!, menu);
} else {
// For the sake of backwards compatibility. An (unlikely) app that relied
// on having menus only inherit from the material Theme could set
// captureInheritedThemes to false and get the original behavior.
if (theme != null) menu = Theme(data: theme!, child: menu);
}
return MediaQuery.removePadding(
context: context,
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
child: Builder(
builder: (BuildContext context) {
return CustomSingleChildLayout(
delegate: _PopupMenuRouteLayout(
position,
itemSizes,
selectedItemIndex,
Directionality.of(context),
),
child: menu,
);
},
),
);
}
}
/// Show a popup menu that contains the `items` at `position`.
///
/// `items` should be non-null and not empty.
///
/// If `initialValue` is specified then the first item with a matching value
/// will be highlighted and the value of `position` gives the rectangle whose
/// vertical center will be aligned with the vertical center of the highlighted
/// item (when possible).
///
/// If `initialValue` is not specified then the top of the menu will be aligned
/// with the top of the `position` rectangle.
///
/// In both cases, the menu position will be adjusted if necessary to fit on the
/// screen.
///
/// Horizontally, the menu is positioned so that it grows in the direction that
/// has the most room. For example, if the `position` describes a rectangle on
/// the left edge of the screen, then the left edge of the menu is aligned with
/// the left edge of the `position`, and the menu grows to the right. If both
/// edges of the `position` are equidistant from the opposite edge of the
/// screen, then the ambient [Directionality] is used as a tie-breaker,
/// preferring to grow in the reading direction.
///
/// The positioning of the `initialValue` at the `position` is implemented by
/// iterating over the `items` to find the first whose
/// [CustomPopupMenuEntry.represents] method returns true for `initialValue`, and then
/// summing the values of [CustomPopupMenuEntry.height] for all the preceding widgets
/// in the list.
///
/// The `elevation` argument specifies the z-coordinate at which to place the
/// menu. The elevation defaults to 8, the appropriate elevation for popup
/// menus.
///
/// The `context` argument is used to look up the [Navigator] and [Theme] for
/// the menu. It is only used when the method is called. Its corresponding
/// widget can be safely removed from the tree before the popup menu is closed.
///
/// The `useRootNavigator` argument is used to determine whether to push the
/// menu to the [Navigator] furthest from or nearest to the given `context`. It
/// is `false` by default.
///
/// The `semanticLabel` argument is used by accessibility frameworks to
/// announce screen transitions when the menu is opened and closed. If this
/// label is not provided, it will default to
/// [MaterialLocalizations.popupMenuLabel].
///
/// See also:
///
/// * [CustomPopupMenuItem], a popup menu entry for a single value.
/// * [PopupMenuDivider], a popup menu entry that is just a horizontal line.
/// * [CheckedPopupMenuItem], a popup menu item with a checkmark.
/// * [PopupMenuButton], which provides an [IconButton] that shows a menu by
/// calling this method automatically.
/// * [SemanticsConfiguration.namesRoute], for a description of edge triggered
/// semantics.
Future<T?> customShowMenu<T>({
required BuildContext context,
required RelativeRect position,
required List<PopupMenuEntry<T>> items,
T? initialValue,
double? elevation,
String? semanticLabel,
Color? barrierColor,
ShapeBorder? shape,
Color? color,
bool captureInheritedThemes = true,
bool useRootNavigator = false,
}) {
assert(items.isNotEmpty);
assert(debugCheckHasMaterialLocalizations(context));
String? label = semanticLabel;
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
label = semanticLabel;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
label = semanticLabel ?? MaterialLocalizations.of(context).popupMenuLabel;
}
return Navigator.of(context, rootNavigator: useRootNavigator).push(
_PopupMenuRoute<T>(
position: position,
items: items,
initialValue: initialValue,
elevation: elevation,
semanticLabel: label,
theme: Theme.of(context),
popupMenuTheme: PopupMenuTheme.of(context),
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: barrierColor,
shape: shape,
color: color,
showMenuContext: context,
captureInheritedThemes: captureInheritedThemes,
),
);
}

View File

@ -0,0 +1,489 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'dropdown_search.dart';
class SelectDialog<T> extends StatefulWidget {
final T? selectedValue;
final List<T>? items;
final bool showSearchBox;
final bool isFilteredOnline;
final ValueChanged<T>? onChanged;
final DropdownSearchOnFind<T>? onFind;
final DropdownSearchPopupItemBuilder<T>? itemBuilder;
final InputDecoration? searchBoxDecoration;
final DropdownSearchItemAsString<T>? itemAsString;
final DropdownSearchFilterFn<T>? filterFn;
final String? hintText;
final double? maxHeight;
final double? dialogMaxWidth;
final Widget? popupTitle;
final bool showSelectedItem;
final DropdownSearchCompareFn<T>? compareFn;
final DropdownSearchPopupItemEnabled<T>? itemDisabled;
///custom layout for empty results
final EmptyBuilder? emptyBuilder;
///custom layout for loading items
final LoadingBuilder? loadingBuilder;
///custom layout for error
final ErrorBuilder? errorBuilder;
///the search box will be focused if true
final bool autoFocusSearchBox;
///text controller to set default search word for example
final TextEditingController? searchBoxController;
///delay before searching
final Duration? searchDelay;
///show or hide favorites items
final bool showFavoriteItems;
///build favorites chips
final FavoriteItemsBuilder<T>? favoriteItemBuilder;
///favorite items alignment
final MainAxisAlignment? favoriteItemsAlignment;
///favorites item
final FavoriteItems<T>? favoriteItems;
const SelectDialog({
Key? key,
this.popupTitle,
this.items,
this.maxHeight,
this.showSearchBox = false,
this.isFilteredOnline = false,
this.onChanged,
this.selectedValue,
this.onFind,
this.itemBuilder,
this.searchBoxDecoration,
this.hintText,
this.itemAsString,
this.filterFn,
this.showSelectedItem = false,
this.compareFn,
this.emptyBuilder,
this.loadingBuilder,
this.errorBuilder,
this.autoFocusSearchBox = false,
this.dialogMaxWidth,
this.itemDisabled,
this.searchBoxController,
this.searchDelay,
this.favoriteItemBuilder,
this.favoriteItems,
this.showFavoriteItems = false,
this.favoriteItemsAlignment = MainAxisAlignment.start,
}) : super(key: key);
@override
_SelectDialogState<T> createState() => _SelectDialogState<T>();
}
class _SelectDialogState<T> extends State<SelectDialog<T?>> {
final FocusNode focusNode = new FocusNode();
final StreamController<List<T?>> _itemsStream = StreamController<List<T?>>.broadcast();
final ValueNotifier<bool> _loadingNotifier = ValueNotifier(false);
final List<T?> _items = <T>[];
late Debouncer _debouncer;
@override
void initState() {
super.initState();
_debouncer = Debouncer(delay: widget.searchDelay);
Future.delayed(
Duration.zero,
() => manageItemsByFilter(widget.searchBoxController?.text ?? '', isFistLoad: true),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (widget.autoFocusSearchBox) FocusScope.of(context).requestFocus(focusNode);
}
@override
void dispose() {
_itemsStream.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
Size deviceSize = MediaQuery.of(context).size;
bool isTablet = deviceSize.width > deviceSize.height;
double maxHeight = deviceSize.height * (isTablet ? .8 : .6);
double maxWidth = deviceSize.width * (isTablet ? .7 : .9);
return Container(
width: widget.dialogMaxWidth ?? maxWidth,
constraints: BoxConstraints(maxHeight: widget.maxHeight ?? maxHeight),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_searchField(),
if (widget.showFavoriteItems == true) _favoriteItemsWidget(),
Expanded(
child: Stack(
children: <Widget>[
StreamBuilder<List<T?>>(
stream: _itemsStream.stream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return _errorWidget(snapshot.error);
} else if (!snapshot.hasData) {
return _loadingWidget();
} else if (snapshot.data!.isEmpty) {
if (widget.emptyBuilder != null)
return widget.emptyBuilder!(context, widget.searchBoxController?.text);
else
return const Center(
child: const Text("No data found"),
);
}
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.symmetric(vertical: 0),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var item = snapshot.data![index];
return _itemWidget(item);
},
);
},
),
_loadingWidget()
],
),
),
],
),
);
}
void _showErrorDialog(dynamic error) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error while getting online items"),
content: _errorWidget(error),
actions: <Widget>[
TextButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop(false);
},
)
],
);
},
);
}
Widget _errorWidget(dynamic error) {
if (widget.errorBuilder != null)
return widget.errorBuilder!(context, widget.searchBoxController?.text, error);
else
return Padding(
padding: EdgeInsets.all(8),
child: Text(
error?.toString() ?? 'Error',
),
);
}
Widget _loadingWidget() {
return ValueListenableBuilder(
valueListenable: _loadingNotifier,
builder: (context, bool isLoading, wid) {
if (isLoading) {
if (widget.loadingBuilder != null)
return widget.loadingBuilder!(context, widget.searchBoxController?.text);
else
return Padding(
padding: const EdgeInsets.all(24.0),
child: const Center(
child: const CircularProgressIndicator(),
),
);
}
return Container();
});
}
void _onTextChanged(String filter) async {
manageItemsByFilter(filter);
}
///Function that filter item (online and offline) base on user filter
///[filter] is the filter keyword
///[isFirstLoad] true if it's the first time we load data from online, false other wises
void manageItemsByFilter(String filter, {bool isFistLoad = false}) async {
_loadingNotifier.value = true;
String encoded(String item) {
String encodedItem = "";
for (int i = 0; i < item.length; i++) {
var char = item[i];
switch (char) {
case 'Á':
case 'á':
case 'ą':
case 'ä':
char = 'a';
break;
case 'é':
case 'É':
char = 'e';
break;
case 'ú':
case 'ű':
case 'ü':
case 'Ú':
case 'Ű':
case 'Ü':
char = 'u';
break;
case 'ö':
case 'ő':
case 'ó':
case 'Ö':
case 'Ő':
case 'Ó':
char = 'o';
break;
case 'í':
case 'Í':
char = 'i';
break;
}
encodedItem += char;
}
return encodedItem;
}
List<T?> applyFilter(String filter) {
return _items.where((i) {
if (widget.filterFn != null)
return (widget.filterFn!(i, filter));
else if (i.toString().toLowerCase().contains(filter.toLowerCase()) ||
encoded(i.toString()).toLowerCase().contains(encoded(filter.toLowerCase()))) {
return true;
} else if (widget.itemAsString != null) {
bool found = (widget.itemAsString!(i)).toLowerCase().contains(filter.toLowerCase());
if (!found) {
found = (encoded(widget.itemAsString!(i))).toLowerCase().contains(encoded(filter.toLowerCase()));
}
return found;
}
return false;
}).toList();
}
//load offline data for the first time
if (isFistLoad && widget.items != null) _items.addAll(widget.items!);
//manage offline items
if (widget.onFind != null && (widget.isFilteredOnline || isFistLoad)) {
try {
final List<T?> onlineItems = [];
onlineItems.addAll(await widget.onFind!(filter));
//Remove all old data
_items.clear();
//add offline items
if (widget.items != null) {
_items.addAll(widget.items!);
//if filter online we filter only local list based on entered keyword (filter)
if (widget.isFilteredOnline == true) {
var filteredLocalList = applyFilter(filter);
_items.clear();
_items.addAll(filteredLocalList);
}
}
//add new online items to list
_items.addAll(onlineItems);
//don't filter data , they are already filtred online and local data are already filtered
if (widget.isFilteredOnline == true)
_addDataToStream(_items);
else
_addDataToStream(applyFilter(filter));
} catch (e) {
_addErrorToStream(e);
//if offline items count > 0 , the error will be not visible for the user
//As solution we show it in dialog
if (widget.items != null && widget.items!.isNotEmpty) {
_showErrorDialog(e);
_addDataToStream(applyFilter(filter));
}
}
} else {
_addDataToStream(applyFilter(filter));
}
_loadingNotifier.value = false;
}
void _addDataToStream(List<T?> data) {
if (_itemsStream.isClosed) return;
_itemsStream.add(data);
}
void _addErrorToStream(Object error, [StackTrace? stackTrace]) {
if (_itemsStream.isClosed) return;
_itemsStream.addError(error, stackTrace);
}
Widget _itemWidget(T? item) {
if (widget.itemBuilder != null)
return InkWell(
child: widget.itemBuilder!(
context,
item,
_manageSelectedItemVisibility(item),
),
onTap: widget.itemDisabled != null && (widget.itemDisabled!(item)) == true ? null : () => _handleSelectItem(item),
);
else
return ListTile(
title: Text(_selectedItemAsString(item)),
selected: _manageSelectedItemVisibility(item),
onTap: widget.itemDisabled != null && (widget.itemDisabled!(item)) == true ? null : () => _handleSelectItem(item),
);
}
/// selected item will be highlighted only when [widget.showSelectedItem] is true,
/// if our object is String [widget.compareFn] is not required , other wises it's required
bool _manageSelectedItemVisibility(T? item) {
if (!widget.showSelectedItem) return false;
if (item is String?) {
return item == widget.selectedValue;
} else {
return widget.compareFn!(item, widget.selectedValue);
}
}
Widget _searchField() {
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: <Widget>[
widget.popupTitle ?? const SizedBox.shrink(),
if (widget.showSearchBox)
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: widget.searchBoxController,
focusNode: focusNode,
onChanged: (f) => _debouncer(() {
_onTextChanged(f);
}),
decoration: widget.searchBoxDecoration ??
InputDecoration(
hintText: widget.hintText,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
),
),
)
]);
}
Widget _favoriteItemsWidget() {
return StreamBuilder<List<T?>>(
stream: _itemsStream.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return _buildFavoriteItems(widget.favoriteItems!(snapshot.data!));
} else {
return Container();
}
});
}
Widget _buildFavoriteItems(List<T?>? favoriteItems) {
if (favoriteItems != null) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
child: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: widget.favoriteItemsAlignment ?? MainAxisAlignment.start,
children: favoriteItems
.map(
(f) => GestureDetector(
onTap: () => _handleSelectItem(f),
child: Container(
margin: EdgeInsets.only(right: 4),
child: widget.favoriteItemBuilder != null
? widget.favoriteItemBuilder!(context, f)
: _favoriteItemDefaultWidget(f),
),
),
)
.toList()),
),
);
}),
);
} else {
return Container();
}
}
void _handleSelectItem(T? selectedItem) {
Navigator.pop(context, selectedItem);
if (widget.onChanged != null) widget.onChanged!(selectedItem);
}
Widget _favoriteItemDefaultWidget(T? item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Theme.of(context).primaryColorLight),
child: Text(
_selectedItemAsString(item),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.subtitle1,
),
);
}
///function that return the String value of an object
String _selectedItemAsString(T? data) {
if (data == null) {
return "";
} else if (widget.itemAsString != null) {
return widget.itemAsString!(data);
} else {
return data.toString();
}
}
}
class Debouncer {
final Duration? delay;
Timer? _timer;
Debouncer({this.delay});
call(Function action) {
_timer?.cancel();
_timer = Timer(delay ?? const Duration(milliseconds: 500), action as void Function());
}
}

View File

@ -12,7 +12,7 @@ import 'package:aitrainer_app/view/customer_fitness_page.dart';
import 'package:aitrainer_app/view/customer_goal_page.dart';
import 'package:aitrainer_app/view/customer_modify_page.dart';
import 'package:aitrainer_app/view/customer_welcome_page.dart';
import 'package:aitrainer_app/view/evaluation.dart';
import 'package:aitrainer_app/view/evaluation_page.dart';
import 'package:aitrainer_app/view/exercise_control_page.dart';
import 'package:aitrainer_app/view/exercise_execute_page.dart';
import 'package:aitrainer_app/view/exercise_execute_plan_add_page.dart';
@ -37,7 +37,7 @@ import 'package:aitrainer_app/view/test_set_new.dart';
import 'package:aitrainer_app/widgets/home.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:aitrainer_app/library/flurry.dart';
import 'package:flurry/flurry.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@ -118,6 +118,11 @@ Future<Null> main() async {
// - https://api.dartlang.org/stable/1.24.2/dart-async/Zone-class.html
// - https://www.dartlang.org/articles/libraries/zones
runZonedGuarded<Future<Null>>(() async {
await Sentry.init(
(options) {
options.dsn = dsn;
},
);
final WorkoutTreeRepository menuTreeRepository = WorkoutTreeRepository();
WidgetsFlutterBinding.ensureInitialized();

View File

@ -1,6 +1,7 @@
import 'dart:collection';
import 'dart:convert';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/evaluation.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_plan_template.dart';
@ -21,7 +22,7 @@ import 'package:aitrainer_app/main.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/env.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:aitrainer_app/library/flurry.dart';
import 'package:flurry/flurry.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:package_info/package_info.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -84,8 +85,8 @@ class Cache with Logging {
AccessToken? accessTokenFacebook;
Customer? userLoggedIn;
String? firebaseUid;
late LoginType loginType;
late PackageInfo packageInfo;
LoginType? loginType;
PackageInfo? packageInfo;
bool hasPurchased = false;
@ -93,6 +94,7 @@ class Cache with Logging {
List<ExerciseType>? _exerciseTypes;
List<ExerciseTree>? _exerciseTree;
List<Evaluation>? _evaluations;
List<Exercise>? _exercises;
ExercisePlan? _myExercisePlan;
@ -204,7 +206,6 @@ class Cache with Logging {
String? detailsJson = sharedPreferences.getString(Cache.activeExercisePlanDetailsKey);
if (detailsJson != null) {
print("Details $detailsJson");
Iterable json = jsonDecode(detailsJson);
this.activeExercisePlanDetails = json.map((details) => ExercisePlanDetail.fromJsonWithExerciseList(details)).toList();
}
@ -641,4 +642,7 @@ class Cache with Logging {
}
return isMuscleDevelopmentSeen!;
}
List<Evaluation>? get evaluations => this._evaluations;
set evaluations(List<Evaluation>? value) => this._evaluations = value;
}

28
lib/model/evaluation.dart Normal file
View File

@ -0,0 +1,28 @@
import 'package:aitrainer_app/model/evaluation_attribute.dart';
class Evaluation {
late int evaluationId;
late String name;
late int exerciseTypeId;
late String unit;
late List attributes;
Evaluation.fromJson(Map json) {
evaluationId = json['evaluationId'];
name = json['name'];
exerciseTypeId = json['exerciseTypeId'];
unit = json['unit'];
this.attributes = json['attributes'].map((attr) => EvaluationAttribute.fromJson(attr)).toList();
}
@override
String toString() {
Map<String, dynamic> json = {
'evaluationId': this.evaluationId,
'name': this.name,
'exerciseTypeId': this.exerciseTypeId,
'unit': this.unit
};
return json.toString();
}
}

View File

@ -0,0 +1,41 @@
class EvaluationAttribute {
late int evaluationAttrId;
late int evaluationId;
late String name;
late String sex;
late int ageMin;
late int ageMax;
late double valueMin;
late double valueMax;
late String evaluationText;
String? suggestion;
EvaluationAttribute.fromJson(Map json) {
evaluationAttrId = json['evaluationAttrId'];
evaluationId = json['evaluationId'];
name = json['name'];
sex = json['sex'];
ageMin = json['ageMin'];
ageMax = json['ageMax'];
valueMin = json['valueMin'];
valueMax = json['valueMax'];
evaluationText = json['evaluation_text'];
suggestion = json['suggestion'];
}
@override
String toString() {
Map<String, dynamic> json = {
'evaluationAttrId': this.evaluationAttrId,
'evaluationId': this.evaluationId,
'name': this.name,
'sex': this.sex,
'ageMin': this.ageMin,
'ageMax': this.ageMax,
'valueMin': this.valueMin,
'valueMax': this.valueMax,
'evaluation_text': this.evaluationText,
};
return json.toString();
}
}

View File

@ -61,4 +61,10 @@ class ExercisePlan {
};
}
}
@override
String toString() {
Map<String, dynamic> json = toJson();
return json.toString();
}
}

View File

@ -12,13 +12,16 @@ extension ExericisePlanDetailStateExt on ExercisePlanDetailState {
class ExercisePlanDetail {
int? exercisePlanDetailId;
late int exercisePlanId;
int? exercisePlanId;
late int exerciseTypeId;
int? serie;
int? repeats;
String? weightEquation;
List? exercises;
/// List<Exercise>
List<Exercise>? exercises;
/// bool finished
bool? finished;
ExercisePlanDetailState state = ExercisePlanDetailState.start;
@ -53,7 +56,7 @@ class ExercisePlanDetail {
jsonExercises = jsonExercises.replaceAll(r'\"null\"', 'null');
print("Exercises $jsonExercises");
//print("Exercises $jsonExercises");
Iterable iterable = jsonDecode(jsonExercises);
this.exercises = iterable.map((exercise) => Exercise.fromJson(exercise)).toList();
} on Exception catch (e) {
@ -62,7 +65,7 @@ class ExercisePlanDetail {
}
Map<String, dynamic> toJson() => {
"exercisePlanId": exercisePlanId,
"exercisePlanId": exercisePlanId == null ? 0 : exercisePlanId,
"exerciseTypeId": exerciseTypeId,
"serie": serie,
"repeats": repeats,
@ -76,6 +79,12 @@ class ExercisePlanDetail {
"serie": serie,
"repeats": repeats,
"weightEquation": weightEquation,
'exercises': exercises!.map((exercise) => exercise.toJson()).toList().toString(),
'exercises': exercises == null ? [].toString() : exercises!.map((exercise) => exercise.toJson()).toList().toString(),
};
@override
String toString() {
Map<String, dynamic> json = toJsonWithExerciseList();
return json.toString();
}
}

View File

@ -1,3 +1,23 @@
enum Sport { football, fitness, footgolf }
extension SportExt on Sport {
String toStr() => this.toString().split(".").last;
bool equalsTo(Sport sport) => this.toString() == sport.toString();
bool equalsStringTo(String sport) => this.toStr() == sport;
String description(Sport sport) {
if (Sport.football.equalsTo(sport)) {
return "Football";
} else if (Sport.fitness.equalsTo(sport)) {
return "Fitness / Body Building";
} else if (Sport.footgolf.equalsTo(sport)) {
return "Footgolf";
} else {
return "Sport";
}
}
}
class FitnessState {
late final String value;
late final String stateText;

View File

@ -7,7 +7,7 @@ class Tracking {
late int customerId;
late DateTime dateAdd;
late String event;
late String eventValue;
String? eventValue;
late String area;
late String platform;
late String version;
@ -19,6 +19,6 @@ class Tracking {
"eventValue": eventValue,
"area": Platform.localeName,
"platform": Platform.isAndroid ? "Android" : "iOS",
"version": Cache().packageInfo.version + "+" + Cache().packageInfo.buildNumber
"version": Cache().packageInfo != null ? Cache().packageInfo!.version + "+" + Cache().packageInfo!.buildNumber : ""
};
}

View File

@ -21,8 +21,8 @@ class GenderItem {
}
class CustomerRepository with Logging {
late Customer customer;
late Customer? _trainee;
Customer? customer;
Customer? _trainee;
List<Customer>? _trainees;
List<CustomerProperty>? _allProperties;
final PropertyRepository propertyRepository = PropertyRepository();
@ -68,51 +68,61 @@ class CustomerRepository with Logging {
}
String? get name {
return this.customer.name != null ? this.customer.name : "";
return this.customer != null && this.customer!.name != null ? this.customer!.name : "";
}
String? get firstName {
return this.customer.firstname != null ? this.customer.firstname : "";
return this.customer != null && this.customer!.firstname != null ? this.customer!.firstname : "";
}
String get sex {
return this.customer.sex == "m" ? "Man" : "Woman";
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!.sex == "m" ? "Man" : "Woman";
}
int? get birthYear {
return this.customer.birthYear;
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!.birthYear;
}
String? get goal {
return this.customer.goal;
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!.goal;
}
String? get fitnessLevel {
return this.customer.fitnessLevel;
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!.fitnessLevel;
}
String? get bodyType {
return this.customer.bodyType;
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!.bodyType;
}
setName(String name) {
this.customer.name = name;
if (this.customer == null) return;
this.customer!.name = name;
}
setFirstName(String firstName) {
this.customer.firstname = firstName;
if (this.customer == null) return;
this.customer!.firstname = firstName;
}
setPassword(String password) {
this.customer.password = password;
if (this.customer == null) return;
this.customer!.password = password;
}
setEmail(String email) {
this.customer.email = email;
if (this.customer == null) return;
this.customer!.email = email;
}
setSex(String sex) {
this.customer.sex = sex;
if (this.customer == null) return;
this.customer!.sex = sex;
}
setWeight(int weight) {
@ -126,19 +136,20 @@ class CustomerRepository with Logging {
}
setCustomerProperty(String propertyName, double value, {id = 0}) {
if (this.customer.properties[propertyName] == null) {
this.customer.properties[propertyName] = CustomerProperty(
if (this.customer == null) throw Exception("Initialize the customer object");
if (this.customer!.properties[propertyName] == null) {
this.customer!.properties[propertyName] = CustomerProperty(
propertyId: propertyRepository.getPropertyByName("Height")!.propertyId,
customerId: this.customer.customerId!,
customerId: this.customer!.customerId!,
propertyValue: value,
dateAdd: DateTime.now());
} else {
this.customer.properties[propertyName]!.propertyValue = value;
this.customer!.properties[propertyName]!.propertyValue = value;
}
this.customer.properties[propertyName]!.dateAdd = DateTime.now();
this.customer.properties[propertyName]!.newData = true;
this.customer!.properties[propertyName]!.dateAdd = DateTime.now();
this.customer!.properties[propertyName]!.newData = true;
if (id > 0) {
this.customer.properties[propertyName]!.customerPropertyId = id;
this.customer!.properties[propertyName]!.customerPropertyId = id;
}
}
@ -151,31 +162,36 @@ class CustomerRepository with Logging {
}
double getCustomerPropertyValue(String propertyName) {
if (this.customer.properties[propertyName] == null) {
if (this.customer == null || this.customer!.properties[propertyName] == null) {
return 0.0;
} else {
return this.customer.properties[propertyName]!.propertyValue;
return this.customer!.properties[propertyName]!.propertyValue;
}
}
CustomerProperty? getCustomerProperty(String propertyName) {
return this.customer.properties[propertyName];
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!.properties[propertyName];
}
setBirthYear(int birthYear) {
this.customer.birthYear = birthYear;
if (this.customer == null) throw Exception("Initialize the customer object");
this.customer!.birthYear = birthYear;
}
setFitnessLevel(String level) {
this.customer.fitnessLevel = level;
if (this.customer == null) throw Exception("Initialize the customer object");
this.customer!.fitnessLevel = level;
}
setGoal(String goal) {
this.customer.goal = goal;
if (this.customer == null) throw Exception("Initialize the customer object");
this.customer!.goal = goal;
}
setBodyType(String bodyType) {
this.customer.bodyType = bodyType;
if (this.customer == null) throw Exception("Initialize the customer object");
this.customer!.bodyType = bodyType;
}
createNew() {
@ -183,7 +199,8 @@ class CustomerRepository with Logging {
}
Customer getCustomer() {
return this.customer;
if (this.customer == null) throw Exception("Initialize the customer object");
return this.customer!;
}
void setCustomer(Customer customer) {
@ -191,12 +208,14 @@ class CustomerRepository with Logging {
}
Future<void> addCustomer() async {
final Customer modelCustomer = customer;
if (this.customer == null) throw Exception("Initialize the customer object");
final Customer modelCustomer = customer!;
await CustomerApi().addCustomer(modelCustomer);
}
Future<void> saveCustomer() async {
final Customer modelCustomer = customer;
if (this.customer == null) throw Exception("Initialize the customer object");
final Customer modelCustomer = customer!;
await CustomerApi().saveCustomer(modelCustomer);
await this.saveProperties(modelCustomer.properties);
}
@ -317,6 +336,7 @@ class CustomerRepository with Logging {
}
void addSizes(String sex) {
if (this.customer == null) throw Exception("Initialize the customer object");
List<Property>? properties = Cache().getProperties();
if (properties == null) {
return;
@ -328,62 +348,62 @@ class CustomerRepository with Logging {
if (element.propertyName == "Shoulder") {
element.top = (122 * distortionHeight).toInt();
element.left = (130 * distortionWidth).toInt();
element.value = this.customer.getProperty("Shoulder");
element.value = this.customer!.getProperty("Shoulder");
manSizes.add(element);
} else if (element.propertyName == "Neck") {
element.top = (68 * distortionHeight).toInt();
element.left = (130 * distortionWidth).toInt();
element.value = this.customer.getProperty("Neck");
element.value = this.customer!.getProperty("Neck");
manSizes.add(element);
} else if (element.propertyName == "Biceps") {
element.top = (178 * distortionHeight).toInt();
element.left = (208 * distortionWidth).toInt();
element.value = this.customer.getProperty("Biceps");
element.value = this.customer!.getProperty("Biceps");
manSizes.add(element);
} else if (element.propertyName == "Chest") {
element.top = (154 * distortionHeight).toInt();
element.left = (130 * distortionWidth).toInt();
element.value = this.customer.getProperty("Chest");
element.value = this.customer!.getProperty("Chest");
manSizes.add(element);
} else if (element.propertyName == "Belly") {
element.top = (244 * distortionHeight).toInt();
element.left = (130 * distortionWidth).toInt();
element.value = this.customer.getProperty("Belly");
element.value = this.customer!.getProperty("Belly");
manSizes.add(element);
} else if (element.propertyName == "Hip") {
element.top = (308 * distortionHeight).toInt();
element.left = (130 * distortionWidth).toInt();
element.value = this.customer.getProperty("Hip");
element.value = this.customer!.getProperty("Hip");
manSizes.add(element);
} else if (element.propertyName == "Thigh Top") {
element.top = (332 * distortionHeight).toInt();
element.left = (165 * distortionWidth).toInt();
element.value = this.customer.getProperty("Thigh Top");
element.value = this.customer!.getProperty("Thigh Top");
manSizes.add(element);
} else if (element.propertyName == "Thigh Middle") {
element.top = (382 * distortionHeight).toInt();
element.left = (100 * distortionWidth).toInt();
element.value = this.customer.getProperty("Thigh Middle");
element.value = this.customer!.getProperty("Thigh Middle");
manSizes.add(element);
} else if (element.propertyName == "Knee") {
element.top = (464 * distortionHeight).toInt();
element.left = (97 * distortionWidth).toInt();
element.value = this.customer.getProperty("Knee");
element.value = this.customer!.getProperty("Knee");
manSizes.add(element);
} else if (element.propertyName == "Calf") {
element.top = (520 * distortionHeight).toInt();
element.left = (97 * distortionWidth).toInt();
element.value = this.customer.getProperty("Calf");
element.value = this.customer!.getProperty("Calf");
manSizes.add(element);
} else if (element.propertyName == "Ankle") {
element.top = (620 * distortionHeight).toInt();
element.left = (150 * distortionWidth).toInt();
element.value = this.customer.getProperty("Ankle");
element.value = this.customer!.getProperty("Ankle");
manSizes.add(element);
} else if (element.propertyName == "Weight") {
element.top = (402 * distortionHeight).toInt();
element.left = (240 * distortionWidth).toInt();
element.value = this.customer.getProperty("Weight");
element.value = this.customer!.getProperty("Weight");
manSizes.add(element);
}
});
@ -392,62 +412,62 @@ class CustomerRepository with Logging {
if (element.propertyName == "Shoulder") {
element.top = (122 * distortionHeight).toInt();
element.left = (151 * distortionWidth).toInt();
element.value = this.customer.getProperty("Shoulder");
element.value = this.customer!.getProperty("Shoulder");
manSizes.add(element);
} else if (element.propertyName == "Neck") {
element.top = (78 * distortionHeight).toInt();
element.left = (151 * distortionWidth).toInt();
element.value = this.customer.getProperty("Neck");
element.value = this.customer!.getProperty("Neck");
manSizes.add(element);
} else if (element.propertyName == "Biceps") {
element.top = (178 * distortionHeight).toInt();
element.left = (212 * distortionWidth).toInt();
element.value = this.customer.getProperty("Biceps");
element.value = this.customer!.getProperty("Biceps");
manSizes.add(element);
} else if (element.propertyName == "Chest") {
element.top = (154 * distortionHeight).toInt();
element.left = (151 * distortionWidth).toInt();
element.value = this.customer.getProperty("Chest");
element.value = this.customer!.getProperty("Chest");
manSizes.add(element);
} else if (element.propertyName == "Belly") {
element.top = (230 * distortionHeight).toInt();
element.left = (151 * distortionWidth).toInt();
element.value = this.customer.getProperty("Belly");
element.value = this.customer!.getProperty("Belly");
manSizes.add(element);
} else if (element.propertyName == "Hip") {
element.top = (294 * distortionHeight).toInt();
element.left = (151 * distortionWidth).toInt();
element.value = this.customer.getProperty("Hip");
element.value = this.customer!.getProperty("Hip");
manSizes.add(element);
} else if (element.propertyName == "Thigh Top") {
element.top = (335 * distortionHeight).toInt();
element.left = (185 * distortionWidth).toInt();
element.value = this.customer.getProperty("Thigh Top");
element.value = this.customer!.getProperty("Thigh Top");
manSizes.add(element);
} else if (element.propertyName == "Thigh Middle") {
element.top = (377 * distortionHeight).toInt();
element.left = (125 * distortionWidth).toInt();
element.value = this.customer.getProperty("Thigh Middle");
element.value = this.customer!.getProperty("Thigh Middle");
manSizes.add(element);
} else if (element.propertyName == "Knee") {
element.top = (468 * distortionHeight).toInt();
element.left = (129 * distortionWidth).toInt();
element.value = this.customer.getProperty("Knee");
element.value = this.customer!.getProperty("Knee");
manSizes.add(element);
} else if (element.propertyName == "Calf") {
element.top = (525 * distortionHeight).toInt();
element.left = (129 * distortionWidth).toInt();
element.value = this.customer.getProperty("Calf");
element.value = this.customer!.getProperty("Calf");
manSizes.add(element);
} else if (element.propertyName == "Ankle") {
element.top = (620 * distortionHeight).toInt();
element.left = (162 * distortionWidth).toInt();
element.value = this.customer.getProperty("Ankle");
element.value = this.customer!.getProperty("Ankle");
manSizes.add(element);
} else if (element.propertyName == "Weight") {
element.top = (402 * distortionHeight).toInt();
element.left = (240 * distortionWidth).toInt();
element.value = this.customer.getProperty("Weight");
element.value = this.customer!.getProperty("Weight");
manSizes.add(element);
}
});

View File

@ -0,0 +1,77 @@
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/evaluation.dart';
import 'package:aitrainer_app/model/evaluation_attribute.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class EvaluationRepository {
List<Evaluation>? evaluations;
EvaluationRepository() {
evaluations = Cache().evaluations;
}
String getEvaluationTextByExerciseType(int exerciseTypeId, double value) {
String? eval;
Evaluation? evaluation = this.getEvaluationByExerciseTypeId(exerciseTypeId);
Customer? customer = Cache().userLoggedIn;
if (evaluation != null && customer != null) {
int? age = customer.age;
if (age != null) {
for (var attribute in evaluation.attributes) {
EvaluationAttribute attr = attribute as EvaluationAttribute;
if (age >= attr.ageMin && age <= attr.ageMax && value >= attr.valueMin && value <= attr.valueMax) {
eval = attr.evaluationText;
break;
}
}
}
}
if (eval == null) {
eval = EvaluationText.fair.toStr();
}
return eval;
}
Evaluation? getEvaluationByExerciseTypeId(int exerciseTypeId) {
Evaluation? eval;
if (evaluations != null) {
for (var evaluation in evaluations!) {
if (evaluation.exerciseTypeId == exerciseTypeId) {
eval = evaluation;
break;
}
}
}
return eval;
}
Color getEvaluationColor(String eval) {
Color color = Colors.yellow[100]!;
if (EvaluationText.very_poor.equalsStringTo(eval)) {
return Colors.red[800]!;
} else if (EvaluationText.poor.equalsStringTo(eval)) {
return Colors.red[400]!;
} else if (EvaluationText.below_average.equalsStringTo(eval)) {
return Colors.orange[600]!;
} else if (EvaluationText.average.equalsStringTo(eval)) {
return Colors.yellow[600]!;
} else if (EvaluationText.above_average.equalsStringTo(eval)) {
return Colors.greenAccent;
} else if (EvaluationText.good.equalsStringTo(eval)) {
return Colors.green[400]!;
} else if (EvaluationText.excellent.equalsStringTo(eval)) {
return Colors.green[600]!;
} else if (EvaluationText.elite.equalsStringTo(eval)) {
return Colors.green[800]!;
}
return color;
}
}

View File

@ -37,6 +37,7 @@ class ExerciseDeviceRepository {
}
List<ExerciseDevice> getGymDevices() {
if (Cache().getDevices() == null) return [];
final List<ExerciseDevice> gymDevices = [];
if (_devices.isEmpty) {
_devices = Cache().getDevices()!;

View File

@ -27,8 +27,13 @@ class ExercisePlanRepository {
ExercisePlan? getExercisePlan() => exercisePlan;
void addDetailToPlan() {
if (exercisePlan == null) {
this.createNewPlan();
}
if (exercisePlan != null && actualPlanDetail != null) {
actualPlanDetail!.exercisePlanId = exercisePlan!.exercisePlanId!;
if (exercisePlan!.exercisePlanId != null) {
actualPlanDetail!.exercisePlanId = exercisePlan!.exercisePlanId!;
}
exercisePlanDetails[actualPlanDetail!.exerciseTypeId] = actualPlanDetail!;
Cache().addToMyExercisePlanDetails(actualPlanDetail!);
}
@ -85,24 +90,14 @@ class ExercisePlanRepository {
}
void removeExerciseTypeFromPlanByExerciseTypeId(int exerciseTypeId) {
if (exercisePlanDetails[exerciseTypeId] == null) return;
exercisePlanDetails[exerciseTypeId]!.change = ModelChange.delete;
Cache().deleteMyExercisePlanDetailByExerciseTypeId(exerciseTypeId);
}
Future<void> saveExercisePlan() async {
if (exercisePlan == null) {
if (Cache().userLoggedIn == null) {
throw Exception("please log in");
}
String exercisePlanName;
if (this.customerId == Cache().userLoggedIn!.customerId) {
exercisePlanName = Cache().userLoggedIn!.name! + " private";
} else {
exercisePlanName = Cache().getTrainee()!.name! + " " + Cache().getTrainee()!.firstname! + " private";
}
exercisePlan = ExercisePlan(exercisePlanName, this.customerId);
this.createNewPlan();
}
if (newPlan) {
exercisePlan!.dateAdd = DateTime.now();
@ -139,6 +134,22 @@ class ExercisePlanRepository {
}
}
void createNewPlan() {
if (Cache().userLoggedIn == null) {
throw Exception("please log in");
}
String exercisePlanName;
if (this.customerId == Cache().userLoggedIn!.customerId) {
exercisePlanName = Cache().userLoggedIn!.name! + " private";
} else {
exercisePlanName = Cache().getTrainee()!.name! + " " + Cache().getTrainee()!.firstname! + " private";
}
exercisePlan = ExercisePlan(exercisePlanName, this.customerId);
newPlan = true;
}
Future<ExercisePlan?> getLastExercisePlan() async {
if (customerId == 0) {
return null;
@ -150,7 +161,12 @@ class ExercisePlanRepository {
}
exercisePlan = await ExercisePlanApi().getLastExercisePlan(customerId);
newPlan = (exercisePlan == null);
if (exercisePlan == null) {
this.createNewPlan();
}
;
Cache().setMyExercisePlan(exercisePlan!);
return exercisePlan;
}
@ -170,16 +186,19 @@ class ExercisePlanRepository {
exercisePlanDetails = listCache;
return;
} else {
list = await ExercisePlanApi().getExercisePlanDetail(exercisePlan!.exercisePlanId!);
if (exercisePlan!.exercisePlanId != null) {
list = await ExercisePlanApi().getExercisePlanDetail(exercisePlan!.exercisePlanId!);
}
}
exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
list.forEach((element) {
newPlan = false;
ExercisePlanDetail detail = element;
exercisePlanDetails[detail.exerciseTypeId] = detail;
});
if (list.isNotEmpty) {
list.forEach((element) {
newPlan = false;
ExercisePlanDetail detail = element;
exercisePlanDetails[detail.exerciseTypeId] = detail;
});
}
Cache().setMyExercisePlanDetails(exercisePlanDetails);
return;

View File

@ -270,16 +270,167 @@ class ExerciseRepository {
void getSameExercise(int exerciseTypeId, String day) {
this.actualExerciseList = [];
if (exerciseList != null) {
int index = 0;
for (int i = 0; i < this.exerciseList!.length; i++) {
Exercise exercise = exerciseList![i];
String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
if (exerciseTypeId == exercise.exerciseTypeId && exerciseDate == day) {
final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
if (exerciseTypeId == exercise.exerciseTypeId && exerciseDate == day && index < 4) {
this.actualExerciseList!.add(exercise);
index++;
}
}
}
}
double calculate1RM(Exercise exercise) {
double weight = exercise.unitQuantity!;
double repeat = exercise.quantity!;
if (weight == 0 || repeat == 0) {
return 0;
}
double rmWendler = weight * repeat * 0.0333 + weight;
double rmOconner = weight * (1 + repeat / 40);
double average = (rmWendler + rmOconner) / 2;
return average;
}
double getBest1RMPercent(Exercise exercise) {
double result = 0;
if (this.exerciseList == null || this.exerciseList!.isEmpty) {
this.exerciseList = Cache().getExercises();
}
final int exerciseTypeId = exercise.exerciseTypeId!;
double toCompare = this.calculate1RM(exercise);
final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(DateTime.now());
List<Exercise> oldExercises = [];
this.exerciseList!.forEach((exercise) {
final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
oldExercises.add(exercise);
}
});
if (oldExercises.isNotEmpty) {
oldExercises.sort((a, b) {
double sumA = 0;
double sumB = 0;
if (a.unitQuantity != null && b.unitQuantity != null) {
sumA = a.quantity! * a.unitQuantity!;
sumB = b.quantity! * b.unitQuantity!;
} else {
sumA = a.quantity!;
sumB = b.quantity!;
}
return sumA >= sumB ? 1 : -1;
});
double withCompare = this.calculate1RM(oldExercises.last);
result = toCompare >= withCompare ? (1 - toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
}
return result;
}
double getLast1RMPercent(Exercise exercise) {
double result = 0;
if (this.exerciseList == null || this.exerciseList!.isEmpty) {
this.exerciseList = Cache().getExercises();
}
final int exerciseTypeId = exercise.exerciseTypeId!;
double toCompare = this.calculate1RM(exercise);
final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
List<Exercise> oldExercises = [];
this.exerciseList!.forEach((exercise) {
final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
oldExercises.add(exercise);
}
});
if (oldExercises.isNotEmpty) {
double withCompare = this.calculate1RM(oldExercises.first);
result = toCompare >= withCompare ? (toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
}
return result;
}
double getBestExercisePercent(Exercise exercise) {
double result = 0;
if (this.exerciseList == null || this.exerciseList!.isEmpty) {
this.exerciseList = Cache().getExercises();
}
final int exerciseTypeId = exercise.exerciseTypeId!;
double toCompare = exercise.unitQuantity != null ? exercise.quantity! * exercise.unitQuantity! : exercise.quantity!;
final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(DateTime.now());
List<Exercise> oldExercises = [];
this.exerciseList!.forEach((exercise) {
final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
oldExercises.add(exercise);
}
});
if (oldExercises.isNotEmpty) {
oldExercises.sort((a, b) {
double sumA = 0;
double sumB = 0;
if (a.unitQuantity != null && b.unitQuantity != null) {
sumA = a.quantity! * a.unitQuantity!;
sumB = b.quantity! * b.unitQuantity!;
} else {
sumA = a.quantity!;
sumB = b.quantity!;
}
return sumA >= sumB ? 1 : -1;
});
double withCompare = oldExercises.last.unitQuantity != null
? oldExercises.last.quantity! * oldExercises.last.unitQuantity!
: oldExercises.last.quantity!;
result = toCompare >= withCompare ? (1 - toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
print("Last Best: ${oldExercises.last} - result: $result");
}
return result;
}
double getLastExercisePercent(Exercise exercise) {
double result = 0;
if (this.exerciseList == null || this.exerciseList!.isEmpty) {
this.exerciseList = Cache().getExercises();
}
final int exerciseTypeId = exercise.exerciseTypeId!;
double toCompare = exercise.unitQuantity != null ? exercise.quantity! * exercise.unitQuantity! : exercise.quantity!;
final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
List<Exercise> oldExercises = [];
this.exerciseList!.forEach((exercise) {
final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
oldExercises.add(exercise);
}
});
if (oldExercises.isNotEmpty) {
double withCompare = oldExercises.first.unitQuantity != null
? oldExercises.first.quantity! * oldExercises.first.unitQuantity!
: oldExercises.first.quantity!;
result = toCompare >= withCompare ? (toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
print("Last Last: ${oldExercises.first} vs. $exercise - - result: $result");
}
return result;
}
void sortByDate() {
if (exerciseList == null || exerciseList!.isEmpty) {
return;

View File

@ -3,7 +3,7 @@ import 'package:aitrainer_app/model/property.dart';
import 'package:aitrainer_app/service/property_service.dart';
class PropertyRepository {
late List<Property>? _properties;
List<Property>? _properties;
Future<List<Property>?> getDBProperties() async {
this._properties = await PropertyApi().getProperties();

View File

@ -112,7 +112,9 @@ class WorkoutTreeRepository with Logging {
parent != null ? parent.nameEnglish : "",
0);
this.tree[exerciseType.name] = menuItem;
menuAsExercise.add(menuItem);
if (isRunning || is1RM) {
menuAsExercise.add(menuItem);
}
//log("ExerciseType in Menu item ${exerciseType.toJson()} is1RM: $is1RM");
});
} else {

View File

@ -8,6 +8,7 @@ import 'package:aitrainer_app/model/cache.dart';
class APIClient with Common, Logging {
Future<String> get(String endPoint, String param) async {
final url = Cache.getBaseUrl() + endPoint + param;
trace("-------- API get " + url);
String authToken = Cache().getAuthToken();
if (authToken.length == 0) {
@ -57,6 +58,7 @@ class APIClient with Common, Logging {
final response = await http.post(uri, headers: {'Authorization': '1', 'Content-Type': 'application/json'}, body: body);
final responseCode = response.statusCode;
if (responseCode != 200) {
trace("authentication response: $responseCode");
return {
"error": "Authentication error, total failure",
};
@ -65,6 +67,7 @@ class APIClient with Common, Logging {
final responseJson = json.decode(response.body);
return responseJson;
} catch (exception) {
print(exception.toString());
return {"error": "Network error, try again later " + exception.toString()};
}
}

View File

@ -91,9 +91,10 @@ class ExercisePlanApi with Logging {
final String responseBody = await _client.get("exercise_plan/last/" + customerId.toString(), body);
exercisePlan = ExercisePlan.fromJson(jsonDecode(responseBody));
} on Exception catch (e) {
print(e.toString());
if (e is NotFoundException) {
log("ExercisePlan not found for " + customerId.toString());
return exercisePlan;
return null;
} else {
throw new Exception(e.toString());
}
@ -104,7 +105,7 @@ class ExercisePlanApi with Logging {
Future<List<ExercisePlanDetail>> getExercisePlanDetail(int exercisePlanId) async {
String body = "";
log(" ===== get exercisePlanDetail $exercisePlanId");
List<ExercisePlanDetail> listExercisePlanDetail;
List<ExercisePlanDetail> listExercisePlanDetail = [];
try {
final String responseBody = await _client.get("exercise_plan_detail/" + exercisePlanId.toString(), body);
log("response body:" + responseBody);

View File

@ -109,7 +109,7 @@ class FirebaseApi with logging.Logging {
// Sign in the user with Firebase. If the nonce we generated earlier does
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
Cache().firebaseUid = userCredential.user!.uid;
log("userCredential: " + userCredential.toString());
log("Apple Credentials: " +
@ -181,7 +181,8 @@ class FirebaseApi with logging.Logging {
idToken: googleAuth.idToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
Cache().firebaseUid = userCredential.user!.uid;
log("GoogleUser: " + googleUser.toString());
userData['email'] = googleUser.email;
@ -230,8 +231,9 @@ class FirebaseApi with logging.Logging {
Map<String, dynamic> userData;
// by default the login method has the next permissions ['email','public_profile']
AccessToken? accessToken = await FacebookAuth.instance.accessToken;
if (accessToken != null) {
final LoginResult result = await FacebookAuth.instance.login();
if (result.status == LoginStatus.success) {
final AccessToken accessToken = result.accessToken!;
log(accessToken.toJson().toString());
Cache().accessTokenFacebook = accessToken;
// get the user data
@ -249,8 +251,9 @@ class FirebaseApi with logging.Logging {
Map<String, dynamic> userData;
// by default the login method has the next permissions ['email','public_profile']
AccessToken? accessToken = await FacebookAuth.instance.accessToken;
if (accessToken != null) {
final LoginResult result = await FacebookAuth.instance.login();
if (result.status == LoginStatus.success) {
final AccessToken accessToken = result.accessToken!;
Cache().accessTokenFacebook = accessToken;
// get the user data
userData = await FacebookAuth.instance.getUserData();

View File

@ -4,6 +4,7 @@ import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/customer_exercise_device.dart';
import 'package:aitrainer_app/model/customer_property.dart';
import 'package:aitrainer_app/model/evaluation.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_device.dart';
import 'package:aitrainer_app/model/exercise_plan_template.dart';
@ -59,6 +60,9 @@ class PackageApi {
Cache().setExercisePlanTemplates(exercisePlanTemplates);
} else if (headRecord[0] == "ExerciseTreeParents") {
exerciseTreeParents = json.map((exerciseTreeParent) => ExerciseTreeParents.fromJson(exerciseTreeParent)).toList();
} else if (headRecord[0] == "Evaluation") {
final List<Evaluation> evaluations = json.map((evaluation) => Evaluation.fromJson(evaluation)).toList();
Cache().evaluations = evaluations;
}
});

View File

@ -98,17 +98,22 @@ mixin Common {
return datePart;
}
static String? emailValidation(String email) {
static String? emailValidation(String? email) {
final String error = "Please type an email address";
if (email == null) {
return error;
}
bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(email);
return emailValid ? null : "Please type an email address";
return emailValid ? null : error;
}
static String? passwordValidation(String? value) {
final String error = "Password too short";
if (value == null || value.length == 0) {
return null;
return error;
}
bool valid = 8 < value.length;
return valid ? null : "Password too short";
return valid ? null : error;
}
static Widget badgedIcon(Color color, IconData icon, String badgeKey) {

View File

@ -73,3 +73,11 @@ extension SizesExt on SizesEnum {
bool equalsTo(SizesEnum event) => this.toString() == event.toString();
bool equalsStringTo(String event) => this.toString() == event;
}
enum EvaluationText { very_poor, poor, fair, below_average, average, above_average, good, excellent, elite }
extension EvaluationTextExt on EvaluationText {
String toStr() => this.toString().split(".").last;
bool equalsTo(EvaluationText eval) => this.toString() == eval.toString();
bool equalsStringTo(String eval) => this.toStr() == eval;
}

View File

@ -3,8 +3,8 @@ import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/service/tracking_service.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/library/flurry.dart';
import 'package:aitrainer_app/model/tracking.dart' as model;
import 'package:flurry/flurry.dart';
import 'package:smartlook/smartlook.dart';
class Track with Logging {

View File

@ -8,6 +8,9 @@ mixin Trans {
}
String t(String text) {
if (context == null) {
throw Exception("Translation: initialize the context");
}
return AppLocalizations.of(context)!.translate(text);
}
}

View File

@ -52,9 +52,11 @@ class AccountPage extends StatelessWidget with Trans {
customerName = accountBloc.customerRepository.firstName! + " " + accountBloc.customerRepository.name!;
customerName = customerName.length < 3 ? t("Personal data") : customerName;
goal = accountBloc.customerRepository.customer.goal != null ? t(accountBloc.customerRepository.customer.goal!) : goal;
fitnessLevel = accountBloc.customerRepository.customer.fitnessLevel != null
? t(capitalize(accountBloc.customerRepository.customer.fitnessLevel!))
goal = accountBloc.customerRepository.customer != null && accountBloc.customerRepository.customer!.goal != null
? t(accountBloc.customerRepository.customer!.goal!)
: goal;
fitnessLevel = accountBloc.customerRepository.customer != null && accountBloc.customerRepository.customer!.fitnessLevel != null
? t(capitalize(accountBloc.customerRepository.customer!.fitnessLevel!))
: fitnessLevel;
bodyType = accountBloc.getAccurateBodyType();
@ -166,8 +168,6 @@ class AccountPage extends StatelessWidget with Trans {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text(t("Available Devices"), style: TextStyle(color: Colors.orange)), Icon(Icons.arrow_forward_ios)]),
//textColor: Colors.orange,
//color: Colors.white,
onPressed: () => {
if (Cache().userLoggedIn != null)
{
@ -185,7 +185,7 @@ class AccountPage extends StatelessWidget with Trans {
String text = "Logout";
Color buttonColor = Colors.orange;
if (accountBloc.customerRepository.customer.email == null) {
if (accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer!.email == null) {
text = "Login";
buttonColor = Colors.blue;
}
@ -220,7 +220,7 @@ class AccountPage extends StatelessWidget with Trans {
}
Widget getMyTrainees(BuildContext context, AccountBloc accountBloc) {
if (accountBloc.customerRepository.customer.trainer == 0) {
if (accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer!.trainer == 0) {
return ListTile(
title: Container(),
);

View File

@ -11,6 +11,10 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
import '../library/dropdown_search.dart';
// ignore: must_be_immutable
class CustomerFitnessPage extends StatefulWidget {
@ -22,10 +26,8 @@ class CustomerFitnessPage extends StatefulWidget {
}
}
// dropbox for professional sport
class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
late String selected;
String? selected;
bool fulldata = false;
@override
@ -41,8 +43,6 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
}
selected = customerRepository.customer.fitnessLevel!;
PreferredSizeWidget _bar = AppBarMin(
back: true,
);
@ -56,6 +56,10 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
child: Builder(builder: (context) {
// ignore: close_sinks
CustomerChangeBloc changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
selected = changeBloc.selectedFitnessItem;
if (selected == null) {
selected = FitnessState.beginner;
}
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
@ -85,7 +89,7 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(customerRepository, FitnessState.beginner),
shape: getShape(changeBloc, FitnessState.beginner),
),
child: Container(
width: cWidth,
@ -103,14 +107,14 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
onPressed: () => {
setState(() {
selected = FitnessState.beginner;
changeBloc.add(CustomerFitnessChange(fitness: selected));
changeBloc.add(CustomerFitnessChange(fitness: selected!));
}),
}),
Divider(),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(customerRepository, FitnessState.intermediate),
shape: getShape(changeBloc, FitnessState.intermediate),
),
child: Container(
width: cWidth,
@ -136,7 +140,7 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
onPressed: () => {
setState(() {
selected = FitnessState.intermediate;
changeBloc.add(CustomerFitnessChange(fitness: selected));
changeBloc.add(CustomerFitnessChange(fitness: selected!));
print(selected);
}),
}),
@ -144,7 +148,7 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(customerRepository, FitnessState.advanced),
shape: getShape(changeBloc, FitnessState.advanced),
),
child: Container(
width: cWidth,
@ -170,7 +174,7 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
onPressed: () => {
setState(() {
selected = FitnessState.advanced;
changeBloc.add(CustomerFitnessChange(fitness: selected));
changeBloc.add(CustomerFitnessChange(fitness: selected!));
print(selected);
}),
}),
@ -178,7 +182,7 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(customerRepository, FitnessState.professional),
shape: getShape(changeBloc, FitnessState.professional),
),
child: Container(
width: cWidth,
@ -204,11 +208,13 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
onPressed: () => {
setState(() {
selected = FitnessState.professional;
changeBloc.add(CustomerFitnessChange(fitness: selected));
changeBloc.add(CustomerFitnessChange(fitness: selected!));
print(selected);
}),
}),
Divider(),
selected == FitnessState.professional ? getSport(changeBloc) : Offstage(),
Divider(),
ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
@ -228,8 +234,8 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
})));
}
dynamic getShape(CustomerRepository customerRepository, String fitnessLevel) {
String selected = customerRepository.fitnessLevel!;
dynamic getShape(CustomerChangeBloc changeBloc, String fitnessLevel) {
String? selected = changeBloc.selectedFitnessItem;
dynamic returnCode = (selected == fitnessLevel)
? RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.orange),
@ -240,4 +246,90 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
//return
return returnCode;
}
Widget getSport(CustomerChangeBloc bloc) {
Sport? selected = bloc.getSelectedSport;
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) => i.equalsTo(s),
showSelectedItem: true,
selectedItem: selected,
itemAsString: (data) => t(data.toStr()),
onChanged: (data) {
bloc.add(CustomerSportChange(sport: data));
},
dropdownBuilder: _customDropDownItem,
popupItemBuilder: _customMenuBuilder,
popupBarrierColor: Colors.white10,
//popupBackgroundColor: Colors.yellow,
items: Sport.values,
dropDownButton: Icon(
Icons.arrow_drop_down,
color: Colors.indigo,
),
));
//items: FitnessItem().toList()));
}
Widget _customMenuBuilder(BuildContext context, Sport sport, bool isSelected) {
//bool selected = bloc.getSelectedSport;
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.toStr()),
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]),
),
subtitle: Text(
t(sport.description(sport)),
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.toStr()),
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]),
),
subtitle: Text(
t(item.description(item)),
style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]),
),
),
);
}
}

View File

@ -9,6 +9,7 @@ import 'package:aitrainer_app/widgets/app_bar_progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
class GoalsItem {
static String muscle = "gain_muscle";
@ -70,7 +71,8 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
InkWell(
child: Text(
AppLocalizations.of(context)!.translate("Set Your Goals"),
style: TextStyle(color: Colors.orange, fontSize: 50, fontFamily: 'Arial', fontWeight: FontWeight.w900),
style:
GoogleFonts.archivoBlack(color: Colors.orange, fontSize: 30, fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
@ -146,6 +148,7 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
}
dynamic getShape(CustomerChangeBloc customerBloc, String goal) {
if (customerBloc.customerRepository.goal == null) return null;
String selectedGoal = customerBloc.customerRepository.goal!;
dynamic returnCode = (selectedGoal == goal)
? RoundedRectangleBorder(

View File

@ -26,12 +26,12 @@ class CustomerModifyPage extends StatelessWidget with Trans {
@override
Widget build(BuildContext context) {
setContext(context);
dynamic arguments = ModalRoute.of(context)!.settings.arguments;
if (arguments is HashMap && arguments['personal_data'] != null) {
fulldata = arguments['personal_data'];
}
setContext(context);
// ignore: close_sinks
final accountBloc = BlocProvider.of<AccountBloc>(context);
@ -88,7 +88,6 @@ class CustomerModifyPage extends StatelessWidget with Trans {
Widget loadForm(CustomerChangeBloc customerBloc) {
return Form(
key: _scaffoldKey,
autovalidateMode: AutovalidateMode.always,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 40, left: 25, right: 25, bottom: 250),
@ -96,7 +95,7 @@ class CustomerModifyPage extends StatelessWidget with Trans {
alignment: Alignment.center,
child: Column(
children: [
Text(t("Edit Profile"), style: GoogleFonts.inter(color: Colors.indigo, fontSize: 16), textAlign: TextAlign.center),
Text(t("Edit Profile"), style: GoogleFonts.archivoBlack(color: Colors.indigo, fontSize: 20), textAlign: TextAlign.center),
Divider(
color: Colors.transparent,
),
@ -114,7 +113,7 @@ class CustomerModifyPage extends StatelessWidget with Trans {
borderSide: BorderSide(color: Colors.green[50]!, width: 0.4),
),
),
initialValue: customerBloc.customerRepository.customer.email,
initialValue: customerBloc.customerRepository.customer!.email,
autovalidateMode: AutovalidateMode.always,
validator: (val) {
String? validator = customerBloc.emailValidation(val!);
@ -147,8 +146,8 @@ class CustomerModifyPage extends StatelessWidget with Trans {
borderSide: BorderSide(color: Colors.green[50]!, width: 0.4),
),
),
initialValue: customerBloc.customerRepository.customer.password,
autovalidateMode: AutovalidateMode.always,
initialValue: customerBloc.customerRepository.customer!.password,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
String? validator = customerBloc.passwordValidation(val!);
return validator == null ? null : t(validator);
@ -173,8 +172,8 @@ class CustomerModifyPage extends StatelessWidget with Trans {
borderSide: BorderSide(color: Colors.green[50]!, width: 0.4),
),
),
initialValue: customerBloc.customerRepository.customer.name,
autovalidateMode: AutovalidateMode.always,
initialValue: customerBloc.customerRepository.customer!.name,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
return customerBloc.nameValidation(val);
},
@ -198,8 +197,8 @@ class CustomerModifyPage extends StatelessWidget with Trans {
borderSide: BorderSide(color: Colors.green[50]!, width: 0.4),
),
),
initialValue: customerBloc.customerRepository.customer.firstname,
autovalidateMode: AutovalidateMode.always,
initialValue: customerBloc.customerRepository.customer!.firstname,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
return customerBloc.nameValidation(val);
},
@ -210,65 +209,92 @@ class CustomerModifyPage extends StatelessWidget with Trans {
Divider(
color: Colors.transparent,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 4,
child: Text(t("Birth Year"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)),
),
NumberPickerWidget(
minValue: 1930,
maxValue: 2100,
initalValue: customerBloc.year!.toInt(),
unit: " ",
color: Colors.indigo,
onChange: (value) => {customerBloc.add(CustomerBirthYearChange(year: value.toInt()))}),
SizedBox(width: 80),
],
Container(
padding: EdgeInsets.only(left: 15),
decoration: BoxDecoration(
color: Colors.white24,
border: Border.all(color: Colors.black, width: 0.4),
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: Text(t("Birth Year"),
style: GoogleFonts.inter(
fontWeight: FontWeight.normal,
fontSize: 18,
))),
NumberPickerWidget(
minValue: 1930,
maxValue: DateTime.now().year,
initalValue: customerBloc.year!.toInt(),
unit: " ",
color: Colors.indigo,
onChange: (value) => {customerBloc.add(CustomerBirthYearChange(year: value.toInt()))}),
SizedBox(width: 30),
],
)),
Divider(
color: Colors.transparent,
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
flex: 4,
child: Text(t("Weight"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)),
),
NumberPickerWidget(
minValue: 0,
maxValue: 200,
initalValue: customerBloc.weight.toInt(),
unit: " ",
color: Colors.indigo,
onChange: (value) => {customerBloc.add(CustomerWeightChange(weight: value.toInt()))}),
SizedBox(width: 80),
],
Container(
padding: EdgeInsets.only(left: 15),
decoration: BoxDecoration(
color: Colors.white24,
border: Border.all(color: Colors.black, width: 0.4),
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
flex: 4,
child: Text(t("Weight"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)),
),
NumberPickerWidget(
minValue: 0,
maxValue: 200,
initalValue: customerBloc.weight.toInt(),
unit: " ",
color: Colors.indigo,
onChange: (value) => {customerBloc.add(CustomerWeightChange(weight: value.toInt()))}),
SizedBox(width: 30),
],
)),
Divider(
color: Colors.transparent,
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
flex: 4,
child: Text(t("Height"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)),
),
NumberPickerWidget(
minValue: 0,
maxValue: 230,
initalValue: customerBloc.height.toInt(),
unit: " ",
color: Colors.indigo[300]!,
onChange: (value) => {customerBloc.add(CustomerHeightChange(height: value.toInt()))}),
SizedBox(width: 80),
],
Container(
padding: EdgeInsets.only(left: 15),
decoration: BoxDecoration(
color: Colors.white24,
border: Border.all(color: Colors.black, width: 0.4),
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
flex: 4,
child: Text(t("Height"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)),
),
NumberPickerWidget(
minValue: 0,
maxValue: 230,
initalValue: customerBloc.height.toInt(),
unit: " ",
color: Colors.indigo[300]!,
onChange: (value) => {customerBloc.add(CustomerHeightChange(height: value.toInt()))}),
SizedBox(width: 30),
],
)),
Divider(
color: Colors.transparent,
),
Divider(),
ToggleSwitch(
minWidth: 80.0,
minHeight: 50.0,
fontSize: 14.0,
initialLabelIndex: customerBloc.customerRepository.customer.sex == "m" ? 0 : 1,
initialLabelIndex: customerBloc.customerRepository.customer!.sex == "m" ? 0 : 1,
activeBgColor: Colors.indigo,
activeFgColor: Colors.white,
inactiveBgColor: Colors.white30,

View File

@ -1,5 +1,6 @@
import 'dart:collection';
import 'dart:ui';
import 'package:aitrainer_app/util/enums.dart';
import 'package:intl/intl.dart';
import 'package:aitrainer_app/bloc/result/result_bloc.dart';
import 'package:aitrainer_app/util/app_language.dart';
@ -281,6 +282,7 @@ class EvaluationPage extends StatelessWidget with Trans {
}
Widget getSuggestionWidget(ResultBloc resultBloc, String title, String picture, String repeats, double percent, String restTime) {
double _opacity = 1;
String unitQuantityUnit = resultBloc.exerciseRepository.exerciseType!.unitQuantityUnit == null
? ""
: resultBloc.exerciseRepository.exerciseType!.unitQuantityUnit!;
@ -325,7 +327,7 @@ class EvaluationPage extends StatelessWidget with Trans {
softWrap: true,
style: GoogleFonts.archivoBlack(
fontSize: 18,
color: Colors.orange,
color: Colors.orange.withOpacity(_opacity),
shadows: <Shadow>[
Shadow(
offset: Offset(5.0, 5.0),
@ -398,7 +400,7 @@ class EvaluationPage extends StatelessWidget with Trans {
Widget getSummary(ResultBloc bloc) {
int index = 0;
List<Text> resultList = [];
List<Widget> resultList = [];
bloc.exerciseRepository.actualExerciseList!.forEach((actual) {
//final String unit = t(bloc.exerciseRepository.exerciseType.unit);
@ -421,36 +423,248 @@ class EvaluationPage extends StatelessWidget with Trans {
exerciseElement = t("3rd Control") + ": ";
}
index++;
resultList.add(
Text(exerciseElement + exerciseRepeats + exerciseUnitQuantity + " " + t(unit),
textAlign: TextAlign.center,
maxLines: 2,
softWrap: true,
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 18,
fontSize: 20,
color: Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(5.0, 5.0),
blurRadius: 12.0,
color: Colors.black54,
),
children: [
TextSpan(text: exerciseElement),
TextSpan(
text: exerciseRepeats + exerciseUnitQuantity + " " + t(unit),
style: GoogleFonts.archivoBlack(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.yellow[300]!,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 12.0,
color: Colors.black54,
),
],
)),
);
),
]),
));
});
return Column(children: resultList);
}
Widget getEvaluationWidget(ResultBloc bloc) {
List<Widget> resultList = [];
print("Act ${bloc.exerciseRepository.actualExerciseList}");
if (bloc.exerciseRepository.actualExerciseList == null || bloc.exerciseRepository.actualExerciseList!.isEmpty) {
return Offstage();
}
final int exerciseTypeId = bloc.exerciseRepository.actualExerciseList![0].exerciseTypeId!;
final double quantity = bloc.exerciseRepository.actualExerciseList![0].quantity!;
String eval = bloc.evaluationRepository.getEvaluationTextByExerciseType(exerciseTypeId, quantity);
Color color = bloc.evaluationRepository.getEvaluationColor(eval);
double compareBest = bloc.exerciseRepository.getBestExercisePercent(bloc.exerciseRepository.actualExerciseList![0]);
double compareLast = bloc.exerciseRepository.getLastExercisePercent(bloc.exerciseRepository.actualExerciseList![0]);
bool has1RM = bloc.exerciseRepository.actualExerciseList![0].unitQuantity != null;
double? bestCompared1RM;
double? lastCompared1RM;
if (has1RM) {
lastCompared1RM = bloc.exerciseRepository.getLast1RMPercent(bloc.exerciseRepository.actualExerciseList![0]);
bestCompared1RM = bloc.exerciseRepository.getBest1RMPercent(bloc.exerciseRepository.actualExerciseList![0]);
}
if (!EvaluationText.fair.equalsStringTo(eval)) {
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.white,
),
children: [
TextSpan(text: t("Your result is: ")),
TextSpan(
text: eval,
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
color: color,
),
),
]),
));
resultList.add(Divider(color: Colors.transparent));
}
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
color: Colors.white,
),
children: [
TextSpan(text: t("Compared with...")),
]),
));
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
color: Colors.white,
),
children: [
TextSpan(text: t("your best")),
TextSpan(text: " "),
TextSpan(text: has1RM ? t("volumen") : t("exercise")),
TextSpan(text: ": "),
TextSpan(
text: compareBest.toStringAsFixed(1) + "%",
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
color: compareBest >= 0 ? Colors.green : Colors.red[600],
),
),
]),
));
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
color: Colors.white,
),
children: [
TextSpan(text: t("your last")),
TextSpan(text: " "),
TextSpan(text: has1RM ? t("volumen") : t("exercise")),
TextSpan(text: ": "),
TextSpan(
text: compareLast.toStringAsFixed(1) + "%",
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
color: compareLast >= 0 ? Colors.green : Colors.red[600],
),
),
]),
));
resultList.add(Divider(color: Colors.transparent));
if (has1RM) {
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
color: Colors.white,
),
children: [
TextSpan(text: t("best")),
TextSpan(text: " "),
TextSpan(text: t("1RM")),
TextSpan(text: ": "),
TextSpan(
text: bestCompared1RM!.toStringAsFixed(1) + "%",
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
color: bestCompared1RM >= 0 ? Colors.green : Colors.red[600],
),
),
]),
));
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
color: Colors.white,
),
children: [
TextSpan(text: t("last")),
TextSpan(text: " "),
TextSpan(text: t("1RM")),
TextSpan(text: ": "),
TextSpan(
text: lastCompared1RM!.toStringAsFixed(1) + "%",
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
color: lastCompared1RM >= 0 ? Colors.green : Colors.red[600],
),
),
]),
));
}
if (!Cache().hasPurchased) {
resultList.add(Divider());
resultList.add(Divider());
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.normal,
color: Colors.white,
),
children: [
TextSpan(
text: t('How can serve you this result?'),
style: GoogleFonts.inter(
color: Colors.white,
),
),
]),
));
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.normal,
color: Colors.white,
),
children: [
TextSpan(
text: t('Get the Fastlane to your'),
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
]),
));
resultList.add(RichText(
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.normal,
color: Colors.white,
),
children: [
TextSpan(
text: t('Development'),
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
color: Colors.yellow[300],
),
)
])));
resultList.add(TextButton(
onPressed: () => {Navigator.of(context).pushNamed("salesPage")},
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_orange_a.png', width: 140, height: 60),
Text(
t("Go"),
style: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
));
}
return Column(children: resultList);
}
Widget getResultSummary(ResultBloc resultBloc) {
return SliverList(
delegate: SliverChildListDelegate(
[Divider(color: Colors.transparent), getSummary(resultBloc)],
[Divider(color: Colors.transparent), getSummary(resultBloc), Divider(color: Colors.transparent), getEvaluationWidget(resultBloc)],
),
);
}

View File

@ -45,7 +45,8 @@ class _ExerciseExecuteAddPage extends State<ExerciseExecutePlanAddPage> with Tra
exercisePlanRepository: planBloc.exercisePlanRepository,
customerId: customerId,
workoutTree: workoutTree,
planBloc: planBloc),
planBloc: planBloc)
..add(ExerciseExecutePlanAddLoad()),
child: BlocConsumer<ExerciseExecutePlanAddBloc, ExerciseExecutePlanAddState>(listener: (context, state) {
if (state is ExerciseExecutePlanAddError) {
ScaffoldMessenger.of(context).showSnackBar(
@ -54,7 +55,7 @@ class _ExerciseExecuteAddPage extends State<ExerciseExecutePlanAddPage> with Tra
}, builder: (context, state) {
// ignore: close_sinks
final exerciseBloc = BlocProvider.of<ExerciseExecutePlanAddBloc>(context);
if (state is ExerciseExecutePlanAddReady) {
if (state is ExerciseExecutePlanAddReady && _controller.hasClients) {
_controller.animateTo(exerciseBloc.scrollOffset, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
}
return ModalProgressHUD(
@ -67,7 +68,10 @@ class _ExerciseExecuteAddPage extends State<ExerciseExecutePlanAddPage> with Tra
}));
}
Form getControlForm(ExerciseExecutePlanAddBloc exerciseBloc) {
Widget getControlForm(ExerciseExecutePlanAddBloc exerciseBloc) {
if (exerciseBloc.exerciseRepository.exerciseType == null || exerciseBloc.quantity == null) {
return Offstage();
}
String exerciseName = AppLanguage().appLocal == Locale("en")
? exerciseBloc.exerciseRepository.exerciseType!.name
: exerciseBloc.exerciseRepository.exerciseType!.nameTranslation;
@ -186,7 +190,7 @@ class _ExerciseExecuteAddPage extends State<ExerciseExecutePlanAddPage> with Tra
minValue: 0,
maxValue: 1000,
fontSize: 16,
initalValue: exerciseBloc.unitQuantity.toInt(),
initalValue: exerciseBloc.unitQuantity!.toInt(),
unit: t(exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!),
color: Colors.yellow[50]!,
onChange: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeUnitQuantity(quantity: value.toDouble()))}),
@ -194,7 +198,7 @@ class _ExerciseExecuteAddPage extends State<ExerciseExecutePlanAddPage> with Tra
minValue: 0,
maxValue: 200,
fontSize: 16,
initalValue: exerciseBloc.quantity.toInt(),
initalValue: exerciseBloc.quantity!.toInt(),
unit: t(exerciseBloc.exerciseRepository.exerciseType!.unit), //t("repeat"),
color: Colors.yellow[50]!,
onChange: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeQuantity(quantity: value.toDouble()))}),

View File

@ -1,6 +1,5 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_log/exercise_log_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/dialog_premium.dart';
@ -196,13 +195,12 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
exercise.summary == null ? "" : exercise.summary!,
style: TextStyle(fontSize: 12, color: Colors.blue[800]),
)),
IconButton(
iconSize: 36,
icon: Icon(CustomIcon.heart_1, color: Colors.orange[800]),
onPressed: () {
evaluation(exerciseRepository, exercise);
},
),
GestureDetector(
onTap: () => evaluation(exerciseRepository, exercise),
child: Image.asset(
"asset/image/kupa.png",
width: 35,
)),
IconButton(
icon: Icon(Icons.delete, color: Colors.black12),
onPressed: () {

View File

@ -107,7 +107,9 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
child: ExerciseSave(
exerciseName: exerciseBloc.exerciseRepository.exerciseType!.nameTranslation,
exerciseDescription: exerciseBloc.exerciseRepository.exerciseType!.descriptionTranslation,
exerciseTask: t("Please take a relative bigger weight and repeat 12-20 times"),
exerciseTask: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null
? t("Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!")
: t("Please repeat as much times as you can! MAXIMIZE it!"),
unit: exerciseBloc.exerciseRepository.exerciseType!.unit,
unitQuantityUnit: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit,
hasUnitQuantity: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null,
@ -121,10 +123,13 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans, Logging {
floatingActionButton: FloatingActionButton.extended(
onPressed: () => save(exerciseBloc, menuBloc),
backgroundColor: Colors.orange[800],
icon: Icon(CustomIcon.save),
icon: Icon(
CustomIcon.save,
size: 20,
),
label: Text(
t("Save"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 12),
),
),
bottomNavigationBar: BottomBarMultipleExercises(
@ -164,11 +169,15 @@ 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?";
showCupertinoDialog(
useRootNavigator: true,
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text(t("Do you save this exercise with these parameters?")),
title: Text(t(question)),
content: Column(children: [
Divider(),
Text(

View File

@ -120,7 +120,7 @@ class LoginPage extends StatelessWidget with Trans {
initialValue: loginBloc.userRepository.user.email,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
String? validationText = loginBloc.emailValidation(val!);
String? validationText = loginBloc.emailValidation(val);
return validationText == null ? null : t(validationText);
},
onChanged: (value) => loginBloc.add(LoginEmailChange(email: value)),
@ -150,9 +150,9 @@ class LoginPage extends StatelessWidget with Trans {
),
),
initialValue: loginBloc.userRepository.user.password,
autovalidateMode: AutovalidateMode.always,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
String? validationText = loginBloc.passwordValidation(val!);
String? validationText = loginBloc.passwordValidation(val);
return validationText == null ? null : t(validationText);
},
onChanged: (value) => loginBloc.add(LoginPasswordChange(password: value)),

View File

@ -163,7 +163,7 @@ class RegistrationPage extends StatelessWidget with Trans {
),
),
initialValue: loginBloc.userRepository.user.password,
autovalidateMode: AutovalidateMode.always,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
final String? validator = loginBloc.passwordValidation(val!);
return validator == null ? null : t(validator);

View File

@ -31,7 +31,7 @@ class ResetPasswordPage extends StatelessWidget with Trans {
if (state is PasswordResetError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(t(state.message), style: TextStyle(color: Colors.white))));
} else if (state is PasswordResetReady) {
} else if (state is PasswordResetFinished) {
Navigator.of(context).pop();
}
},

View File

@ -150,7 +150,7 @@ class SalesPage extends StatelessWidget with Trans, Logging {
bloc.product2Display.forEach((element) {
final String title = element.sort == 3 ? t("Montly") : t("Annual");
final String desc4 = element.sort == 1 ? "" : t("Predictions with Artificial Intelligence");
late String badge;
String? badge;
if (element.sort == 2) {
badge = t("14% discount");
} else if (element.sort == 1) {

View File

@ -3,12 +3,16 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/library/fade_in.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.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/dialog_common.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:aitrainer_app/widgets/victory_widget.dart';
import 'package:ezanimation/ezanimation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
@ -64,7 +68,9 @@ class TestSetExecute extends StatelessWidget with Trans {
}),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => executeExercise(executeBloc, executeBloc.getNext()!, context),
onPressed: () => executeBloc.getNext() != null
? executeExercise(executeBloc, executeBloc.getNext()!, context)
: Navigator.of(context).pushNamed('home'),
backgroundColor: Colors.orange[800],
icon: Icon(CustomIcon.weight_hanging),
label: Text(
@ -86,11 +92,6 @@ class TestSetExecute extends StatelessWidget with Trans {
tiles.add(getStartTile(bloc));
tiles.addAll(getExerciseTiles(bloc, context));
tiles.add(getEndTile());
/* if (bloc.isDone100Percent()) {
tiles.add(Victory(
victory: true,
));
} */
return tiles;
}
@ -254,7 +255,9 @@ class TestSetExecute extends StatelessWidget with Trans {
//if (element != null && element.exerciseTypeId != null) {
tiles.add(GestureDetector(
onDoubleTap: () => print("Execute ${element.exerciseType!.nameTranslation}"),
onTap: () => executeExercise(bloc, element, context),
onTap: () => element.state.equalsTo(ExercisePlanDetailState.finished)
? evaluation(bloc, element)
: executeExercise(bloc, element, context),
child: ExerciseTile(
bloc: bloc,
exercisePlanDetail: element,
@ -266,12 +269,27 @@ class TestSetExecute extends StatelessWidget with Trans {
return tiles;
}
void evaluation(TestSetExecuteBloc bloc, ExercisePlanDetail planDetail) {
if (planDetail.exercises != null && planDetail.exercises!.isNotEmpty) {
ExerciseRepository exerciseRepository = ExerciseRepository();
exerciseRepository.start = planDetail.exercises![0].dateAdd;
exerciseRepository.exerciseList = Cache().getExercises();
exerciseRepository.actualExerciseList = planDetail.exercises!;
print("actualEx ${exerciseRepository.actualExerciseList}");
LinkedHashMap args = LinkedHashMap();
args['exerciseRepository'] = exerciseRepository;
args['exercise'] = planDetail.exercises![0];
args['past'] = true;
Navigator.of(context).pushNamed('evaluationPage', arguments: args);
}
}
void executeExercise(TestSetExecuteBloc bloc, ExercisePlanDetail exercisePlanDetail, BuildContext context) {
ExercisePlanDetail? next = bloc.getNext();
if (next != null) {
final HashMap args = HashMap();
args['exerciseType'] = exercisePlanDetail.exerciseType;
args['exerciseType'] = next.exerciseType;
args['exercisePlanDetailId'] = exercisePlanDetail.exercisePlanDetailId;
args['testSetExecuteBloc'] = bloc;
String title = "";
@ -314,17 +332,43 @@ class TestSetExecute extends StatelessWidget with Trans {
}
// ignore: must_be_immutable
class ExerciseTile extends StatelessWidget with Trans {
class ExerciseTile extends StatefulWidget {
final TestSetExecuteBloc bloc;
final ExercisePlanDetail exercisePlanDetail;
ExerciseTile({required this.bloc, required this.exercisePlanDetail});
@override
_ExerciseTileState createState() => _ExerciseTileState();
}
class _ExerciseTileState extends State<ExerciseTile> with Trans {
final EzAnimation animation = EzAnimation(1.0, 30.0, Duration(seconds: 3), reverseCurve: Curves.easeIn);
@override
void initState() {
animation.start();
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {});
}
});
super.initState();
}
@override
bool didUpdateWidget(ExerciseTile oldWidget) {
super.didUpdateWidget(oldWidget);
animation.start();
return true;
}
Widget getIndicator(ExercisePlanDetailState state) {
ExercisePlanDetail? next = bloc.getNext();
ExercisePlanDetail? next = widget.bloc.getNext();
bool actual = false;
if (next != null) {
if (next.exerciseTypeId == exercisePlanDetail.exerciseTypeId) {
if (next.exerciseTypeId == widget.exercisePlanDetail.exerciseTypeId) {
actual = true;
}
}
@ -357,10 +401,11 @@ class ExerciseTile extends StatelessWidget with Trans {
@override
Widget build(BuildContext context) {
final ExercisePlanDetailState state = exercisePlanDetail.state;
final ExercisePlanDetailState state = widget.exercisePlanDetail.state;
final bool done = state.equalsTo(ExercisePlanDetailState.finished);
final String countSerie = exercisePlanDetail.exercises == null ? "1" : (exercisePlanDetail.exercises!.length).toString();
final String serie = exercisePlanDetail.exerciseType!.unitQuantityUnit == null ? "/1" : "/4";
final String countSerie = widget.exercisePlanDetail.exercises == null ? "1" : (widget.exercisePlanDetail.exercises!.length).toString();
final String serie = widget.exercisePlanDetail.exerciseType!.unitQuantityUnit == null ? "/1" : "/4";
setContext(context);
return Container(
color: Colors.transparent,
@ -387,8 +432,8 @@ class ExerciseTile extends StatelessWidget with Trans {
width: 120,
height: 80,
child: MenuImage(
imageName: bloc.getActualImageName(exercisePlanDetail.exerciseType!.exerciseTypeId),
workoutTreeId: bloc.getActualWorkoutTreeId(exercisePlanDetail.exerciseType!.exerciseTypeId)!,
imageName: widget.bloc.getActualImageName(widget.exercisePlanDetail.exerciseType!.exerciseTypeId),
workoutTreeId: widget.bloc.getActualWorkoutTreeId(widget.exercisePlanDetail.exerciseType!.exerciseTypeId)!,
)),
SizedBox(
width: 10,
@ -403,7 +448,7 @@ class ExerciseTile extends StatelessWidget with Trans {
),
children: [
TextSpan(
text: exercisePlanDetail.exerciseType!.nameTranslation,
text: widget.exercisePlanDetail.exerciseType!.nameTranslation,
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
@ -421,20 +466,20 @@ class ExerciseTile extends StatelessWidget with Trans {
),
],
)),
exercisePlanDetail.exerciseType!.unitQuantityUnit != null
widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null
? TextSpan(
text: "\n",
)
: TextSpan(),
exercisePlanDetail.exerciseType!.unitQuantityUnit != null
widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null
? TextSpan(
text: t(exercisePlanDetail.exerciseType!.unitQuantityUnit!) + ": ",
text: t(widget.exercisePlanDetail.exerciseType!.unitQuantityUnit!) + ": ",
style: GoogleFonts.inter(
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold))
: TextSpan(),
exercisePlanDetail.exerciseType!.unitQuantityUnit != null
widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null
? TextSpan(
text: t(bloc.getExerciseWeight(exercisePlanDetail)),
text: t(widget.bloc.getExerciseWeight(widget.exercisePlanDetail)),
style: GoogleFonts.inter(
fontSize: 12,
))
@ -443,11 +488,11 @@ class ExerciseTile extends StatelessWidget with Trans {
text: "\n",
),
TextSpan(
text: t(exercisePlanDetail.exerciseType!.unit) + ": ",
text: t(widget.exercisePlanDetail.exerciseType!.unit) + ": ",
style: GoogleFonts.inter(
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)),
TextSpan(
text: bloc.repeatTimesText(exercisePlanDetail),
text: widget.bloc.repeatTimesText(widget.exercisePlanDetail),
style: GoogleFonts.inter(
fontSize: 12,
)),
@ -465,6 +510,19 @@ class ExerciseTile extends StatelessWidget with Trans {
)),
]),
)),
done
? AnimatedBuilder(
animation: animation,
builder: (context, snapshot) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Image.asset(
"asset/image/kupa.png",
width: animation.value,
),
Text("Result", style: GoogleFonts.inter(fontSize: 10, color: Colors.white)),
]);
})
: Offstage()
]),
),
),

View File

@ -92,9 +92,11 @@ class TestSetNew extends StatelessWidget with Trans {
return ExerciseSave(
exerciseName: bloc.exerciseType.nameTranslation,
exerciseDescription: bloc.exerciseType.descriptionTranslation,
exerciseTask: t("Please take a relative bigger weight and repeat 12-20 times"),
exerciseTask: bloc.exerciseType.unitQuantityUnit != null
? t("Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!")
: t("Please repeat as much times as you can! MAXIMIZE it!"),
unit: bloc.exerciseType.unit,
unitQuantityUnit: bloc.exerciseType.unitQuantityUnit!,
unitQuantityUnit: bloc.exerciseType.unitQuantityUnit,
hasUnitQuantity: bloc.exerciseType.unitQuantityUnit != null,
onQuantityChanged: (value) {
bloc.add(TestSetNewChangeQuantity(quantity: double.parse(value)));

View File

@ -241,7 +241,7 @@ class _BMIState extends State<BMI> with Trans {
}
Widget getHeightInput() {
if (widget.exerciseBloc.customerRepository.customer.birthYear! < 2003) {
if (widget.exerciseBloc.customerRepository.customer!.birthYear! < 2003) {
return Flexible(
child: TextFormField(
focusNode: _nodeText2,

View File

@ -3,7 +3,7 @@ import 'package:aitrainer_app/bloc/exercise_new/exercise_new_bloc.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/model/fitness_state.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:aitrainer_app/library/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
@ -196,7 +196,7 @@ class _BMRState extends State<BMR> with Trans {
}
Widget getHeightInput() {
if (widget.exerciseBloc.customerRepository.customer.birthYear! < 2003) {
if (widget.exerciseBloc.customerRepository.customer!.birthYear! < 2003) {
return Flexible(
child: TextFormField(
focusNode: _nodeText2,

View File

@ -71,7 +71,7 @@ class _BottomBarMultipleExercisesState extends State<BottomBarMultipleExercises>
],
),
),
height: 90,
height: 70,
child: BlocConsumer<TestSetExecuteBloc, TestSetExecuteState>(listener: (context, state) {
if (state is TestSetExecuteError) {
ScaffoldMessenger.of(context)

View File

@ -1,10 +1,12 @@
import 'package:aitrainer_app/library/gradient_bottom_navigation_bar.dart';
import 'dart:ui';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:convex_bottom_bar/convex_bottom_bar.dart';
import 'package:flutter/material.dart';
// ignore: must_be_immutable
@ -34,91 +36,93 @@ class _NawDrawerWidget extends State<BottomNavigator> with Trans, Logging {
Widget build(BuildContext context) {
final Color bgrColor = Color(0xffb4f500);
final Color bgrColorEnd = Colors.blue;
final Color active = Colors.black;
final Color inactive = Colors.black26;
final Color active = Colors.white;
final Color inactive = Colors.black38;
setContext(context);
return GradientBottomNavigationBar(
currentIndex: widget.bottomNavIndex, // this will be set when a new tab is tapped
backgroundColorStart: bgrColorEnd,
backgroundColorEnd: bgrColor,
fixedColor: active,
items: [
BottomNavigationBarItem(
backgroundColor: bgrColor,
icon: Common.badgedIcon(inactive, Icons.home, "home"),
activeIcon: Common.badgedIcon(active, Icons.home, "home"),
title: Text(t("Home"), style: TextStyle(fontSize: 12))),
BottomNavigationBarItem(
backgroundColor: bgrColor,
icon: Common.badgedIcon(inactive, Icons.trending_up, "development"),
activeIcon: Common.badgedIcon(active, Icons.trending_up, "development"),
title: Text(
t("My Development"),
style: TextStyle(fontSize: 12),
),
),
BottomNavigationBarItem(
backgroundColor: bgrColor,
icon: Icon(Icons.featured_play_list, color: inactive),
activeIcon: Icon(
Icons.featured_play_list,
color: active,
),
title: Text(
t("My Training Plan"),
style: TextStyle(fontSize: 12),
),
),
BottomNavigationBarItem(
backgroundColor: bgrColor,
icon: Common.badgedIcon(inactive, Icons.person, "account"),
activeIcon: Common.badgedIcon(active, Icons.person, "account"),
title: Text(
t("Account"),
style: TextStyle(fontSize: 12),
)),
BottomNavigationBarItem(
backgroundColor: bgrColor,
icon: Icon(Icons.settings, color: inactive),
activeIcon: Icon(Icons.settings, color: active),
title: Text(t("Settings"), style: TextStyle(fontSize: 12))),
],
onTap: (index) {
setState(() {
widget.bottomNavIndex = index;
switch (index) {
case 0:
Navigator.of(context).pop();
Track().track(TrackingEvent.home);
Navigator.of(context).pushNamed('home');
return StyleProvider(
style: Style(),
child: ConvexAppBar(
initialActiveIndex: widget.bottomNavIndex,
curve: Curves.easeIn,
style: TabStyle.react,
color: inactive,
height: 55,
gradient: LinearGradient(colors: [bgrColorEnd, bgrColor], stops: [0.1, .75]),
items: [
TabItem(
isIconBlend: false,
title: t("Home"),
icon: Common.badgedIcon(inactive, Icons.home, "home"),
activeIcon: Common.badgedIcon(active, Icons.home, "home"),
),
TabItem(
title: t("Growth"),
icon: Common.badgedIcon(inactive, Icons.trending_up, "development"),
activeIcon: Common.badgedIcon(active, Icons.trending_up, "development"),
),
TabItem(
title: t("Training"),
icon: Icon(Icons.featured_play_list, color: inactive),
activeIcon: Icon(Icons.featured_play_list, color: active)),
TabItem(
title: t("Account"),
icon: Common.badgedIcon(inactive, Icons.person, "account"),
activeIcon: Common.badgedIcon(active, Icons.person, "account"),
),
TabItem(title: t("Settings"), icon: Icon(Icons.settings, color: inactive), activeIcon: Icon(Icons.settings, color: active)),
],
onTap: (index) {
setState(() {
widget.bottomNavIndex = index;
switch (index) {
case 0:
Navigator.of(context).pop();
Track().track(TrackingEvent.home);
Navigator.of(context).pushNamed('home');
break;
case 1:
Navigator.of(context).pop();
Track().track(TrackingEvent.my_development);
Navigator.of(context).pushNamed('myDevelopment');
break;
case 2:
Navigator.of(context).pop();
Track().track(TrackingEvent.my_exerciseplan);
Navigator.of(context).pushNamed('myExercisePlan');
break;
case 1:
Navigator.of(context).pop();
Track().track(TrackingEvent.my_development);
Navigator.of(context).pushNamed('myDevelopment');
break;
case 2:
Navigator.of(context).pop();
Track().track(TrackingEvent.my_exerciseplan);
Navigator.of(context).pushNamed('myExercisePlan');
break;
case 3:
Navigator.of(context).pop();
Track().track(TrackingEvent.account);
Navigator.of(context).pushNamed('account');
break;
case 3:
Navigator.of(context).pop();
Track().track(TrackingEvent.account);
Navigator.of(context).pushNamed('account');
break;
case 4:
Navigator.of(context).pop();
Track().track(TrackingEvent.settings);
Navigator.of(context).pushNamed('settings');
break;
case 4:
Navigator.of(context).pop();
Track().track(TrackingEvent.settings);
Navigator.of(context).pushNamed('settings');
break;
}
});
});
break;
}
});
}));
}
}
class Style extends StyleHook {
@override
double get activeIconSize => 50;
@override
double get activeIconMargin => 10;
@override
double get iconSize => 20;
@override
TextStyle textStyle(Color color) {
return TextStyle(fontSize: 12, color: color);
}
}

View File

@ -42,7 +42,7 @@ class _DialogPremiumState extends State<DialogCommon> with Trans {
),
elevation: 0,
backgroundColor: Colors.transparent,
child: contentBox(context),
child: SingleChildScrollView(padding: EdgeInsets.only(bottom: 30), child: contentBox(context)),
);
}

View File

@ -129,109 +129,105 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
return KeyboardActions(
config: _buildConfig(context),
child: Container(
padding: const EdgeInsets.only(top: 10, left: 55, right: 55),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
widget.exerciseName,
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
fontSize: 24,
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,
),
],
),
overflow: TextOverflow.fade,
maxLines: 4,
softWrap: true,
textAlign: TextAlign.center,
scrollDirection: Axis.vertical,
child: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[
Text(
widget.exerciseName,
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
fontSize: 24,
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,
),
],
),
overflow: TextOverflow.fade,
maxLines: 4,
softWrap: true,
textAlign: TextAlign.center,
),
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.only(top: 10, left: 55, right: 55),
child: Text(
widget.exerciseDescription,
style: GoogleFonts.inter(fontSize: 12, color: Colors.yellow[300]),
maxLines: 1,
overflow: TextOverflow.fade,
softWrap: true,
)),
InkWell(
child: Text(
t("More »"),
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>');
})
},
),
Divider(
color: Colors.transparent,
),
Text(
t(widget.exerciseTask),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.orange,
fontWeight: FontWeight.bold,
),
maxLines: 3,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
),
Divider(
color: Colors.transparent,
),
columnQuantityUnit(),
Divider(
color: Colors.transparent,
),
columnQuantity(),
Divider(
color: Colors.transparent,
),
widget.hasUnitQuantity
? Text(
t("Step") + ": " + "1/4",
style: GoogleFonts.inter(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.bold,
),
SizedBox(
height: 15,
),
Text(
widget.exerciseDescription,
style: GoogleFonts.inter(fontSize: 12, color: Colors.yellow[300]),
maxLines: 1,
overflow: TextOverflow.fade,
softWrap: true,
),
InkWell(
child: Text(
t("More »"),
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>');
})
},
),
Divider(
color: Colors.transparent,
),
widget.hasUnitQuantity
? Text(
t(widget.exerciseTask),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.orange,
fontWeight: FontWeight.bold,
),
maxLines: 3,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
)
: Offstage(),
Divider(
color: Colors.transparent,
),
columnQuantityUnit(),
Divider(
color: Colors.transparent,
),
columnQuantity(),
Divider(
color: Colors.transparent,
),
widget.hasUnitQuantity
? Text(
t("Step") + ": " + "1/4",
style: GoogleFonts.inter(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.bold,
),
maxLines: 3,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
)
: Offstage(),
Divider(
color: Colors.transparent,
),
Divider(
color: Colors.transparent,
),
/* TextButton(
maxLines: 3,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: true,
)
: Offstage(),
Divider(
color: Colors.transparent,
),
Divider(
color: Colors.transparent,
),
/* TextButton(
onPressed: () {
widget.onSubmit();
/* showDialog(
@ -252,43 +248,46 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
),
],
)), */
]),
)));
]),
)));
}
Column columnQuantityUnit() {
Column row = Column();
Widget columnQuantityUnit() {
Widget row = Padding(padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: Column());
if (widget.hasUnitQuantity) {
row = Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
TextFormField(
focusNode: _nodeText1,
controller: _controller1,
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),
),
),
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]),
onChanged: (value) => widget.onUnitQuantityChanged!(value)),
]);
row = Padding(
padding: const EdgeInsets.only(top: 10, left: 55, right: 55),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
TextFormField(
focusNode: _nodeText1,
controller: _controller1,
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),
),
),
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]),
onChanged: (value) => widget.onUnitQuantityChanged!(value)),
]));
}
return row;
}
Column columnQuantity() {
Widget columnQuantity() {
if (widget.unit == "second") {
return Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
Padding(
padding: const EdgeInsets.only(bottom: 0),
padding: const EdgeInsets.only(top: 10, left: 55, right: 55),
//padding: const EdgeInsets.only(bottom: 0),
child: StreamBuilder<int>(
stream: stopWatchTimer.rawTime,
initialData: stopWatchTimer.rawTime.valueWrapper?.value,
@ -300,13 +299,14 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
padding: const EdgeInsets.all(8),
child: Text(
displayTime,
style: const TextStyle(fontSize: 40, fontFamily: 'Helvetica', fontWeight: FontWeight.bold, color: Colors.white),
style: const TextStyle(fontSize: 35, fontFamily: 'Helvetica', fontWeight: FontWeight.bold, color: Colors.white),
),
),
]);
})),
Padding(
padding: const EdgeInsets.all(2),
//padding: const EdgeInsets.all(2),
padding: const EdgeInsets.only(top: 10, left: 25, right: 25),
child: Column(
children: <Widget>[
Padding(
@ -315,7 +315,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
padding: const EdgeInsets.symmetric(horizontal: 5),
child: IconButton(
padding: const EdgeInsets.all(2),
color: Colors.white70,
@ -328,7 +328,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
padding: const EdgeInsets.symmetric(horizontal: 5),
child: IconButton(
padding: const EdgeInsets.all(2),
iconSize: 40,
@ -341,7 +341,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
padding: const EdgeInsets.symmetric(horizontal: 5),
child: IconButton(
padding: const EdgeInsets.all(2),
iconSize: 40,
@ -364,30 +364,32 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
TimePickerWidget(onChange: (value) => widget.onQuantityChanged((value).toString()))
]);
}
Column row = Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
TextFormField(
focusNode: _nodeText2,
controller: _controller2,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5),
labelText: t(widget.unit),
labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.orange[50], decorationColor: Colors.black12),
fillColor: Colors.black38,
filled: true,
border: OutlineInputBorder(
gapPadding: 8.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.black26, width: 0.4),
Widget row = Container(
padding: const EdgeInsets.only(top: 10, left: 55, right: 55),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
TextFormField(
focusNode: _nodeText2,
controller: _controller2,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5),
labelText: t(widget.unit),
labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.orange[50], decorationColor: Colors.black12),
fillColor: Colors.black38,
filled: true,
border: OutlineInputBorder(
gapPadding: 8.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.black26, width: 0.4),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]),
onChanged: (value) {
widget.onQuantityChanged(value);
},
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]),
onChanged: (value) {
widget.onQuantityChanged(value);
},
),
]);
]));
return row;
}

View File

@ -36,7 +36,7 @@ class _HomePageState extends State<AitrainerHome> with Logging {
}
Future runDelayedEvent() async {
await Future.delayed(Duration(seconds: 2), () async {
await Future.delayed(Duration(milliseconds: 500), () async {
// ignore: close_sinks
SessionBloc sessionBloc = BlocProvider.of<SessionBloc>(context);
if (sessionBloc.state != SessionReady()) {

View File

@ -68,7 +68,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
@override
bool didUpdateWidget(MenuPageWidget oldWidget) {
super.didUpdateWidget(oldWidget);
scrollController.animateTo(40, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
scrollController.animateTo(5, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
return true;
}
@ -234,17 +234,18 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
SliverGrid getFilterElements(MenuBloc menuBloc) {
List<Widget> list = [];
int index = 0;
menuBloc.exerciseDeviceRepository.getGymDevices().forEach((element) {
String deviceName = AppLanguage().appLocal == Locale('en') ? element.name : element.nameTranslation;
ChoiceChip chip = ChoiceChip(
labelPadding: EdgeInsets.only(right: 5),
labelPadding: EdgeInsets.only(right: 3),
avatar: Icon(
Icons.remove_circle_outline,
color: Colors.orange,
size: 18,
size: 10,
),
label: Text(deviceName),
labelStyle: TextStyle(fontSize: 9, color: Colors.black),
labelStyle: TextStyle(fontSize: 9, color: Colors.indigo),
selectedColor: Colors.white,
selected: menuBloc.selectedDevice(element.exerciseDeviceId),
backgroundColor: Colors.blue[100],
@ -252,6 +253,11 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
onSelected: (value) => menuBloc.add(MenuFilterExerciseType(deviceId: element.exerciseDeviceId)),
);
list.add(chip);
if (index == 4) {
list.add(Divider());
}
index++;
});
SliverGrid sliverList = SliverGrid(

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:aitrainer_app/library/dropdown_search.dart';
//import 'package:aitrainer_app/library/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

View File

@ -35,19 +35,23 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
void initState() {
super.initState();
_scrollController = FixedExtentScrollController(initialItem: widget.initalValue);
_scrollController.animateToItem(widget.initalValue, duration: Duration(milliseconds: 100), curve: Curves.easeIn);
}
@override
void didUpdateWidget(NumberPickerWidget oldWidget) {
super.didUpdateWidget(oldWidget);
_scrollController.animateToItem(widget.initalValue, duration: Duration(milliseconds: 100), curve: Curves.easeIn);
}
Widget durationPicker({bool inSeconds = false, bool inHundredths = false}) {
double value = 0;
return CupertinoPicker(
scrollController: _scrollController,
useMagnifier: true,
magnification: 1.2,
diameterRatio: 0.85,
backgroundColor: Colors.transparent,
selectionOverlay: Container(),
onSelectedItemChanged: (x) {
currentData = x.toDouble();
value = x.toDouble();
@ -57,7 +61,7 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
},
children: List.generate(
widget.maxValue, (index) => Text('$index ' + widget.unit, style: TextStyle(color: widget.color, fontSize: widget.fontSize))),
itemExtent: 40,
itemExtent: 30,
);
}
@ -66,32 +70,30 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
Widget build(BuildContext context) {
setContext(context);
return Container(
//color: Colors.white24,
color: Colors.transparent,
width: MediaQuery.of(context).size.width * .40,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width * .35,
child: Center(
child: Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width * 0.95,
height: MediaQuery.of(context).size.height * 0.25,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(child: durationPicker()),
],
)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width * .35,
child: Center(
child: Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width * 0.95,
//height: MediaQuery.of(context).size.height * 0.25,
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(child: durationPicker()),
],
)),
),
],
),
),
],
),
);
}

View File

@ -77,14 +77,14 @@ packages:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.3"
version: "2.0.0"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.5"
version: "0.4.7"
build_daemon:
dependency: transitive
description:
@ -98,21 +98,21 @@ packages:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.4"
version: "2.0.0"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.5"
version: "1.12.2"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.10"
version: "6.1.12"
built_collection:
dependency: transitive
description:
@ -154,7 +154,7 @@ packages:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.1"
chewie:
dependency: transitive
description:
@ -211,6 +211,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
convex_bottom_bar:
dependency: "direct main"
description:
name: convex_bottom_bar
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
coverage:
dependency: transitive
description:
@ -252,7 +259,7 @@ packages:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.14"
version: "2.0.0"
devicelocale:
dependency: "direct main"
description:
@ -260,13 +267,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.1"
dropdown_search:
dependency: "direct main"
description:
name: dropdown_search
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
equatable:
dependency: "direct main"
description:
@ -315,7 +315,7 @@ packages:
name: firebase_analytics
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.0-dev.0"
version: "8.0.0-dev.2"
firebase_analytics_platform_interface:
dependency: transitive
description:
@ -336,28 +336,28 @@ packages:
name: firebase_auth
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.3"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "4.0.2"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.0.5"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.3"
firebase_core_platform_interface:
dependency: transitive
description:
@ -378,21 +378,21 @@ packages:
name: firebase_messaging
url: "https://pub.dartlang.org"
source: hosted
version: "9.1.0"
version: "9.1.1"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.0.4"
fixnum:
dependency: transitive
description:
@ -407,6 +407,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.30.0"
flurry:
dependency: "direct main"
description:
name: flurry
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.7"
flutter:
dependency: "direct main"
description: flutter
@ -425,21 +432,21 @@ packages:
name: flutter_facebook_auth
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.1+1"
version: "3.3.2+2"
flutter_facebook_auth_platform_interface:
dependency: transitive
description:
name: flutter_facebook_auth_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
version: "2.4.1"
flutter_facebook_auth_web:
dependency: transitive
description:
name: flutter_facebook_auth_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0+2"
version: "2.4.1+1"
flutter_html:
dependency: "direct main"
description:
@ -545,7 +552,7 @@ packages:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
version: "1.0.0"
html:
dependency: transitive
description:
@ -615,7 +622,7 @@ packages:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
version: "4.0.1"
keyboard_actions:
dependency: "direct main"
description:
@ -636,7 +643,7 @@ packages:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.4"
version: "1.0.1"
matcher:
dependency: transitive
description:
@ -832,7 +839,7 @@ packages:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.8"
version: "1.0.0"
purchases_flutter:
dependency: "direct main"
description:
@ -881,7 +888,7 @@ packages:
name: sentry
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0-nullsafety.1"
version: "5.0.0"
shared_preferences:
dependency: "direct dev"
description:
@ -963,14 +970,14 @@ packages:
name: smartlook
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.6"
version: "1.0.7"
source_gen:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.10+3"
version: "1.0.0"
source_map_stack_trace:
dependency: transitive
description:
@ -1040,7 +1047,7 @@ packages:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "2.0.0"
string_scanner:
dependency: transitive
description:
@ -1208,7 +1215,7 @@ packages:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.4"
version: "1.0.0"
webview_flutter:
dependency: transitive
description:

View File

@ -27,7 +27,7 @@ dependencies:
cupertino_icons: ^1.0.0
google_fonts: ^2.0.0
devicelocale: ^0.4.1
sentry: ^4.1.0-nullsafety.1
sentry: ^5.0.0
flutter_bloc: ^7.0.0
equatable: ^2.0.0
@ -54,25 +54,28 @@ dependencies:
confetti: ^0.6.0-nullsafety
crypto: ^3.0.0
carousel_slider: ^4.0.0-nullsafety.0
dropdown_search: ^0.5.0
#dropdown_search: ^0.5.0
convex_bottom_bar: ^3.0.0
firebase_core: ^1.0.2
firebase_analytics: ^8.0.0-dev.0
firebase_messaging: ^9.1.0
firebase_core: ^1.0.3
firebase_analytics: ^8.0.0-dev.2
firebase_messaging: ^9.1.1
flutter_local_notifications: ^5.0.0
firebase_auth: ^1.0.1
flutter_facebook_auth: ^3.3.1+1
firebase_auth: ^1.0.3
flutter_facebook_auth: ^3.3.2
google_sign_in: ^5.0.1
apple_sign_in: ^0.1.0
smartlook: ^1.0.6
smartlook: ^1.0.7
flurry: ^0.0.4
#animated_widgets: ^1.0.6
mockito: ^5.0.3
sqflite: ^2.0.0+3
flutter_secure_storage: ^4.1.0
#social_share: ^2.1.1
flutter_localizations:
sdk: flutter
@ -167,6 +170,7 @@ flutter:
- asset/image/BMI_graph_C.png
- asset/image/BMI_mutato.png
- asset/image/haken.png
- asset/image/kupa.png
- asset/image/pict_calorie.png
- asset/image/pict_development_by_bodypart_percent.png
@ -226,6 +230,8 @@ flutter:
- asset/menu/3.bcs1.jpg
- asset/menu/300m.jpg
- asset/menu/400m.jpg
- asset/menu/FG_1_test.jpg
- asset/menu/FG_1_training.jpg
- asset/menu/alternate_dumbbell_presses.jpg
- asset/menu/alternate_standing_shoulder_press.jpg
- asset/menu/arnold_press.jpg