wt1.1.2 new treeview, bar chart for muscle development

This commit is contained in:
Bossanyi Tibor 2020-09-27 18:42:28 +02:00
parent 4a193e085f
commit 89c06123d2
43 changed files with 2135 additions and 514 deletions

View File

@ -1,5 +1,5 @@
{
"Customers And Exercises": "Customers And Exercises",
"Network Error, please try again later": "Network Error, please try again later",
"Home": "Home",
"Customers": "Customers",
"Exercises": "Exercises",
@ -66,7 +66,9 @@
"Do you save this exercise with these parameters?":"Do you save this exercise with these parameters?",
"The number of the exercise": "The number of the exercise",
"The number of the exercise done with": "The number of the exercise done with",
"Please repeat with ": "Please repeat with ",
"Please repeat with": "Please repeat with",
"Execute the": "Execute the",
". set!":". set!",
"repeat": "repeat",
@ -109,6 +111,7 @@
"Description": "Description",
"Make your first test": "Make your first test",
"finished": "finished",
"Why do you need Exercise Control?" : "Why do you need Exercise Control?",
"Your 1RM:":"Your 1RM:",
@ -149,5 +152,22 @@
"My Whole Body Development": "My Whole Body Development",
"Development Of Muscles": "Development Of Muscles",
"Predictions": "Predictions",
"My Trainee's Exercise Logs": "My Trainee's Exercise Logs"
"My Trainee's Exercise Logs": "My Trainee's Exercise Logs",
"My Development By Muscle": "My Development By Muscle",
"Here you see you development in the last period." : "Here you see your development in the last period.",
"Sum Of Mass":"Sum Of Mass",
"Percent": "Percent",
"One Max Rep": "One Max Rep",
"Detailed": "Detailed",
"Weekly": "Weekly",
"Monthly": "Monthly",
"Yearly": "Yearly",
"times!": "times!",
"Execute your active Exercise Plan!": "Execute your active Exercise Plan!",
"Select the muscle type and tap on the exercise. One the next page enter the weight and repeat.": "Select the muscle type and tap on the exercise. One the next page enter the weight and repeat.",
"Custom Exercise Plan": "Custom Exercise Plan",
"Select manually the exercises what you would like to have in your plan. At the end don't forget to save.": "Select manually the exercises what you would like to have in your plan. At the end don't forget to save."
}

View File

@ -1,5 +1,5 @@
{
"Customers And Exercises": "Ügyfelek és gyakorlatok",
"Network Error, please try again later": "Hálózati hiba, kérlek próbáld meg később",
"Home": "Főoldal",
"Customers": "Ügyfelek",
"Exercises": "Gyakorlatok",
@ -55,8 +55,9 @@
"Delete": "Törlés",
"The number of the exercise": "Írd be a gyakorlat számát",
"The number of the exercise done with": "Írd be, mennyivel csináltad a gyakorlatot",
"Please repeat with ": "Kérlek ismételd",
"Please repeat with": "Kérlek ismételd",
"Execute the": "Hajtsd végre a(z)",
". set!":". sorozatot!",
"Name": "Név",
"Exercise": "Gyakorlat",
@ -69,6 +70,7 @@
"with": "",
"Do you save this exercise with these parameters?":"Elmented a gyakorlatot ezekkel az adatokkal?",
"repeat": "ismétlés",
"meter": "meter",
"percent": "százalék",
@ -109,6 +111,7 @@
"Description": "Leírás",
"Make your first test": "Végezd el az első tesztet",
"finished": "végrehajtva",
"Why do you need Exercise Control?": "Miért szükséges a kontrollgyakorlat?",
"Your 1RM:":"Maxerőd:",
"Your Real 1RM:":"Ellenőrzött maxerő:",
@ -149,5 +152,22 @@
"My Whole Body Development": "Testem fejlődése",
"Development Of Muscles": "Izomcsoportok fejlődése",
"Predictions": "Előrejelzések",
"My Trainee's Exercise Logs": "Kliensem edzésnaplőja"
"My Trainee's Exercise Logs": "Kliensem edzésnaplőja",
"My Development By Muscle": "Izomcsoportok fejlődése",
"Here you see you development in the last period." : "Itt az izomcsoportok fejlődését látod az elmúlt időszakban. A pontos képhez három diagram közül választhatsz: 'Gyakorlat össztömeg', 'Maxerő' és 'Százalékos fejlődés', illetv választhatsz 4 különböző időszaki bontásból is: 'Részletes', 'Heti', 'Havi', 'Éves",
"Sum Of Mass":"Össztömeg",
"Percent": "Százalék",
"One Max Rep": "Maxerő",
"Detailed": "Részletes",
"Weekly": "Heti",
"Monthly": "Havi",
"Yearly": "Éves",
"times!": "ismétléssel!",
"Execute your active Exercise Plan!": "Hajtsd végre az aktív edzéstervedet",
"Select the muscle type and tap on the exercise. One the next page enter the weight and repeat.": "Válaszd ki az izomcsoporton belül a gyakorlatot, és a következő oldalon add meg a súlyt és az ismétlés számot.",
"Custom Exercise Plan": "Egyedi edzésterv",
"Select manually the exercises what you would like to have in your plan. At the end don't forget to save.": "Válaszd ki a gyakorlatokat, amelyeket szeretnél végrehajtani a tervedben. Utána ne felejtsd el elmenteni."
}

View File

@ -0,0 +1,292 @@
import 'dart:async';
import 'dart:collection';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:aitrainer_app/util/calculate.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:meta/meta.dart';
import 'package:fl_chart/fl_chart.dart';
part 'development_by_muscle_event.dart';
part 'development_by_muscle_state.dart';
class DateRate {
static String daily = "daily";
static String weekly = "weekly";
static String monthly = "monthly";
static String yearly = "yearly";
}
class DiagramType {
static String sumMass = "sumMass";
static String oneRepMax = "oneRepMax";
static String percent = "percent";
}
class ChartDataExtended {
final List<BarChartGroupData> data;
final double interval;
final int gridInterval;
const ChartDataExtended({this.data, this.interval, this.gridInterval});
Map<String, dynamic> toJson() =>
{
"data": data.toString(),
"interval" : interval.toString(),
"gridInterval" : gridInterval,
};
}
class DevelopmentByMuscleBloc extends Bloc<DevelopmentByMuscleEvent, DevelopmentByMuscleState> with Calculate, Common {
final WorkoutTreeRepository workoutTreeRepository;
final ExerciseRepository exerciseRepository = ExerciseRepository();
LinkedHashMap<int, ChartDataExtended> listChartData = LinkedHashMap();
List<BarChartGroupData> chartData;
String diagramType = DiagramType.sumMass;
String dateRate = DateRate.daily;
double basePercent = 0;
@override
DevelopmentByMuscleBloc({
this.workoutTreeRepository}) :
super(DevelopmentByMuscleStateInitial());
Future<void> getData() async {
workoutTreeRepository.sortedTree = null;
workoutTreeRepository.sortByMuscleType();
workoutTreeRepository.sortedTree.forEach((key, value) {
List<WorkoutMenuTree> listWorkoutTree = value;
listWorkoutTree.forEach((workoutTree) {
workoutTree.selected = false;
});
});
this.getChartData();
}
void getChartData() {
List<Exercise> exercises = exerciseRepository.getExerciseList();
exercises.sort( (a, b) => a.exerciseTypeId.compareTo(b.exerciseTypeId));
exercises = this.groupByDate(exercises);
int origExerciseTypeId = 0;
int x = 0;
chartData = List();
double maxData = 0;
double minData = 9999999999999;
exercises.forEach((exercise) {
if ( exercise.unitQuantity != null ) {
if (origExerciseTypeId != exercise.exerciseTypeId && diagramType == DiagramType.percent) {
this.basePercent = getBasePercent(exercise);
}
double diagramValue = this.getDiagramValue(exercise);
if (maxData < diagramValue) {
maxData = diagramValue;
}
if (minData > diagramValue) {
minData = diagramValue;
}
//print("exercise " + exercise.exerciseTypeId.toString() + " d " + getDateFormat(exercise.dateAdd) + " mass " + sumMass.toString());
//print("date " + exercise.dateAdd.toIso8601String() + " epoch " + exercise.dateAdd.millisecondsSinceEpoch.toString() );
BarChartGroupData data = BarChartGroupData(
x: exercise.dateAdd.millisecondsSinceEpoch,
barRods: [
BarChartRodData(y: diagramValue, width: 12, color: Colors.lightBlue)
]
);
if (origExerciseTypeId != exercise.exerciseTypeId) {
if (origExerciseTypeId == 0) {
origExerciseTypeId = exercise.exerciseTypeId;
chartData.add(data);
} else {
double dInterval = ((maxData + minData) / 8).roundToDouble();
int gridInterval = (dInterval / 3).floor();
ChartDataExtended extended = ChartDataExtended(
data: chartData, interval: dInterval, gridInterval: gridInterval);
listChartData[origExerciseTypeId] = extended;
maxData = 0;
minData = 9999999999999;
chartData = List();
chartData.add(data);
origExerciseTypeId = exercise.exerciseTypeId;
}
} else {
origExerciseTypeId = exercise.exerciseTypeId;
chartData.add(data);
}
}
});
listChartData.forEach((key, value) {
print ("typeid " + key.toString() + " chardata " + value.toJson().toString());
});
return;
}
String getDatePart(DateTime date) {
String datePart = DateFormat('MM.dd', AppLanguage().appLocal.toString()).format(date);;
if ( this.dateRate == DateRate.weekly ) {
datePart = weekNumber(date).toString();
} else if ( this.dateRate == DateRate.monthly ) {
datePart = DateFormat('MMM', AppLanguage().appLocal.toString()).format(date);
} else if ( this.dateRate == DateRate.yearly ) {
datePart = DateFormat('y', AppLanguage().appLocal.toString()).format(date);
} else if ( this.dateRate == DateRate.daily ) {
datePart = DateFormat('MM.dd', AppLanguage().appLocal.toString()).format(date);
}
return datePart;
}
List<Exercise> groupByDate(List<Exercise> exercises) {
List<Exercise> groupedExercises = List();
String origDatePart;
int countExercises = 0;
double sumQuantity = 0;
double maxQuantity = 0;
Exercise origExercise;
exercises.forEach((exercise) {
String exerciseDatePart = getDatePart(exercise.dateAdd);
if ( origExercise != null ) {
if ( origExercise.exerciseTypeId != exercise.exerciseTypeId || origDatePart != exerciseDatePart ) {
Exercise newExercise = origExercise.copy();
newExercise.datePart = origDatePart;
if (this.diagramType == DiagramType.oneRepMax || this.diagramType == DiagramType.percent) {
newExercise.quantity = maxQuantity;
} else {
newExercise.quantity = sumQuantity / countExercises;
}
groupedExercises.add(newExercise);
countExercises = 0;
sumQuantity = 0;
maxQuantity = 0;
}
}
origExercise = exercise;
origDatePart = exerciseDatePart;
double newQuantity = getQuantityByDate(exercise);
sumQuantity += newQuantity;
if (maxQuantity < newQuantity) {
maxQuantity = newQuantity;
}
countExercises++;
});
groupedExercises.forEach((element) {
print("grouped " + element.toJson().toString());
});
return groupedExercises;
}
double getQuantityByDate(Exercise exercise) {
double sum = 0;
if ( this.diagramType == DiagramType.sumMass ) {
if ( exercise.unitQuantity != null ) {
sum = exercise.quantity * exercise.unitQuantity;
} else {
sum = exercise.quantity;
}
} else if ( this.diagramType == DiagramType.oneRepMax || this.diagramType == DiagramType.percent ) {
if ( exercise.unitQuantity != null ) {
sum = calculate1RM(exercise.quantity, exercise.unitQuantity);
} else {
sum = exercise.quantity;
}
}
return sum;
}
double getBasePercent(Exercise exercise) {
if ( exercise.unitQuantity != null ) {
this.basePercent = calculate1RM(exercise.quantity, exercise.unitQuantity);
} else {
this.basePercent = exercise.quantity;
}
//print("Base percent of " + exercise.exerciseTypeId.toString() + ": " + basePercent.toString());
return this.basePercent;
}
double getDiagramValue(Exercise exercise) {
double value = 0;
/*if ( diagramType == DiagramType.sumMass) {
value = exercise.quantity;
if ( exercise.unitQuantity != null ) {
value *= exercise.unitQuantity;
}
} else if ( diagramType == DiagramType.oneRepMax) {
if ( exercise.unitQuantity != null ) {
value = calculate1RM(exercise.quantity, exercise.unitQuantity);
}
} else if ( diagramType == DiagramType.percent) {
if ( exercise.unitQuantity != null ) {
double oneRepMax = calculate1RM(exercise.quantity, exercise.unitQuantity);
value = (oneRepMax / this.basePercent) * 100;
//print("Percent of " + exercise.exerciseTypeId.toString() + ": " + value.toString() + " date " + exercise.dateAdd.toIso8601String() + " 1RM " + oneRepMax.toString());
} else {
value = (exercise.quantity / this.basePercent ) * 100;
}*/
if ( diagramType == DiagramType.percent) {
if ( exercise.unitQuantity != null ) {
double oneRepMax = calculate1RM(exercise.quantity, exercise.unitQuantity);
value = (oneRepMax / this.basePercent) * 100;
//print("Percent of " + exercise.exerciseTypeId.toString() + ": " + value.toString() + " date " + exercise.dateAdd.toIso8601String() + " 1RM " + oneRepMax.toString());
} else {
value = (exercise.quantity / this.basePercent ) * 100;
}
} else {
return exercise.quantity;
}
return value;
}
String getDateFormat(DateTime datetime) {
return DateFormat('yMd', AppLanguage().appLocal.toString()).format(datetime);
}
@override
Stream<DevelopmentByMuscleState> mapEventToState(DevelopmentByMuscleEvent event) async* {
try {
if (event is DevelopmentByMuscleLoad) {
yield DevelopmentByMuscleLoadingState();
await getData();
yield DevelopmentByMuscleReadyState();
} else if ( event is DevelopmentByMuscleDiagramTypeChange ) {
yield DevelopmentByMuscleLoadingState();
String type = event.diagramType;
this.diagramType = type;
getChartData();
yield DevelopmentByMuscleReadyState();
} else if ( event is DevelopmentByMuscleDateRateChange ) {
yield DevelopmentByMuscleLoadingState();
String dateRate = event.dateRate;
this.dateRate = dateRate;
getChartData();
yield DevelopmentByMuscleReadyState();
}
} on Exception catch (e) {
yield DevelopmentByMuscleErrorState(message: e.toString());
}
}
}

