WT 1.1.22 reengineering: Training Plan execution, registration

This commit is contained in:
bossanyit
2021-08-25 01:05:03 +02:00
parent 6b25fdfe0e
commit cebd83648b
51 changed files with 2605 additions and 663 deletions
@@ -29,8 +29,8 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
weight = this.customerRepository.getWeight() == 0 ? 60 : this.customerRepository.getWeight();
height = this.customerRepository.getHeight() == 0 ? 170 : this.customerRepository.getHeight();
selectedSport = customerRepository.getSport();
print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString());
// selectedSport = customerRepository.getSport();
//print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString());
}
Sport? selectedSport;
@@ -41,6 +41,7 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
CustomerChangeEvent event,
) async* {
try {
print("Event: $event");
if (event is CustomerLoad) {
yield CustomerChangeLoading();
yield CustomerDataChanged();
@@ -63,7 +64,7 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
year = event.year;
yield CustomerDataChanged();
} else if (event is CustomerWeightChange) {
yield CustomerChangeLoading();
//yield CustomerChangeLoading();
customerRepository.setWeight(event.weight);
weight = event.weight.toDouble();
yield CustomerDataChanged();
@@ -88,12 +89,43 @@ class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState>
customerRepository.setName(event.name);
yield CustomerDataChanged();
} else if (event is CustomerGenderChange) {
yield CustomerChangeLoading();
customerRepository.setSex(event.gender == 0 ? "m" : "w");
yield CustomerDataChanged();
} else if (event is CustomerSportChange) {
yield CustomerChangeLoading();
selectedSport = event.sport;
yield CustomerDataChanged();
} else if (event is CustomerSaveFitness) {
yield CustomerChangeLoading();
if (customerRepository.customer!.fitnessLevel == null) {
throw Exception("Please select your fitness level");
}
yield CustomerSaveSuccess();
} else if (event is CustomerSaveGoal) {
yield CustomerChangeLoading();
if (customerRepository.customer!.goal == null) {
throw Exception("Please select your goal");
}
yield CustomerSaveSuccess();
} else if (event is CustomerSaveSex) {
yield CustomerChangeLoading();
if (customerRepository.customer!.sex == null) {
throw Exception("Please select your biologial gender");
}
yield CustomerSaveSuccess();
} else if (event is CustomerSaveWeight) {
yield CustomerChangeLoading();
if (customerRepository.customer!.getProperty("Weight") == null) {
throw Exception("Please select your weight");
}
yield CustomerSaveSuccess();
} else if (event is CustomerSaveHeight) {
yield CustomerChangeLoading();
if (customerRepository.customer!.getProperty("Height") == null) {
throw Exception("Please select your height");
}
yield CustomerSaveSuccess();
} else if (event is CustomerSave) {
yield CustomerSaving();
if (validation()) {
@@ -114,3 +114,23 @@ class CustomerLoad extends CustomerChangeEvent {
class CustomerSave extends CustomerChangeEvent {
const CustomerSave();
}
class CustomerSaveGoal extends CustomerChangeEvent {
const CustomerSaveGoal();
}
class CustomerSaveFitness extends CustomerChangeEvent {
const CustomerSaveFitness();
}
class CustomerSaveSex extends CustomerChangeEvent {
const CustomerSaveSex();
}
class CustomerSaveWeight extends CustomerChangeEvent {
const CustomerSaveWeight();
}
class CustomerSaveHeight extends CustomerChangeEvent {
const CustomerSaveHeight();
}
+23
View File
@@ -1,13 +1,17 @@
import 'dart:async';
import 'dart:collection';
import 'package:intl/intl.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_ability.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/exercise_device_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
@@ -111,6 +115,22 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> with Trans, Logging {
listFilterDevice.remove(deviceId);
}
yield MenuReady();
} else if (event is MenuStartTrial) {
yield MenuLoading();
final DateTime start = event.start;
CustomerRepository customerRepository = CustomerRepository();
customerRepository.customer = Cache().userLoggedIn;
customerRepository.customer!.trialDate = start;
Cache().userLoggedIn!.trialDate = start;
customerRepository.saveCustomer();
if (DateTime.now().difference(start).inHours < 1) {
Cache().hasPurchased = true;
log("Trial mode on!");
Track().track(TrackingEvent.trial, eventValue: DateFormat('yyyy-MM-dd HH:mm:ss').format(start));
}
yield MenuReady();
}
} on Exception catch (ex) {
yield MenuError(message: ex.toString());
@@ -134,6 +154,9 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> with Trans, Logging {
case "my_body":
ability = ExerciseAbility.none;
break;
case "training_execute":
ability = ExerciseAbility.training_execute;
break;
}
log("Ability: " + ability.toString() + " name: " + name);
}
+11
View File
@@ -56,4 +56,15 @@ class MenuRecreateTree extends MenuEvent {
class MenuFilterExerciseType extends MenuEvent {
final int deviceId;
const MenuFilterExerciseType({required this.deviceId});
@override
List<Object> get props => [deviceId];
}
class MenuStartTrial extends MenuEvent {
final DateTime start;
const MenuStartTrial({required this.start});
@override
List<Object> get props => [start];
}
+2
View File
@@ -13,6 +13,7 @@ part 'session_state.dart';
class SessionBloc extends Bloc<SessionEvent, SessionState> with Logging {
final Session session;
StreamSubscription? _sub;
SessionBloc({required this.session}) : super(SessionInitial());
@@ -41,6 +42,7 @@ class SessionBloc extends Bloc<SessionEvent, SessionState> with Logging {
@override
Future<void> close() async {
await this.close();
_sub?.cancel();
super.close();
}
}
@@ -59,10 +59,12 @@ class TrainingEvaluationBloc extends Bloc<TrainingEvaluationEvent, TrainingEvalu
trainingPlanBloc.getMyPlan()!.days[day]!.forEach((element) {
//if (!element.state.equalsTo(ExercisePlanDetailState.extra)) {
if (!findExerciseInEvaluationList(element.exerciseTypeId!)) {
addEvaluationExercise(element);
} else {
//editEvaluationExercise(element);
if (element.exerciseTypeId != null) {
if (!findExerciseInEvaluationList(element.exerciseTypeId!)) {
addEvaluationExercise(element);
} else {
//editEvaluationExercise(element);
}
}
//}
});
@@ -93,7 +95,7 @@ class TrainingEvaluationBloc extends Bloc<TrainingEvaluationEvent, TrainingEvalu
exercise.type = TrainingEvaluationExerciseType.weightBased;
this.evaluationList.add(exercise);
} else {
if (detail.exerciseType!.unitQuantityUnit == null) {
if (detail.exerciseType!.unitQuantityUnit == null || detail.weight == null) {
exercise.type = TrainingEvaluationExerciseType.repeatBased;
exercise.repeats = detail.repeats;
exercise.maxRepeats = getMaxRepeatsByExerciseType(detail.exerciseTypeId!);
+57 -14
View File
@@ -46,7 +46,7 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
try {
if (event is TrainingPlanActivate) {
yield TrainingPlanLoading();
_myPlan = await trainingPlanRepository.activateTrainingPlan(event.trainingPlanId);
_myPlan = trainingPlanRepository.activateTrainingPlan(event.trainingPlanId);
_myPlan!.type = CustomerTrainingPlanType.template;
menuBloc.menuTreeRepository.sortedTree.forEach((name, list) {
@@ -72,7 +72,6 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
event.detail.repeats =
Common.reCalculateRepeatsByChangedWeight(event.detail.weight!, event.detail.repeats!.toDouble(), event.weight);
event.detail.weight = event.weight;
print(" weight: ${event.weight} new repeats: ${event.detail.repeats}");
yield TrainingPlanReady();
} else if (event is TrainingPlanRepeatsChange) {
@@ -102,13 +101,12 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
exercise.unitQuantity = event.detail.weight;
exercise.dateAdd = DateTime.now();
event.detail.exercises.add(exercise);
if (event.detail.exercises.length >= event.detail.set!) {
if (this.isAllDetailsSameExerciseFinished(event.detail)) {
event.detail.state = ExercisePlanDetailState.finished;
} else if (event.detail.exercises.length >= 0) {
event.detail.state = ExercisePlanDetailState.inProgress;
}
// recalculate the weight to the original planned repeats for the next details
if (exercise.unitQuantity != null && exercise.unitQuantity! > 0) {
for (var nextDetail in _myPlan!.details) {
double weightFromPlan = trainingPlanRepository.getOriginalWeight(this.getMyPlan()!.trainingPlanId!, nextDetail);
@@ -357,6 +355,23 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
return workoutTree.imageName;
}
int getStep(CustomerTrainingPlanDetails detail) {
List<CustomerTrainingPlanDetails> details = getAllDetailsSameExercise(detail);
int step = 0;
int indexElement = 0;
details.forEach((element) {
if (indexElement == 0) {
step = element.exercises.length;
} else {
step += element.exercises.length;
}
indexElement++;
});
//print("STEP: $step ");
return step;
}
CustomerTrainingPlanDetails? getNext() {
if (_myPlan == null || _myPlan!.details.isEmpty) {
return null;
@@ -373,12 +388,16 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
break;
} else {
final int step = detail.exercises.length;
if (step < minStep && !detail.state.equalsTo(ExercisePlanDetailState.skipped) && day == detail.day) {
if (step < minStep &&
step < detail.set! &&
!isAllDetailsSameExerciseFinished(detail) &&
!detail.state.equalsTo(ExercisePlanDetailState.skipped) &&
day == detail.day) {
next = detail;
minStep = step;
if (detail.parallel != true) {
break;
}
//if (detail.parallel != true) {
break;
//}
}
}
}
@@ -423,18 +442,23 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
int indexInProgress = 0;
int indexInStart = 0;
final String day = dayNames[this.activeDayIndex];
CustomerTrainingPlanDetails? prev;
for (var detail in _myPlan!.days[day]!) {
if (detail.state == ExercisePlanDetailState.inProgress) {
//print("Offset detail $detail");
if (detail.state == ExercisePlanDetailState.inProgress || detail.state == ExercisePlanDetailState.start) {
prev = detail;
break;
}
if (detail.state == ExercisePlanDetailState.start) {
break;
if (prev != null && prev.exerciseTypeId != detail.exerciseTypeId && detail.state != ExercisePlanDetailState.extra) {
//print(" --- offset + 1");
indexInStart++;
indexInProgress++;
}
indexInStart++;
indexInProgress++;
prev = detail;
}
int index = indexInStart > indexInProgress ? indexInStart : indexInProgress;
offset = index * 80;
offset = (index) * 270;
print("Offset: $offset day: $day ($indexInStart, $indexInProgress)");
return offset;
}
@@ -573,4 +597,23 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
}
return exists;
}
List<CustomerTrainingPlanDetails> getAllDetailsSameExercise(CustomerTrainingPlanDetails detail) {
List<CustomerTrainingPlanDetails> list = [];
getMyPlan()!.details.forEach((element) {
if (detail.exerciseTypeId == element.exerciseTypeId) {
list.add(element);
}
});
return list;
}
bool isAllDetailsSameExerciseFinished(CustomerTrainingPlanDetails detail) {
bool allFinished = true;
List<CustomerTrainingPlanDetails> list = getAllDetailsSameExercise(detail);
for (var listDetail in list) {
allFinished = allFinished && listDetail.exercises.length >= listDetail.set!;
}
return allFinished;
}
}
+13 -4
View File
@@ -13,7 +13,10 @@ import 'package:aitrainer_app/view/customer_bodytype_animation.dart';
import 'package:aitrainer_app/view/customer_exercise_device.dart';
import 'package:aitrainer_app/view/customer_fitness_page.dart';
import 'package:aitrainer_app/view/customer_goal_page.dart';
import 'package:aitrainer_app/view/customer_height_page.dart';
import 'package:aitrainer_app/view/customer_modify_page.dart';
import 'package:aitrainer_app/view/customer_sex_page.dart';
import 'package:aitrainer_app/view/customer_weight_page.dart';
import 'package:aitrainer_app/view/customer_welcome_page.dart';
import 'package:aitrainer_app/view/evaluation_page.dart';
import 'package:aitrainer_app/view/exercise_control_page.dart';
@@ -23,6 +26,7 @@ import 'package:aitrainer_app/view/login.dart';
import 'package:aitrainer_app/view/exercise_new_page.dart';
import 'package:aitrainer_app/view/training_plan_custom.dart';
import 'package:aitrainer_app/view/training_plan_custom_add.dart';
import 'package:aitrainer_app/view/training_plan_execute.dart';
import 'package:aitrainer_app/view/training_plans_page.dart';
import 'package:aitrainer_app/view/mydevelopment_body_page.dart';
import 'package:aitrainer_app/view/mydevelopment_muscle_page.dart';
@@ -146,7 +150,6 @@ Future<Null> main() async {
}
print(" -- FireBase init..");
await FirebaseApi().initializeFlutterFire();
runApp(MultiBlocProvider(
providers: [
@@ -177,8 +180,8 @@ Future<Null> main() async {
BlocProvider<TestSetExecuteBloc>(
create: (BuildContext context) => TestSetExecuteBloc(),
),
BlocProvider<TutorialBloc>(
create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())),
/* BlocProvider<TutorialBloc>(
create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())), */
BlocProvider<TrainingPlanBloc>(create: (context) {
final MenuBloc menuBloc = BlocProvider.of<MenuBloc>(context);
return TrainingPlanBloc(menuBloc: menuBloc, trainingPlanRepository: TrainingPlanRepository());
@@ -196,6 +199,7 @@ Future<void> initThirdParty() async {
await FlurryData.initialize(androidKey: "JNYCTCWBT34FM3J8TV36", iosKey: "3QBG7BSMGPDH24S8TRQP", enableLog: true);
FlutterUxcam.optIntoSchematicRecordings();
}
await FirebaseApi().initializeFlutterFire();
}
class WorkoutTestApp extends StatelessWidget {
@@ -217,6 +221,7 @@ class WorkoutTestApp extends StatelessWidget {
//facebookAppEvents.setAdvertiserTracking(enabled: true);
initThirdParty();
return MaterialApp(
localizationsDelegates: [
// ... app-specific localization delegate[s] here
@@ -249,6 +254,9 @@ class WorkoutTestApp extends StatelessWidget {
'customerModifyPage': (context) => CustomerModifyPage(),
'customerGoalPage': (context) => CustomerGoalPage(),
'customerFitnessPage': (context) => CustomerFitnessPage(),
'customerSexPage': (context) => CustomerSexPage(),
'customerWeightPage': (context) => CustomerWeightPage(),
'customerHeightPage': (context) => CustomerHeightPage(),
'customerBodyTypePage': (context) => CustomerBodyTypeAnimationPage(),
'customerWelcomePage': (context) => CustomerWelcomePage(),
'customerExerciseDevicePage': (context) => CustomerExerciseDevicePage(),
@@ -275,7 +283,8 @@ class WorkoutTestApp extends StatelessWidget {
'myTrainingPlanCustom': (context) => TrainingPlanCustomPage(),
'myTrainingPlanCustomAdd': (context) => TrainingPlanCustomAddPage(),
'myTrainingPlanActivate': (context) => TrainingPlanActivatePage(),
'myTrainingPlanExecute': (context) => TrainingPlanExecutePage(),
'myTrainingPlanExecute2': (context) => TrainingPlanExecutePage(),
'myTrainingPlanExecute': (context) => TrainingPlanExecute(),
'myTrainingPlanExercise': (context) => TrainingPlanExercise(),
'myTrainingEvaluation': (context) => TrainingEvaluationPage(),
},
+29 -1
View File
@@ -186,8 +186,8 @@ class Cache with Logging {
Cache._internal() {
String testEnv = EnvironmentConfig.test_env;
this.testEnvironment = testEnv;
print("testEnv $testEnv");
if (testEnv == "1") {
print("testEnv $testEnv");
baseUrl = baseUrlTest;
liveServer = false;
}
@@ -234,6 +234,17 @@ class Cache with Logging {
sharedPreferences.setString(Cache.myTrainingPlanKey, myTrainingPlanJson);
}
Future<void> deleteMyTrainingPlan() async {
if (myTrainingPlan == null) {
return;
}
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
SharedPreferences sharedPreferences;
sharedPreferences = await prefs;
sharedPreferences.remove(Cache.myTrainingPlanKey);
}
Future<void> getMyTrainingPlan() async {
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
SharedPreferences sharedPreferences;
@@ -744,4 +755,21 @@ class Cache with Logging {
List<TrainingPlanDay> getTrainingPlanDays() => this._trainingPlanDays;
setTrainingPlanDays(value) => this._trainingPlanDays = value;
bool canTrial() {
if (Cache().userLoggedIn == null) {
return false;
}
for (var element in _purchases) {
if (element.customerId == Cache().userLoggedIn!.customerId) {
return false;
}
}
if (userLoggedIn!.trialDate != null) {
return false;
}
return true;
}
}
+4 -1
View File
@@ -24,6 +24,7 @@ class Customer {
DateTime? dateChange;
int? emailSubscription;
int? sportId;
DateTime? trialDate;
LinkedHashMap<String, CustomerProperty> properties = LinkedHashMap();
@@ -70,6 +71,7 @@ class Customer {
this.dataPolicyAllowed = json['dataPolicyAllowed'];
this.emailSubscription = json['emailSubscription'];
this.sportId = json['sportId'];
this.trialDate = json['trialDate'] == null ? null : DateTime.parse(json['trialDate']);
this.dateAdd = json['dateAdd'] == null ? DateTime.parse("0000-00-00") : DateTime.parse(json['dateAdd']);
this.dateChange = json['dateChange'] == null ? DateTime.parse("0000-00-00") : DateTime.parse(json['dateChange']);
@@ -93,7 +95,8 @@ class Customer {
"dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd!),
"dateChange": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateChange!),
"emailSubscription": this.emailSubscription,
"sportId": this.sportId
"sportId": this.sportId,
"trialDate": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.trialDate!),
};
double getProperty(String propertyName) {
+1 -1
View File
@@ -1,4 +1,4 @@
enum ExerciseAbility { oneRepMax, endurance, running, mini_test_set, paralell_test, training, none }
enum ExerciseAbility { oneRepMax, endurance, running, mini_test_set, paralell_test, training, training_execute, none }
extension ExerciseAbilityExt on ExerciseAbility {
String enumToString() => this.toString().split(".").last;
+1
View File
@@ -75,6 +75,7 @@ class WorkoutMenuTree {
"is1RM": is1RM.toString(),
"isRunning": isRunning.toString(),
"sort": sort,
"internalName": internalName,
};
}
+1 -1
View File
@@ -168,7 +168,7 @@ class CustomerRepository with Logging {
if (this.customer!.properties[propertyName] == null) {
this.customer!.properties[propertyName] = CustomerProperty(
propertyId: propertyRepository.getPropertyByName("Height")!.propertyId,
customerId: this.customer!.customerId!,
customerId: this.customer!.customerId == null ? 0 : this.customer!.customerId!,
propertyValue: value,
dateAdd: DateTime.now());
} else {
+50 -4
View File
@@ -4,9 +4,10 @@ import 'package:aitrainer_app/model/customer_training_plan_details.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'package:aitrainer_app/model/fitness_state.dart';
import 'package:aitrainer_app/model/training_plan.dart';
import 'package:aitrainer_app/repository/training_plan_day_repository.dart';
import 'package:aitrainer_app/service/training_plan_service.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/util/common.dart';
class TrainingPlanRepository {
@@ -40,14 +41,14 @@ class TrainingPlanRepository {
/// 2. calculate customer_training_plan_details weights / repleats
/// 3. create new customer_training_plan
Future<CustomerTrainingPlan?> activateTrainingPlan(int trainingPlanId) async {
CustomerTrainingPlan? activateTrainingPlan(int trainingPlanId) {
print(" **** Activate Plan: $trainingPlanId");
// 1. deactivate
if (Cache().getCustomerTrainingPlans() != null) {
Cache().getCustomerTrainingPlans()!.forEach((plan) {
plan.active = false;
if (plan.customerTrainingPlanId != null) {
TrainingPlanApi().updateCustomerTrainingPlan(plan, plan.customerTrainingPlanId!);
//TrainingPlanApi().updateCustomerTrainingPlan(plan, plan.customerTrainingPlanId!);
}
});
}
@@ -58,7 +59,7 @@ class TrainingPlanRepository {
plan.active = true;
plan.status = "open";
plan.dateAdd = DateTime.now();
plan.name = getTrainingPlanById(trainingPlanId)!.nameTranslations["hu"];
plan.name = getTrainingPlanById(trainingPlanId)!.nameTranslations[AppLanguage().appLocal.toString()];
TrainingPlan? trainingPlan = this.getTrainingPlanById(trainingPlanId);
if (trainingPlan == null || trainingPlan.details == null) {
@@ -138,6 +139,23 @@ class TrainingPlanRepository {
return plan;
}
int? getTrainingPlanByInternalName(String internalName) {
int? id;
if (Cache().getTrainingPlans() == null) {
return id;
}
for (var trainingPlan in Cache().getTrainingPlans()!) {
print("internal ${trainingPlan.internalName}");
if (trainingPlan.internalName == internalName) {
id = trainingPlan.trainingPlanId;
break;
}
}
return id;
}
CustomerTrainingPlanDetails getCalculatedWeightRepeats(int exerciseTypeId, CustomerTrainingPlanDetails detail) {
double weight = -1;
if (Cache().getExercises() == null) {
@@ -247,4 +265,32 @@ class TrainingPlanRepository {
return recalculatedDetail;
}
void generateTrainingPlan() {
int? trainingPlanId;
if (Cache().userLoggedIn == null) {
return;
}
bool isWoman = Cache().userLoggedIn!.sex == "w";
if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("beginner_man");
} else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("beginner_split");
} else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("man_routine4");
} else {
trainingPlanId = isWoman ? getTrainingPlanByInternalName("5day") : getTrainingPlanByInternalName("5day");
}
print("Generated plan $trainingPlanId fitness ${Cache().userLoggedIn!.fitnessLevel} - ${FitnessState.beginner}");
if (trainingPlanId != null) {
CustomerTrainingPlan? customerTrainingPlan = activateTrainingPlan(trainingPlanId);
if (customerTrainingPlan != null) {
Cache().myTrainingPlan = customerTrainingPlan;
}
}
}
}
+1
View File
@@ -30,6 +30,7 @@ class ExerciseTreeApi with Logging {
Future<String> buildImage(String imageUrl, int treeId) async {
String assetImage = 'asset/menu/' + imageUrl.substring(7);
print("asset image $assetImage");
return await rootBundle.load(assetImage).then((value) {
return assetImage;
}).catchError((_) {
+36 -31
View File
@@ -4,12 +4,10 @@ import 'package:crypto/crypto.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/service/logging.dart' as logging;
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/material.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
@@ -37,7 +35,8 @@ class FirebaseApi with logging.Logging {
await Firebase.initializeApp();
this.appleSignInAvailable = await SignInWithApple.isAvailable();
AwesomeNotifications().initialize(
/* AwesomeNotifications().initialize(
// set the icon to null if you want to use the default app icon
null,
[
@@ -55,7 +54,7 @@ class FirebaseApi with logging.Logging {
// This is very important to not harm the user experience
AwesomeNotifications().requestPermissionToSendNotifications();
}
});
}); */
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true, // Required to display a heads up notification
@@ -71,32 +70,6 @@ class FirebaseApi with logging.Logging {
}
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
print('Handling a background message: ${message.messageId}');
if (!StringUtils.isNullOrEmpty(message.notification?.title, considerWhiteSpaceAsEmpty: true) ||
!StringUtils.isNullOrEmpty(message.notification?.body, considerWhiteSpaceAsEmpty: true)) {
print('message also contained a notification: ${message.notification}');
String? imageUrl;
imageUrl ??= message.notification!.android?.imageUrl;
imageUrl ??= message.notification!.apple?.imageUrl;
Map<String, dynamic> notificationAdapter = {
NOTIFICATION_CHANNEL_KEY: 'basic_channel',
NOTIFICATION_ID: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_ID] ?? message.messageId ?? math.Random().nextInt(2147483647),
NOTIFICATION_TITLE: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_TITLE] ?? message.notification?.title,
NOTIFICATION_BODY: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_BODY] ?? message.notification?.body,
NOTIFICATION_LAYOUT: StringUtils.isNullOrEmpty(imageUrl) ? 'Default' : 'BigPicture',
NOTIFICATION_BIG_PICTURE: imageUrl
};
AwesomeNotifications().createNotificationFromJsonData(notificationAdapter);
} else {
AwesomeNotifications().createNotificationFromJsonData(message.data);
}
}
Future<String> signInEmail(String? email, String? password) async {
if (email == null) {
throw Exception("Please type an email address");
@@ -395,7 +368,7 @@ class FirebaseApi with logging.Logging {
}
Future<void> setupRemoteConfig() async {
initializeFlutterFire();
//initializeFlutterFire();
RemoteConfig? remoteConfig;
try {
remoteConfig = RemoteConfig.instance;
@@ -420,3 +393,35 @@ class FirebaseApi with logging.Logging {
}
}
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
print('Handling a background message ${message.messageId}');
}
/* Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
print('Handling a background message: ${message.messageId}');
if (!StringUtils.isNullOrEmpty(message.notification?.title, considerWhiteSpaceAsEmpty: true) ||
!StringUtils.isNullOrEmpty(message.notification?.body, considerWhiteSpaceAsEmpty: true)) {
print('message also contained a notification: ${message.notification}');
String? imageUrl;
imageUrl ??= message.notification!.android?.imageUrl;
imageUrl ??= message.notification!.apple?.imageUrl;
Map<String, dynamic> notificationAdapter = {
NOTIFICATION_CHANNEL_KEY: 'basic_channel',
NOTIFICATION_ID: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_ID] ?? message.messageId ?? math.Random().nextInt(2147483647),
NOTIFICATION_TITLE: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_TITLE] ?? message.notification?.title,
NOTIFICATION_BODY: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_BODY] ?? message.notification?.body,
NOTIFICATION_LAYOUT: StringUtils.isNullOrEmpty(imageUrl) ? 'Default' : 'BigPicture',
NOTIFICATION_BIG_PICTURE: imageUrl
};
AwesomeNotifications().createNotificationFromJsonData(notificationAdapter);
} else {
AwesomeNotifications().createNotificationFromJsonData(message.data);
}
} */
+1
View File
@@ -57,6 +57,7 @@ enum TrackingEvent {
training_plan_execute,
training_plan_finished,
training_plan_custom,
trial
}
T enumFromString<T>(Iterable<T> values, String value) {
+3 -1
View File
@@ -46,7 +46,9 @@ class RevenueCatPurchases with Logging {
log("Purchaserinfo not reachable " + e.toString());
}
}
if (Cache().userLoggedIn!.admin == 1) {
bool inTrial = Cache().userLoggedIn!.trialDate != null && DateTime.now().difference(Cache().userLoggedIn!.trialDate!).inDays < 10;
log("Trial mode: $inTrial date: ${Cache().userLoggedIn!.trialDate}");
if (Cache().userLoggedIn!.admin == 1 || inTrial) {
Cache().hasPurchased = true;
}
}
+2 -1
View File
@@ -19,12 +19,13 @@ class Track with Logging {
Track._internal();
void track(TrackingEvent event, {String eventValue = ""}) {
analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue});
if (!isInDebugMode) {
FlurryData.logEvent(event.toString());
// Smartlook.setGlobalEventProperty(event.toString(), eventValue, false);
FlutterUxcam.logEventWithProperties(event.enumToString(), {"value": eventValue});
model.Tracking tracking = model.Tracking();
analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue});
//analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue});
tracking.customerId = Cache().userLoggedIn == null ? 0 : Cache().userLoggedIn!.customerId!;
tracking.event = event.enumToString();
if (eventValue.isNotEmpty) {
+21
View File
@@ -12,6 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:firebase_in_app_messaging/firebase_in_app_messaging.dart';
// ignore: must_be_immutable
class AccountPage extends StatelessWidget with Trans {
@@ -151,6 +152,7 @@ class AccountPage extends StatelessWidget with Trans {
),
devices(context, accountBloc),
loginOut(context, accountBloc),
//messaging(),
//getMyTrainees(context, accountBloc),
]);
}
@@ -180,6 +182,25 @@ class AccountPage extends StatelessWidget with Trans {
return element;
}
ListTile messaging() {
FirebaseInAppMessaging fiam = FirebaseInAppMessaging();
ListTile element = ListTile(
leading: Icon(Icons.message),
title: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.white38,
onSurface: Colors.grey,
),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(t("Trigger message"), style: TextStyle(color: Colors.purple)),
]),
onPressed: () => fiam.triggerEvent("mydevelopment"),
),
);
return element;
}
ListTile loginOut(BuildContext context, AccountBloc accountBloc) {
ListTile element = ListTile();
+160 -184
View File
@@ -1,6 +1,7 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/sport.dart';
import 'package:aitrainer_app/util/app_localization.dart';
@@ -14,6 +15,7 @@ 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 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
import '../library/dropdown_search/dropdown_search.dart';
@@ -31,11 +33,13 @@ class CustomerFitnessPage extends StatefulWidget {
class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
String? selected;
bool fulldata = false;
late CustomerChangeBloc changeBloc;
late double cWidth;
@override
Widget build(BuildContext context) {
setContext(context);
final double cWidth = MediaQuery.of(context).size.width * 0.75;
cWidth = MediaQuery.of(context).size.width * 0.75;
CustomerRepository customerRepository;
dynamic args = ModalRoute.of(context)!.settings.arguments;
if (args is HashMap && args['personal_data'] != null) {
@@ -49,186 +53,122 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
back: true,
);
if (!fulldata) {
_bar = AppBarProgress(max: 50, min: 26);
_bar = AppBarProgress(max: 30, min: 15);
}
final double h = 27;
return Scaffold(
appBar: _bar,
body: BlocProvider(
appBar: _bar,
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_plainblack_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
height: double.infinity,
width: double.infinity,
child: BlocProvider(
create: (context) => CustomerChangeBloc(customerRepository: customerRepository),
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(
padding: EdgeInsets.only(bottom: 200),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Divider(),
Wrap(
//runAlignment: WrapAlignment.center,
alignment: WrapAlignment.center,
children: [
Text(
t("Your Fitness State"),
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.orange,
fontSize: 30,
fontWeight: FontWeight.w900,
),
)
]),
Divider(),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(changeBloc, FitnessState.beginner),
),
child: Container(
width: cWidth,
child: Column(
children: [
Text(t("Beginner"),
textWidthBasis: TextWidthBasis.longestLine,
style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900)),
Text(
t("I am beginner"),
style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100),
),
],
)),
onPressed: () => {
setState(() {
selected = FitnessState.beginner;
changeBloc.add(CustomerFitnessChange(fitness: selected!));
}),
}),
Divider(),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(changeBloc, FitnessState.intermediate),
),
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
t("Intermediate"),
style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
InkWell(
child: Text(
t("I am intermediate"),
style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
),
),
onPressed: () => {
setState(() {
selected = FitnessState.intermediate;
changeBloc.add(CustomerFitnessChange(fitness: selected!));
print(selected);
}),
}),
Divider(),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(changeBloc, FitnessState.advanced),
),
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
t("Advanced"),
style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
InkWell(
child: Text(
t("I am advanced"),
style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
),
),
onPressed: () => {
setState(() {
selected = FitnessState.advanced;
changeBloc.add(CustomerFitnessChange(fitness: selected!));
print(selected);
}),
}),
Divider(),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(changeBloc, FitnessState.professional),
),
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
AppLocalizations.of(context)!.translate("Professional"),
style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
InkWell(
child: Text(
AppLocalizations.of(context)!.translate("I am professional"),
style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
),
),
onPressed: () => {
setState(() {
selected = FitnessState.professional;
changeBloc.add(CustomerFitnessChange(fitness: selected!));
print(selected);
}),
}),
Divider(),
Text(
t("Your Primary Sport") + ":",
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.orange,
fontSize: 20,
),
child: BlocConsumer<CustomerChangeBloc, CustomerChangeState>(
listener: (context, state) {
if (state is CustomerSaveError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("customerSexPage", arguments: changeBloc.customerRepository);
}
},
builder: (context, state) {
changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
return ModalProgressHUD(
child: getPage(),
inAsyncCall: state is CustomerChangeLoading,
opacity: 0.5,
color: Colors.black54,
progressIndicator: CircularProgressIndicator(),
);
},
),
)),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => {
if (!fulldata)
{
changeBloc.add(CustomerSaveFitness()),
}
else
{
changeBloc.add(CustomerSave()),
}
},
backgroundColor: Color(0xffb4f500),
icon: Icon(
CustomIcon.save,
color: Colors.black,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
),
);
}
Widget getPage() {
final double h = 27;
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: h,
),
Wrap(alignment: WrapAlignment.center, children: [
Text(
t("Your Fitness State"),
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w900,
),
)
]),
SizedBox(
height: h,
),
getButton("Beginner", "I am beginner", FitnessState.beginner),
SizedBox(
height: h,
),
getButton("Intermediate", "I am intermediate", FitnessState.intermediate),
SizedBox(
height: h,
),
getButton("Advanced", "I am advanced", FitnessState.advanced),
SizedBox(
height: h,
),
getButton("Professional", "I am professional", FitnessState.professional),
/* Divider(),
Text(
t("Your Primary Sport") + ":",
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.orange,
fontSize: 20,
),
getSport(changeBloc),
Divider(),
), */
//getSport(changeBloc),
/* Divider(),
ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
@@ -240,22 +180,58 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> with Trans {
Navigator.of(context).pop(),
if (!fulldata) {Navigator.of(context).pushNamed("customerBodyTypePage", arguments: customerRepository)}
},
)
],
),
) */
],
),
);
}
TextButton getButton(String title, String desc, String state) {
return TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(changeBloc, state),
),
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
AppLocalizations.of(context)!.translate(title),
style: TextStyle(color: Colors.white, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900),
),
);
})));
highlightColor: Colors.white,
),
InkWell(
child: Text(
AppLocalizations.of(context)!.translate(desc),
style: TextStyle(color: Colors.white, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
),
),
onPressed: () => {
changeBloc.add(CustomerFitnessChange(fitness: state)),
});
}
dynamic getShape(CustomerChangeBloc changeBloc, String fitnessLevel) {
String? selected = changeBloc.selectedFitnessItem;
dynamic returnCode = (selected == fitnessLevel)
? RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.orange),
side: BorderSide(
width: 4,
color: Color(0xffb4f500),
),
borderRadius: BorderRadius.circular(12),
)
: RoundedRectangleBorder(
side: BorderSide(width: 1, color: Colors.blue),
side: BorderSide(width: 4, color: Colors.white24),
borderRadius: BorderRadius.circular(12),
);
//return
return returnCode;
+120 -66
View File
@@ -10,6 +10,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
enum Goals { gain_muscle, weight_loss, endurance, muscle_endurance, flexibility, gain_strength, explosiveness, shape_forming }
@@ -69,18 +70,21 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
CustomerRepository customerRepository;
dynamic args = ModalRoute.of(context)!.settings.arguments;
if (args is HashMap && args['personal_data'] != null) {
fulldata = args['personal_data'];
customerRepository = args['bloc'];
if (args != null) {
if (args is HashMap && args['personal_data'] != null) {
fulldata = args['personal_data'];
customerRepository = args['bloc'];
} else {
customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
}
} else {
customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
customerRepository = CustomerRepository();
}
PreferredSizeWidget _bar = AppBarMin(
back: true,
back: false,
);
if (!fulldata) {
_bar = AppBarProgress(max: 50, min: 26);
_bar = AppBarProgress(max: 14, min: 0);
}
return Scaffold(
@@ -88,7 +92,7 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.jpg'),
image: AssetImage('asset/image/WT_plainblack_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
@@ -97,75 +101,120 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
width: double.infinity,
child: BlocProvider(
create: (context) => CustomerChangeBloc(customerRepository: customerRepository),
child: Builder(builder: (context) {
changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
return SingleChildScrollView(
child: Center(
child: Column(
children: [
Divider(),
Wrap(alignment: WrapAlignment.center, children: [
Text(
t("Set Your Primary Goal"),
maxLines: 2,
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.orange,
fontSize: 30,
shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 3.0,
color: Colors.black87,
),
],
),
),
]),
Divider(),
getItem(changeBloc, Goals.gain_muscle),
Divider(),
getItem(changeBloc, Goals.weight_loss),
Divider(),
getItem(changeBloc, Goals.shape_forming),
Divider(),
getItem(changeBloc, Goals.endurance),
Divider(),
getItem(changeBloc, Goals.gain_strength),
Divider(),
getItem(changeBloc, Goals.muscle_endurance),
Divider(),
getItem(changeBloc, Goals.flexibility),
Divider(),
getItem(changeBloc, Goals.explosiveness),
Divider(),
],
),
));
}),
child: BlocConsumer<CustomerChangeBloc, CustomerChangeState>(
listener: (context, state) {
if (state is CustomerSaveError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
Navigator.of(context).pushNamed("customerFitnessPage", arguments: changeBloc.customerRepository);
}
},
builder: (context, state) {
changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
return ModalProgressHUD(
child: getPage(),
inAsyncCall: state is CustomerChangeLoading,
opacity: 0.5,
color: Colors.black54,
progressIndicator: CircularProgressIndicator(),
);
},
),
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => {
//changingViewModel.saveCustomer(),
changeBloc.add(CustomerSave()),
Navigator.of(context).pop(),
if (!fulldata) {Navigator.of(context).pushNamed("customerFitnessPage", arguments: changeBloc.customerRepository)}
print("Fulldata: $fulldata bloc $changeBloc"),
if (!fulldata)
{
print("Savegoal"),
changeBloc.add(CustomerSaveGoal()),
}
else
{
changeBloc.add(CustomerSave()),
}
},
backgroundColor: Colors.orange[800],
backgroundColor: Color(0xffb4f500),
icon: Icon(
CustomIcon.save,
size: 20,
color: Colors.black,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 12),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
),
);
}
Widget getPage() {
final double h = 27;
return SingleChildScrollView(
child: Center(
child: Column(
children: [
Divider(),
Wrap(alignment: WrapAlignment.center, children: [
Text(
t("Set Your Primary Goal"),
maxLines: 2,
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 30,
shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 3.0,
color: Colors.black87,
),
],
),
),
]),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.gain_muscle),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.weight_loss),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.shape_forming),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.endurance),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.gain_strength),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.muscle_endurance),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.flexibility),
SizedBox(
height: h,
),
getItem(changeBloc, Goals.explosiveness),
SizedBox(
height: h,
),
],
),
));
}
Widget getItem(CustomerChangeBloc changeBloc, Goals goal) {
return Stack(alignment: Alignment.bottomLeft, children: [
TextButton(
@@ -188,7 +237,7 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
child: Text(
t(goal.description(goal)),
style: GoogleFonts.archivoBlack(
color: Colors.yellow[300],
color: Colors.white,
fontSize: 28,
shadows: <Shadow>[
Shadow(
@@ -204,13 +253,18 @@ class _CustomerGoalPage extends State<CustomerGoalPage> with Trans {
}
dynamic getShape(CustomerChangeBloc customerBloc, String goal) {
if (customerBloc.customerRepository.goal == null) return null;
dynamic baseCode = RoundedRectangleBorder(
side: BorderSide(width: 2, color: Colors.white24),
borderRadius: BorderRadius.circular(12),
);
if (customerBloc.customerRepository.goal == null) return baseCode;
String selectedGoal = customerBloc.customerRepository.goal!;
dynamic returnCode = (selectedGoal == goal)
? RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.red),
side: BorderSide(width: 6, color: Color(0xffb4f500)),
borderRadius: BorderRadius.circular(12),
)
: null;
: baseCode;
//return
return returnCode;
}
+178
View File
@@ -0,0 +1,178 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
import 'package:aitrainer_app/widgets/app_bar_progress.dart';
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 'package:syncfusion_flutter_gauges/gauges.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
// ignore: must_be_immutable
class CustomerHeightPage extends StatefulWidget {
late _CustomerHeightPageState _state;
_CustomerHeightPageState createState() {
_state = _CustomerHeightPageState();
return _state;
}
}
class _CustomerHeightPageState extends State<CustomerHeightPage> with Trans {
String? selected;
bool fulldata = false;
late CustomerChangeBloc changeBloc;
late double cWidth;
@override
Widget build(BuildContext context) {
setContext(context);
final CustomerRepository customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
PreferredSizeWidget _bar = AppBarMin(
back: true,
);
if (!fulldata) {
_bar = AppBarProgress(max: 75, min: 60);
}
return Scaffold(
appBar: _bar,
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_plainblack_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
height: double.infinity,
width: double.infinity,
child: BlocProvider(
create: (context) => CustomerChangeBloc(customerRepository: customerRepository),
child: BlocConsumer<CustomerChangeBloc, CustomerChangeState>(
listener: (context, state) {
if (state is CustomerSaveError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("registration", arguments: changeBloc.customerRepository);
}
},
builder: (context, state) {
changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
return getPage();
},
),
)),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => changeBloc.add(CustomerSaveHeight()),
backgroundColor: Color(0xffb4f500),
icon: Icon(
CustomIcon.save,
color: Colors.black,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Finish"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
),
);
}
Widget getPage() {
final double h = 27;
cWidth = MediaQuery.of(context).size.width * 0.75;
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: h,
),
Wrap(alignment: WrapAlignment.center, children: [
Text(
t("What is your height?"),
textAlign: TextAlign.center,
maxLines: 2,
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w900,
),
)
]),
SizedBox(
height: h,
),
getButton(),
],
),
);
}
Widget getButton() {
double mediaWidth = MediaQuery.of(context).size.width * .4;
double mediaHeight = MediaQuery.of(context).size.height * .4;
return Row(children: [
changeBloc.customerRepository.customer!.sex == "m"
? Image.asset(
"asset/image/test_picto_m.png",
height: mediaHeight,
width: mediaWidth,
)
: Image.asset(
"asset/image/test_picto_w.png",
height: mediaHeight,
width: mediaWidth,
),
SfLinearGauge(
minimum: 140,
maximum: 220,
markerPointers: [
LinearWidgetPointer(
value: changeBloc.height,
offset: 55,
position: LinearElementPosition.inside,
markerAlignment: LinearMarkerAlignment.center,
child: Container(
height: 25,
width: 55,
color: Colors.transparent,
child: Text(changeBloc.height.toString(),
style: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xffb4f500),
)),
),
),
LinearShapePointer(
height: 25,
width: 55,
color: Color(0xffb4f500),
value: changeBloc.height,
onValueChanged: (value) => {
changeBloc.add(CustomerHeightChange(height: value.toInt())),
},
),
],
orientation: LinearGaugeOrientation.vertical,
majorTickStyle: LinearTickStyle(length: 20, color: Colors.white),
axisLabelStyle: TextStyle(fontSize: 12.0, color: Colors.white),
axisTrackStyle:
LinearAxisTrackStyle(color: Colors.cyan, edgeStyle: LinearEdgeStyle.bothFlat, thickness: 1.0, borderColor: Colors.white))
]);
}
}
+185
View File
@@ -0,0 +1,185 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
import 'package:aitrainer_app/widgets/app_bar_progress.dart';
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 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
// ignore: must_be_immutable
class CustomerSexPage extends StatefulWidget {
late _CustomerSexPageState _state;
_CustomerSexPageState createState() {
_state = _CustomerSexPageState();
return _state;
}
}
class _CustomerSexPageState extends State<CustomerSexPage> with Trans {
String? selected;
bool fulldata = false;
late CustomerChangeBloc changeBloc;
late double cWidth;
@override
Widget build(BuildContext context) {
setContext(context);
final CustomerRepository customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
PreferredSizeWidget _bar = AppBarMin(
back: true,
);
if (!fulldata) {
_bar = AppBarProgress(max: 45, min: 30);
}
return Scaffold(
appBar: _bar,
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_plainblack_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
height: double.infinity,
width: double.infinity,
child: BlocProvider(
create: (context) => CustomerChangeBloc(customerRepository: customerRepository),
child: BlocConsumer<CustomerChangeBloc, CustomerChangeState>(
listener: (context, state) {
if (state is CustomerSaveError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("customerWeightPage", arguments: changeBloc.customerRepository);
}
},
builder: (context, state) {
changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
return ModalProgressHUD(
child: getPage(),
inAsyncCall: state is CustomerChangeLoading,
opacity: 0.5,
color: Colors.black54,
progressIndicator: CircularProgressIndicator(),
);
},
),
)),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => {
changeBloc.add(CustomerSaveSex()),
},
backgroundColor: Color(0xffb4f500),
icon: Icon(
CustomIcon.save,
color: Colors.black,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
),
);
}
Widget getPage() {
final double h = 27;
cWidth = MediaQuery.of(context).size.width * 0.75;
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: h,
),
Wrap(alignment: WrapAlignment.center, children: [
Text(
t("What is your biological sex?"),
textAlign: TextAlign.center,
maxLines: 2,
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w900,
),
)
]),
SizedBox(
height: h,
),
getButton("Man", "", "m"),
SizedBox(
height: h,
),
getButton("Woman", "", "w"),
],
),
);
}
TextButton getButton(String title, String desc, String state) {
return TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(10.0),
shape: getShape(changeBloc, state),
),
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: ListTile(
leading: Icon(
state == "m" ? Icons.male_outlined : Icons.female,
color: Color(0xffb4f500),
size: 60,
),
title: Text(
AppLocalizations.of(context)!.translate(title),
style: TextStyle(color: Colors.white, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900),
),
)),
],
),
),
onPressed: () => {
print("Sex $state"),
changeBloc.add(CustomerGenderChange(gender: state == "m" ? 0 : 1)),
});
}
dynamic getShape(CustomerChangeBloc changeBloc, String sex) {
String? selected = changeBloc.customerRepository.customer!.sex;
dynamic returnCode = (selected == sex)
? RoundedRectangleBorder(
side: BorderSide(
width: 4,
color: Color(0xffb4f500),
),
borderRadius: BorderRadius.circular(12),
)
: RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.white24),
borderRadius: BorderRadius.circular(12),
);
return returnCode;
}
}
+147
View File
@@ -0,0 +1,147 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
import 'package:aitrainer_app/widgets/app_bar_progress.dart';
import 'package:aitrainer_app/widgets/number_picker.dart';
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 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
// ignore: must_be_immutable
class CustomerWeightPage extends StatefulWidget {
late _CustomerWeightPageState _state;
_CustomerWeightPageState createState() {
_state = _CustomerWeightPageState();
return _state;
}
}
class _CustomerWeightPageState extends State<CustomerWeightPage> with Trans {
String? selected;
bool fulldata = false;
late CustomerChangeBloc changeBloc;
late double cWidth;
@override
Widget build(BuildContext context) {
setContext(context);
final CustomerRepository customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository;
PreferredSizeWidget _bar = AppBarMin(
back: true,
);
if (!fulldata) {
_bar = AppBarProgress(max: 60, min: 45);
}
return Scaffold(
appBar: _bar,
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_plainblack_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
height: double.infinity,
width: double.infinity,
child: BlocProvider(
create: (context) => CustomerChangeBloc(customerRepository: customerRepository),
child: BlocConsumer<CustomerChangeBloc, CustomerChangeState>(
listener: (context, state) {
if (state is CustomerSaveError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is CustomerSaveSuccess) {
Navigator.of(context).pop();
Navigator.of(context).pushNamed("customerHeightPage", arguments: changeBloc.customerRepository);
}
},
builder: (context, state) {
changeBloc = BlocProvider.of<CustomerChangeBloc>(context);
return ModalProgressHUD(
child: getPage(),
inAsyncCall: state is CustomerChangeLoading,
opacity: 0.5,
color: Colors.black54,
progressIndicator: CircularProgressIndicator(),
);
},
),
)),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => {
changeBloc.add(CustomerSaveWeight()),
},
backgroundColor: Color(0xffb4f500),
icon: Icon(
CustomIcon.save,
color: Colors.black,
size: 26,
),
label: Text(
fulldata ? t("Save") : t("Next"),
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
),
);
}
Widget getPage() {
final double h = 27;
cWidth = MediaQuery.of(context).size.width * 0.75;
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: h,
),
Wrap(alignment: WrapAlignment.center, children: [
Text(
t("What is your weight?"),
textAlign: TextAlign.center,
maxLines: 2,
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w900,
),
)
]),
SizedBox(
height: h,
),
getButton(),
],
),
);
}
Widget getButton() {
return NumberPickerWidget(
minValue: 40,
maxValue: 150,
fontSize: 30,
diameterRatio: 1.1,
itemExtent: 40,
fontWeight: FontWeight.w700,
initalValue: changeBloc.weight.toInt(),
unit: t("kg"),
color: Color(0xffb4f500),
onChange: (value) => changeBloc.add(CustomerWeightChange(weight: value)));
}
}
+66 -12
View File
@@ -1,6 +1,10 @@
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/library/button_animations.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
import 'package:flutter/material.dart';
import 'package:flutter_fadein/flutter_fadein.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
// ignore: must_be_immutable
class CustomerWelcomePage extends StatefulWidget {
@@ -12,11 +16,14 @@ class CustomerWelcomePage extends StatefulWidget {
}
}
class _CustomerWelcomePageState extends State<CustomerWelcomePage> {
class _CustomerWelcomePageState extends State<CustomerWelcomePage> with Trans {
@override
Widget build(BuildContext context) {
setContext(context);
return Scaffold(
appBar: AppBarMin(),
appBar: AppBarMin(
back: true,
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
@@ -30,17 +37,64 @@ class _CustomerWelcomePageState extends State<CustomerWelcomePage> {
child: Center(
child: Column(
children: [
Divider(
color: Colors.transparent,
SizedBox(
height: 200,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.orange,
onSurface: Colors.white,
CircularPercentIndicator(
radius: 250.0,
animation: true,
animationDuration: 4800,
lineWidth: 20.0,
percent: 0.98,
curve: Curves.bounceInOut,
backgroundWidth: 4,
center: Text(
t("Training Plan Generation"),
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
fontWeight: FontWeight.bold,
fontSize: 28.0,
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,
),
],
),
),
child: InkWell(child: Text(AppLocalizations.of(context)!.translate("Next"))),
onPressed: () => {Navigator.of(context).pop(), Navigator.of(context).pushNamed("home")},
)
circularStrokeCap: CircularStrokeCap.round,
progressColor: Color(0xffb4f500),
backgroundColor: Colors.black,
),
SizedBox(
height: 90,
),
FadeIn(
child: Container(
width: 160,
height: 80,
child: GestureDetector(
onTap: () => Navigator.of(context).popAndPushNamed("home"),
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_orange_a.png', width: 140, height: 80),
Text(
t("Next"),
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.white),
),
],
),
)),
duration: Duration(seconds: 6),
),
],
),
))),
+1 -1
View File
@@ -502,7 +502,7 @@ class EvaluationPage extends StatelessWidget with Trans {
Divider(color: Colors.transparent),
getSuggestionWidget(resultBloc, "Gain Strength", "asset/image/pict_weight_volumen_tonna.png", "3x4-8", 0.95, "3-5"),
Divider(color: Colors.transparent),
getSuggestionWidget(resultBloc, "Endurance", "asset/image/pict_reps_volumen_db.png", "4x25-35", 0.50, "3"),
getSuggestionWidget(resultBloc, "Muscle Endurance", "asset/image/pict_reps_volumen_db.png", "4x25-35", 0.50, "3"),
],
),
);
+103 -22
View File
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/bloc/login/login_bloc.dart';
import 'package:aitrainer_app/repository/training_plan_repository.dart';
import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
@@ -33,17 +34,18 @@ class RegistrationPage extends StatelessWidget with Trans {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(t(state.message), style: TextStyle(color: Colors.white))));
} else if (state is LoginSuccess) {
TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository();
showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
title: t("Successful Registration"),
descriptions: t("Now we would like to know you better to lift the experience of the app."),
description2: t("Please go through the pages, it will take couple of minutes!"),
descriptions: t("Based on your initial data, we will generate the personalized training plan for you."),
text: "OK",
onTap: () => {Navigator.of(context).pushNamed('customerModifyPage')},
onTap: () => {Navigator.of(context).pushNamed('customerWelcomePage')},
onCancel: () => {
Navigator.of(context).pushNamed("home"),
trainingPlanRepository.generateTrainingPlan(),
Navigator.of(context).pushNamed("customerWelcomePage"),
},
);
});
@@ -83,7 +85,7 @@ class RegistrationPage extends StatelessWidget with Trans {
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_login.jpg'),
image: AssetImage('asset/image/WT_menu_dark.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
@@ -99,17 +101,32 @@ class RegistrationPage extends StatelessWidget with Trans {
child: Container(
padding: const EdgeInsets.only(left: 20, right: 20),
child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 10.0), children: <Widget>[
GestureDetector(
onTap: () => loginBloc.add(LoginSkip()),
child: Text(
t("I Execute My First Test Now"),
textAlign: TextAlign.right,
style: GoogleFonts.inter(color: loginBloc.testColor, decoration: TextDecoration.underline, fontWeight: FontWeight.bold),
)),
SizedBox(
height: 120,
height: 100,
),
ListTile(
title: Text(
t("SignUp"),
style: GoogleFonts.inter(
color: Colors.white,
fontWeight: FontWeight.w500,
shadows: <Shadow>[
Shadow(
offset: Offset(3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
],
),
)),
SizedBox(
height: 20,
),
ListTile(title: Text(t("SignUp"), style: GoogleFonts.inter())),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@@ -140,7 +157,6 @@ class RegistrationPage extends StatelessWidget with Trans {
: Offstage(),
],
),
//ListTile(title: Text(t("OR"), style: GoogleFonts.inter())),
Divider(
color: Colors.transparent,
),
@@ -149,6 +165,10 @@ class RegistrationPage extends StatelessWidget with Trans {
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15),
labelText: t('Email'),
labelStyle: TextStyle(
fontSize: 14,
color: Color(0xffb4f500),
),
fillColor: Colors.white24,
filled: true,
border: OutlineInputBorder(
@@ -165,7 +185,7 @@ class RegistrationPage extends StatelessWidget with Trans {
},
onChanged: (value) => loginBloc.add(LoginEmailChange(email: value)),
keyboardType: TextInputType.emailAddress,
style: new TextStyle(fontSize: 16, color: Colors.indigo),
style: new TextStyle(fontSize: 16, color: Colors.white),
),
Divider(
color: Colors.transparent,
@@ -174,7 +194,10 @@ class RegistrationPage extends StatelessWidget with Trans {
key: LibraryKeys.loginPasswordField,
obscureText: loginBloc.obscure,
decoration: InputDecoration(
labelStyle: TextStyle(fontSize: 14),
labelStyle: TextStyle(
fontSize: 14,
color: Color(0xffb4f500),
),
contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15),
suffixIcon: IconButton(
onPressed: () => {loginBloc.add(LoginPasswordChangeObscure())},
@@ -197,7 +220,7 @@ class RegistrationPage extends StatelessWidget with Trans {
},
onChanged: (value) => loginBloc.add(LoginPasswordChange(password: value)),
keyboardType: TextInputType.visiblePassword,
style: new TextStyle(fontSize: 16, color: Colors.indigo),
style: new TextStyle(fontSize: 16, color: Colors.white),
),
Divider(
color: Colors.transparent,
@@ -214,7 +237,22 @@ class RegistrationPage extends StatelessWidget with Trans {
),
title: Text(
t("With the registration I accept the data policy and the terms of use."),
style: GoogleFonts.inter(color: Colors.indigo),
style: GoogleFonts.inter(
color: Colors.white,
fontWeight: FontWeight.w500,
shadows: <Shadow>[
Shadow(
offset: Offset(3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
],
),
),
),
Row(mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[
@@ -242,7 +280,23 @@ class RegistrationPage extends StatelessWidget with Trans {
InkWell(
child: Text(
t('Login'),
style: GoogleFonts.inter(decoration: TextDecoration.underline),
style: GoogleFonts.inter(
decoration: TextDecoration.underline,
color: Colors.white,
fontWeight: FontWeight.w500,
shadows: <Shadow>[
Shadow(
offset: Offset(3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
],
),
),
onTap: () => Navigator.of(context).pushNamed('login'),
),
@@ -250,7 +304,23 @@ class RegistrationPage extends StatelessWidget with Trans {
InkWell(
child: Text(
t('Terms Of Use'),
style: GoogleFonts.inter(decoration: TextDecoration.underline),
style: GoogleFonts.inter(
decoration: TextDecoration.underline,
color: Colors.white,
fontWeight: FontWeight.w500,
shadows: <Shadow>[
Shadow(
offset: Offset(3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 6.0,
color: Colors.black54,
),
],
),
),
onTap: () => {
showDialog(
@@ -263,7 +333,18 @@ class RegistrationPage extends StatelessWidget with Trans {
InkWell(
child: Text(
t('Privacy'),
style: GoogleFonts.inter(decoration: TextDecoration.underline),
style: GoogleFonts.inter(
decoration: TextDecoration.underline,
color: Colors.white,
fontWeight: FontWeight.w500,
shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 10.0,
color: Colors.black87,
),
],
),
),
onTap: () => {
showDialog(
+28 -3
View File
@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/bloc/settings/settings_bloc.dart';
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
@@ -17,7 +19,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:toggle_switch/toggle_switch.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/services.dart';
import 'dart:async';
// ignore: must_be_immutable
class SettingsPage extends StatelessWidget with Trans {
@@ -81,7 +84,7 @@ class SettingsPage extends StatelessWidget with Trans {
Track().track(TrackingEvent.settings_lang, eventValue: lang)
})),
getServer(settingsBloc),
getTuturialBasic(settingsBloc),
//getTuturialBasic(settingsBloc),
getTermsOfUse(),
getPrivacy(),
getFaq(),
@@ -96,7 +99,7 @@ class SettingsPage extends StatelessWidget with Trans {
title: Container(),
);
}
print("Live: ${Cache().liveServer}");
return ListTile(
leading: Icon(Icons.data_usage_sharp),
subtitle: Text("For Test purpuses select Test-Server. After that please restart the the App"),
@@ -260,4 +263,26 @@ class SettingsPage extends StatelessWidget with Trans {
),
);
}
Future<void> _printAndCopy(String cmd) async {
print(cmd);
await Clipboard.setData(ClipboardData(text: cmd));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Copied to Clipboard')),
);
}
void link1() {
String? cmd;
String cmdSuffix;
if (Platform.isIOS) {
cmd = '/usr/bin/xcrun simctl openurl booted';
} else if (Platform.isAndroid) {
cmd = '\$ANDROID_HOME/platform-tools/adb shell \'am start'
' -a android.intent.action.VIEW'
' -c android.intent.category.BROWSABLE -d';
cmdSuffix = "'";
}
}
}
+4 -1
View File
@@ -128,7 +128,10 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
child: Text(
parentTitle,
maxLines: 2,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
)),
],
),
+649
View File
@@ -0,0 +1,649 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/customer_training_plan_details.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/dialog_html.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:badges/badges.dart';
import 'package:extended_tabs/extended_tabs.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';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
class TrainingPlanExecute extends StatefulWidget {
const TrainingPlanExecute({Key? key}) : super(key: key);
@override
_TrainingPlanExecuteState createState() => _TrainingPlanExecuteState();
}
class _TrainingPlanExecuteState extends State<TrainingPlanExecute> with Trans {
@override
Widget build(BuildContext context) {
final TrainingPlanBloc bloc = BlocProvider.of<TrainingPlanBloc>(context);
bloc.activateDays();
setContext(context);
return Scaffold(
appBar: AppBarNav(depth: 0),
body: Container(
padding: EdgeInsets.all(0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_black_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: BlocConsumer<TrainingPlanBloc, TrainingPlanState>(listener: (context, state) {
if (state is TrainingPlanError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is TrainingPlanDayFinished) {
bloc.celebrating = false;
final HashMap args = HashMap();
args["bloc"] = bloc;
args["day"] = bloc.dayNames[bloc.activeDayIndex];
Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args);
} else if (state is TrainingPlanDayReadyToRestart) {
if (!bloc.celebrating) {
showCupertinoDialog(
useRootNavigator: true,
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text(t("The training is finished")),
content: Column(children: [Divider(), Text(t("Do you want to restart, or select a new Training Plan?"))]),
actions: [
TextButton(
child: Text(t("New Training Plan"), textAlign: TextAlign.center),
onPressed: () => {
Navigator.pop(context),
Navigator.of(context).popAndPushNamed('myTrainingPlans'),
bloc.restarting = false,
}),
TextButton(
child: Text(t("Restart")),
onPressed: () {
bloc.restart();
Navigator.pop(context);
Navigator.of(context).popAndPushNamed('home');
},
)
],
));
}
}
}, builder: (context, state) {
return ModalProgressHUD(
child: ExerciseTabs(bloc: bloc),
inAsyncCall: state is TrainingPlanLoading,
opacity: 0.5,
color: Colors.black54,
progressIndicator: CircularProgressIndicator(),
);
}),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
final HashMap args = HashMap();
args["bloc"] = bloc;
args["day"] = bloc.dayNames[bloc.activeDayIndex];
bloc.getNext() != null
? _ExerciseListState.executeExercise(bloc, bloc.getNext()!, context)
: Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args);
},
backgroundColor: Colors.orange[600], //Color(0xffb4f500),
icon: Icon(
CustomIcon.weight_hanging,
color: Colors.black,
),
label: Text(
t("Next Exercise"),
style: GoogleFonts.inter(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16),
),
),
);
}
}
class ExerciseTabs extends StatefulWidget {
final TrainingPlanBloc bloc;
ExerciseTabs({required this.bloc});
@override
_ExerciseTabs createState() => _ExerciseTabs();
}
class _ExerciseTabs extends State<ExerciseTabs> with TickerProviderStateMixin {
late TabController tabController;
@override
void initState() {
super.initState();
tabController = TabController(length: widget.bloc.dayNames.length, vsync: this);
tabController.animateTo(widget.bloc.activeDayIndex, duration: Duration(milliseconds: 300));
}
@override
void dispose() {
tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return getTabs(widget.bloc);
}
Widget getTabs(TrainingPlanBloc bloc) {
return Column(children: [
Text(
bloc.getMyPlan()!.name!,
style: GoogleFonts.archivoBlack(
fontSize: 14,
color: Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black87,
),
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black87,
),
],
),
),
ExtendedTabBar(
indicator: BoxDecoration(
color: Colors.black87,
border: Border(
bottom: BorderSide(width: 4.0, color: Color(0xffb4f500)),
// top: BorderSide(width: 4.0, color: Colors.blue),
)),
labelPadding: EdgeInsets.only(left: 0, right: 0),
tabs: getTabNames(),
controller: tabController,
onTap: (index) => bloc.activeDayIndex = index,
),
Expanded(
child: ExtendedTabBarView(
children: getExerciseLists(),
controller: tabController,
/// if link is true and current tabbarview over scroll,
/// it will check and scroll ancestor or child tabbarView.
link: true,
/// cache page count
/// default is 0.
/// if cacheExtent is 1, it has two pages in cache
/// null is infinity, it will cache all pages
cacheExtent: 0,
)),
]);
}
List<Tab> getTabNames() {
List<Tab> tabs = [];
final int tabCount = widget.bloc.dayNames.length;
double cWidth = MediaQuery.of(context).size.width;
widget.bloc.dayNames.forEach((element) {
final Widget widget = Container(
//height: 40,
padding: EdgeInsets.only(top: 3, left: 10, right: 10, bottom: 3),
width: (cWidth / tabCount),
color: Colors.white10,
child: RichText(
textScaleFactor: 0.8,
text: TextSpan(
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
children: [
TextSpan(
text: AppLocalizations.of(context)!.translate("Training Day") + ": \n",
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(5.0, 5.0),
blurRadius: 12.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 12.0,
color: Colors.black54,
),
],
)),
TextSpan(
text: element,
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xffb4f500),
shadows: <Shadow>[
Shadow(
offset: Offset(5.0, 5.0),
blurRadius: 12.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 12.0,
color: Colors.black54,
),
],
)),
])));
tabs.add(Tab(child: widget));
});
return tabs;
}
List<Widget> getExerciseLists() {
List<Widget> list = [];
widget.bloc.dayNames.forEach((element) {
list.add(ExerciseList(bloc: widget.bloc, dayName: element));
});
return list;
}
}
class ExerciseList extends StatefulWidget {
final TrainingPlanBloc bloc;
final String dayName;
ExerciseList({required this.bloc, required this.dayName});
@override
_ExerciseListState createState() => _ExerciseListState();
}
class _ExerciseListState extends State<ExerciseList> with Trans {
final scrollController = ScrollController();
double offset = 5;
@override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) {
animate();
});
super.initState();
}
@override
void didUpdateWidget(ExerciseList page) {
super.didUpdateWidget(page);
WidgetsBinding.instance!.addPostFrameCallback((_) {
animate();
});
}
void animate() {
offset = widget.bloc.getOffset();
if (scrollController.hasClients) {
scrollController.animateTo(offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
}
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
static void executeExercise(TrainingPlanBloc bloc, CustomerTrainingPlanDetails detail, BuildContext context) {
CustomerTrainingPlanDetails? next = bloc.getNext();
if (next != null) {
String title = "";
String description = "";
String description2 = "";
if (next.exerciseTypeId != detail.exerciseTypeId) {
title = AppLocalizations.of(context)!.translate("Stop!");
description = AppLocalizations.of(context)!.translate("Please continue with the next exercise in the queue:") +
next.exerciseType!.nameTranslation;
} else {
final HashMap args = HashMap();
args['exerciseType'] = next.exerciseType;
args['customerTrainingPlanDetails'] = detail;
Navigator.of(context).pushNamed('myTrainingPlanExercise', arguments: args);
return;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DialogCommon(
title: title,
descriptions: description,
description2: description2,
text: "OK",
onTap: () => {Navigator.of(context).pop()},
onCancel: () => {Navigator.of(context).pop()},
);
});
} else {
Navigator.of(context).pushNamed('home');
}
}
@override
Widget build(BuildContext context) {
setContext(context);
return CustomScrollView(controller: scrollController, slivers: [
SliverList(delegate: SliverChildListDelegate(getTiles(widget.bloc))),
]);
}
List<Widget> getTiles(TrainingPlanBloc bloc) {
List<Widget> tiles = [];
tiles.addAll(getExerciseTiles(bloc, context));
return tiles;
}
List<Widget> getExerciseTiles(TrainingPlanBloc bloc, BuildContext context) {
List<Widget> tiles = [];
CustomerTrainingPlanDetails? prev;
if (bloc.getMyPlan() != null &&
bloc.getMyPlan()!.details.isNotEmpty &&
bloc.getMyPlan()!.days[widget.dayName] != null &&
bloc.getMyPlan()!.days[widget.dayName]!.isNotEmpty) {
bloc.getMyPlan()!.days[widget.dayName]!.forEach((element) {
if (prev != null && prev!.exerciseTypeId != element.exerciseTypeId) {
tiles.add(GestureDetector(
onTap: () =>
bloc.getNext() != null ? executeExercise(bloc, bloc.getNext()!, context) : Navigator.of(context).pushNamed('home'),
child: ExerciseTile(bloc: bloc, detail: element)));
}
prev = element;
});
}
return tiles;
}
}
// ignore: must_be_immutable
class ExerciseTile extends StatelessWidget with Trans {
final TrainingPlanBloc bloc;
final CustomerTrainingPlanDetails detail;
ExerciseTile({required this.bloc, required this.detail});
Widget getExerciseQuantities(CustomerTrainingPlanDetails detail, int step, bool noFilter) {
bool skipped = detail.state == ExercisePlanDetailState.skipped;
String quantities = "";
String set = "";
List<TextSpan> spans = [];
if (detail.exerciseType!.name == "Warming Up") {
quantities = t("Min. 10 minutes");
spans.add(
TextSpan(text: quantities),
);
} else if (detail.exerciseType!.name == "Stretching") {
quantities = t("Recommended");
spans.add(
TextSpan(text: quantities),
);
} else {
set = detail.set! > 1 ? "${detail.set} " + t("set") : "";
List<CustomerTrainingPlanDetails> details = bloc.getAllDetailsSameExercise(detail);
int index = 0;
bool isWeight = true;
if (set.length > 0) {
spans.add(
TextSpan(
text: step.toString() + " ",
style: GoogleFonts.archivoBlack(
color: Colors.orange[600],
)),
);
spans.add(
TextSpan(text: "/ " + set),
);
}
details.forEach((element) {
quantities = "";
String delimiter = ",";
if (index == 0) {
delimiter = "";
}
if (element.repeats == -1) {
quantities += delimiter + " MAX ";
} else {
quantities += delimiter + " ${element.repeats}";
}
if (element.exerciseType!.unitQuantityUnit != null) {
quantities += "x";
if (element.weight == -1 || element.weight == -2 || element.weight == -3) {
quantities += "? kg";
} else {
num weight = element.weight! % element.weight!.round() == 0 ? element.weight!.round() : element.weight!;
quantities += "$weight kg";
}
} else {
isWeight = false;
}
if (step == index && noFilter) {
spans.add(
TextSpan(
text: quantities,
style: GoogleFonts.archivoBlack(
color: Colors.orange[600],
),
),
);
} else {
spans.add(
TextSpan(text: quantities),
);
}
index++;
});
if (isWeight) {
quantities += " kg";
}
}
return RichText(
text: TextSpan(
style: GoogleFonts.archivoBlack(
fontSize: 20,
color: skipped ? Colors.grey : Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black87,
),
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black87,
),
],
),
children: spans),
);
}
@override
Widget build(BuildContext context) {
setContext(context);
final CustomerTrainingPlanDetails? next = bloc.getNext();
final bool noFilter = next != null && next.exerciseTypeId == detail.exerciseTypeId;
final bool done = detail.state == ExercisePlanDetailState.finished;
final int step = bloc.getStep(detail); //detail.exercises.length;
final bool buddyWarning = detail.exerciseType == null ? false : detail.exerciseType!.buddyWarning;
return Container(
child: Stack(alignment: Alignment.bottomLeft, children: [
Badge(
elevation: 0,
padding: EdgeInsets.all(0),
position: BadgePosition.topEnd(top: 5, end: 5),
animationDuration: Duration(milliseconds: 1500),
animationType: BadgeAnimationType.fade,
badgeColor: Colors.transparent,
showBadge: noFilter || done,
badgeContent: IconButton(
iconSize: 40,
onPressed: () => !done ? skip() : {},
icon: Icon(
done ? CustomIcon.ok_circled : Icons.cancel,
color: done ? Colors.green[600] : Colors.red[600],
)),
child: Badge(
elevation: 0,
padding: EdgeInsets.all(0),
position: BadgePosition.topStart(top: 5, start: 5),
animationDuration: Duration(milliseconds: 500),
animationType: BadgeAnimationType.slide,
badgeColor: Colors.transparent,
showBadge: true,
badgeContent: IconButton(
iconSize: 36,
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) {
return DialogHTML(
title: detail.exerciseType!.nameTranslation,
htmlData: '<p>' + detail.exerciseType!.descriptionTranslation + '</p>');
}),
icon: Icon(
Icons.info_outline,
color: Colors.yellow[200],
)),
child: Badge(
elevation: 0,
padding: EdgeInsets.all(0),
position: BadgePosition.topEnd(top: 65, end: 1),
animationDuration: Duration(milliseconds: 500),
animationType: BadgeAnimationType.fade,
badgeColor: Colors.transparent,
showBadge: buddyWarning,
badgeContent: IconButton(
iconSize: 50,
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
warning: true,
text: "Warning",
descriptions: t("Attention!"),
description2: t("The safe and exact execution of this exercise you need a training buddy or a trainer"),
description3: t("Execution at your own risk!"),
onTap: () => Navigator.of(context).pop(),
onCancel: () => Navigator.of(context).pop(),
title: t('Training Buddy'),
);
}),
icon: Icon(
CustomIcon.exclamation_circle,
color: Colors.red[800],
)),
child: Column(children: [
MenuImage(
imageName: bloc.getActualImageName(detail.exerciseType!.exerciseTypeId),
workoutTreeId: bloc.getActualWorkoutTreeId(detail.exerciseType!.exerciseTypeId)!,
radius: 0,
filter: !noFilter,
),
Container(
padding: EdgeInsets.only(left: 20, top: 10, bottom: 0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_zold.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
foregroundDecoration: !noFilter
? BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
)
: null,
height: 80,
width: double.infinity,
child: getExerciseQuantities(detail, step, noFilter),
)
])))),
Container(
padding: EdgeInsets.only(left: 15, bottom: 80, right: 15),
width: double.infinity,
color: Colors.transparent,
child: Text(
detail.exerciseType!.nameTranslation,
maxLines: 3,
style: GoogleFonts.archivoBlack(
color: noFilter ? Colors.white : Colors.grey,
fontSize: 36,
height: 1.1,
shadows: <Shadow>[
Shadow(
offset: Offset(16.0, 16.0),
blurRadius: 16.0,
color: Colors.black54,
),
Shadow(
offset: Offset(16.0, 16.0),
blurRadius: 16.0,
color: Colors.black54,
),
],
),
),
),
]),
);
}
void skip() {
showCupertinoDialog(
useRootNavigator: true,
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text(t("You want to skip really this exercise?")),
content: Column(children: [
Divider(),
]),
actions: [
TextButton(
child: Text(t("No")),
onPressed: () => {
Navigator.pop(context),
}),
TextButton(
child: Text(t("Yes")),
onPressed: () {
Navigator.pop(context);
bloc.add(TrainingPlanSkipExercise(detail: detail));
},
)
],
));
}
}
+170
View File
@@ -0,0 +1,170 @@
import 'package:aitrainer_app/util/trans.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
// ignore: must_be_immutable
class DialogTrialWidget extends StatefulWidget {
final String title, description;
final Widget widget;
final VoidCallback onTap;
final VoidCallback? onCancel;
DialogTrialWidget({Key? key, required this.title, required this.description, required this.widget, required this.onTap, this.onCancel})
: super(key: key);
@override
_DialogTrialWidgetState createState() {
return _DialogTrialWidgetState();
}
}
class _DialogTrialWidgetState extends State<DialogTrialWidget> with Trans {
@override
Widget build(BuildContext context) {
setContext(context);
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(31),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: contentBox(context),
);
}
contentBox(context) {
return Stack(alignment: AlignmentDirectional.topStart, children: [
Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 20, top: 24, right: 20, bottom: 30),
margin: EdgeInsets.only(top: 30),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [BoxShadow(color: Colors.black, offset: Offset(0, 10), blurRadius: 10)],
image: DecorationImage(
image: AssetImage('asset/image/WT_black_G_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 5,
),
Stack(
alignment: AlignmentDirectional.topEnd,
children: [
Text(
widget.title,
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
fontSize: 20,
color: Colors.yellow[400],
shadows: <Shadow>[
Shadow(
offset: Offset(5.0, 5.0),
blurRadius: 12.0,
color: Colors.black54,
),
Shadow(
offset: Offset(-3.0, 3.0),
blurRadius: 12.0,
color: Colors.black54,
),
],
),
),
],
),
SizedBox(
height: 35,
),
Text(
widget.description,
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.bold,
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,
),
],
),
textAlign: TextAlign.center,
),
SizedBox(
height: 15,
),
widget.widget,
SizedBox(
height: 52,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: widget.onCancel,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_lila_b.png', width: 100, height: 45),
Text(
t("Nem"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
)),
GestureDetector(
onTap: widget.onTap,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45),
Text(
t("Igen"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
))
],
),
],
),
),
],
),
GestureDetector(
onTap: () {
if (widget.onCancel == null) {
Navigator.of(context).pop();
} else {
widget.onCancel!();
}
},
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 28,
child: Text(
"X",
style: GoogleFonts.archivoBlack(fontSize: 32, color: Colors.white54),
),
)),
]);
}
@override
void dispose() {
super.dispose();
}
}
+2 -2
View File
@@ -98,7 +98,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
}
SchedulerBinding.instance!.addPostFrameCallback((_) {
final TutorialBloc bloc = BlocProvider.of<TutorialBloc>(context);
/* //final TutorialBloc bloc = BlocProvider.of<TutorialBloc>(context);
if (bloc.actualCheck == "directTest") {
Timer(
Duration(milliseconds: 2000),
@@ -116,7 +116,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
);
})
});
}
} */
});
}
+13 -2
View File
@@ -4,10 +4,11 @@ import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/view/customer_goal_page.dart';
import 'package:aitrainer_app/view/login.dart';
import 'package:aitrainer_app/view/menu_page.dart';
import 'package:aitrainer_app/view/registration.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
@@ -37,6 +38,15 @@ class _HomePageState extends State<AitrainerHome> with Logging, Trans {
SchedulerBinding.instance!.addPostFrameCallback((_) {
runDelayedEvent();
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('-- FirebaseMessaging: Got a message whilst in the foreground!');
print('-- FirebaseMessaging: Message data: ${message.data}');
if (message.notification != null) {
print('-- FirebaseMessaging: Message also contained a notification: ${message.notification}');
}
});
}
Future runDelayedEvent() async {
@@ -58,6 +68,7 @@ class _HomePageState extends State<AitrainerHome> with Logging, Trans {
final appcastURL = "https://raw.githubusercontent.com/bossanyit/appcast/main/android_rss.xml";
final cfg = AppcastConfiguration(url: appcastURL, supportedOS: ['android']);
print("Packageinfo ${Cache().packageInfo}");
return Scaffold(
key: _scaffoldKey,
body: UpgradeAlert(
@@ -101,7 +112,7 @@ class _HomePageState extends State<AitrainerHome> with Logging, Trans {
if (Cache().startPage == 'login') {
return LoginPage();
} else if (Cache().startPage == 'registration') {
return RegistrationPage();
return CustomerGoalPage();
} else {
return MenuPage(parent: 0);
}
+25 -1
View File
@@ -3,16 +3,19 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:aitrainer_app/library/image_cache.dart' as wt;
import 'package:aitrainer_app/library/transparent_image.dart';
import 'package:flutter_html/shims/dart_ui_real.dart';
// ignore: must_be_immutable
class MenuImage extends StatelessWidget {
final int? workoutTreeId;
final String imageName;
bool filter;
double radius;
MenuImage({
required this.workoutTreeId,
required this.imageName,
this.radius = 24,
this.filter = false,
});
@override
@@ -23,10 +26,16 @@ class MenuImage extends StatelessWidget {
String? imageString = this.getImage(workoutTreeId!, imageName);
Widget? widget;
if (imageString != null) {
print("MemoryImage $workoutTreeId - $imageName");
widget = ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: Colors.transparent,
padding: EdgeInsets.zero,
color: Colors.black38,
foregroundDecoration: BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
),
child: FadeInImage(
fadeInDuration: Duration(milliseconds: 100),
image: MemoryImage(base64Decode(imageString)),
@@ -35,10 +44,18 @@ class MenuImage extends StatelessWidget {
));
} else {
if (imageName.contains("https")) {
print("https image $workoutTreeId - $imageName");
if (!wt.ImageCache().existsImageInMap(workoutTreeId!, imageName)) {
widget = ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Container(
padding: EdgeInsets.zero,
foregroundDecoration: filter
? BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
)
: null,
color: Colors.transparent,
child: FadeInImage(
fadeInDuration: Duration(milliseconds: 500),
@@ -51,7 +68,14 @@ class MenuImage extends StatelessWidget {
widget = ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Container(
padding: EdgeInsets.zero,
color: Colors.transparent,
foregroundDecoration: filter
? BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
)
: null,
child: Image.asset(imageName),
));
}
+107 -39
View File
@@ -1,11 +1,12 @@
import 'dart:collection';
import 'dart:ui';
import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
import 'package:aitrainer_app/model/exercise_ability.dart';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/repository/training_plan_repository.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:aitrainer_app/widgets/dialog_trial.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:aitrainer_app/widgets/menu_search_bar.dart';
import 'package:aitrainer_app/util/app_language.dart';
@@ -15,9 +16,9 @@ import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/tutorial_widget.dart';
import 'package:badges/badges.dart';
import 'package:ezanimation/ezanimation.dart';
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
@@ -39,7 +40,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
final double baseWidth = 312;
final double baseHeight = 675.2;
late MenuBloc menuBloc;
late TutorialBloc tutorialBloc;
//late TutorialBloc tutorialBloc;
final scrollController = ScrollController();
final bool activeExercisePlan = Cache().activeExercisePlan != null;
final EzAnimation animation = EzAnimation(35.0, 10.0, Duration(seconds: 2), reverseCurve: Curves.linear);
@@ -59,41 +60,11 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance!.addPostFrameCallback((_) {
menuBloc.add(MenuCreate());
//runDelayedEvent();
});
super.initState();
}
Future runDelayedEvent() async {
bool isFirst = false;
await Future.delayed(Duration(milliseconds: 600), () async {
if (Cache().userLoggedIn != null) {
if (Cache().userLoggedIn!.sex == "m") {
tutorialBloc.tutorialName = ActivityDone.tutorialBasicChestPress.toStr();
} else {
tutorialBloc.tutorialName = ActivityDone.tutorialBasicLegPress.toStr();
}
}
if (!tutorialBloc.isTutorialDone()) {
if (tutorialBloc.isActive == false && tutorialBloc.canActivate) {
tutorialBloc.canActivate = true;
tutorialBloc.isActive = true;
tutorialBloc.menuBloc = menuBloc;
tutorialBloc.add(TutorialLoad());
tutorialBloc.init();
isFirst = true;
}
}
});
final bool canActivate = tutorialBloc.activateTutorial();
if (canActivate) {
if (!isFirst) {
TutorialWidget().tip(context);
}
}
}
@override
bool didUpdateWidget(MenuPageWidget oldWidget) {
super.didUpdateWidget(oldWidget);
@@ -102,10 +73,68 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
return true;
}
Future runDelayedEvent() async {
await Future.delayed(Duration(milliseconds: 3000), () async {
if (Cache().userLoggedIn != null) {
await initDynamicLinks();
}
if (Cache().canTrial()) {
showDialog(
context: context,
builder: (BuildContext context) {
return DialogTrialWidget(
title: "10 days Premium for free",
description: "Would you like to try all premium functions for 10 days, without any subscription or bank card data?",
widget: Column(children: [
Text(
"If you click to 'Yes', all premium functions will be available right now.",
style: GoogleFonts.inter(color: Colors.white),
),
Divider(),
Text(
"If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.",
style: GoogleFonts.inter(color: Colors.white),
),
]),
onCancel: () => {
Navigator.of(context).pop(),
menuBloc.add(MenuStartTrial(start: DateTime.parse("1900-01-01 00:00:00"))),
},
onTap: () => {Navigator.of(context).pop(), menuBloc.add(MenuStartTrial(start: DateTime.now()))},
);
});
}
});
}
Future<void> initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(onSuccess: (PendingDynamicLinkData? dynamicLink) async {
final Uri? deepLink = dynamicLink?.link;
print("DeepLink: $deepLink");
if (deepLink != null) {
// ignore: unawaited_futures
final String deepLinkPath = deepLink.path.replaceFirst("/", "");
Navigator.pushNamed(context, deepLinkPath);
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
final PendingDynamicLinkData? data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri? deepLink = data?.link;
print("Pending DeepLink: $deepLink");
if (deepLink != null) {
// ignore: unawaited_futures
final String deepLinkPath = deepLink.path.replaceFirst("/", "");
Navigator.pushNamed(context, deepLinkPath);
}
}
@override
Widget build(BuildContext context) {
menuBloc = BlocProvider.of<MenuBloc>(context);
tutorialBloc = BlocProvider.of<TutorialBloc>(context);
//tutorialBloc = BlocProvider.of<TutorialBloc>(context);
setContext(context);
double cWidth = MediaQuery.of(context).size.width;
double cHeight = MediaQuery.of(context).size.height;
@@ -181,6 +210,10 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
});
}
_columnChildren.add(SizedBox(
height: 50,
));
SliverList sliverList = SliverList(
delegate: SliverChildListDelegate(_columnChildren),
);
@@ -292,7 +325,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
}
},
),
Cache().myTrainingPlan != null
/* Cache().myTrainingPlan != null
? GestureDetector(
onTap: () => showDialog(
context: context,
@@ -326,7 +359,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
),
);
}))
: Offstage(),
: Offstage(), */
/* activeExercisePlan
? SizedBox(
width: 10,
@@ -376,24 +409,59 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
}
void menuClick(WorkoutMenuTree workoutTree, MenuBloc menuBloc) {
if (tutorialBloc.isActive) {
/* if (tutorialBloc.isActive) {
final String checkText = workoutTree.nameEnglish;
if (!tutorialBloc.checkAction(checkText)) {
return;
}
}
} */
print("ability: ${menuBloc.ability} tree: $workoutTree parent: ${workoutTree.parent}");
if (workoutTree.child == false) {
if (menuBloc.ability != null && ExerciseAbility.mini_test_set.equalsTo(menuBloc.ability!) && workoutTree.parent != 0) {
HashMap args = HashMap();
args['templateName'] = workoutTree.nameEnglish;
args['templateNameTranslation'] = workoutTree.name;
Navigator.of(context).pushNamed('testSetEdit', arguments: args);
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
} else if (menuBloc.ability != null && ExerciseAbility.training.equalsTo(menuBloc.ability!) && workoutTree.parent != 0) {
HashMap<String, dynamic> args = HashMap();
args['parentName'] = workoutTree.internalName;
Navigator.of(context).pushNamed("myTrainingPlanActivate", arguments: args);
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
} else if (workoutTree.internalName == "training_execute") {
/* Cache().myTrainingPlan = null;
Cache().deleteMyTrainingPlan(); */
if (Cache().myTrainingPlan != null) {
final TrainingPlanBloc bloc = BlocProvider.of<TrainingPlanBloc>(context);
bloc.setMyPlan(Cache().myTrainingPlan);
Navigator.of(context).pushNamed("myTrainingPlanExecute");
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
title: t("No selected Training Plan"),
descriptions: t("Based on your initial data, we will generate the personalized training plan for you."),
text: "OK",
onTap: () {
TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository();
trainingPlanRepository.generateTrainingPlan();
final TrainingPlanBloc bloc = BlocProvider.of<TrainingPlanBloc>(context);
bloc.setMyPlan(Cache().myTrainingPlan);
Future.delayed(Duration(milliseconds: 1000), () async {
Navigator.of(context).pushNamed("myTrainingPlanExecute");
});
},
onCancel: () => {
Navigator.of(context).pop(),
},
);
});
}
} else {
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
}
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
} else {
menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id));
+14 -3
View File
@@ -11,6 +11,9 @@ class NumberPickerWidget extends StatefulWidget {
final String unit;
final Color color;
double? fontSize;
double? diameterRatio;
double? itemExtent;
FontWeight? fontWeight;
NumberPickerWidget(
{Key? key,
@@ -19,10 +22,16 @@ class NumberPickerWidget extends StatefulWidget {
required this.initalValue,
required this.unit,
this.fontSize,
this.diameterRatio,
this.itemExtent,
this.fontWeight,
required this.color,
required this.onChange})
: super(key: key) {
fontSize = fontSize ?? 20;
diameterRatio = diameterRatio ?? 0.85;
itemExtent = itemExtent ?? 30;
fontWeight = fontWeight ?? FontWeight.normal;
}
@override
_NumberPickerWidgetState createState() => _NumberPickerWidgetState();
@@ -59,7 +68,7 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
scrollController: _scrollController,
useMagnifier: true,
magnification: 1.2,
diameterRatio: 0.85,
diameterRatio: widget.diameterRatio!,
backgroundColor: Colors.transparent,
selectionOverlay: Container(),
onSelectedItemChanged: (x) {
@@ -72,8 +81,10 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
widget.onChange(value);
},
children: List.generate(
widget.maxValue, (index) => Text('$index ' + widget.unit, style: TextStyle(color: widget.color, fontSize: widget.fontSize))),
itemExtent: 30,
widget.maxValue,
(index) => Text('$index ' + widget.unit,
style: TextStyle(color: widget.color, fontSize: widget.fontSize, fontWeight: widget.fontWeight))),
itemExtent: widget.itemExtent!,
);
}