View File

@ -0,0 +1,29 @@
part of 'development_by_muscle_bloc.dart';
@immutable
abstract class DevelopmentByMuscleEvent extends Equatable {
const DevelopmentByMuscleEvent();
@override
List<Object> get props => [];
}
class DevelopmentByMuscleLoad extends DevelopmentByMuscleEvent {
const DevelopmentByMuscleLoad();
}
class DevelopmentByMuscleDateRateChange extends DevelopmentByMuscleEvent {
final String dateRate;
const DevelopmentByMuscleDateRateChange({this.dateRate});
@override
List<Object> get props => [dateRate];
}
class DevelopmentByMuscleDiagramTypeChange extends DevelopmentByMuscleEvent {
final String diagramType;
const DevelopmentByMuscleDiagramTypeChange({this.diagramType});
@override
List<Object> get props => [diagramType];
}

View File

@ -0,0 +1,30 @@
part of 'development_by_muscle_bloc.dart';
@immutable
abstract class DevelopmentByMuscleState extends Equatable{
const DevelopmentByMuscleState();
@override
List<Object> get props => [];
}
class DevelopmentByMuscleStateInitial extends DevelopmentByMuscleState {
const DevelopmentByMuscleStateInitial();
}
class DevelopmentByMuscleLoadingState extends DevelopmentByMuscleState {
const DevelopmentByMuscleLoadingState();
}
class DevelopmentByMuscleReadyState extends DevelopmentByMuscleState {
const DevelopmentByMuscleReadyState();
}
class DevelopmentByMuscleErrorState extends DevelopmentByMuscleState {
final String message;
const DevelopmentByMuscleErrorState({this.message});
@override
List<Object> get props => [message];
}

View File

@ -1,6 +1,6 @@
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
@ -8,7 +8,7 @@ import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class ExerciseAddByPlanFormBloc extends FormBloc<String, String> {
final ExerciseRepository exerciseRepository;
final ExercisePlanRepository exercisePlanRepository;
final WorkoutTree workoutTree;
final WorkoutMenuTree workoutTree;
final customerId;
Customer customer;
int step = 1;
@ -40,12 +40,12 @@ class ExerciseAddByPlanFormBloc extends FormBloc<String, String> {
} else if ( Cache().getTrainee().customerId == customerId) {
customer = Cache().getTrainee();
}
exercisePlanRepository.setActualPlanDetail(workoutTree.exerciseType);
exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType);
exerciseRepository.customer = customer;
countSteps = exercisePlanRepository.actualPlanDetail.serie;
countSteps = exercisePlanRepository.getActualPlanDetail().serie;
unitQuantity1Field.updateInitialValue(exercisePlanRepository.actualPlanDetail.weightEquation);
quantity1Field.updateInitialValue(exercisePlanRepository.actualPlanDetail.repeats.toString());
unitQuantity1Field.updateInitialValue(exercisePlanRepository.getActualPlanDetail().weightEquation);
quantity1Field.updateInitialValue(exercisePlanRepository.getActualPlanDetail().repeats.toString());
}
@ -59,7 +59,7 @@ class ExerciseAddByPlanFormBloc extends FormBloc<String, String> {
exerciseRepository.setUnitQuantity(unitQuantity1Field.valueToDouble);
exerciseRepository.setQuantity(quantity1Field.valueToDouble);
exerciseRepository.exercise.exercisePlanDetailId =
exercisePlanRepository.actualPlanDetail.exercisePlanDetailId;
exercisePlanRepository.getActualPlanDetail().exercisePlanDetailId;
exerciseRepository.exercise.unit = workoutTree.exerciseType.unit;
workoutTree.executed = true;
print("On Submitting Add Exercise By Plan " + exerciseRepository.exercise.toJson().toString());

View File

@ -1,8 +1,7 @@
import 'dart:async';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
@ -13,12 +12,11 @@ part 'exercise_by_plan_event.dart';
part 'exercise_by_plan_state.dart';
class ExerciseByPlanBloc extends Bloc<ExerciseByPlanEvent, ExerciseByPlanState> {
final ExerciseRepository exerciseRepository;
final WorkoutTreeRepository menuTreeRepository;
final ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository();
int customerId;
@override
ExerciseByPlanBloc({this.exerciseRepository, this.menuTreeRepository}) : super(ExerciseByPlanStateInitial());
ExerciseByPlanBloc({this.menuTreeRepository}) : super(ExerciseByPlanStateInitial());
Future<void> getData() async {
exercisePlanRepository.setCustomerId(customerId);
@ -28,11 +26,11 @@ class ExerciseByPlanBloc extends Bloc<ExerciseByPlanEvent, ExerciseByPlanState>
menuTreeRepository.sortByMuscleType();
menuTreeRepository.sortedTree.forEach((key, value) {
List<WorkoutTree> listWorkoutTree = value;
List<WorkoutMenuTree> listWorkoutTree = value;
listWorkoutTree.forEach((workoutTree) {
workoutTree.selected = false;
if (exercisePlanRepository.exercisePlanDetails.length > 0) {
if (exercisePlanRepository.exercisePlanDetails[workoutTree.exerciseTypeId] != null) {
if (exercisePlanRepository.getExercisePlanDetailSize() > 0) {
if (exercisePlanRepository.getExercisePlanDetailByExerciseId(workoutTree.exerciseTypeId) != null) {
workoutTree.selected = true;
}
}

View File

@ -1,9 +1,7 @@
import 'dart:async';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
@ -15,11 +13,10 @@ part 'exercise_plan_state.dart';
class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
final WorkoutTreeRepository menuTreeRepository;
final ExerciseRepository exerciseRepository;
final ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository();
ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository();
int customerId;
ExercisePlanBloc({this.exerciseRepository, this.menuTreeRepository}) : super(ExercisePlanInitial());
ExercisePlanBloc({this.menuTreeRepository}) : super(ExercisePlanInitial());
Future<void> getData() async {
exercisePlanRepository.setCustomerId(customerId);
@ -29,12 +26,11 @@ class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
menuTreeRepository.sortByMuscleType();
menuTreeRepository.sortedTree.forEach((key, value) {
List<WorkoutTree> listWorkoutTree = value;
List<WorkoutMenuTree> listWorkoutTree = value;
listWorkoutTree.forEach((workoutTree) {
workoutTree.selected = false;
if (exercisePlanRepository.exercisePlanDetails.length > 0) {
if (exercisePlanRepository.exercisePlanDetails[workoutTree.exerciseTypeId] != null) {
//print("bingo");
if (exercisePlanRepository.getExercisePlanDetailSize() > 0) {
if (exercisePlanRepository.getExercisePlanDetailByExerciseId(workoutTree.exerciseTypeId) != null) {
workoutTree.selected = true;
}
}
@ -42,6 +38,8 @@ class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
});
}
void setExercisePlanRepository(ExercisePlanRepository repo ) => exercisePlanRepository = repo;
@override
Stream<ExercisePlanState> mapEventToState(ExercisePlanEvent event) async* {
try {
@ -50,38 +48,62 @@ class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
await this.getData();
yield ExercisePlanReady();
}
if (event is ExercisePlanUpdate) {
if (event is ExercisePlanUpdateUI) {
yield ExercisePlanLoading();
WorkoutTree workoutTree = event.workoutTree;
WorkoutMenuTree workoutTree = event.workoutTree;
if (workoutTree != null) {
exercisePlanRepository.addExerciseTypeToPlan(workoutTree.exerciseType);
exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType);
workoutTree.selected = true;
}
yield ExercisePlanReady();
} else if (event is ExercisePlanRemoveExercise) {
}
else if (event is ExercisePlanAddExercise) {
yield ExercisePlanLoading();
ExercisePlanDetail planDetail = event.exercisePlanDetail;
exercisePlanRepository.removeExerciseTypeFromPlan(planDetail.exerciseType);
exercisePlanRepository.actualPlanDetail = planDetail;
/*if ( exercisePlanRepository.exercisePlanDetails[planDetail.exerciseTypeId] == null ) {
print("Add plan detail " + planDetail.toJson().toString());
exercisePlanRepository.actualPlanDetail.change = ExercisePlanDetailChange.add;
} else {
print("Update plan detail " + planDetail.toJson().toString());
exercisePlanRepository.actualPlanDetail.change = ExercisePlanDetailChange.update;
}
exercisePlanRepository.addDetailToPlan();*/
if (exercisePlanRepository.getExercisePlanDetailSize() != 0) {
await exercisePlanRepository.saveExercisePlan();
}
yield ExercisePlanReady();
}
else if (event is ExercisePlanRemoveExercise) {
yield ExercisePlanLoading();
ExercisePlanDetail planDetail = event.exercisePlanDetail;
exercisePlanRepository.removeExerciseTypeFromPlanByExerciseTypeId(planDetail.exerciseTypeId);
this.menuTreeRepository.sortedTree.forEach((key, value) {
List<WorkoutTree> listTreeItem = value;
List<WorkoutMenuTree> listTreeItem = value;
listTreeItem.forEach((element) {
if ( element.exerciseType.exerciseTypeId == planDetail.exerciseTypeId) {
element.selected = false;
}
});
});
yield ExercisePlanReady();
} else if (event is ExercisePlanUpdateExercise) {
yield ExercisePlanReady();
} else if (event is ExercisePlanSave) {
if (exercisePlanRepository.getExercisePlanDetailSize() == 0) {
throw Exception("Please select an exercise");
} else {
yield ExercisePlanLoading();
if (exercisePlanRepository.getExercisePlanDetailSize() != 0) {
exercisePlanRepository.saveExercisePlan();
yield ExercisePlanReady();
}
yield ExercisePlanReady();
}
} on Exception catch (e) {
yield ExercisePlanError(message: e.toString());
}

View File

@ -11,21 +11,16 @@ class ExercisePlanLoad extends ExercisePlanEvent {
const ExercisePlanLoad();
}
class ExercisePlanUpdate extends ExercisePlanEvent {
final WorkoutTree workoutTree;
const ExercisePlanUpdate({this.workoutTree});
// update UI
class ExercisePlanUpdateUI extends ExercisePlanEvent {
final WorkoutMenuTree workoutTree;
const ExercisePlanUpdateUI({this.workoutTree});
@override
List<Object> get props => [workoutTree];
}
class ExercisePlanAddExercise extends ExercisePlanEvent {
final ExerciseType exerciseType;
const ExercisePlanAddExercise({this.exerciseType});
@override
List<Object> get props => [exerciseType];
}
class ExercisePlanRemoveExercise extends ExercisePlanEvent {
final ExercisePlanDetail exercisePlanDetail;
@ -35,15 +30,10 @@ class ExercisePlanRemoveExercise extends ExercisePlanEvent {
List<Object> get props => [exercisePlanDetail];
}
class ExercisePlanUpdateExercise extends ExercisePlanEvent {
class ExercisePlanAddExercise extends ExercisePlanEvent {
final ExercisePlanDetail exercisePlanDetail;
const ExercisePlanUpdateExercise({this.exercisePlanDetail});
const ExercisePlanAddExercise({this.exercisePlanDetail});
@override
List<Object> get props => [exercisePlanDetail];
}
class ExercisePlanSave extends ExercisePlanEvent {
const ExercisePlanSave();
}
}

View File

@ -1,4 +1,5 @@
import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
@ -36,29 +37,29 @@ class ExercisePlanCustomerFormBloc extends FormBloc<String, String> {
weightField
]);
String repeatsInitial = exercisePlanRepository.actualPlanDetail != null &&
exercisePlanRepository.actualPlanDetail.repeats != null ?
exercisePlanRepository.actualPlanDetail.repeats.toString() : "12";
String repeatsInitial = exercisePlanRepository.getActualPlanDetail() != null &&
exercisePlanRepository.getActualPlanDetail().repeats != null ?
exercisePlanRepository.getActualPlanDetail().repeats.toString() : "12";
quantityField.updateInitialValue(repeatsInitial);
String serieInitial = exercisePlanRepository.actualPlanDetail != null &&
exercisePlanRepository.actualPlanDetail.serie != null ?
exercisePlanRepository.actualPlanDetail.serie.toString() : "3";
String serieInitial = exercisePlanRepository.getActualPlanDetail() != null &&
exercisePlanRepository.getActualPlanDetail().serie != null ?
exercisePlanRepository.getActualPlanDetail().serie.toString() : "3";
serieField.updateInitialValue(serieInitial);
String weightInitial = exercisePlanRepository.actualPlanDetail != null &&
exercisePlanRepository.actualPlanDetail.weightEquation != null ?
exercisePlanRepository.actualPlanDetail.weightEquation : "30";
String weightInitial = exercisePlanRepository.getActualPlanDetail() != null &&
exercisePlanRepository.getActualPlanDetail().weightEquation != null ?
exercisePlanRepository.getActualPlanDetail().weightEquation : "30";
weightField.updateInitialValue(weightInitial);
quantityField.onValueChanges(onData: (previous, current) async* {
exercisePlanRepository.actualPlanDetail.repeats = current.valueToInt;
exercisePlanRepository.getActualPlanDetail().repeats = current.valueToInt;
});
serieField.onValueChanges(onData: (previous, current) async* {
exercisePlanRepository.actualPlanDetail.serie = current.valueToInt;
exercisePlanRepository.getActualPlanDetail().serie = current.valueToInt;
});
weightField.onValueChanges(onData: (previous, current) async* {
exercisePlanRepository.actualPlanDetail.weightEquation = current.value;
exercisePlanRepository.getActualPlanDetail().weightEquation = current.value;
});
}
@ -69,12 +70,16 @@ class ExercisePlanCustomerFormBloc extends FormBloc<String, String> {
emitLoading(progress: 30);
// Emit either Loaded or Error
exercisePlanRepository.actualPlanDetail.repeats = quantityField.valueToInt;
exercisePlanRepository.actualPlanDetail.serie = serieField.valueToInt;
exercisePlanRepository.actualPlanDetail.weightEquation = weightField.value;
exercisePlanRepository.addToPlan();
planBloc.add(ExercisePlanUpdate());
exercisePlanRepository.getActualPlanDetail().repeats = quantityField.valueToInt;
exercisePlanRepository.getActualPlanDetail().serie = serieField.valueToInt;
exercisePlanRepository.getActualPlanDetail().weightEquation = weightField.value;
if ( exercisePlanRepository.exercisePlanDetails[exercisePlanRepository.getActualPlanDetail()] == null ) {
exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.add;
} else {
exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.update;
}
exercisePlanRepository.addDetailToPlan();
planBloc.add(ExercisePlanAddExercise(exercisePlanDetail: exercisePlanRepository.getActualPlanDetail()));
emitSuccess(canSubmitAgain: false);
} on Exception catch (ex) {

View File

@ -1,6 +1,6 @@
import 'dart:async';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

View File

@ -17,7 +17,7 @@ class MenuLoading extends MenuState {
}
class MenuReady extends MenuState {
final WorkoutTree workoutTree;
final WorkoutMenuTree workoutTree;
const MenuReady({this.workoutTree});

View File

@ -15,12 +15,13 @@ import 'package:aitrainer_app/view/exercise_control_page.dart';
import 'package:aitrainer_app/view/exercise_execute_by_plan_page.dart';
import 'package:aitrainer_app/view/exercise_log_page.dart';
import 'package:aitrainer_app/view/exercise_plan_custom_page.dart';
import 'package:aitrainer_app/view/exercise_plan_detail_add_page.dart';
import 'package:aitrainer_app/view/exercise_plan_custom_detail_add_page.dart';
import 'package:aitrainer_app/view/exercise_type_description.dart';
import 'package:aitrainer_app/view/gdpr.dart';
import 'package:aitrainer_app/view/login.dart';
import 'package:aitrainer_app/view/exercise_new_page.dart';
import 'package:aitrainer_app/view/menu_page.dart';
import 'package:aitrainer_app/view/mydevelopment_muscle_page.dart';
import 'package:aitrainer_app/view/mydevelopment_page.dart';
import 'package:aitrainer_app/view/myexcercise_plan_page.dart';
import 'package:aitrainer_app/view/registration.dart';
@ -34,6 +35,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:sentry/sentry.dart';
import 'bloc/account/account_bloc.dart';
import 'bloc/development_by_muscle/development_by_muscle_bloc.dart';
import 'bloc/exercise_by_plan/exercise_by_plan_bloc.dart';
import 'bloc/exercise_plan/exercise_plan_bloc.dart';
import 'bloc/menu/menu_bloc.dart';
@ -108,7 +110,7 @@ Future<Null> main() async {
final WorkoutTreeRepository menuTreeRepository = WorkoutTreeRepository();
runApp(
MultiBlocProvider(
providers: [
providers: [
BlocProvider<SessionBloc>(
create: (BuildContext context) => SessionBloc(session: Session()),
),
@ -125,7 +127,12 @@ Future<Null> main() async {
create: (BuildContext context) => ExercisePlanBloc(menuTreeRepository: menuTreeRepository),
),
BlocProvider<ExerciseByPlanBloc>(
create: (BuildContext context) => ExerciseByPlanBloc(menuTreeRepository: menuTreeRepository, exerciseRepository: ExerciseRepository()),
create: (BuildContext context) => ExerciseByPlanBloc(
menuTreeRepository: menuTreeRepository),
),
BlocProvider<DevelopmentByMuscleBloc>(
create: (BuildContext context) => DevelopmentByMuscleBloc(
workoutTreeRepository: menuTreeRepository),
),
],
@ -195,6 +202,7 @@ class AitrainerApp extends StatelessWidget {
'exercisePlanDetailAdd': (context) => ExercisePlanDetailAddPage(),
'exerciseByPlanPage': (context) => ExerciseByPlanPage(),
'exerciseAddByPlanPage': (context) => ExerciseAddByPlanPage(),
'mydevelopmentMusclePage': (context) => MyDevelopmentMusclePage(),
},
initialRoute: 'home',
title: 'WorkoutTest',

View File

@ -4,7 +4,7 @@ import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/service/exercise_tree_service.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart';
@ -59,9 +59,10 @@ class Cache {
List<Exercise> _exercises;
ExercisePlan _myExercisePlan;
List<ExercisePlanDetail> _myExercisesPlanDetail;
LinkedHashMap _tree = LinkedHashMap<String, WorkoutTree>();
LinkedHashMap<int, ExercisePlanDetail> _myExercisesPlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
LinkedHashMap _tree = LinkedHashMap<String, WorkoutMenuTree>();
double _percentExercises = -1;
Customer _trainee;
@ -178,56 +179,63 @@ class Cache {
this._exercisesTrainee = exercises;
}
void setWorkoutTree( LinkedHashMap<String, WorkoutTree> tree) {
void setWorkoutMenuTree( LinkedHashMap<String, WorkoutMenuTree> tree) {
this._tree = tree;
}
List<ExerciseType> getExerciseTypes() {
return this._exerciseTypes;
List<ExerciseType> getExerciseTypes() => this._exerciseTypes;
List<ExerciseTree> getExerciseTree() => this._exerciseTree;
List<Exercise> getExercises() => this._exercises;
List<Exercise> getExercisesTrainee() => this._exercisesTrainee;
LinkedHashMap<String, WorkoutMenuTree> getWorkoutMenuTree() => this._tree;
void setPercentExercises(double percent) => this._percentExercises = percent;
double getPercentExercises() => this._percentExercises;
void addExercise(Exercise exercise) => _exercises.add(exercise);
void addExerciseTrainee(Exercise exercise) => _exercisesTrainee.add(exercise);
Customer getTrainee() => this._trainee;
void setTrainee(Customer trainee) => _trainee = trainee;
void setTraineeExercisePlan(ExercisePlan exercisePlan) => this._traineeExercisePlan = exercisePlan;
ExercisePlan getTraineesExercisePlan() => this._traineeExercisePlan;
void setMyExercisePlan(ExercisePlan exercisePlan) => _myExercisePlan = exercisePlan;
ExercisePlan getMyExercisePlan() => _myExercisePlan;
void setMyExercisePlanDetails(LinkedHashMap<int, ExercisePlanDetail> listExercisePlanDetail)
=> _myExercisesPlanDetails = listExercisePlanDetail;
void addToMyExercisePlanDetails(ExercisePlanDetail detail) => _myExercisesPlanDetails[detail.exerciseTypeId] = detail;
LinkedHashMap<int, ExercisePlanDetail> getMyExercisePlanDetails() => _myExercisesPlanDetails;
void resetMyExercisePlanDetails() => _myExercisesPlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
void updateMyExercisePlanDetail(ExercisePlanDetail detail) {
this.addToMyExercisePlanDetails(detail);
}
List<ExerciseTree> getExerciseTree() {
return this._exerciseTree;
void deleteMyExercisePlanDetail(ExercisePlanDetail detail)
=> this.deleteMyExercisePlanDetailByExerciseTypeId(detail.exerciseTypeId);
void deletedMyExercisePlanDetail(ExercisePlanDetail detail)
=> this._myExercisesPlanDetails[detail.exerciseTypeId].change = ExercisePlanDetailChange.deleted;
void deleteMyExercisePlanDetailByExerciseTypeId(int exerciseTypeId) {
this._myExercisesPlanDetails[exerciseTypeId].change = ExercisePlanDetailChange.delete;
}
List<Exercise> getExercises() {
return this._exercises;
}
List<Exercise> getExercisesTrainee() {
return this._exercisesTrainee;
}
LinkedHashMap<String, WorkoutTree> getWorkoutTree() {
return this._tree;
}
void setPercentExercises(double percent) {
this._percentExercises = percent;
}
double getPercentExercises() {
return this._percentExercises;
}
void addExercise(Exercise exercise) {
_exercises.add(exercise);
}
void addExerciseTrainee(Exercise exercise) {
_exercisesTrainee.add(exercise);
}
Customer getTrainee() {
return this._trainee;
}
void setTrainee(Customer trainee) {
this._trainee = trainee;
}
void setTraineeExercisePlan(ExercisePlan exercisePlan) {
this._traineeExercisePlan = exercisePlan;
}
}

View File

@ -1,3 +1,4 @@
import 'package:aitrainer_app/util/common.dart';
import 'package:intl/intl.dart';
class Exercise {
@ -10,6 +11,8 @@ class Exercise {
DateTime dateAdd;
int exercisePlanDetailId;
String datePart;
Exercise({this.exerciseTypeId, this.customerId, this.quantity, this.dateAdd});
@ -21,6 +24,7 @@ class Exercise {
this.unit = json['unit'];
this.unitQuantity = json['unitQuantity'];
this.dateAdd = DateTime.parse( json['dateAdd'] );
this.datePart = DateFormat('yyyy-MM-dd').format(this.dateAdd);
}
Map<String, dynamic> toJson() =>
@ -33,4 +37,16 @@ class Exercise {
"dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd),
"exercisePlanDetailId": exercisePlanDetailId,
};
Exercise copy() {
Exercise newExercise = Exercise();
newExercise.exerciseTypeId = this.exerciseTypeId;
newExercise.customerId = this.customerId;
newExercise.quantity = this.quantity;
newExercise.unit = this.unit;
newExercise.unitQuantity = this.unitQuantity;
newExercise.dateAdd = this.dateAdd;
newExercise.exercisePlanDetailId = this.exercisePlanDetailId;
return newExercise;
}
}

View File

@ -1,7 +1,12 @@
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'exercise_type.dart';
class ExercisePlanDetailChange {
static const String add = "add";
static const String delete = "delete";
static const String update = "update";
static const String deleted = "deleted";
}
class ExercisePlanDetail {
int exercisePlanDetailId;
int exercisePlanId;
@ -11,6 +16,7 @@ class ExercisePlanDetail {
String weightEquation;
ExerciseType exerciseType;
String change; // 1: update -1:delete 0: new
ExercisePlanDetail(int exerciseTypeId) {
this.exerciseTypeId = exerciseTypeId;

View File

@ -2,7 +2,7 @@ import 'dart:ui';
import 'exercise_type.dart';
class WorkoutTree {
class WorkoutMenuTree {
int id;
int parent;
String name;
@ -20,7 +20,7 @@ class WorkoutTree {
String exerciseDetail;
String nameEnglish;
WorkoutTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child,
WorkoutMenuTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child,
this.exerciseTypeId, this.exerciseType, this.base, this.is1RM, this.nameEnglish);
Map<String, dynamic> toJson() {

View File

@ -8,31 +8,34 @@ import 'package:aitrainer_app/service/exercise_plan_service.dart';
class ExercisePlanRepository {
bool newPlan = true;
ExercisePlan _exercisePlan;
ExercisePlan exercisePlan;
LinkedHashMap<int, ExercisePlanDetail> exercisePlanDetails =
LinkedHashMap<int, ExercisePlanDetail>();
LinkedHashMap<int, ExercisePlanDetail> _origExercisePlanDetails =
LinkedHashMap<int, ExercisePlanDetail>();
int _customerId = 0;
int customerId = 0;
ExercisePlanDetail actualPlanDetail;
void setCustomerId( int customerId ) {
this._customerId = customerId;
this.customerId = customerId;
}
int getCustomerId() {
return this._customerId;
int getCustomerId() => customerId;
void setExercisePlan(ExercisePlan plan) {
this.exercisePlan = plan;
}
void addExerciseTypeToPlan(ExerciseType exerciseType) {
setActualPlanDetail(exerciseType);
}
void addToPlan() {
ExercisePlan getExercisePlan() => exercisePlan;
void addDetailToPlan() {
actualPlanDetail.exercisePlanId = exercisePlan.exercisePlanId;
exercisePlanDetails[actualPlanDetail.exerciseTypeId] = actualPlanDetail;
Cache().addToMyExercisePlanDetails(actualPlanDetail);
}
void setActualPlanDetail(ExerciseType exerciseType) {
ExercisePlanDetail getExercisePlanDetailByExerciseId(int exerciseTypeId) => exercisePlanDetails[exerciseTypeId];
void setActualPlanDetailByExerciseType(ExerciseType exerciseType) {
ExercisePlanDetail detail = exercisePlanDetails[exerciseType.exerciseTypeId];
if ( detail != null ) {
actualPlanDetail = detail;
@ -42,11 +45,14 @@ class ExercisePlanRepository {
actualPlanDetail.exerciseType = exerciseType;
}
int getPlanDetailId(int exerciseTypeId) {
ExercisePlanDetail detail = exercisePlanDetails[exerciseTypeId];
return detail.exercisePlanDetailId;
ExercisePlanDetail getActualPlanDetail() => actualPlanDetail;
void setActualPlanDetail( ExercisePlanDetail detail ) {
this.actualPlanDetail = detail;
}
int getPlanDetailId(int exerciseTypeId) => exercisePlanDetails[exerciseTypeId].exercisePlanDetailId;
String getPlanDetail(int exerciseTypeId) {
ExercisePlanDetail detail = exercisePlanDetails[exerciseTypeId];
String detailString = "";
@ -70,96 +76,121 @@ class ExercisePlanRepository {
exercisePlanDetail.serie = serie;
exercisePlanDetail.repeats = repeat;
exercisePlanDetail.weightEquation = weight;
exercisePlanDetail.change = ExercisePlanDetailChange.update;
exercisePlanDetails[exerciseType.exerciseTypeId] = exercisePlanDetail;
}
void removeExerciseTypeFromPlan(ExerciseType exerciseType) {
exercisePlanDetails.remove(exerciseType.exerciseTypeId);
removeExerciseTypeFromPlanByExerciseTypeId(exerciseType.exerciseTypeId);
}
void removeExerciseTypeFromPlanByExerciseTypeId(int exerciseTypeId) {
exercisePlanDetails[exerciseTypeId].change = ExercisePlanDetailChange.delete;
Cache().deleteMyExercisePlanDetailByExerciseTypeId(exerciseTypeId);
}
Future<void> saveExercisePlan() async {
if ( _exercisePlan == null ) {
if ( exercisePlan == null ) {
if ( Cache().userLoggedIn == null ) {
throw Exception("please log in");
}
String exercisePlanName;
if ( this._customerId == Cache().userLoggedIn.customerId) {
if ( this.customerId == Cache().userLoggedIn.customerId) {
exercisePlanName = Cache().userLoggedIn.name + " private";
} else {
exercisePlanName = Cache().getTrainee().name + " " + Cache().getTrainee().firstname + " private";
}
_exercisePlan = ExercisePlan(exercisePlanName, this._customerId);
exercisePlan = ExercisePlan(exercisePlanName, this.customerId);
}
if ( newPlan ) {
_exercisePlan.dateAdd = DateTime.now();
_exercisePlan.private = true;
exercisePlan.dateAdd = DateTime.now();
exercisePlan.private = true;
ExercisePlan savedExercisePlan =
await ExercisePlanApi().saveExercisePlan(_exercisePlan);
await ExercisePlanApi().saveExercisePlan(exercisePlan);
exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
LinkedHashMap<int, ExercisePlanDetail> savedExercisePlanDetails = LinkedHashMap();
exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
exercisePlanDetail.exercisePlanId = savedExercisePlan.exercisePlanId;
await ExercisePlanApi().saveExercisePlanDetail(exercisePlanDetail);
});
ExercisePlanDetail savedDetail = await ExercisePlanApi().saveExercisePlanDetail(exercisePlanDetail);
savedExercisePlanDetails[savedDetail.exerciseTypeId] = savedDetail;
});
exercisePlan = savedExercisePlan;
exercisePlanDetails = savedExercisePlanDetails;
} else {
await ExercisePlanApi().updateExercisePlan(_exercisePlan, _exercisePlan.exercisePlanId);
//await ExercisePlanApi().updateExercisePlan(exercisePlan, exercisePlan.exercisePlanId);
_origExercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
if (exercisePlanDetails[exercisePlanDetail.exercisePlanDetailId] == null) {
exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
if ( exercisePlanDetail.change == ExercisePlanDetailChange.delete ) {
await ExercisePlanApi()
.deleteExercisePlanDetail(exercisePlanDetail.exercisePlanDetailId);
} else {
exercisePlanDetail.change = ExercisePlanDetailChange.deleted;
Cache().deletedMyExercisePlanDetail(exercisePlanDetail);
} else if ( exercisePlanDetail.change == ExercisePlanDetailChange.update ) {
await ExercisePlanApi()
.updateExercisePlanDetail(exercisePlanDetail, exercisePlanDetail.exercisePlanDetailId);
Cache().updateMyExercisePlanDetail(exercisePlanDetail);
} else if ( exercisePlanDetail.change == ExercisePlanDetailChange.add ) {
await ExercisePlanApi()
.saveExercisePlanDetail(exercisePlanDetail);
Cache().addToMyExercisePlanDetails(exercisePlanDetail);
}
});
exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async{
exercisePlanDetail.exercisePlanId = _exercisePlan.exercisePlanId;
if ( _origExercisePlanDetails[exercisePlanDetail.exercisePlanDetailId] == null) {
await ExercisePlanApi()
.saveExercisePlanDetail(exercisePlanDetail);
}
});
}
}
Future<ExercisePlan> getLastExercisePlan() async {
if ( _customerId == 0) {
if ( customerId == 0) {
return null;
}
_exercisePlan = await ExercisePlanApi().getLastExercisePlan(_customerId);
newPlan = (_exercisePlan == null);
print("New plan: " + newPlan.toString());
ExercisePlan myExercisePlan = Cache().getMyExercisePlan();
if ( myExercisePlan != null ) {
exercisePlan = myExercisePlan;
return myExercisePlan;
}
return _exercisePlan;
exercisePlan = await ExercisePlanApi().getLastExercisePlan(customerId);
newPlan = (exercisePlan == null);
print("New plan: " + newPlan.toString());
Cache().setMyExercisePlan(exercisePlan);
return exercisePlan;
}
Future<void> getExercisePlanDetails() async {
if (_exercisePlan == null) {
if (exercisePlan == null) {
ExercisePlan exercisePlan = await this.getLastExercisePlan();
if ( exercisePlan == null ) {
exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
_origExercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
return;
}
}
exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
_origExercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
List<ExercisePlanDetail> list =
await ExercisePlanApi().getExercisePlanDetail(_exercisePlan.exercisePlanId);
List<ExercisePlanDetail> list = List();
LinkedHashMap<int, ExercisePlanDetail> listCache = Cache().getMyExercisePlanDetails();
if ( listCache.length > 0) {
exercisePlanDetails = listCache;
return;
} else {
list = await ExercisePlanApi().getExercisePlanDetail(exercisePlan.exercisePlanId);
}
exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
list.forEach((element) {
newPlan = false;
ExercisePlanDetail detail = element;
_origExercisePlanDetails[detail.exerciseTypeId] = detail;
exercisePlanDetails[detail.exerciseTypeId] = detail;
});
Cache().setMyExercisePlanDetails(exercisePlanDetails);
return;
}

View File

@ -4,7 +4,7 @@ import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/service/exercise_service.dart';
class ExerciseRepository {
@ -120,14 +120,14 @@ class ExerciseRepository {
List<int> baseTreeItem = List();
List<int> checkedBaseTreeItem = List();
int count1RMExercises = 0;
LinkedHashMap<String, WorkoutTree> tree = Cache().getWorkoutTree();
LinkedHashMap<String, WorkoutMenuTree> tree = Cache().getWorkoutMenuTree();
if ( tree == null ) {
return;
}
tree.forEach((key, value) {
WorkoutTree treeItem = value;
WorkoutMenuTree treeItem = value;
if (treeItem.exerciseType != null &&
treeItem.exerciseType.base == true &&
!baseTreeItem.contains(treeItem.parent)) {
@ -148,7 +148,7 @@ class ExerciseRepository {
if ( !checkedExerciseTypeId.contains(exercise.exerciseTypeId )) {
checkedExerciseTypeId.add(exercise.exerciseTypeId);
tree.forEach((key, value) {
WorkoutTree treeItem = value;
WorkoutMenuTree treeItem = value;
if (treeItem.exerciseType != null
&& treeItem.exerciseType.base == true
&& exercise.exerciseTypeId == treeItem.exerciseType.exerciseTypeId

View File

@ -3,7 +3,7 @@ import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/service/exercise_tree_service.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart';
@ -29,8 +29,8 @@ class Antagonist {
}
class WorkoutTreeRepository {
final LinkedHashMap tree = LinkedHashMap<String, WorkoutTree>();
SplayTreeMap sortedTree = SplayTreeMap<String, List<WorkoutTree>>();
final LinkedHashMap tree = LinkedHashMap<String, WorkoutMenuTree>();
SplayTreeMap sortedTree = SplayTreeMap<String, List<WorkoutMenuTree>>();
bool isEnglish;
final Map<String, int> _antagonist = {
@ -61,7 +61,7 @@ class WorkoutTreeRepository {
if ( is1RM == false && treeItem.parentId != 0) {
is1RM = isParent1RM(treeItem.parentId);
}
this.tree[treeItem.name] = WorkoutTree(
this.tree[treeItem.name] = WorkoutMenuTree(
treeItem.treeId,
treeItem.parentId,
treeName,
@ -87,7 +87,7 @@ class WorkoutTreeRepository {
String assetImage = 'asset/menu/' + exerciseType.imageUrl.substring(7);
bool is1RM = this.isParent1RM(exerciseType.treeId);
exerciseType.is1RM = is1RM;
this.tree[exerciseType.name] = WorkoutTree(
this.tree[exerciseType.name] = WorkoutMenuTree(
exerciseType.exerciseTypeId,
exerciseType.treeId,
exerciseTypeName,
@ -103,7 +103,7 @@ class WorkoutTreeRepository {
);
});
Cache().setWorkoutTree(tree);
Cache().setWorkoutMenuTree(tree);
ExerciseRepository exerciseRepository = ExerciseRepository();
exerciseRepository.getBaseExerciseFinishedPercent();
}
@ -112,7 +112,7 @@ class WorkoutTreeRepository {
bool isTreeItem1RM = false;
this.tree.forEach((key, value) {
WorkoutTree treeItem = value as WorkoutTree;
WorkoutMenuTree treeItem = value as WorkoutMenuTree;
if ( treeItem.id == treeId ) {
isTreeItem1RM = treeItem.is1RM;
//print (treeItem.name + " 1RM " + treeItem.is1RM.toString() );
@ -124,9 +124,9 @@ class WorkoutTreeRepository {
}
LinkedHashMap getBranch(int parent) {
LinkedHashMap branch = LinkedHashMap<String, WorkoutTree>();
LinkedHashMap branch = LinkedHashMap<String, WorkoutMenuTree>();
tree.forEach((key, value) {
WorkoutTree workoutTree = value as WorkoutTree;
WorkoutMenuTree workoutTree = value as WorkoutMenuTree;
if ( parent == workoutTree.parent) {
branch[key] = value;
}
@ -134,10 +134,10 @@ class WorkoutTreeRepository {
return branch;
}
List<WorkoutTree> getBranchList(int parent) {
List branch = List<WorkoutTree>();
List<WorkoutMenuTree> getBranchList(int parent) {
List branch = List<WorkoutMenuTree>();
tree.forEach((key, value) {
WorkoutTree workoutTree = value as WorkoutTree;
WorkoutMenuTree workoutTree = value as WorkoutMenuTree;
if ( parent == workoutTree.parent) {
branch.add(workoutTree);
}
@ -146,9 +146,9 @@ class WorkoutTreeRepository {
}
void sortByMuscleType() {
sortedTree = SplayTreeMap<String, List<WorkoutTree>>();
sortedTree = SplayTreeMap<String, List<WorkoutMenuTree>>();
tree.forEach((key, value) {
WorkoutTree workoutTree = value as WorkoutTree;
WorkoutMenuTree workoutTree = value as WorkoutMenuTree;
//print("treeitem: " + workoutTree.toJson().toString());
/*if ( workoutTree.exerciseType != null) {
print("treeItem exerciseTye " + workoutTree.exerciseType.toJson().toString());

View File

@ -21,12 +21,13 @@ class APIClient with Common {
'Content-Type': 'application/json',
'Authorization' : "Bearer " + authToken }
);
print(" ------------get response code: " + response.statusCode.toString());
if(response.statusCode == 200) {
return utf8.decode(response.bodyBytes);
} else if(response.statusCode == 404 ) {
throw NotFoundException(message: "Not Found");
} else {
throw Exception("Unable to perform HTTP request!");
throw Exception("Network Error, please try again later");
}
}
@ -49,8 +50,9 @@ class APIClient with Common {
},
body: body,
);
String decodedResponse = utf8convert(response.body);
print(" ------------ response: " + decodedResponse);
print(" ------------post response code: " + response.statusCode.toString());
final String decodedResponse = utf8convert(response.body);
print(" ------------ response: $decodedResponse");
return decodedResponse;
}
@ -59,6 +61,7 @@ class APIClient with Common {
try {
final body = '{"username":"$email", "password":"$password"}';
print("authentication with $email");
final response = await http.post(
uri,
headers: {

View File

@ -1,4 +1,3 @@
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/util/not_found_exception.dart';
@ -17,7 +16,7 @@ class ExercisePlanApi {
"exercise_plan",
body);
savedExercisePlan = ExercisePlan.fromJson(jsonDecode(responseBody));
Cache().setTraineeExercisePlan(savedExercisePlan);
} on Exception catch(e) {
throw new Exception(e.toString());
}
@ -28,14 +27,14 @@ class ExercisePlanApi {
ExercisePlan exercisePlan,
int exercisePlanId) async {
String body = JsonEncoder().convert(exercisePlan.toJson());
print(" ===== saving exercisePlan $exercisePlan");
print(" ===== update exercisePlan $exercisePlan");
ExercisePlan updatedExercisePlan;
try {
final String responseBody = await _client.post(
"exercise_plan/" + exercisePlanId.toString(),
body);
updatedExercisePlan = ExercisePlan.fromJson(jsonDecode(responseBody));
Cache().setTraineeExercisePlan(updatedExercisePlan);
} on Exception catch(e) {
throw new Exception(e.toString());
}
@ -44,7 +43,7 @@ class ExercisePlanApi {
Future<ExercisePlanDetail> saveExercisePlanDetail(ExercisePlanDetail exercisePlanDetail) async {
String body = JsonEncoder().convert(exercisePlanDetail.toJson());
print(" ===== update exercisePlanDetail $exercisePlanDetail");
print(" ===== save exercisePlanDetail $exercisePlanDetail");
ExercisePlanDetail savedExercisePlanDetail;
try {
final String responseBody = await _client.post(

135
lib/treeview/tree_view.dart Normal file
View File

@ -0,0 +1,135 @@
/// Tree view widget library
library tree_view;
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class TreeView extends InheritedWidget {
final List<Widget> children;
final bool startExpanded;
TreeView({
Key key,
@required List<Widget> children,
bool startExpanded = false,
}) : this.children = children,
this.startExpanded = startExpanded,
super(
key: key,
child: _TreeViewData(
children: children,
),
);
static TreeView of(BuildContext context) {
//return context.inheritFromWidgetOfExactType(TreeView);
return context.dependOnInheritedWidgetOfExactType(aspect: TreeView);
}
@override
bool updateShouldNotify(TreeView oldWidget) {
if (oldWidget.children == this.children &&
oldWidget.startExpanded == this.startExpanded) {
return false;
}
return true;
}
}
class _TreeViewData extends StatelessWidget {
final List<Widget> children;
const _TreeViewData({
this.children,
});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: children.length,
itemBuilder: (context, index) {
return children.elementAt(index);
},
);
}
}
class TreeViewChild extends StatefulWidget {
final bool startExpanded;
final Widget parent;
final List<Widget> children;
final VoidCallback onTap;
TreeViewChild({
@required this.parent,
@required this.children,
this.startExpanded,
this.onTap,
Key key,
}) : super(key: key) {
assert(parent != null);
assert(children != null);
}
@override
TreeViewChildState createState() => TreeViewChildState();
TreeViewChild copyWith(
TreeViewChild source, {
bool startExpanded,
Widget parent,
List<Widget> children,
VoidCallback onTap,
}) {
return TreeViewChild(
parent: parent ?? source.parent,
children: children ?? source.children,
startExpanded: startExpanded ?? source.startExpanded,
onTap: onTap ?? source.onTap,
);
}
}
class TreeViewChildState extends State<TreeViewChild> {
bool isExpanded;
@override
void initState() {
super.initState();
isExpanded = widget.startExpanded;
}
@override
void didChangeDependencies() {
isExpanded = widget.startExpanded ?? TreeView.of(context).startExpanded;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
child: widget.parent,
onTap: widget.onTap ?? () => toggleExpanded(),
),
AnimatedContainer(
duration: Duration(milliseconds: 400),
child: isExpanded
? Column(
mainAxisSize: MainAxisSize.min,
children: widget.children,
)
: Offstage(),
),
],
);
}
void toggleExpanded() {
setState(() {
this.isExpanded = !this.isExpanded;
});
}
}

15
lib/util/calculate.dart Normal file
View File

@ -0,0 +1,15 @@
mixin Calculate {
double calculate1RM(double quantity, double unitQuantity) {
double weight = unitQuantity;
double repeat = quantity;
if ( weight == 0 || repeat == 0) {
return 0;
}
double rmWendler = weight * repeat * 0.0333 + weight;
double rmOconner = weight * (1 + repeat / 40);
double average = (rmWendler + rmOconner)/2;
return average;
}
}

View File

@ -73,4 +73,10 @@ mixin Common {
return _passwordRegExp.hasMatch(password);
}
/// Calculates week number from a date as per https://en.wikipedia.org/wiki/ISO_week_date#Calculation
int weekNumber(DateTime date) {
int dayOfYear = int.parse(DateFormat("D").format(date));
return ((dayOfYear - date.weekday + 10) / 7).floor();
}
}

View File

@ -3,7 +3,7 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_add_by_plan_bloc.dart';
import 'package:aitrainer_app/bloc/exercise_by_plan/exercise_by_plan_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/splash.dart';
@ -25,7 +25,7 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
// ignore: close_sinks
final ExerciseByPlanBloc bloc = arguments['blocExerciseByPlan'];
final int customerId = arguments['customerId'];
final WorkoutTree workoutTree = arguments['workoutTree'];
final WorkoutMenuTree workoutTree = arguments['workoutTree'];
final ExerciseRepository exerciseRepository = ExerciseRepository();
setContext(context);
@ -136,38 +136,15 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(color: Colors.transparent,),
Text("Execute the " + (i+1).toString() + ". set!",
Text(t("Execute the") + " " + (i+1).toString() + t(". set!"),
style: TextStyle(),),
TextFieldBlocBuilder(
readOnly: exerciseBloc.step != i+1,
textFieldBloc: exerciseBloc.quantity1Field,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Colors.deepOrange,
fontWeight: FontWeight.bold),
inputFormatters: [
WhitelistingTextInputFormatter(RegExp(r"[\d.]"))
],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(
fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: t("The number of the exercise"),
labelStyle: TextStyle(fontSize: 12, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: t("Please repeat with ") + exerciseBloc.unitQuantity1Field.value + " " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit + " " +
exerciseBloc.exercisePlanRepository.actualPlanDetail.repeats.toString() + " times!",
),
),
TextFieldBlocBuilder(
readOnly: exerciseBloc.step != i+1,
textFieldBloc: exerciseBloc.unitQuantity1Field,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontSize: 18,
color: Colors.black54,
fontWeight: FontWeight.bold),
inputFormatters: [
@ -178,11 +155,35 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(
fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w100),
labelStyle: TextStyle(fontSize: 12, color: Colors.deepOrange, fontWeight: FontWeight.normal),
fontSize: 14, color: Colors.black54, fontWeight: FontWeight.w100),
labelStyle: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit,
),
),
TextFieldBlocBuilder(
readOnly: exerciseBloc.step != i+1,
textFieldBloc: exerciseBloc.quantity1Field,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Colors.deepOrange,
fontWeight: FontWeight.bold),
inputFormatters: [
WhitelistingTextInputFormatter(RegExp(r"[\d.]"))
],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(
fontSize: 14, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: t("The number of the exercise"),
labelStyle: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: t("Please repeat with") + " "+ exerciseBloc.unitQuantity1Field.value + " " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit + " " +
exerciseBloc.exercisePlanRepository.getActualPlanDetail().repeats.toString() + " " + t("times!"),
),
),
RaisedButton(
padding: EdgeInsets.all(0),

View File

@ -1,23 +1,25 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_by_plan/exercise_by_plan_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/treeview/tree_view.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_common.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/exercise_type_widget.dart';
import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_treeview/tree_view.dart';
class ExerciseByPlanPage extends StatefulWidget {
@override
_ExerciseByPlanPage createState() => _ExerciseByPlanPage();
}
class _ExerciseByPlanPage extends State<ExerciseByPlanPage> {
class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
// ignore: close_sinks
ExerciseByPlanBloc bloc;
@ -38,21 +40,24 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> {
final int customerId = arguments['customerId'];
bloc = BlocProvider.of<ExerciseByPlanBloc>(context);
bloc.customerId = customerId;
setContext(context);
return Scaffold(
key: _scaffoldKey,
appBar: AppBarCommonNav(),
body: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
image: DecorationImage(
image: customerId == Cache().userLoggedIn.customerId ? AssetImage('asset/image/WT_light_background.png'):
AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
key: _scaffoldKey,
appBar: AppBarCommonNav(),
body: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
image: DecorationImage(
image: customerId == Cache().userLoggedIn.customerId
? AssetImage('asset/image/WT_light_background.png')
: AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
child: BlocConsumer<ExerciseByPlanBloc, ExerciseByPlanState>(listener: (context, state) {
),
child: BlocConsumer<ExerciseByPlanBloc, ExerciseByPlanState>(
listener: (context, state) {
if (state is ExerciseByPlanError) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
@ -64,124 +69,147 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> {
LoadingDialog();
}
},
// ignore: missing_return
builder: (context, state) {
builder: (context, state) {
if (state is ExerciseByPlanStateInitial || state is ExerciseByPlanLoading) {
return Container();
} else if (state is ExerciseByPlanReady) {
return exerciseWidget(bloc);
} else if (state is ExerciseByPlanError) {
} else {
return exerciseWidget(bloc);
}
})));
})),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 2),
);
}
Widget exerciseWidget(ExerciseByPlanBloc bloc) {
final LinkedHashMap args = LinkedHashMap();
TreeViewController _treeViewController = TreeViewController(children: nodeExercisePlan(bloc));
TreeViewTheme _treeViewTheme = TreeViewTheme(
expanderTheme: ExpanderThemeData(
type: ExpanderType.plusMinus,
modifier: ExpanderModifier.circleOutlined,
position: ExpanderPosition.start,
color: Colors.black26,
size: 10,
),
labelStyle: TextStyle(fontSize: 14, letterSpacing: 0, color: Colors.blue.shade800),
parentLabelStyle: TextStyle(
fontSize: 14,
letterSpacing: 0.3,
fontWeight: FontWeight.w800,
color: Colors.orange.shade600,
),
iconTheme: IconThemeData(
size: 20,
color: Colors.blue.shade800,
),
colorScheme: bloc.customerId == Cache().userLoggedIn.customerId ? ColorScheme.light(background: Colors.transparent) : ColorScheme.dark(background: Colors.transparent),
);
return Scaffold(
backgroundColor: Colors.transparent,
body: TreeView(
controller: _treeViewController,
allowParentSelect: false,
supportParentDoubleTap: false,
//onExpansionChanged: _expandNodeHandler,
onNodeTap: (key) {
/* Node<dynamic> node = _treeViewController.getNode(key);
WorkoutTree workoutTree = node.data as WorkoutTree;
bloc.exercisePlanRepository.setActualPlanDetail(workoutTree.exerciseType);
print("change node " + node.label + " key " + key);
bloc.add(ExercisePlanUpdate(workoutTree: workoutTree));
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc); */
Node<dynamic> node = _treeViewController.getNode(key);
WorkoutTree workoutTree = node.data as WorkoutTree;
args['blocExerciseByPlan'] = bloc;
args['customerId'] = bloc.customerId;
args['workoutTree'] = workoutTree;
Navigator.of(context).pushNamed("exerciseAddByPlanPage", arguments: args);
},
theme: _treeViewTheme,
),
//bottomNavigationBar: BottomNavigator(bottomNavIndex: 2),
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
return TreeView(
startExpanded: false,
children: nodeExercisePlan(bloc),
);
}
List<Node> nodeExercisePlan(ExerciseByPlanBloc bloc) {
List<Node> nodes = List<Node>();
Node actualNode;
List<Widget> nodeExercisePlan(ExerciseByPlanBloc bloc) {
List<Widget> exerciseTypes = List();
bool isEnglish = AppLanguage().appLocal == Locale("en");
Card explanation = Card(
color: Colors.white38,
child: Container(
padding: EdgeInsets.only(left: 10, right: 5, top: 12, bottom: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
Icon(
Icons.info,
color: Colors.orangeAccent,
),
Text(" "),
Flexible(
child:
Text(
t("Execute your active Exercise Plan!"),
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
],
),
Divider(
color: Colors.transparent,
),
Text(
t("Select the muscle type and tap on the exercise. One the next page enter the weight and repeat."),
style: TextStyle(fontSize: 12, fontWeight: FontWeight.normal),
),
],
)));
exerciseTypes.add(explanation);
bloc.menuTreeRepository.sortedTree.forEach((name, list) {
List<WorkoutTree> listWorkoutItem = list;
List<Node> listExerciseTypePerMuscle = List<Node>();
NodeIcon icon;
listWorkoutItem.forEach((element) {
WorkoutTree treeItem = element;
if ( treeItem.selected ) {
icon =
treeItem.executed == false ? NodeIcon(codePoint: Icons.bubble_chart.codePoint, color: "blueAccent") :
NodeIcon(codePoint: Icons.check_box.codePoint, color: "green");
String exerciseLabel = isEnglish
? treeItem.name
: treeItem.exerciseType == null ? treeItem.name : treeItem.exerciseType.nameTranslation;
List<Node<dynamic>> planDetailList = List<Node<dynamic>>();
String planDetail = bloc.exercisePlanRepository.getPlanDetail(treeItem.exerciseTypeId);
if (planDetail.length > 0) {
exerciseLabel += " (" + planDetail + ")";
}
actualNode = Node(
label: exerciseLabel,
key: treeItem.id.toString(),
data: treeItem,
expanded: planDetailList.length > 0 ? true : false,
children: [],
icon: icon);
listExerciseTypePerMuscle.add(actualNode);
}
});
if (name != null) {
actualNode = Node(
label: name,
key: name,
expanded: true,
children: listExerciseTypePerMuscle,
icon: NodeIcon(codePoint: Icons.perm_identity.codePoint, color: "orange"));
nodes.add(actualNode);
}
exerciseTypes.add(
Container(
margin: const EdgeInsets.only(left: 4.0),
child: TreeViewChild(
startExpanded: true,
parent: ExerciseTypeWidget(exerciseTypeName: name), //_getExerciseWidget(exerciseTypeName: name),
children: _getChildList(list, bloc),
)));
});
return nodes;
return exerciseTypes;
}
List<Widget> _getChildList(List<WorkoutMenuTree> listWorkoutTree, ExerciseByPlanBloc bloc) {
List<Widget> list = List();
listWorkoutTree.forEach((element) {
if ( element.selected) {
list.add(
TreeViewChild(
startExpanded: false,
parent:
Card(
margin: EdgeInsets.only(left: 10, top: 5),
color: Colors.white54,
child: Container(
padding: const EdgeInsets.only(left: 5, top: 0, right: 5, bottom: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
icon: element.executed ? Icon(Icons.check_box, color: Colors.green):
Icon(Icons.indeterminate_check_box, color: Colors.blue.shade800,),
onPressed: () => {
addExerciseByPlanEvent(bloc, element)
},
),
SizedBox(width: 20),
Flexible(
child:
InkWell(
child:
Text(
element.name,
textAlign: TextAlign.start,
style: TextStyle(fontSize: 12, color: Colors.black),
),
onTap: () => {
addExerciseByPlanEvent(bloc, element)
},
),
),
IconButton(
padding: EdgeInsets.all(0),
icon: Icon(Icons.description, color: Colors.black12,),
onPressed: () {
},
),
]),
)
),
children: [
]
)
);
}
});
return list;
}
void addExerciseByPlanEvent(ExerciseByPlanBloc bloc, WorkoutMenuTree workoutTree) {
LinkedHashMap args = LinkedHashMap();
args['blocExerciseByPlan'] = bloc;
args['customerId'] = bloc.customerId;
args['workoutTree'] = workoutTree;
Navigator.of(context).pushNamed("exerciseAddByPlanPage", arguments: args);
}
}

View File

@ -4,7 +4,6 @@ import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_common.dart';
@ -21,7 +20,6 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans {
Widget build(BuildContext context) {
LinkedHashMap arguments = ModalRoute.of(context).settings.arguments;
final ExerciseRepository exerciseRepository = arguments['exerciseRepository'];
final CustomerRepository customerRepository = arguments['customerRepository'];
final int customerId = arguments['customerId'];
setContext(context);

View File

@ -2,6 +2,7 @@ import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart';
import 'package:aitrainer_app/bloc/exercise_plan_custom_form.dart';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_common.dart';
@ -34,8 +35,8 @@ class _ExercisePlanDetailAddPage extends State<ExercisePlanDetailAddPage> with T
String exerciseName = "";
if (bloc != null) {
exerciseName = AppLanguage().appLocal == Locale("en")
? bloc.exercisePlanRepository.actualPlanDetail.exerciseType.name
: bloc.exercisePlanRepository.actualPlanDetail.exerciseType.nameTranslation;
? bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name
: bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.nameTranslation;
}
return Form(
@ -118,9 +119,10 @@ class _ExercisePlanDetailAddPage extends State<ExercisePlanDetailAddPage> with T
color: Colors.red.shade300,
focusColor: Colors.white,
onPressed: () => {
print("Remove " + bloc.exercisePlanRepository.actualPlanDetail.exerciseType.name),
print("Remove " + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name),
bloc.exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.delete,
planBloc.add(ExercisePlanRemoveExercise(
exercisePlanDetail: bloc.exercisePlanRepository.actualPlanDetail)),
exercisePlanDetail: bloc.exercisePlanRepository.getActualPlanDetail() )),
Navigator.of(context).pop(),
},
child: Text(t(

View File

@ -1,24 +1,26 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/treeview/tree_view.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_common.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/exercise_type_widget.dart';
import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_treeview/tree_view.dart';
class ExercisePlanCustomPage extends StatefulWidget {
@override
_ExercisePlanCustomPage createState() => _ExercisePlanCustomPage();
}
class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> {
class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> with Trans {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
// ignore: close_sinks
ExercisePlanBloc bloc;
@ -36,10 +38,10 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> {
@override
Widget build(BuildContext context) {
LinkedHashMap arguments = ModalRoute.of(context).settings.arguments;
final ExerciseRepository exerciseRepository = arguments['exerciseRepository'];
final int customerId = arguments['customerId'];
bloc = BlocProvider.of<ExercisePlanBloc>(context);
bloc.customerId = customerId;
setContext(context);
return Scaffold(
key: _scaffoldKey,
@ -54,135 +56,158 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> {
alignment: Alignment.center,
),
),
child: BlocConsumer<ExercisePlanBloc, ExercisePlanState>(listener: (context, state) {
if (state is ExercisePlanError) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
state.message,
),
backgroundColor: Colors.orange,
));
} else if (state is ExercisePlanLoading) {
LoadingDialog();
}
},
child: BlocConsumer<ExercisePlanBloc, ExercisePlanState>(
listener: (context, state) {
if (state is ExercisePlanError) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
state.message,
),
backgroundColor: Colors.orange,
));
} else if (state is ExercisePlanLoading) {
LoadingDialog();
}
},
// ignore: missing_return
builder: (context, state) {
if (state is ExercisePlanInitial) {
builder: (context, state) {
if (state is ExercisePlanReady) {
return exerciseWidget(bloc);
}
return Container();
} else if (state is ExercisePlanReady) {
return exerciseWidget(bloc);
} else if (state is ExercisePlanError) {
return exerciseWidget(bloc);
} else if (state is ExercisePlanLoading) {
return Container();
}
})));
}
)
),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 2),
);
}
Widget exerciseWidget(ExercisePlanBloc bloc) {
TreeViewController _treeViewController = TreeViewController(children: nodeExercisePlan(bloc));
TreeViewTheme _treeViewTheme = TreeViewTheme(
expanderTheme: ExpanderThemeData(
type: ExpanderType.plusMinus,
modifier: ExpanderModifier.circleOutlined,
position: ExpanderPosition.start,
color: Colors.black26,
size: 10,
),
labelStyle: TextStyle(fontSize: 14, letterSpacing: 0, color: Colors.blue.shade800),
parentLabelStyle: TextStyle(
fontSize: 14,
letterSpacing: 0.3,
fontWeight: FontWeight.w800,
color: Colors.orange.shade600,
),
iconTheme: IconThemeData(
size: 20,
color: Colors.blue.shade800,
),
colorScheme: bloc.customerId == Cache().userLoggedIn.customerId ? ColorScheme.light(background: Colors.transparent) : ColorScheme.dark(background: Colors.transparent),
);
return Scaffold(
backgroundColor: Colors.transparent,
body: TreeView(
controller: _treeViewController,
allowParentSelect: true,
supportParentDoubleTap: false,
//onExpansionChanged: _expandNodeHandler,
onNodeTap: (key) {
Node<dynamic> node = _treeViewController.getNode(key);
WorkoutTree workoutTree = node.data as WorkoutTree;
bloc.exercisePlanRepository.setActualPlanDetail(workoutTree.exerciseType);
print("change node " + node.label + " key " + key);
bloc.add(ExercisePlanUpdate(workoutTree: workoutTree));
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc);
},
theme: _treeViewTheme,
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blueAccent,
child: Icon(Icons.save_alt),
onPressed: () => {
bloc.add(ExercisePlanSave()),
if (bloc.exercisePlanRepository.getExercisePlanDetailSize() > 0) {
Navigator.of(context).pop()
}
}
),
//bottomNavigationBar: BottomNavigator(bottomNavIndex: 2),
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
return TreeView(
startExpanded: false,
children: _getTreeChildren(bloc),
);
}
List<Node> nodeExercisePlan(ExercisePlanBloc bloc) {
List<Node> nodes = List<Node>();
Node actualNode;
bool isEnglish = AppLanguage().appLocal == Locale("en");
List<Widget> _getTreeChildren(ExercisePlanBloc bloc) {
List<Widget> exerciseTypes = List();
bloc.menuTreeRepository.sortedTree.forEach((name, list) {
List<WorkoutTree> listWorkoutItem = list;
List<Node> listExerciseTypePerMuscle = List<Node>();
NodeIcon icon;
listWorkoutItem.forEach((element) {
WorkoutTree treeItem = element;
icon =
treeItem.selected == true ? NodeIcon(codePoint: Icons.bubble_chart.codePoint, color: "blueAccent") : null;
Card explanation = Card(
color: Colors.white60,
child: Container(
padding: EdgeInsets.only(left: 10, right: 5, top: 12, bottom: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
Icon(
Icons.info,
color: Colors.orangeAccent,
),
Text(" "),
Text(
t("Custom Exercise Plan"),
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
],
),
Divider(
color: Colors.transparent,
),
Text(
t("Select manually the exercises what you would like to have in your plan. At the end don't forget to save."),
style: TextStyle(fontSize: 12, fontWeight: FontWeight.normal),
),
String exerciseLabel = isEnglish
? treeItem.name
: treeItem.exerciseType == null ? treeItem.name : treeItem.exerciseType.nameTranslation;
],
)
)
);
exerciseTypes.add(explanation);
List<Node<dynamic>> planDetailList = List<Node<dynamic>>();
String planDetail = bloc.exercisePlanRepository.getPlanDetail(treeItem.exerciseTypeId);
if (planDetail.length > 0) {
exerciseLabel += " (" + planDetail +")";
}
actualNode = Node(
label: exerciseLabel,
key: treeItem.id.toString(),
data: treeItem,
expanded: planDetailList.length > 0 ? true : false,
children: [],
icon: icon);
listExerciseTypePerMuscle.add(actualNode);
});
//print ("Node name " + name);
if (name != null) {
actualNode = Node(
label: name, // AppLocalizations.of(context).translate(name),
key: name,
expanded: true,
children: listExerciseTypePerMuscle,
icon: NodeIcon(codePoint: Icons.perm_identity.codePoint, color: "orange"));
nodes.add(actualNode);
}
bloc.menuTreeRepository.sortedTree.forEach((name, list) {
exerciseTypes.add(Container(
margin: const EdgeInsets.only(left: 4.0),
child: TreeViewChild(
startExpanded: true,
parent: ExerciseTypeWidget(exerciseTypeName: name),
children: _getChildList(list, bloc),
)));
});
return nodes;
return exerciseTypes;
}
List<Widget> _getChildList(List<WorkoutMenuTree> listWorkoutTree, ExercisePlanBloc bloc) {
List<Widget> list = List();
listWorkoutTree.forEach((element) {
list.add(
TreeViewChild(
startExpanded: false,
parent:
Card(
margin: EdgeInsets.only(left: 10, top: 5),
color: Colors.white54,
child: Container(
padding: const EdgeInsets.only(left: 5, top: 0, right: 5, bottom: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
icon: element.selected ? Icon(Icons.check, color: Colors.greenAccent.shade700,) : Icon(Icons.add, color: Colors.blue.shade400,),
onPressed: () => {
bloc.add(ExercisePlanUpdateUI(workoutTree: element)),
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc),
},
),
SizedBox(width: 20),
Flexible(
child:
InkWell(
child:
Text(
element.name,
textAlign: TextAlign.start,
style: TextStyle(fontSize: 12, color: Colors.black),
),
onTap: () => {
bloc.add(ExercisePlanUpdateUI(workoutTree: element)),
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc),
},
),
),
InkWell(
child:
element.selected && bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].change != null ?
Text(bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].repeats.toString() +
" x " + bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].weightEquation +
" " + element.exerciseType.unitQuantityUnit, style: TextStyle(fontSize: 9, color: Colors.green),) : Text(""),
onTap: () => {
bloc.add(ExercisePlanUpdateUI(workoutTree: element)),
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc),
},
),
IconButton(
padding: EdgeInsets.all(0),
icon: Icon(Icons.description, color: Colors.black12,),
onPressed: () {
},
),
]),
)
),
children: [
]
)
);
});
return list;
}
}

View File

@ -0,0 +1,322 @@
import 'dart:collection';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/exercise_type_widget.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/bloc/development_by_muscle/development_by_muscle_bloc.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/treeview/tree_view.dart';
import 'package:aitrainer_app/widgets/app_bar_common.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class MyDevelopmentMusclePage extends StatefulWidget {
@override
_MyDevelopmentMuscleState createState() => _MyDevelopmentMuscleState();
}
class _MyDevelopmentMuscleState extends State<MyDevelopmentMusclePage> with Common, Trans {
// ignore: close_sinks
DevelopmentByMuscleBloc bloc;
double cWidth;
@override
void initState() {
super.initState();
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance.addPostFrameCallback((_) {
BlocProvider.of<DevelopmentByMuscleBloc>(context).add(DevelopmentByMuscleLoad());
});
}
@override
Widget build(BuildContext context) {
bloc = BlocProvider.of<DevelopmentByMuscleBloc>(context);
cWidth = mediaSizeWidth(context);
setContext(context);
return Scaffold(
appBar: AppBarCommonNav(),
body: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: BlocConsumer<DevelopmentByMuscleBloc, DevelopmentByMuscleState>(
listener: (context, state) {
if (state is DevelopmentByMuscleErrorState) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
state.message,
),
backgroundColor: Colors.orange,
));
} else if (state is DevelopmentByMuscleLoadingState) {
LoadingDialog();
}
},
builder: (context, state) {
if (state is DevelopmentByMuscleStateInitial) {
return Container();
} else {
return TreeView(
startExpanded: false,
children: _getTreeChildren(bloc.workoutTreeRepository.sortedTree, bloc),
);
}
;
},
),
),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 1),
);
}
List<Widget> _getTreeChildren(SplayTreeMap tree, DevelopmentByMuscleBloc bloc) {
List<Widget> exerciseTypes = List();
Card explanation = Card(
color: Colors.white60,
child: Container(
padding: EdgeInsets.only(left: 10, right: 5, top: 12),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
Icon(
Icons.info,
color: Colors.orangeAccent,
),
Text(" "),
Text(
t("My Development By Muscle"),
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
],
),
Divider(
color: Colors.transparent,
),
Text(
t("Here you see you development in the last period."),
style: TextStyle(fontSize: 12, fontWeight: FontWeight.normal),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ChoiceChip(
avatar: Icon(Icons.bubble_chart,),
label: Text(t('Sum Of Mass')),
labelStyle: TextStyle(fontSize: 9,color: Colors.black),
selectedColor: Colors.lightBlueAccent,
selected: bloc.diagramType == DiagramType.sumMass,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDiagramTypeChange(diagramType: DiagramType.sumMass))
},
),
ChoiceChip(
avatar: Icon(Icons.accessibility_new),
label: Text(t('One Max Rep')),
labelStyle: TextStyle(fontSize: 9,color: Colors.black),
selectedColor: Colors.lightBlueAccent,
selected: bloc.diagramType == DiagramType.oneRepMax,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDiagramTypeChange(diagramType: DiagramType.oneRepMax))
},
),
ChoiceChip(
avatar: Icon(Icons.perm_device_information),
label: Text(t('Percent')),
labelStyle: TextStyle(fontSize: 9,color: Colors.black),
selectedColor: Colors.lightBlueAccent,
selected: bloc.diagramType == DiagramType.percent,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDiagramTypeChange(diagramType: DiagramType.percent))
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ChoiceChip(
avatar: Icon(Icons.timer),
label: Text(t('Detailed')),
labelStyle: TextStyle(fontSize: 9, color: Colors.black),
disabledColor: Colors.black26,
selectedColor: Colors.greenAccent,
selected: bloc.dateRate == DateRate.daily,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDateRateChange(dateRate: DateRate.daily))
},
),
ChoiceChip(
avatar: Icon(Icons.timer),
label: Text(t('Weekly')),
labelStyle: TextStyle(fontSize: 9, color: Colors.black),
selectedColor: Colors.greenAccent,
disabledColor: Colors.white12,
tooltip: "Heti bontás",
selected: bloc.dateRate == DateRate.weekly,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDateRateChange(dateRate: DateRate.weekly))
},
),
ChoiceChip(
avatar: Icon(Icons.timer),
label: Text(t('Monthly')),
labelStyle: TextStyle(fontSize: 9, color: Colors.black),
selectedColor: Colors.greenAccent,
disabledColor: Colors.black26,
selected: bloc.dateRate == DateRate.monthly,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDateRateChange(dateRate: DateRate.monthly))
},
),
ChoiceChip(
avatar: Icon(Icons.timer),
label: Text(t('Yearly')),
labelStyle: TextStyle(fontSize: 9, color: Colors.black),
selectedColor: Colors.greenAccent,
disabledColor: Colors.white70,
selected: bloc.dateRate == DateRate.yearly,
onSelected: (value) => {
bloc.add(DevelopmentByMuscleDateRateChange(dateRate: DateRate.yearly))
},
),
],
)
],
)
)
);
exerciseTypes.add(explanation);
tree.forEach((name, list) {
List<WorkoutMenuTree> listWorkoutMenuTree = list;
exerciseTypes.add(Container(
margin: const EdgeInsets.only(left: 4.0),
child: TreeViewChild(
startExpanded: true,
parent: _getExerciseWidget(exerciseTypeName: name),
children: _getChildList(list, bloc),
)));
});
return exerciseTypes;
}
Widget _getExerciseWidget({@required String exerciseTypeName, List<WorkoutMenuTree> list}) {
return ExerciseTypeWidget(exerciseTypeName: exerciseTypeName);
}
List<Widget> _getChildList(List<WorkoutMenuTree> listWorkoutTree, DevelopmentByMuscleBloc bloc) {
List<Widget> list = List();
listWorkoutTree.forEach((element) {
bool hasNoData = (bloc.listChartData[element.exerciseTypeId] == null);
String unit = " kg";
if ( bloc.diagramType == DiagramType.percent) {
unit = " %";
}
list.add(SizedBox(
width: cWidth * 0.85,
height: hasNoData ? 0 : 200,
child: Container(
padding: const EdgeInsets.only(left:5, top:5, right:5, bottom: 5),
color: Colors.white70,
child: Column(mainAxisSize: MainAxisSize.min, children: [
hasNoData
? Container()
: Text(
element.exerciseType.nameTranslation,
style: TextStyle(color: Colors.deepOrange),
),
hasNoData
? Container(
//child: Text("no data for " + element.exerciseType.nameTranslation),
)
: Expanded(
//fit: FlexFit.loose,
child: BarChart(
BarChartData(
alignment: BarChartAlignment.spaceAround,
barTouchData: BarTouchData(
touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.white70,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
return BarTooltipItem(
rod.y.toStringAsFixed(0) + unit,
TextStyle(color: Colors.black54, fontSize: 12, fontWeight: FontWeight.bold),
);
}),
),
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
textStyle: TextStyle(fontSize: 8, color: Colors.blueGrey),
getTitles: (double value) {
var date = new DateTime.fromMillisecondsSinceEpoch(value.toInt());
//String strDate = DateFormat('MM.dd.', AppLanguage().appLocal.toString()).format(date);
String strDate = bloc.getDatePart(date);
return strDate;
},
),
leftTitles: SideTitles(
showTitles: true,
textStyle: TextStyle(fontSize: 8, color: Colors.blueGrey),
interval: bloc.listChartData[element.exerciseTypeId] == null
? 100
: bloc.listChartData[element.exerciseTypeId].interval,
margin: 10,
getTitles: (double value) {
return value.toStringAsFixed(0) + unit;
})),
borderData: FlBorderData(
show: false,
),
gridData: FlGridData(
show: true,
checkToShowHorizontalLine: (value) =>
value % bloc.listChartData[element.exerciseTypeId].gridInterval == 0,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.black26,
strokeWidth: 0.5,
);
},
),
groupsSpace: 2,
barGroups: bloc.listChartData[element.exerciseTypeId] == null
? []
: bloc.listChartData[element.exerciseTypeId].data,
),
swapAnimationDuration: Duration(milliseconds: 1200),
),
)
]),
),
));
});
return list;
}
}

View File

@ -8,6 +8,7 @@ import 'package:aitrainer_app/widgets/app_bar_common.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MyDevelopmentPage extends StatefulWidget {
@override
@ -84,10 +85,10 @@ class _MyDevelopmentPage extends State<MyDevelopmentPage> with Trans {
fit: StackFit.passthrough,
overflow: Overflow.clip,
children: [
Image.asset('asset/image/lock.png',
/*Image.asset('asset/image/lock.png',
height: 40,
width: 40,
),
),*/
FlatButton(
padding: EdgeInsets.all(20),
textColor: Colors.white,
@ -95,6 +96,8 @@ class _MyDevelopmentPage extends State<MyDevelopmentPage> with Trans {
focusColor: Colors.blueAccent,
onPressed: () =>
{
Navigator.of(context).pushNamed('mydevelopmentMusclePage',
arguments: args)
},
child: Text(t("Development Of Muscles"),
style: TextStyle(fontSize: 18),)

View File

@ -151,7 +151,7 @@ class _AppBarNav extends State<AppBarNav> with SingleTickerProviderStateMixin {
lineHeight: 14.0,
percent: percent,
center: Text(
(percent * 100).toStringAsFixed(0) + "% finished",
(percent * 100).toStringAsFixed(0) + "% " + AppLocalizations.of(context).translate("finished"),
style: new TextStyle(fontSize: 12.0),
),
trailing: Icon(percent > 0.6 ? Icons.mood : Icons.mood_bad, color: colorAnim.value,),

View File

@ -75,7 +75,7 @@ class _AppBarCommonNav extends State<AppBarCommonNav> with SingleTickerProvider
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () =>
{
Navigator.of(context).pop()
Navigator.of(context).pushNamed('home')
},
)
);
@ -119,7 +119,7 @@ class _AppBarCommonNav extends State<AppBarCommonNav> with SingleTickerProvider
lineHeight: 14.0,
percent: percent,
center: Text(
(percent * 100).toStringAsFixed(0) + "% finished",
(percent * 100).toStringAsFixed(0) + "% " + AppLocalizations.of(context).translate("finished"),
style: new TextStyle(fontSize: 12.0),
),
trailing: Icon(percent > 0.6 ? Icons.mood : Icons.mood_bad, color: colorAnim.value,),

View File

@ -91,12 +91,12 @@ class _NawDrawerWidget extends State<BottomNavigator> {
break;
case 1:
//Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed('myDevelopment');
break;
case 2:
//Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed('myExercisePlan');
break;

View File

@ -0,0 +1,30 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ExerciseTypeWidget extends StatelessWidget {
final String exerciseTypeName;
//final DateTime lastModified;
ExerciseTypeWidget({@required this.exerciseTypeName});
@override
Widget build(BuildContext context) {
Widget exerciseTypeNameWidget = Text(
this.exerciseTypeName,
style: TextStyle(fontWeight: FontWeight.w800, color: Colors.blueAccent, backgroundColor: Colors.transparent),
);
Icon icon = Icon(Icons.person);
return Card(
color: Colors.white38,
shadowColor: Colors.black54,
elevation: 0.0,
child: ListTile(
leading: icon,
title: exerciseTypeNameWidget,
//subtitle: lastModifiedWidget,
),
);
}
}

View File

@ -45,9 +45,6 @@ class _HomePageState extends State<AitrainerHome> {
SettingsBloc settingsBloc = BlocProvider.of<SettingsBloc>(context);
settingsBloc.context = context;
sessionBloc.add(SessionStart(settingsBloc: settingsBloc));
//String lang = AppLanguage().appLocal.languageCode;
//print (" -- Loading delayed lang $lang");
//settingsBloc.add(SettingsChangeLanguage(language: lang));
}
}
});

View File

@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
@ -31,7 +31,7 @@ class MenuPageWidget extends StatelessWidget {
menuBloc.menuTreeRepository
.getBranch(menuBloc.parent)
.forEach((treeName, value) {
WorkoutTree workoutTree = value as WorkoutTree;
WorkoutMenuTree workoutTree = value as WorkoutMenuTree;
_columnChildren.add(Container(
padding: EdgeInsets.only(top: 16.0),
child: Center(
@ -83,7 +83,7 @@ class MenuPageWidget extends StatelessWidget {
}
void menuClick(
WorkoutTree workoutTree, MenuBloc menuBloc, BuildContext context) {
WorkoutMenuTree workoutTree, MenuBloc menuBloc, BuildContext context) {
print("Hi!, Menu clicked " + workoutTree.id.toString());
if (workoutTree.child == false) {
menuBloc.add(MenuTreeDown(parent: workoutTree.id));
@ -107,7 +107,7 @@ class MenuPageWidget extends StatelessWidget {
}
}
dynamic getShape(WorkoutTree workoutTree) {
dynamic getShape(WorkoutMenuTree workoutTree) {
bool base = workoutTree.base;
dynamic returnCode = (base == true)
? RoundedRectangleBorder(
@ -117,7 +117,7 @@ class MenuPageWidget extends StatelessWidget {
return returnCode;
}
dynamic _getButtonImage(WorkoutTree workoutTree) {
dynamic _getButtonImage(WorkoutMenuTree workoutTree) {
dynamic image;
try {
image = Image.asset(

View File

@ -225,6 +225,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
file:
dependency: transitive
description:
@ -239,6 +246,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.11"
fl_chart:
dependency: "direct main"
description:
name: fl_chart
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.1"
flutter:
dependency: "direct main"
description: flutter
@ -333,14 +347,14 @@ packages:
name: freezed
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.6"
version: "0.12.1"
freezed_annotation:
dependency: transitive
description:
name: freezed_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.0+1"
version: "0.12.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
@ -401,7 +415,7 @@ packages:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.14"
version: "2.1.18"
intl:
dependency: "direct dev"
description:
@ -429,7 +443,7 @@ packages:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.1.0"
json_rpc_2:
dependency: transitive
description:
@ -514,6 +528,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path_drawing:
dependency: transitive
description:
name: path_drawing
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.1+1"
path_parsing:
dependency: transitive
description:
name: path_parsing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
path_provider_linux:
dependency: transitive
description:
@ -528,6 +556,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+1"
pedantic:
dependency: transitive
description:
@ -541,7 +576,7 @@ packages:
name: percent_indicator
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.6"
version: "2.1.7+3"
petitparser:
dependency: transitive
description:
@ -639,7 +674,7 @@ packages:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.10"
version: "0.5.12"
shared_preferences_linux:
dependency: transitive
description:
@ -668,6 +703,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+1"
shelf:
dependency: transitive
description:
@ -707,7 +749,7 @@ packages:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6"
version: "0.9.7+1"
source_map_stack_trace:
dependency: transitive
description:
@ -876,6 +918,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.3"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.3"
xdg_directories:
dependency: transitive
description:
@ -889,7 +938,7 @@ packages:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.0"
version: "4.5.1"
yaml:
dependency: transitive
description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.1.0+21
version: 1.1.0+22
environment:
sdk: ">=2.7.0 <3.0.0"
@ -34,13 +34,14 @@ dependencies:
flutter_facebook_login: ^3.0.0
flutter_bloc: ^6.0.5
equatable: ^1.2.5
freezed: ^0.11.6
freezed: ^0.12.1
flutter_form_bloc: ^0.19.0
spider_chart: ^0.1.5
rainbow_color: ^0.1.1
percent_indicator: ^2.1.6
percent_indicator: ^2.1.7+3
gradient_bottom_navigation_bar: ^1.0.0+4
flutter_treeview: ^0.6.0+1
fl_chart: ^0.11.1
mockito: ^4.1.1
@ -58,7 +59,7 @@ dev_dependencies:
http: 0.12.1
intl: 0.16.1
shared_preferences: ^0.5.10
shared_preferences: ^0.5.12
flutter_launcher_icons: ^0.8.0

View File

@ -0,0 +1,193 @@
import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:test/test.dart';
import 'mocks.dart';
main() {
SimExercisePlanRepository _exercisePlanRepository;
ExercisePlanBloc bloc;
int _customerId;
Future<void> setUpPlan() async {
final String planName2 = "Test Plan2";
ExercisePlan plan2 = ExercisePlan(planName2, 101);
_exercisePlanRepository.setCustomerId(101);
_exercisePlanRepository.setExercisePlan(plan2);
ExercisePlanDetail detail3 = ExercisePlanDetail(3);
detail3.repeats = 23;
detail3.weightEquation = "60";
detail3.serie = 4;
_exercisePlanRepository.setActualPlanDetail(detail3);
_exercisePlanRepository.addDetailToPlan();
ExercisePlanDetail detail4 = ExercisePlanDetail(4);
detail4.repeats = 12;
detail4.weightEquation = "95";
detail4.serie = 3;
_exercisePlanRepository.setActualPlanDetail(detail4);
_exercisePlanRepository.addDetailToPlan();
await _exercisePlanRepository.saveExercisePlan();
}
setUp(() async {
_exercisePlanRepository = SimExercisePlanRepository();
WorkoutTreeRepository menuTreeRepository = WorkoutTreeRepository();
bloc = ExercisePlanBloc(menuTreeRepository: menuTreeRepository);
bloc.setExercisePlanRepository(_exercisePlanRepository);
_customerId = 62;
await setUpPlan();
});
/*
group('New Plan', () {
test('Get Data', () async {
});
test('Add bloc', () async {
});
test('Update bloc', () async {
});
test('Delete bloc', () async {
});
});*/
group('Existing Plan', () {
test('Get Data', () async {
bloc.customerId = 101;
bloc.add(ExercisePlanLoad());
final expectedResponse = [
ExercisePlanLoading(),
ExercisePlanReady()
];
expectLater(
bloc,
emitsInOrder(expectedResponse),
).then((_) {
expect(bloc.exercisePlanRepository.newPlan, false);
expect(bloc.exercisePlanRepository.exercisePlan.name, "Test Plan2" );
expect(bloc.exercisePlanRepository.exercisePlanDetails[4].weightEquation, "95");
expect(Cache().getMyExercisePlan().name, "Test Plan2");
expect(Cache().getMyExercisePlanDetails()[4].weightEquation, "95");
});
});
test('Add bloc', () async {
bloc.customerId = 101;
bloc.exercisePlanRepository.getLastExercisePlan();
bloc.exercisePlanRepository.getExercisePlanDetails();
ExercisePlanDetail detail4 = ExercisePlanDetail(5);
detail4.repeats = 20;
detail4.weightEquation = "55";
detail4.serie = 3;
bloc.add(ExercisePlanAddExercise(exercisePlanDetail: detail4));
final expectedResponse2 = [
ExercisePlanLoading(),
ExercisePlanReady()
];
expectLater(
bloc,
emitsInOrder(expectedResponse2),
).then((_) {
expect(bloc.exercisePlanRepository.newPlan, false);
expect(bloc.exercisePlanRepository.exercisePlan.customerId, 101);
expect(bloc.exercisePlanRepository.exercisePlanDetails.length, 3);
expect(bloc.exercisePlanRepository.exercisePlanDetails[5].repeats, 20);
expect(bloc.exercisePlanRepository.exercisePlanDetails[5].change, ExercisePlanDetailChange.add);
expect(Cache()
.getMyExercisePlan()
.name, "Test Plan2");
expect(Cache().getMyExercisePlanDetails()[5].weightEquation, "55");
expect(Cache().getMyExercisePlanDetails()[5].repeats, 20);
});
});
test('Update bloc', () async {
bloc.customerId = 101;
bloc.exercisePlanRepository.getLastExercisePlan();
bloc.exercisePlanRepository.getExercisePlanDetails();
bloc.exercisePlanRepository.exercisePlanDetails[3].repeats = 25;
bloc.add(ExercisePlanAddExercise(exercisePlanDetail: bloc.exercisePlanRepository.exercisePlanDetails[3]));
final expectedResponse2 = [
ExercisePlanLoading(),
ExercisePlanReady()
];
expectLater(
bloc,
emitsInOrder(expectedResponse2),
).then((_) {
expect(bloc.exercisePlanRepository.newPlan, false);
expect(bloc.exercisePlanRepository.exercisePlanDetails[3].repeats, 25);
expect(Cache().getMyExercisePlanDetails()[3].repeats, 25);
});
});
test('Delete bloc', () async {
bloc.customerId = 101;
bloc.exercisePlanRepository.getLastExercisePlan();
bloc.exercisePlanRepository.getExercisePlanDetails();
bloc.add(ExercisePlanRemoveExercise(exercisePlanDetail: bloc.exercisePlanRepository.exercisePlanDetails[3]));
final expectedResponse2 = [
ExercisePlanLoading(),
ExercisePlanReady()
];
expectLater(
bloc,
emitsInOrder(expectedResponse2),
).then((_) {
expect(bloc.exercisePlanRepository.newPlan, false);
expect(bloc.exercisePlanRepository.exercisePlanDetails.length, 2);
expect(bloc.exercisePlanRepository.exercisePlanDetails[3], isNull);
expect(Cache().getMyExercisePlanDetails()[3], isNull);
});
});
test('Test Trainee', () async {
/*bloc.customerId = 102;
bloc.exercisePlanRepository.customerId = 102;
bloc.add(ExercisePlanLoad());
final expectedResponse = [
ExercisePlanLoading(),
ExercisePlanReady()
];
expectLater(
bloc,
emitsInOrder(expectedResponse),
).then((_) {
expect(bloc.exercisePlanRepository.newPlan, true);
expect(bloc.exercisePlanRepository.exercisePlanDetails.length, 0);
});*/
});
});
}

View File

@ -0,0 +1,158 @@
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import 'mocks.dart';
main() {
SimExercisePlanRepository _exercisePlanRepository;
int _customerId;
Future<void> setUpPlan() async {
final String planName2 = "Test Plan2";
ExercisePlan plan2 = ExercisePlan(planName2, 101);
_exercisePlanRepository.setCustomerId(101);
_exercisePlanRepository.setExercisePlan(plan2);
ExercisePlanDetail detail3 = ExercisePlanDetail(3);
detail3.repeats = 23;
detail3.weightEquation = "60";
detail3.serie = 4;
_exercisePlanRepository.setActualPlanDetail(detail3);
_exercisePlanRepository.addDetailToPlan();
ExercisePlanDetail detail4 = ExercisePlanDetail(4);
detail4.repeats = 12;
detail4.weightEquation = "95";
detail4.serie = 3;
_exercisePlanRepository.setActualPlanDetail(detail4);
_exercisePlanRepository.addDetailToPlan();
await _exercisePlanRepository.saveExercisePlan();
}
setUp(() async {
_exercisePlanRepository = SimExercisePlanRepository();
_customerId = 62;
await setUpPlan();
});
group('New Plan', () {
test('add new plan and plan details and save', () async {
final String planName = "Boss Test Plan";
ExercisePlan plan = ExercisePlan(planName, _customerId);
_exercisePlanRepository.setExercisePlan(plan);
ExercisePlanDetail detail1 = ExercisePlanDetail(37);
detail1.repeats = 12;
detail1.weightEquation = "80";
detail1.serie = 4;
_exercisePlanRepository.setActualPlanDetail(detail1);
_exercisePlanRepository.addDetailToPlan();
ExercisePlanDetail detail2 = ExercisePlanDetail(55);
detail2.repeats = 13;
detail2.weightEquation = "55";
detail2.serie = 3;
_exercisePlanRepository.setActualPlanDetail(detail2);
_exercisePlanRepository.addDetailToPlan();
await _exercisePlanRepository.saveExercisePlan();
expect(_exercisePlanRepository.getExercisePlan().name, planName);
expect(_exercisePlanRepository.getExercisePlan().exercisePlanId > 0, true);
ExercisePlanDetail detail =
_exercisePlanRepository.getExercisePlanDetailByExerciseId(55);
expect(detail.exercisePlanId, _exercisePlanRepository.getExercisePlan().exercisePlanId);
});
test('save new plan and plan details second', () async {
int customerId = 100;
final String planName = "Boss2 Test Plan";
ExercisePlan plan = ExercisePlan(planName, customerId);
_exercisePlanRepository.setExercisePlan(plan);
ExercisePlanDetail detail1 = ExercisePlanDetail(12);
detail1.repeats = 10;
detail1.weightEquation = "40";
detail1.serie = 5;
_exercisePlanRepository.setActualPlanDetail(detail1);
_exercisePlanRepository.addDetailToPlan();
ExercisePlanDetail detail2 = ExercisePlanDetail(13);
detail2.repeats = 33;
detail2.weightEquation = "15";
detail2.serie = 3;
_exercisePlanRepository.setActualPlanDetail(detail2);
_exercisePlanRepository.addDetailToPlan();
await _exercisePlanRepository.saveExercisePlan();
expect(_exercisePlanRepository.getExercisePlan().name, planName);
expectLater(_exercisePlanRepository.getExercisePlan().exercisePlanId > 0, true);
ExercisePlanDetail detail = _exercisePlanRepository.getExercisePlanDetailByExerciseId(13);
expect(detail.exercisePlanId, _exercisePlanRepository.getExercisePlan().exercisePlanId);
expect(detail.repeats, 33);
});
});
group('Existing Plan', () {
test('Get Last Plan and Details from DB', () async {
ExercisePlan exercisePlan = await _exercisePlanRepository.getLastExercisePlan();
expect(exercisePlan.customerId,101);
await _exercisePlanRepository.getExercisePlanDetails();
expect(_exercisePlanRepository.exercisePlanDetails[3].repeats,23 );
expect(_exercisePlanRepository.exercisePlanDetails[4].weightEquation,"95" );
//Test Cache
expect(Cache().getMyExercisePlan().name,"Test Plan2");
expect(Cache().getMyExercisePlanDetails()[3].weightEquation,"60");
});
test('Add new PlanDetail', () async {
_exercisePlanRepository.newPlan = false;
ExercisePlanDetail detail4 = ExercisePlanDetail(5);
detail4.repeats = 6;
detail4.weightEquation = "105";
detail4.serie = 3;
_exercisePlanRepository.setActualPlanDetail(detail4);
_exercisePlanRepository.addDetailToPlan();
await _exercisePlanRepository.saveExercisePlan();
expect(_exercisePlanRepository.getExercisePlan().name, "Test Plan2");
expect(Cache().getMyExercisePlanDetails()[5].weightEquation, "105");
expect(Cache().getMyExercisePlanDetails()[5].repeats, 6);
});
test('Delete from PlanDetails', () async {
_exercisePlanRepository.removeExerciseTypeFromPlanByExerciseTypeId(4);
_exercisePlanRepository.saveExercisePlan();
expect(_exercisePlanRepository.exercisePlanDetails[4].change, ExercisePlanDetailChange.delete);
expect(Cache().getMyExercisePlanDetails()[4], isNull);
});
});
}

181
test/mocks.dart Normal file
View File

@ -0,0 +1,181 @@
import 'dart:collection';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/service/exercise_plan_service.dart';
import 'package:mockito/mockito.dart';
class MockExercisePlanApi extends Mock implements ExercisePlanApi {
static final MockExercisePlanApi _singleton = MockExercisePlanApi._internal();
factory MockExercisePlanApi() {
return _singleton;
}
MockExercisePlanApi._internal() {}
final List<ExercisePlan> memoryExercisePlan = List();
final List<ExercisePlanDetail> memoryExercisePlanDetail = List();
Future<ExercisePlan> saveExercisePlan(ExercisePlan plan) async {
memoryExercisePlan.add(plan);
plan.exercisePlanId = memoryExercisePlan.length;
return plan;
}
Future<ExercisePlanDetail> saveExercisePlanDetail(ExercisePlanDetail detail) async {
memoryExercisePlanDetail.add(detail);
detail.exercisePlanDetailId = memoryExercisePlanDetail.length;
return detail;
}
Future<ExercisePlanDetail> updateExercisePlanDetail(ExercisePlanDetail detail, int planDetailId) async {
ExercisePlanDetail updated;
memoryExercisePlanDetail.forEach((element) {
if ( element.exercisePlanDetailId == planDetailId ) {
element = detail;
}
});
return updated;
}
Future<void> deleteExercisePlanDetail(int exercisePlanId) async {
int index = -1;
int countIndex = 0;
memoryExercisePlanDetail.forEach((element) {
if ( element.exercisePlanId == exercisePlanId ) {
index = countIndex;
}
countIndex++;
});
if ( index > -1) {
memoryExercisePlanDetail.removeAt(index);
}
}
Future<List<ExercisePlanDetail>> getExercisePlanDetail(int exercisePlanId) async {
List<ExercisePlanDetail> foundList = List();
memoryExercisePlanDetail.forEach((element) {
if ( element.exercisePlanId == exercisePlanId ) {
foundList.add(element);
}
});
return foundList;
}
Future<ExercisePlan> getLastExercisePlan(int customerId) async {
ExercisePlan found;
memoryExercisePlan.forEach((element) {
if ( element.customerId == customerId ) {
found = element;
}
});
return found;
}
}
class SimExercisePlanRepository with ExercisePlanRepository {
Future<void> getExercisePlanDetails() async {
if (exercisePlan == null) {
ExercisePlan exercisePlan = await this.getLastExercisePlan();
if ( exercisePlan == null ) {
exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
return;
}
}
List<ExercisePlanDetail> list = List();
LinkedHashMap<int, ExercisePlanDetail> listCache = Cache().getMyExercisePlanDetails();
if ( listCache.length > 0) {
exercisePlanDetails = listCache;
return;
} else {
list = await MockExercisePlanApi().getExercisePlanDetail(exercisePlan.exercisePlanId);
}
exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
list.forEach((element) {
newPlan = false;
ExercisePlanDetail detail = element;
exercisePlanDetails[detail.exerciseTypeId] = detail;
});
Cache().setMyExercisePlanDetails(exercisePlanDetails);
return;
}
Future<ExercisePlan> getLastExercisePlan() async {
if ( customerId == 0) {
return null;
}
ExercisePlan myExercisePlan = Cache().getMyExercisePlan();
if ( myExercisePlan != null ) {
newPlan = false;
return myExercisePlan;
}
exercisePlan = await MockExercisePlanApi().getLastExercisePlan(customerId);
newPlan = (exercisePlan == null);
Cache().setMyExercisePlan(exercisePlan);
return exercisePlan;
}
Future<void> saveExercisePlan() async {
if ( exercisePlan == null ) {
if ( Cache().userLoggedIn == null ) {
throw Exception("please log in");
}
String exercisePlanName;
if ( this.customerId == Cache().userLoggedIn.customerId) {
exercisePlanName = Cache().userLoggedIn.name + " private";
} else {
exercisePlanName = Cache().getTrainee().name + " " + Cache().getTrainee().firstname + " private";
}
exercisePlan = ExercisePlan(exercisePlanName, this.customerId);
}
if ( newPlan ) {
exercisePlan.dateAdd = DateTime.now();
exercisePlan.private = true;
ExercisePlan savedExercisePlan
= await MockExercisePlanApi().saveExercisePlan(exercisePlan);
LinkedHashMap<int, ExercisePlanDetail> savedExercisePlanDetails = LinkedHashMap();
exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
exercisePlanDetail.exercisePlanId = savedExercisePlan.exercisePlanId;
ExercisePlanDetail savedDetail = await MockExercisePlanApi().saveExercisePlanDetail(exercisePlanDetail);
savedExercisePlanDetails[savedDetail.exerciseTypeId] = savedDetail;
});
exercisePlan = savedExercisePlan;
} else {
await MockExercisePlanApi().updateExercisePlan(exercisePlan, exercisePlan.exercisePlanId);
exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
if ( exercisePlanDetail.change == ExercisePlanDetailChange.delete ) {
await MockExercisePlanApi()
.deleteExercisePlanDetail(exercisePlanDetail.exercisePlanDetailId);
Cache().deleteMyExercisePlanDetail(exercisePlanDetail);
} else if ( exercisePlanDetail.change == ExercisePlanDetailChange.update ) {
await MockExercisePlanApi()
.updateExercisePlanDetail(exercisePlanDetail, exercisePlanDetail.exercisePlanDetailId);
Cache().updateMyExercisePlanDetail(exercisePlanDetail);
} else if ( exercisePlanDetail.change == ExercisePlanDetailChange.add ) {
await MockExercisePlanApi()
.saveExercisePlanDetail(exercisePlanDetail);
Cache().addToMyExercisePlanDetails(exercisePlanDetail);
}
});
}
}
}