wt1.1.2e NumberPicker

This commit is contained in:
Bossanyi Tibor 2020-10-21 15:08:15 +02:00
parent c83d224768
commit 7354611881
69 changed files with 2741 additions and 1271 deletions

View File

@ -33,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 28 compileSdkVersion 30
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
@ -44,12 +44,12 @@ android {
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.aitrainer.aitrainer_app" applicationId "com.aitrainer.aitrainer_app"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 30
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
multiDexEnabled true
} }
signingConfigs { signingConfigs {
@ -63,7 +63,6 @@ android {
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release signingConfig signingConfigs.release
} }
@ -76,6 +75,7 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-analytics:17.2.2' implementation 'com.google.firebase:firebase-analytics:17.6.0'
implementation 'com.facebook.android:facebook-login:[5,6)' implementation 'com.facebook.android:facebook-login:5.5.1'
implementation 'com.android.support:multidex:1.0.3'
} }

View File

@ -1,14 +1,14 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.72' ext.kotlin_version = '1.4.10'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath "com.android.tools.build:gradle:3.5.0" classpath "com.android.tools.build:gradle:4.0.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.gms:google-services:4.3.3" classpath "com.google.gms:google-services:4.3.4"
} }
} }

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,4 +1,5 @@
{ {
"OK": "OK",
"Network Error, please try again later": "Network Error, please try again later", "Network Error, please try again later": "Network Error, please try again later",
"Home": "Home", "Home": "Home",
"Customers": "Customers", "Customers": "Customers",
@ -117,9 +118,12 @@
"Your 1RM:":"Your 1RM:", "Your 1RM:":"Your 1RM:",
"Your Real 1RM:":"Your Real 1RM:", "Your Real 1RM:":"Your Real 1RM:",
"Check":"Check", "Check":"Check",
"1st Control Exercise:": "1st Control Exercise:", "Control Exercise:": "Control Exercise:",
"2nd Control Exercise:": "2nd Control Exercise:", "Summary of your test":"Summary of your test",
"3rd Control Exercise:": "3rd Control Exercise:", "Test":"Test",
"1st Control": "1st Control",
"2nd Control": "2nd Control",
"3rd Control": "3rd Control",
"My Development":"My Development", "My Development":"My Development",
"My Training Plan":"My Training Plan", "My Training Plan":"My Training Plan",
@ -144,7 +148,7 @@
"Edit My Custom Plan": "Edit My Custom Plan", "Edit My Custom Plan": "Edit My Custom Plan",
"Suggested Training Plan": "Suggested Training Plan", "Suggested Training Plan": "Suggested Training Plan",
"My Special Plan": "My Special Plan", "My Special Plan": "My Special Plan",
"My Arnold's Plan":"My Arnold's Plan", "My Arnold's Plan":"Stars Execercise Plan",
"My Trainee's Plan": "My Trainee's Plan", "My Trainee's Plan": "My Trainee's Plan",
"Execute My Trainee's Training Plan": "Execute My Trainee's Training Plan", "Execute My Trainee's Training Plan": "Execute My Trainee's Training Plan",
@ -164,6 +168,8 @@
"Monthly": "Monthly", "Monthly": "Monthly",
"Yearly": "Yearly", "Yearly": "Yearly",
"times!": "times!", "times!": "times!",
"Please repeat with ": "Please repeat with ",
"max times!": "max times!",
"Execute your active Exercise Plan!": "Execute your active Exercise Plan!", "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.", "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.",
@ -194,5 +200,9 @@
"Bring me there": "Bring me there", "Bring me there": "Bring me there",
"My Body Development": "My Body Development", "My Body Development": "My Body Development",
"You see here your whole body development by muscle groups.": "You see here your whole body development by muscle groups." "You see here your whole body development by muscle groups.": "You see here your whole body development by muscle groups.",
"Are you sure to logout?": "Are you sure to logout?",
"hu_with": " ",
"Are you sure to delete this exercise?": "Are you sure to delete this exercise?"
} }

View File

@ -1,4 +1,5 @@
{ {
"OK": "OK",
"Network Error, please try again later": "Hálózati hiba, kérlek próbáld meg később", "Network Error, please try again later": "Hálózati hiba, kérlek próbáld meg később",
"Home": "Főoldal", "Home": "Főoldal",
"Customers": "Ügyfelek", "Customers": "Ügyfelek",
@ -144,7 +145,7 @@
"Edit My Custom Plan": "Egyéni edzésterv", "Edit My Custom Plan": "Egyéni edzésterv",
"Suggested Training Plan": "Javasolt edzésterv", "Suggested Training Plan": "Javasolt edzésterv",
"My Special Plan": "Speciális edzésterv", "My Special Plan": "Speciális edzésterv",
"My Arnold's Plan": "Arnold edzésterve rám szabva", "My Arnold's Plan": "Sztárok edzésterve rám szabva",
"My Trainee's Plan" : "Kliensem edzésterve", "My Trainee's Plan" : "Kliensem edzésterve",
"Execute My Trainee's Training Plan": "Kliensem edzéstervének végrehajtása", "Execute My Trainee's Training Plan": "Kliensem edzéstervének végrehajtása",
@ -164,6 +165,8 @@
"Monthly": "Havi", "Monthly": "Havi",
"Yearly": "Éves", "Yearly": "Éves",
"times!": "ismétléssel!", "times!": "ismétléssel!",
"Please repeat with ": "Kérlek ismételd meg ",
"max times!": " max. számú ismétléssel!",
"Execute your active Exercise Plan!": "Hajtsd végre az aktív edzéstervedet", "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.", "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.",
@ -193,5 +196,16 @@
"Bring me there": "Vigyél oda", "Bring me there": "Vigyél oda",
"My Body Development": "Testem fejlődése", "My Body Development": "Testem fejlődése",
"You see here your whole body development by muscle groups.": "Itt láthatod a tested fejlődését izomcsoportonként" "You see here your whole body development by muscle groups.": "Itt láthatod a tested fejlődését izomcsoportonként",
"Are you sure to logout?": "Biztos, hogy kijelentkezel?",
"hu_with": "-mal",
"Control Exercise:": "Gyakorlat végrehajtása:",
"Test":"Teszt",
"1st Control": "1. kontrollgyakorlat",
"2nd Control": "2. kontrollgyakorlat",
"3rd Control": "3. kontrollgyakorlat",
"Summary of your test":"A teszt összefoglalása:",
"Are you sure to delete this exercise?": "Biztos, hogy törlöd a gyakorlatot?"
} }

View File

@ -34,6 +34,7 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
//route to Login Page //route to Login Page
} else if (event is AccountLogInFinished) { } else if (event is AccountLogInFinished) {
customerRepository.customer = event.customer; customerRepository.customer = event.customer;
this.loggedIn = true;
yield AccountLoggedIn(); yield AccountLoggedIn();
} else if (event is AccountLogout) { } else if (event is AccountLogout) {
await Cache().logout(); await Cache().logout();

View File

@ -188,7 +188,7 @@ class GroupChart extends GroupData with Calculate {
BarChartGroupData data = BarChartGroupData( BarChartGroupData data = BarChartGroupData(
x: exercise.dateAdd.millisecondsSinceEpoch, x: exercise.dateAdd.millisecondsSinceEpoch,
barRods: [ barRods: [
BarChartRodData(y: diagramValue, width: 12, color: Colors.lightBlue) BarChartRodData(y: diagramValue, width: 12, colors: [Colors.lightBlue, Colors.lightBlueAccent])
] ]
); );
_chartData.add(data); _chartData.add(data);

View File

@ -1,21 +0,0 @@
part of 'exercise_by_plan_bloc.dart';
@immutable
abstract class ExerciseByPlanEvent extends Equatable {
const ExerciseByPlanEvent();
@override
List<Object> get props => [];
}
class AddExerciseByPlanEvent extends ExerciseByPlanEvent {
final ExerciseType exerciseType;
const AddExerciseByPlanEvent({this.exerciseType});
@override
List<Object> get props => [exerciseType];
}
class ExerciseByPlanLoad extends ExerciseByPlanEvent {
const ExerciseByPlanLoad();
}

View File

@ -1,31 +0,0 @@
part of 'exercise_by_plan_bloc.dart';
@immutable
abstract class ExerciseByPlanState extends Equatable {
const ExerciseByPlanState();
@override
List<Object> get props => [];
}
class ExerciseByPlanStateInitial extends ExerciseByPlanState {
const ExerciseByPlanStateInitial();
}
class ExerciseByPlanLoading extends ExerciseByPlanState {
const ExerciseByPlanLoading();
}
// updated screen
class ExerciseByPlanReady extends ExerciseByPlanState {
const ExerciseByPlanReady();
}
// error splash screen
class ExerciseByPlanError extends ExerciseByPlanState {
final String message;
const ExerciseByPlanError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,91 @@
import 'dart:async';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'exercise_control_event.dart';
part 'exercise_control_state.dart';
class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlState> {
final ExerciseRepository exerciseRepository;
final bool readonly;
final double percentToCalculate;
int step = 1;
final List<double> repeats = List();
double initialRM;
double unitQuantity;
double quantity;
double origQuantity;
double firstQuantity; // quantity of the first test
double firstUnitQuantity; // unit quantity of the first test
@override
ExerciseControlBloc({this.exerciseRepository, this.readonly, this.percentToCalculate}) : super(ExerciseControlInitial()) {
firstUnitQuantity = exerciseRepository.exercise.unitQuantity;
firstQuantity = exerciseRepository.exercise.quantity;
repeats.add(firstUnitQuantity);
repeats.add(firstQuantity);
initialRM = this.calculate1RM(percent75: false);
unitQuantity = this.calculate1RM(percent75: true).roundToDouble();
quantity = percentToCalculate == 0.75 ? 12 : 30;
origQuantity = quantity;
exerciseRepository.setUnitQuantity(unitQuantity);
}
@override
Stream<ExerciseControlState> mapEventToState(ExerciseControlEvent event) async* {
try {
if (event is ExerciseControlLoad) {
yield ExerciseControlLoading();
step = 1;
yield ExerciseControlReady();
} else if (event is ExerciseControlQuantityChange ) {
yield ExerciseControlLoading();
if ( event.step == step) {
exerciseRepository.setQuantity(event.quantity);
quantity = event.quantity;
}
yield ExerciseControlReady();
} else if (event is ExerciseControlSubmit ) {
yield ExerciseControlLoading();
if ( event.step == step) {
step++;
//print("step " + step.toString() + " quantity " + quantity.toString() + " origQ: " + origQuantity.toString());
repeats.add(quantity);
quantity = origQuantity;
await exerciseRepository.addExercise();
exerciseRepository.setQuantity(quantity);
}
yield ExerciseControlReady();
}
} on Exception catch (e) {
yield ExerciseControlError(message: e.toString());
}
}
double calculate1RM({bool percent75}) {
if (exerciseRepository.exercise == null) {
exerciseRepository.getLastExercise();
}
double weight = exerciseRepository.exercise.unitQuantity;
double repeat = exerciseRepository.exercise.quantity;
if ( weight == 0 || repeat == 0) {
return 0;
}
double rmWendler = weight * repeat * 0.0333 + weight;
double rmOconner = weight * (1 + repeat / 40);
double average = (rmWendler + rmOconner) / 2;
return percent75 ? average * this.percentToCalculate : average;
}
}

View File

@ -0,0 +1,24 @@
part of 'exercise_control_bloc.dart';
@immutable
abstract class ExerciseControlEvent extends Equatable {
const ExerciseControlEvent();
@override
List<Object> get props => [];
}
class ExerciseControlLoad extends ExerciseControlEvent {
const ExerciseControlLoad();
}
class ExerciseControlQuantityChange extends ExerciseControlEvent {
final double quantity;
final int step;
const ExerciseControlQuantityChange({this.quantity, this.step});
}
class ExerciseControlSubmit extends ExerciseControlEvent {
final int step;
const ExerciseControlSubmit({this.step});
}

View File

@ -0,0 +1,30 @@
part of 'exercise_control_bloc.dart';
@immutable
abstract class ExerciseControlState extends Equatable {
const ExerciseControlState();
@override
List<Object> get props => [];
}
class ExerciseControlInitial extends ExerciseControlState {
const ExerciseControlInitial();
}
class ExerciseControlLoading extends ExerciseControlState {
const ExerciseControlLoading();
}
class ExerciseControlReady extends ExerciseControlState {
const ExerciseControlReady();
}
class ExerciseControlError extends ExerciseControlState {
final String message;
const ExerciseControlError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -1,132 +0,0 @@
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class ExerciseControlFormBloc extends FormBloc<String, String> {
final ExerciseRepository exerciseRepository;
int step = 1;
final double percentToCalculate;
final bool readonly;
String times;
final initialRMField = TextFieldBloc(
);
final quantity1Field = TextFieldBloc(
);
final unitQuantity1Field = TextFieldBloc(
);
final quantity2Field = TextFieldBloc(
);
final unitQuantity2Field = TextFieldBloc(
);
final quantity3Field = TextFieldBloc(
);
final unitQuantity3Field = TextFieldBloc(
);
ExerciseControlFormBloc({this.exerciseRepository, this.percentToCalculate, this.readonly}) {
addFieldBlocs(fieldBlocs: [
initialRMField,
quantity1Field,
unitQuantity1Field,
quantity2Field,
unitQuantity2Field,
quantity3Field,
unitQuantity3Field,
]);
initialRMField.updateInitialValue(calculate1RM(percent75: false).toStringAsFixed(0));
unitQuantity1Field.updateInitialValue(calculate1RM(percent75: true).toStringAsFixed(0));
unitQuantity2Field.updateInitialValue(calculate1RM(percent75: true).toStringAsFixed(0));
unitQuantity3Field.updateInitialValue(calculate1RM(percent75: true).toStringAsFixed(0));
quantity1Field.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setQuantity(current.valueToDouble);
exerciseRepository.setUnitQuantity(unitQuantity1Field.valueToDouble);
});
quantity2Field.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setQuantity(current.valueToDouble);
exerciseRepository.setUnitQuantity(unitQuantity2Field.valueToDouble);
});
quantity3Field.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setQuantity(current.valueToDouble);
exerciseRepository.setUnitQuantity(unitQuantity3Field.valueToDouble);
});
times = percentToCalculate == 0.5 ? "30-35" : "12";
}
@override
void onLoading() {
step = 1;
super.onLoading();
}
@override
void onSubmitting() async {
print("on Submitting Custom form");
try {
emitLoading(progress: 30);
if ( step == 1) {
//unitQuantity2Field.updateInitialValue(calculate1RM(percent75: true).toStringAsFixed(0));
//unitQuantity3Field.updateInitialValue(calculate1RM(percent75: true).toStringAsFixed(0));
step = 2;
} else if ( step == 2) {
//unitQuantity3Field.updateInitialValue(calculate1RM(percent75: true).toStringAsFixed(0));
step = 3;
} else if ( step == 3 ) {
//updatedRMField.updateInitialValue(calculate1RM(percent75: false).toStringAsFixed(0));
}
if ( ! readonly ) {
await exerciseRepository.addExercise();
}
emitSuccess(canSubmitAgain: true);
} on Exception catch (ex) {
emitFailure(failureResponse: ex.toString());
}
}
double calculate1RM({bool percent75}) {
if (exerciseRepository.exercise == null) {
exerciseRepository.getLastExercise();
}
double weight = exerciseRepository.exercise.unitQuantity;
double repeat = exerciseRepository.exercise.quantity;
if ( weight == 0 || repeat == 0) {
return 0;
}
double rmWendler = weight * repeat * 0.0333 + weight;
double rmOconner = weight * (1 + repeat / 40);
double average = (rmWendler + rmOconner) / 2;
return percent75 ? average * this.percentToCalculate : average;
}
//@override
Future<void> close() {
initialRMField.close();
quantity1Field.close();
unitQuantity1Field.close();
quantity2Field.close();
unitQuantity2Field.close();
quantity3Field.close();
unitQuantity3Field.close();
return super.close();
}
}

View File

@ -7,16 +7,16 @@ import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
part 'exercise_by_plan_event.dart'; part 'exercise_execute_plan_event.dart';
part 'exercise_by_plan_state.dart'; part 'exercise_execute_plan_state.dart';
class ExerciseByPlanBloc extends Bloc<ExerciseByPlanEvent, ExerciseByPlanState> { class ExerciseExecutePlanBloc extends Bloc<ExerciseExecutePlanEvent, ExerciseExecutePlanState> {
final WorkoutTreeRepository menuTreeRepository; final WorkoutTreeRepository menuTreeRepository;
final ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository(); final ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository();
int customerId; int customerId;
@override @override
ExerciseByPlanBloc({this.menuTreeRepository}) : super(ExerciseByPlanStateInitial()); ExerciseExecutePlanBloc({this.menuTreeRepository}) : super(ExerciseByPlanStateInitial());
Future<void> getData() async { Future<void> getData() async {
exercisePlanRepository.setCustomerId(customerId); exercisePlanRepository.setCustomerId(customerId);
@ -39,7 +39,7 @@ class ExerciseByPlanBloc extends Bloc<ExerciseByPlanEvent, ExerciseByPlanState>
} }
@override @override
Stream<ExerciseByPlanState> mapEventToState(ExerciseByPlanEvent event) async* { Stream<ExerciseExecutePlanState> mapEventToState(ExerciseExecutePlanEvent event) async* {
try { try {
if (event is ExerciseByPlanLoad) { if (event is ExerciseByPlanLoad) {
yield ExerciseByPlanLoading(); yield ExerciseByPlanLoading();

View File

@ -0,0 +1,21 @@
part of 'exercise_execute_plan_bloc.dart';
@immutable
abstract class ExerciseExecutePlanEvent extends Equatable {
const ExerciseExecutePlanEvent();
@override
List<Object> get props => [];
}
class AddExerciseByPlanEvent extends ExerciseExecutePlanEvent {
final ExerciseType exerciseType;
const AddExerciseByPlanEvent({this.exerciseType});
@override
List<Object> get props => [exerciseType];
}
class ExerciseByPlanLoad extends ExerciseExecutePlanEvent {
const ExerciseByPlanLoad();
}

View File

@ -0,0 +1,31 @@
part of 'exercise_execute_plan_bloc.dart';
@immutable
abstract class ExerciseExecutePlanState extends Equatable {
const ExerciseExecutePlanState();
@override
List<Object> get props => [];
}
class ExerciseByPlanStateInitial extends ExerciseExecutePlanState {
const ExerciseByPlanStateInitial();
}
class ExerciseByPlanLoading extends ExerciseExecutePlanState {
const ExerciseByPlanLoading();
}
// updated screen
class ExerciseByPlanReady extends ExerciseExecutePlanState {
const ExerciseByPlanReady();
}
// error splash screen
class ExerciseByPlanError extends ExerciseExecutePlanState {
final String message;
const ExerciseByPlanError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,89 @@
import 'dart:async';
import 'package:aitrainer_app/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.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:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'exercise_execute_plan_add_event.dart';
part 'exercise_execute_plan_add_state.dart';
class ExerciseExecutePlanAddBloc extends Bloc<ExerciseExecutePlanAddEvent, ExerciseExecutePlanAddState> {
final ExerciseRepository exerciseRepository;
final ExercisePlanRepository exercisePlanRepository;
final WorkoutMenuTree workoutTree;
final ExerciseExecutePlanBloc planBloc;
final int customerId;
Customer customer;
int step = 1;
int countSteps = 1;
double quantity;
double unitQuantity;
double scrollOffset = 0;
@override
ExerciseExecutePlanAddBloc({
this.exerciseRepository,
this.exercisePlanRepository,
this.customerId,
this.workoutTree,
this.planBloc}): super(ExerciseExecutePlanAddInitial()) {
exerciseRepository.exerciseType = workoutTree.exerciseType;
if ( Cache().userLoggedIn.customerId == customerId) {
customer = Cache().userLoggedIn;
} else if ( Cache().getTrainee().customerId == customerId) {
customer = Cache().getTrainee();
}
exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType);
exerciseRepository.customer = customer;
countSteps = exercisePlanRepository.getActualPlanDetail().serie;
unitQuantity = double.parse(exercisePlanRepository.getActualPlanDetail().weightEquation);
quantity = exercisePlanRepository.getActualPlanDetail().repeats.toDouble();
exerciseRepository.setQuantity(quantity);
exerciseRepository.setUnitQuantity(unitQuantity);
}
@override
Stream<ExerciseExecutePlanAddState> mapEventToState(ExerciseExecutePlanAddEvent event) async* {
try {
if (event is ExerciseExecutePlanAddLoad) {
yield ExerciseExecutePlanAddLoading();
yield ExerciseExecutePlanAddReady();
} else if (event is ExerciseExecutePlanAddChangeQuantity) {
yield ExerciseExecutePlanAddLoading();
quantity = event.quantity;
exerciseRepository.setQuantity(quantity);
yield ExerciseExecutePlanAddReady();
} else if (event is ExerciseExecutePlanAddChangeUnitQuantity) {
yield ExerciseExecutePlanAddLoading();
unitQuantity = event.quantity;
exerciseRepository.setUnitQuantity(unitQuantity);
yield ExerciseExecutePlanAddReady();
} else if (event is ExerciseExecutePlanAddSubmit) {
yield ExerciseExecutePlanAddLoading();
exerciseRepository.exercise.exercisePlanDetailId =
exercisePlanRepository.getActualPlanDetail().exercisePlanDetailId;
exerciseRepository.exercise.unit = workoutTree.exerciseType.unit;
workoutTree.executed = true;
print("On Submitting Exercise Execute Plan Add " + exerciseRepository.exercise.toJson().toString());
await exerciseRepository.addExercise();
step++;
scrollOffset = step * 200.0;
planBloc.add(ExerciseByPlanLoad());
yield ExerciseExecutePlanAddReady();
}
} on Exception catch (e) {
yield ExerciseExecutePlanAddError(message: e.toString());
}
}
}

View File

@ -0,0 +1,33 @@
part of 'exercise_execute_plan_add_bloc.dart';
@immutable
abstract class ExerciseExecutePlanAddEvent extends Equatable {
const ExerciseExecutePlanAddEvent();
@override
List<Object> get props => [];
}
class ExerciseExecutePlanAddLoad extends ExerciseExecutePlanAddEvent {
const ExerciseExecutePlanAddLoad();
}
class ExerciseExecutePlanAddChangeQuantity extends ExerciseExecutePlanAddEvent {
final double quantity;
const ExerciseExecutePlanAddChangeQuantity({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExerciseExecutePlanAddChangeUnitQuantity extends ExerciseExecutePlanAddEvent {
final double quantity;
const ExerciseExecutePlanAddChangeUnitQuantity({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExerciseExecutePlanAddSubmit extends ExerciseExecutePlanAddEvent {
const ExerciseExecutePlanAddSubmit();
}

View File

@ -0,0 +1,31 @@
part of 'exercise_execute_plan_add_bloc.dart';
@immutable
abstract class ExerciseExecutePlanAddState extends Equatable {
const ExerciseExecutePlanAddState();
@override
List<Object> get props => [];
}
class ExerciseExecutePlanAddInitial extends ExerciseExecutePlanAddState {
const ExerciseExecutePlanAddInitial();
}
class ExerciseExecutePlanAddLoading extends ExerciseExecutePlanAddState {
const ExerciseExecutePlanAddLoading();
}
// updated screen
class ExerciseExecutePlanAddReady extends ExerciseExecutePlanAddState {
const ExerciseExecutePlanAddReady();
}
// error splash screen
class ExerciseExecutePlanAddError extends ExerciseExecutePlanAddState {
final String message;
const ExerciseExecutePlanAddError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -1,68 +0,0 @@
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'menu/menu_bloc.dart';
class ExerciseFormBloc extends FormBloc<String, String> {
final ExerciseRepository exerciseRepository;
final MenuBloc menuBloc;
final quantityField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final unitField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final unitQuantityField = TextFieldBloc();
final unitQuantityUnitField = TextFieldBloc();
ExerciseFormBloc({this.exerciseRepository, this.menuBloc}) {
addFieldBlocs(fieldBlocs: [
quantityField,
unitField,
unitQuantityField,
unitQuantityUnitField
]);
quantityField.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setQuantity(current.valueToDouble);
});
unitField.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setUnit(current.value);
});
unitQuantityField.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setUnitQuantity(current.valueToDouble);
});
}
@override
void onSubmitting() async {
try {
emitLoading(progress: 30);
// Emit either Loaded or Error
await exerciseRepository.addExercise();
menuBloc.add(MenuTreeDown(parent: 0));
emitSuccess(canSubmitAgain: false);
} on Exception catch (ex) {
emitFailure(failureResponse: ex.toString());
}
}
@override
Future<void> close() {
unitQuantityField.close();
unitQuantityUnitField.close();
return super.close();
}
}

View File

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'exercise_log_event.dart';
part 'exercise_log_state.dart';
class ExerciseLogBloc extends Bloc<ExerciseLogEvent, ExerciseLogState> {
final ExerciseRepository exerciseRepository;
@override
ExerciseLogBloc({this.exerciseRepository}) : super(ExerciseLogInitial());
@override
Stream<ExerciseLogState> mapEventToState(ExerciseLogEvent event) async* {
try {
if (event is ExerciseLogLoad) {
yield ExerciseLogLoading();
yield ExerciseLogReady();
} else if ( event is ExerciseLogDelete ) {
yield ExerciseLogLoading();
exerciseRepository.exerciseList.remove(event.exercise);
await exerciseRepository.deleteExercise(event.exercise);
yield ExerciseLogReady();
}
} on Exception catch (e) {
yield ExerciseLogError(message: e.toString());
}
}
}

View File

@ -0,0 +1,22 @@
part of 'exercise_log_bloc.dart';
@immutable
abstract class ExerciseLogEvent extends Equatable {
const ExerciseLogEvent();
@override
List<Object> get props => [];
}
class ExerciseLogLoad extends ExerciseLogEvent {
const ExerciseLogLoad();
}
class ExerciseLogDelete extends ExerciseLogEvent {
final Exercise exercise;
const ExerciseLogDelete({this.exercise});
@override
List<Object> get props => [exercise];
}

View File

@ -0,0 +1,30 @@
part of 'exercise_log_bloc.dart';
@immutable
abstract class ExerciseLogState extends Equatable {
const ExerciseLogState();
@override
List<Object> get props => [];
}
class ExerciseLogInitial extends ExerciseLogState {
const ExerciseLogInitial();
}
class ExerciseLogLoading extends ExerciseLogState {
const ExerciseLogLoading();
}
class ExerciseLogReady extends ExerciseLogState {
const ExerciseLogReady();
}
class ExerciseLogError extends ExerciseLogState {
final String message;
const ExerciseLogError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,53 @@
import 'dart:async';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:meta/meta.dart';
part 'exercise_new_event.dart';
part 'exercise_new_state.dart';
class ExerciseNewBloc extends Bloc<ExerciseNewEvent, ExerciseNewState> {
final ExerciseRepository exerciseRepository;
final MenuBloc menuBloc;
double quantity = 12;
double unitQuantity = 30;
@override
ExerciseNewBloc({this.exerciseRepository, this.menuBloc, ExerciseType exerciseType}) : super(ExerciseNewInitial()) {
exerciseRepository.setUnit(exerciseType.unit);
}
@override
Stream<ExerciseNewState> mapEventToState(ExerciseNewEvent event) async* {
try {
if (event is ExerciseNewLoad) {
yield ExerciseNewLoading();
yield ExerciseNewReady();
} else if ( event is ExerciseNewQuantityChange ) {
yield ExerciseNewLoading();
exerciseRepository.setQuantity(event.quantity);
quantity = event.quantity;
yield ExerciseNewReady();
} else if ( event is ExerciseNewQuantityUnitChange ) {
yield ExerciseNewLoading();
exerciseRepository.setUnitQuantity(event.quantity);
unitQuantity = event.quantity;
yield ExerciseNewReady();
} else if ( event is ExerciseNewSubmit ) {
yield ExerciseNewLoading();
await exerciseRepository.addExercise();
menuBloc.add(MenuTreeDown(parent: 0));
yield ExerciseNewReady();
}
} on Exception catch (e) {
yield ExerciseNewError(message: e.toString());
}
}
}

View File

@ -0,0 +1,34 @@
part of 'exercise_new_bloc.dart';
@immutable
abstract class ExerciseNewEvent extends Equatable {
const ExerciseNewEvent();
@override
List<Object> get props => [];
}
class ExerciseNewLoad extends ExerciseNewEvent {
const ExerciseNewLoad();
}
class ExerciseNewQuantityChange extends ExerciseNewEvent {
final double quantity;
const ExerciseNewQuantityChange({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExerciseNewQuantityUnitChange extends ExerciseNewEvent {
final double quantity;
const ExerciseNewQuantityUnitChange({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExerciseNewSubmit extends ExerciseNewEvent {
const ExerciseNewSubmit();
}

View File

@ -0,0 +1,29 @@
part of 'exercise_new_bloc.dart';
@immutable
abstract class ExerciseNewState extends Equatable {
const ExerciseNewState();
@override
List<Object> get props => [];
}
class ExerciseNewInitial extends ExerciseNewState {
const ExerciseNewInitial();
}
class ExerciseNewLoading extends ExerciseNewState {
const ExerciseNewLoading();
}
class ExerciseNewReady extends ExerciseNewState {
const ExerciseNewReady();
}
class ExerciseNewError extends ExerciseNewState {
final String message;
const ExerciseNewError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -30,7 +30,8 @@ class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
listWorkoutTree.forEach((workoutTree) { listWorkoutTree.forEach((workoutTree) {
workoutTree.selected = false; workoutTree.selected = false;
if (exercisePlanRepository.getExercisePlanDetailSize() > 0) { if (exercisePlanRepository.getExercisePlanDetailSize() > 0) {
if (exercisePlanRepository.getExercisePlanDetailByExerciseId(workoutTree.exerciseTypeId) != null) { ExercisePlanDetail planDetail = exercisePlanRepository.getExercisePlanDetailByExerciseId(workoutTree.exerciseTypeId);
if (planDetail != null && planDetail.change != ExercisePlanDetailChange.deleted) {
workoutTree.selected = true; workoutTree.selected = true;
} }
} }
@ -66,23 +67,22 @@ class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
ExercisePlanDetail planDetail = event.exercisePlanDetail; ExercisePlanDetail planDetail = event.exercisePlanDetail;
exercisePlanRepository.actualPlanDetail = planDetail; 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) { if (exercisePlanRepository.getExercisePlanDetailSize() != 0) {
await exercisePlanRepository.saveExercisePlan(); await exercisePlanRepository.saveExercisePlan();
} }
this.menuTreeRepository.sortedTree.forEach((key, value) {
List<WorkoutMenuTree> listTreeItem = value;
listTreeItem.forEach((element) {
if ( element.exerciseType.exerciseTypeId == planDetail.exerciseTypeId) {
element.selected = true;
}
});
});
yield ExercisePlanReady(); yield ExercisePlanReady();
} }
else if (event is ExercisePlanRemoveExercise) { else if (event is ExercisePlanRemoveExercise) {
yield ExercisePlanLoading(); yield ExercisePlanLoading();
ExercisePlanDetail planDetail = event.exercisePlanDetail; ExercisePlanDetail planDetail = event.exercisePlanDetail;
@ -101,6 +101,7 @@ class ExercisePlanBloc extends Bloc<ExercisePlanEvent, ExercisePlanState> {
exercisePlanRepository.saveExercisePlan(); exercisePlanRepository.saveExercisePlan();
} }
yield ExercisePlanReady(); yield ExercisePlanReady();
} }

View File

@ -0,0 +1,87 @@
import 'dart:async';
import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'exercise_plan_custom_add_event.dart';
part 'exercise_plan_custom_add_state.dart';
class ExercisePlanDetailChange {
static const String add = "add";
static const String delete = "delete";
static const String update = "update";
static const String deleted = "deleted";
}
class ExercisePlanCustomAddBloc extends Bloc<ExercisePlanCustomAddEvent, ExercisePlanCustomAddState> {
final ExercisePlanRepository exercisePlanRepository;
final ExercisePlanBloc planBloc;
final WorkoutMenuTree workoutMenuTree;
double quantity;
double serie;
double quantityUnit;
@override
ExercisePlanCustomAddBloc({this.exercisePlanRepository, this.planBloc, this.workoutMenuTree}) : super(ExercisePlanCustomAddInitial()) {
exercisePlanRepository.setActualPlanDetailByExerciseType(workoutMenuTree.exerciseType);
quantity = exercisePlanRepository.getActualPlanDetail().repeats != null ?
exercisePlanRepository.getActualPlanDetail().repeats.toDouble() : 12;
serie = exercisePlanRepository.getActualPlanDetail().serie != null ?
exercisePlanRepository.getActualPlanDetail().serie.toDouble() : 3;
quantityUnit = exercisePlanRepository.getActualPlanDetail().weightEquation != null ?
double.parse(exercisePlanRepository.getActualPlanDetail().weightEquation) : 30;
exercisePlanRepository.getActualPlanDetail().weightEquation = quantityUnit.toString();
exercisePlanRepository.getActualPlanDetail().serie = serie.toInt();
exercisePlanRepository.getActualPlanDetail().repeats = quantity.toInt();
}
@override
Stream<ExercisePlanCustomAddState> mapEventToState(ExercisePlanCustomAddEvent event) async* {
try {
if (event is ExercisePlanCustomAddLoad) {
yield ExercisePlanCustomAddLoading();
yield ExercisePlanCustomAddReady();
} else if ( event is ExercisePlanCustomAddChangeSerie ) {
yield ExercisePlanCustomAddLoading();
serie = event.quantity;
exercisePlanRepository.getActualPlanDetail().serie = event.quantity.toInt();
yield ExercisePlanCustomAddReady();
} else if ( event is ExercisePlanCustomAddChangeQuantity ) {
yield ExercisePlanCustomAddLoading();
quantity = event.quantity;
exercisePlanRepository.getActualPlanDetail().repeats = event.quantity.toInt();
yield ExercisePlanCustomAddReady();
} else if ( event is ExercisePlanCustomAddChangeQuantityUnit ) {
yield ExercisePlanCustomAddLoading();
quantityUnit = event.quantity;
exercisePlanRepository.getActualPlanDetail().weightEquation = event.quantity.toStringAsFixed(0);
yield ExercisePlanCustomAddReady();
} else if ( event is ExercisePlanCustomAddSubmit) {
yield ExercisePlanCustomAddLoading();
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()));
yield ExercisePlanCustomAddReady();
} else if ( event is ExercisePlanCustomAddRemove ) {
yield ExercisePlanCustomAddLoading();
print("Remove " + exercisePlanRepository.getActualPlanDetail().exerciseType.name);
exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.delete;
planBloc.add(ExercisePlanRemoveExercise(exercisePlanDetail: exercisePlanRepository.getActualPlanDetail()));
yield ExercisePlanCustomAddReady();
}
} on Exception catch (e) {
yield ExercisePlanCustomAddError(message: e.toString());
}
}
}

View File

@ -0,0 +1,45 @@
part of 'exercise_plan_custom_add_bloc.dart';
@immutable
abstract class ExercisePlanCustomAddEvent extends Equatable {
const ExercisePlanCustomAddEvent();
@override
List<Object> get props => [];
}
class ExercisePlanCustomAddLoad extends ExercisePlanCustomAddEvent {
const ExercisePlanCustomAddLoad();
}
class ExercisePlanCustomAddChangeSerie extends ExercisePlanCustomAddEvent {
final double quantity;
const ExercisePlanCustomAddChangeSerie({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExercisePlanCustomAddChangeQuantity extends ExercisePlanCustomAddEvent {
final double quantity;
const ExercisePlanCustomAddChangeQuantity({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExercisePlanCustomAddChangeQuantityUnit extends ExercisePlanCustomAddEvent {
final double quantity;
const ExercisePlanCustomAddChangeQuantityUnit({this.quantity});
@override
List<Object> get props => [quantity];
}
class ExercisePlanCustomAddSubmit extends ExercisePlanCustomAddEvent {
const ExercisePlanCustomAddSubmit();
}
class ExercisePlanCustomAddRemove extends ExercisePlanCustomAddEvent {
const ExercisePlanCustomAddRemove();
}

View File

@ -0,0 +1,30 @@
part of 'exercise_plan_custom_add_bloc.dart';
@immutable
abstract class ExercisePlanCustomAddState extends Equatable {
const ExercisePlanCustomAddState();
@override
List<Object> get props => [];
}
class ExercisePlanCustomAddInitial extends ExercisePlanCustomAddState {
const ExercisePlanCustomAddInitial();
}
class ExercisePlanCustomAddLoading extends ExercisePlanCustomAddState {
const ExercisePlanCustomAddLoading();
}
class ExercisePlanCustomAddReady extends ExercisePlanCustomAddState {
const ExercisePlanCustomAddReady();
}
class ExercisePlanCustomAddError extends ExercisePlanCustomAddState {
final String message;
const ExercisePlanCustomAddError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -1,99 +0,0 @@
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';
class ExercisePlanCustomerFormBloc extends FormBloc<String, String> {
final ExercisePlanRepository exercisePlanRepository;
final ExercisePlanBloc planBloc;
final serieField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final quantityField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final weightField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final exerciseTypeField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
ExercisePlanCustomerFormBloc({this.exercisePlanRepository, this.planBloc}) {
addFieldBlocs(fieldBlocs: [
quantityField,
serieField,
weightField
]);
String repeatsInitial = exercisePlanRepository.getActualPlanDetail() != null &&
exercisePlanRepository.getActualPlanDetail().repeats != null ?
exercisePlanRepository.getActualPlanDetail().repeats.toString() : "12";
quantityField.updateInitialValue(repeatsInitial);
String serieInitial = exercisePlanRepository.getActualPlanDetail() != null &&
exercisePlanRepository.getActualPlanDetail().serie != null ?
exercisePlanRepository.getActualPlanDetail().serie.toString() : "3";
serieField.updateInitialValue(serieInitial);
String weightInitial = exercisePlanRepository.getActualPlanDetail() != null &&
exercisePlanRepository.getActualPlanDetail().weightEquation != null ?
exercisePlanRepository.getActualPlanDetail().weightEquation : "30";
weightField.updateInitialValue(weightInitial);
quantityField.onValueChanges(onData: (previous, current) async* {
exercisePlanRepository.getActualPlanDetail().repeats = current.valueToInt;
});
serieField.onValueChanges(onData: (previous, current) async* {
exercisePlanRepository.getActualPlanDetail().serie = current.valueToInt;
});
weightField.onValueChanges(onData: (previous, current) async* {
exercisePlanRepository.getActualPlanDetail().weightEquation = current.value;
});
}
@override
void onSubmitting() async {
print("On Submitting Custom Plan form");
try {
emitLoading(progress: 30);
// Emit either Loaded or Error
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) {
emitFailure(failureResponse: ex.toString());
}
}
//@override
Future<void> close() {
quantityField.close();
serieField.close();
weightField.close();
exerciseTypeField.close();
return super.close();
}
}

View File

@ -0,0 +1,772 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:infinite_listview/infinite_listview.dart';
/// Created by Marcin Szałek
///Define a text mapper to transform the text displayed by the picker
typedef String TextMapper(String numberText);
///NumberPicker is a widget designed to pick a number between #minValue and #maxValue
class NumberPicker extends StatelessWidget {
///height of every list element for normal number picker
///width of every list element for horizontal number picker
static const double kDefaultItemExtent = 60.0;
///width of list view for normal number picker
///height of list view for horizontal number picker
static const double kDefaultListViewCrossAxisSize = 120.0;
///constructor for horizontal number picker
NumberPicker.horizontal({
Key key,
@required int initialValue,
@required this.minValue,
@required this.maxValue,
@required this.onChanged,
this.textMapper,
this.itemExtent = kDefaultItemExtent,
this.listViewHeight = kDefaultListViewCrossAxisSize,
this.step = 1,
this.zeroPad = false,
this.highlightSelectedValue = true,
this.decoration,
this.haptics = false,
this.textStyle,
this.textStyleHighlighted
}) : assert(initialValue != null),
assert(minValue != null),
assert(maxValue != null),
assert(maxValue > minValue),
assert(initialValue >= minValue && initialValue <= maxValue),
assert(step > 0),
selectedIntValue = initialValue,
selectedDecimalValue = -1,
decimalPlaces = 0,
intScrollController = ScrollController(
initialScrollOffset: (initialValue - minValue) ~/ step * itemExtent,
),
scrollDirection = Axis.horizontal,
decimalScrollController = null,
listViewWidth = 3 * itemExtent,
infiniteLoop = false,
integerItemCount = (maxValue - minValue) ~/ step + 1,
super(key: key);
///constructor for integer number picker
NumberPicker.integer({
Key key,
@required int initialValue,
@required this.minValue,
@required this.maxValue,
@required this.onChanged,
this.textMapper,
this.itemExtent = kDefaultItemExtent,
this.listViewWidth = kDefaultListViewCrossAxisSize,
this.step = 1,
this.scrollDirection = Axis.vertical,
this.infiniteLoop = false,
this.zeroPad = false,
this.highlightSelectedValue = true,
this.decoration,
this.haptics = false,
this.textStyle,
this.textStyleHighlighted
}) : assert(initialValue != null),
assert(minValue != null),
assert(maxValue != null),
assert(maxValue > minValue),
assert(initialValue >= minValue && initialValue <= maxValue),
assert(step > 0),
assert(scrollDirection != null),
selectedIntValue = initialValue,
selectedDecimalValue = -1,
decimalPlaces = 0,
intScrollController = infiniteLoop
? InfiniteScrollController(
initialScrollOffset:
(initialValue - minValue) ~/ step * itemExtent,
)
: ScrollController(
initialScrollOffset:
(initialValue - minValue) ~/ step * itemExtent,
),
decimalScrollController = null,
listViewHeight = 3 * itemExtent,
integerItemCount = (maxValue - minValue) ~/ step + 1,
super(key: key);
///constructor for decimal number picker
NumberPicker.decimal({
Key key,
@required double initialValue,
@required this.minValue,
@required this.maxValue,
@required this.onChanged,
this.textMapper,
this.decimalPlaces = 1,
this.itemExtent = kDefaultItemExtent,
this.listViewWidth = kDefaultListViewCrossAxisSize,
this.highlightSelectedValue = true,
this.decoration,
this.haptics = false,
this.textStyle,
this.textStyleHighlighted
}) : assert(initialValue != null),
assert(minValue != null),
assert(maxValue != null),
assert(decimalPlaces != null && decimalPlaces > 0),
assert(maxValue > minValue),
assert(initialValue >= minValue && initialValue <= maxValue),
selectedIntValue = initialValue.floor(),
selectedDecimalValue = ((initialValue - initialValue.floorToDouble()) *
math.pow(10, decimalPlaces))
.round(),
intScrollController = ScrollController(
initialScrollOffset: (initialValue.floor() - minValue) * itemExtent,
),
decimalScrollController = ScrollController(
initialScrollOffset: ((initialValue - initialValue.floorToDouble()) *
math.pow(10, decimalPlaces))
.roundToDouble() *
itemExtent,
),
listViewHeight = 3 * itemExtent,
step = 1,
scrollDirection = Axis.vertical,
integerItemCount = maxValue.floor() - minValue.floor() + 1,
infiniteLoop = false,
zeroPad = false,
super(key: key);
///called when selected value changes
final ValueChanged<num> onChanged;
///min value user can pick
final int minValue;
///max value user can pick
final int maxValue;
///build the text of each item on the picker
final TextMapper textMapper;
///inidcates how many decimal places to show
/// e.g. 0=>[1,2,3...], 1=>[1.0, 1.1, 1.2...] 2=>[1.00, 1.01, 1.02...]
final int decimalPlaces;
///height of every list element in pixels
final double itemExtent;
///height of list view in pixels
final double listViewHeight;
///width of list view in pixels
final double listViewWidth;
///ScrollController used for integer list
final ScrollController intScrollController;
///ScrollController used for decimal list
final ScrollController decimalScrollController;
///Currently selected integer value
final int selectedIntValue;
///Currently selected decimal value
final int selectedDecimalValue;
///If currently selected value should be highlighted
final bool highlightSelectedValue;
///Decoration to apply to central box where the selected value is placed
final Decoration decoration;
///Step between elements. Only for integer datePicker
///Examples:
/// if step is 100 the following elements may be 100, 200, 300...
/// if min=0, max=6, step=3, then items will be 0, 3 and 6
/// if min=0, max=5, step=3, then items will be 0 and 3.
final int step;
/// Direction of scrolling
final Axis scrollDirection;
///Repeat values infinitely
final bool infiniteLoop;
///Pads displayed integer values up to the length of maxValue
final bool zeroPad;
///Amount of items
final int integerItemCount;
///Whether to trigger haptic pulses or not
final bool haptics;
///TextStyle of the non-highlighted numbers
final TextStyle textStyle;
///TextStyle of the highlighted numbers
final TextStyle textStyleHighlighted;
//
//----------------------------- PUBLIC ------------------------------
//
/// Used to animate integer number picker to new selected value
void animateInt(int valueToSelect) {
int diff = valueToSelect - minValue;
int index = diff ~/ step;
animateIntToIndex(index);
}
/// Used to animate integer number picker to new selected index
void animateIntToIndex(int index) {
_animate(intScrollController, index * itemExtent);
}
/// Used to animate decimal part of double value to new selected value
void animateDecimal(int decimalValue) {
_animate(decimalScrollController, decimalValue * itemExtent);
}
/// Used to animate decimal number picker to selected value
void animateDecimalAndInteger(double valueToSelect) {
animateInt(valueToSelect.floor());
animateDecimal(((valueToSelect - valueToSelect.floorToDouble()) *
math.pow(10, decimalPlaces))
.round());
}
//
//----------------------------- VIEWS -----------------------------
//
///main widget
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
if (infiniteLoop) {
return _integerInfiniteListView(themeData);
}
if (decimalPlaces == 0) {
return _integerListView(themeData);
} else {
return Row(
children: <Widget>[
_integerListView(themeData),
_decimalListView(themeData),
],
mainAxisAlignment: MainAxisAlignment.center,
);
}
}
Widget _integerListView(ThemeData themeData) {
TextStyle defaultStyle = textStyle == null ?
themeData.textTheme.body1 : textStyle;
TextStyle selectedStyle = textStyleHighlighted == null ?
themeData.textTheme.headline.copyWith(color: themeData.accentColor)
: textStyleHighlighted;
var listItemCount = integerItemCount + 2;
return Listener(
onPointerUp: (ev) {
///used to detect that user stopped scrolling
if (intScrollController.position.activity is HoldScrollActivity) {
animateInt(selectedIntValue);
}
},
child: NotificationListener(
child: Container(
height: listViewHeight,
width: listViewWidth,
child: Stack(
children: <Widget>[
ListView.builder(
scrollDirection: scrollDirection,
controller: intScrollController,
itemExtent: itemExtent,
itemCount: listItemCount,
cacheExtent: _calculateCacheExtent(listItemCount),
itemBuilder: (BuildContext context, int index) {
final int value = _intValueFromIndex(index);
//define special style for selected (middle) element
final TextStyle itemStyle =
value == selectedIntValue && highlightSelectedValue
? selectedStyle
: defaultStyle;
bool isExtra = index == 0 || index == listItemCount - 1;
return isExtra
? Container() //empty first and last element
: Center(
child: Text(
getDisplayedValue(value),
style: itemStyle,
),
);
},
),
_NumberPickerSelectedItemDecoration(
axis: scrollDirection,
itemExtent: itemExtent,
decoration: decoration,
),
],
),
),
onNotification: _onIntegerNotification,
),
);
}
Widget _decimalListView(ThemeData themeData) {
TextStyle defaultStyle = textStyle == null ?
themeData.textTheme.body1 : textStyle;
TextStyle selectedStyle = textStyleHighlighted == null ?
themeData.textTheme.headline.copyWith(color: themeData.accentColor)
: textStyleHighlighted;
int decimalItemCount =
selectedIntValue == maxValue ? 3 : math.pow(10, decimalPlaces) + 2;
return Listener(
onPointerUp: (ev) {
///used to detect that user stopped scrolling
if (decimalScrollController.position.activity is HoldScrollActivity) {
animateDecimal(selectedDecimalValue);
}
},
child: NotificationListener(
child: Container(
height: listViewHeight,
width: listViewWidth,
child: Stack(
children: <Widget>[
ListView.builder(
controller: decimalScrollController,
itemExtent: itemExtent,
itemCount: decimalItemCount,
itemBuilder: (BuildContext context, int index) {
final int value = index - 1;
//define special style for selected (middle) element
final TextStyle itemStyle =
value == selectedDecimalValue && highlightSelectedValue
? selectedStyle
: defaultStyle;
bool isExtra = index == 0 || index == decimalItemCount - 1;
return isExtra
? Container() //empty first and last element
: Center(
child: Text(
value.toString().padLeft(decimalPlaces, '0'),
style: itemStyle,
),
);
},
),
_NumberPickerSelectedItemDecoration(
axis: scrollDirection,
itemExtent: itemExtent,
decoration: decoration,
),
],
),
),
onNotification: _onDecimalNotification,
),
);
}
Widget _integerInfiniteListView(ThemeData themeData) {
TextStyle defaultStyle = textStyle == null ?
themeData.textTheme.body1 : textStyle;
TextStyle selectedStyle = textStyleHighlighted == null ?
themeData.textTheme.headline.copyWith(color: themeData.accentColor)
: textStyleHighlighted;
return Listener(
onPointerUp: (ev) {
///used to detect that user stopped scrolling
if (intScrollController.position.activity is HoldScrollActivity) {
_animateIntWhenUserStoppedScrolling(selectedIntValue);
}
},
child: NotificationListener(
child: Container(
height: listViewHeight,
width: listViewWidth,
child: Stack(
children: <Widget>[
InfiniteListView.builder(
controller: intScrollController,
itemExtent: itemExtent,
itemBuilder: (BuildContext context, int index) {
final int value = _intValueFromIndex(index);
//define special style for selected (middle) element
final TextStyle itemStyle =
value == selectedIntValue && highlightSelectedValue
? selectedStyle
: defaultStyle;
return Center(
child: Text(
getDisplayedValue(value),
style: itemStyle,
),
);
},
),
_NumberPickerSelectedItemDecoration(
axis: scrollDirection,
itemExtent: itemExtent,
decoration: decoration,
),
],
),
),
onNotification: _onIntegerNotification,
),
);
}
String getDisplayedValue(int value) {
final text = zeroPad
? value.toString().padLeft(maxValue.toString().length, '0')
: value.toString();
return textMapper != null ? textMapper(text) : text;
}
//
// ----------------------------- LOGIC -----------------------------
//
int _intValueFromIndex(int index) {
index--;
index %= integerItemCount;
return minValue + index * step;
}
bool _onIntegerNotification(Notification notification) {
if (notification is ScrollNotification) {
//calculate
int intIndexOfMiddleElement =
(notification.metrics.pixels / itemExtent).round();
if (!infiniteLoop) {
intIndexOfMiddleElement =
intIndexOfMiddleElement.clamp(0, integerItemCount - 1);
}
int intValueInTheMiddle = _intValueFromIndex(intIndexOfMiddleElement + 1);
intValueInTheMiddle = _normalizeIntegerMiddleValue(intValueInTheMiddle);
if (_userStoppedScrolling(notification, intScrollController)) {
//center selected value
animateIntToIndex(intIndexOfMiddleElement);
}
//update selection
if (intValueInTheMiddle != selectedIntValue) {
num newValue;
if (decimalPlaces == 0) {
//return integer value
newValue = (intValueInTheMiddle);
} else {
if (intValueInTheMiddle == maxValue) {
//if new value is maxValue, then return that value and ignore decimal
newValue = (intValueInTheMiddle.toDouble());
animateDecimal(0);
} else {
//return integer+decimal
double decimalPart = _toDecimal(selectedDecimalValue);
newValue = ((intValueInTheMiddle + decimalPart).toDouble());
}
}
if (haptics) {
HapticFeedback.selectionClick();
}
onChanged(newValue);
}
}
return true;
}
bool _onDecimalNotification(Notification notification) {
if (notification is ScrollNotification) {
//calculate middle value
int indexOfMiddleElement =
(notification.metrics.pixels + listViewHeight / 2) ~/ itemExtent;
int decimalValueInTheMiddle = indexOfMiddleElement - 1;
decimalValueInTheMiddle =
_normalizeDecimalMiddleValue(decimalValueInTheMiddle);
if (_userStoppedScrolling(notification, decimalScrollController)) {
//center selected value
animateDecimal(decimalValueInTheMiddle);
}
//update selection
if (selectedIntValue != maxValue &&
decimalValueInTheMiddle != selectedDecimalValue) {
double decimalPart = _toDecimal(decimalValueInTheMiddle);
double newValue = ((selectedIntValue + decimalPart).toDouble());
if (haptics) {
HapticFeedback.selectionClick();
}
onChanged(newValue);
}
}
return true;
}
///There was a bug, when if there was small integer range, e.g. from 1 to 5,
///When user scrolled to the top, whole listview got displayed.
///To prevent this we are calculating cacheExtent by our own so it gets smaller if number of items is smaller
double _calculateCacheExtent(int itemCount) {
double cacheExtent = 250.0; //default cache extent
if ((itemCount - 2) * kDefaultItemExtent <= cacheExtent) {
cacheExtent = ((itemCount - 3) * kDefaultItemExtent);
}
return cacheExtent;
}
///When overscroll occurs on iOS,
///we can end up with value not in the range between [minValue] and [maxValue]
///To avoid going out of range, we change values out of range to border values.
int _normalizeMiddleValue(int valueInTheMiddle, int min, int max) {
return math.max(math.min(valueInTheMiddle, max), min);
}
int _normalizeIntegerMiddleValue(int integerValueInTheMiddle) {
//make sure that max is a multiple of step
int max = (maxValue ~/ step) * step;
return _normalizeMiddleValue(integerValueInTheMiddle, minValue, max);
}
int _normalizeDecimalMiddleValue(int decimalValueInTheMiddle) {
return _normalizeMiddleValue(
decimalValueInTheMiddle, 0, math.pow(10, decimalPlaces) - 1);
}
///indicates if user has stopped scrolling so we can center value in the middle
bool _userStoppedScrolling(
Notification notification,
ScrollController scrollController,
) {
return notification is UserScrollNotification &&
notification.direction == ScrollDirection.idle &&
scrollController.position.activity is! HoldScrollActivity;
}
/// Allows to find currently selected element index and animate this element
/// Use it only when user manually stops scrolling in infinite loop
void _animateIntWhenUserStoppedScrolling(int valueToSelect) {
// estimated index of currently selected element based on offset and item extent
int currentlySelectedElementIndex =
intScrollController.offset ~/ itemExtent;
// when more(less) than half of the top(bottom) element is hidden
// then we should increment(decrement) index in case of positive(negative) offset
if (intScrollController.offset > 0 &&
intScrollController.offset % itemExtent > itemExtent / 2) {
currentlySelectedElementIndex++;
} else if (intScrollController.offset < 0 &&
intScrollController.offset % itemExtent < itemExtent / 2) {
currentlySelectedElementIndex--;
}
animateIntToIndex(currentlySelectedElementIndex);
}
///converts integer indicator of decimal value to double
///e.g. decimalPlaces = 1, value = 4 >>> result = 0.4
/// decimalPlaces = 2, value = 12 >>> result = 0.12
double _toDecimal(int decimalValueAsInteger) {
return double.parse((decimalValueAsInteger * math.pow(10, -decimalPlaces))
.toStringAsFixed(decimalPlaces));
}
///scroll to selected value
_animate(ScrollController scrollController, double value) {
scrollController.animateTo(
value,
duration: Duration(seconds: 1),
curve: ElasticOutCurve(),
);
}
}
class _NumberPickerSelectedItemDecoration extends StatelessWidget {
final Axis axis;
final double itemExtent;
final Decoration decoration;
const _NumberPickerSelectedItemDecoration(
{Key key,
@required this.axis,
@required this.itemExtent,
@required this.decoration})
: super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: IgnorePointer(
child: Container(
width: isVertical ? double.infinity : itemExtent,
height: isVertical ? itemExtent : double.infinity,
decoration: decoration,
),
),
);
}
bool get isVertical => axis == Axis.vertical;
}
///Returns AlertDialog as a Widget so it is designed to be used in showDialog method
class NumberPickerDialog extends StatefulWidget {
final int minValue;
final int maxValue;
final int initialIntegerValue;
final double initialDoubleValue;
final int decimalPlaces;
final Widget title;
final EdgeInsets titlePadding;
final Widget confirmWidget;
final Widget cancelWidget;
final int step;
final bool infiniteLoop;
final bool zeroPad;
final bool highlightSelectedValue;
final Decoration decoration;
final TextMapper textMapper;
final bool haptics;
///constructor for integer values
NumberPickerDialog.integer({
@required this.minValue,
@required this.maxValue,
@required this.initialIntegerValue,
this.title,
this.titlePadding,
this.step = 1,
this.infiniteLoop = false,
this.zeroPad = false,
this.highlightSelectedValue = true,
this.decoration,
this.textMapper,
this.haptics = false,
Widget confirmWidget,
Widget cancelWidget,
}) : confirmWidget = confirmWidget ?? Text("OK"),
cancelWidget = cancelWidget ?? Text("CANCEL"),
decimalPlaces = 0,
initialDoubleValue = -1.0;
///constructor for decimal values
NumberPickerDialog.decimal({
@required this.minValue,
@required this.maxValue,
@required this.initialDoubleValue,
this.decimalPlaces = 1,
this.title,
this.titlePadding,
this.highlightSelectedValue = true,
this.decoration,
this.textMapper,
this.haptics = false,
Widget confirmWidget,
Widget cancelWidget,
}) : confirmWidget = confirmWidget ?? Text("OK"),
cancelWidget = cancelWidget ?? Text("CANCEL"),
initialIntegerValue = -1,
step = 1,
infiniteLoop = false,
zeroPad = false;
@override
State<NumberPickerDialog> createState() => _NumberPickerDialogControllerState(
initialIntegerValue, initialDoubleValue);
}
class _NumberPickerDialogControllerState extends State<NumberPickerDialog> {
int selectedIntValue;
double selectedDoubleValue;
_NumberPickerDialogControllerState(
this.selectedIntValue, this.selectedDoubleValue);
void _handleValueChanged(num value) {
if (value is int) {
setState(() => selectedIntValue = value);
} else {
setState(() => selectedDoubleValue = value);
}
}
NumberPicker _buildNumberPicker() {
if (widget.decimalPlaces > 0) {
return NumberPicker.decimal(
initialValue: selectedDoubleValue,
minValue: widget.minValue,
maxValue: widget.maxValue,
decimalPlaces: widget.decimalPlaces,
highlightSelectedValue: widget.highlightSelectedValue,
decoration: widget.decoration,
onChanged: _handleValueChanged,
textMapper: widget.textMapper,
haptics: widget.haptics,
);
} else {
return NumberPicker.integer(
initialValue: selectedIntValue,
minValue: widget.minValue,
maxValue: widget.maxValue,
step: widget.step,
infiniteLoop: widget.infiniteLoop,
zeroPad: widget.zeroPad,
highlightSelectedValue: widget.highlightSelectedValue,
decoration: widget.decoration,
onChanged: _handleValueChanged,
textMapper: widget.textMapper,
haptics: widget.haptics,
);
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: widget.title,
titlePadding: widget.titlePadding,
content: _buildNumberPicker(),
actions: [
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: widget.cancelWidget,
),
FlatButton(
onPressed: () => Navigator.of(context).pop(widget.decimalPlaces > 0
? selectedDoubleValue
: selectedIntValue),
child: widget.confirmWidget),
],
);
}
}

View File

@ -1,8 +1,11 @@
/// Tree view widget library /// Tree view widget library
library tree_view; library tree_view;
import 'package:aitrainer_app/util/common.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'dart:math' as math;
class TreeView extends InheritedWidget { class TreeView extends InheritedWidget {
final List<Widget> children; final List<Widget> children;
@ -39,7 +42,7 @@ class TreeView extends InheritedWidget {
class _TreeViewData extends StatelessWidget { class _TreeViewData extends StatelessWidget {
final List<Widget> children; final List<Widget> children;
const _TreeViewData({ _TreeViewData({
this.children, this.children,
}); });
@ -90,13 +93,30 @@ class TreeViewChild extends StatefulWidget {
} }
} }
class TreeViewChildState extends State<TreeViewChild> { class TreeViewChildState extends State<TreeViewChild> with Common, SingleTickerProviderStateMixin {
bool isExpanded; bool isExpanded;
final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
Color _color;
double _opacity = 0;
/* List<Widget> listWidgets; */
AnimationController _controller;
Animation<double> sizeAnimation;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
isExpanded = widget.startExpanded; isExpanded = widget.startExpanded;
_color = Colors.transparent;
_opacity = 0;
/* listWidgets = List.from(widget.children); */
/* _controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
sizeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);*/
} }
@override @override
@ -114,22 +134,107 @@ class TreeViewChildState extends State<TreeViewChild> {
child: widget.parent, child: widget.parent,
onTap: widget.onTap ?? () => toggleExpanded(), onTap: widget.onTap ?? () => toggleExpanded(),
), ),
AnimatedContainer( Flexible(
duration: Duration(milliseconds: 400), child: Container(
child: isExpanded /*animation: _controller,
builder: (BuildContext context, Widget child) {
return Transform.scale(
scale: _controller.value,
child: child,
);
},*/
/* color: _color,
duration: Duration(seconds: 2),
curve: Curves.easeInOut,*/
//height: double.infinity,
/*child: isExpanded //animateChildren()
? Column( ? Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: widget.children, children: widget.children,
) )
: Offstage(), : Offstage(),*/
), child:
/*AnimatedCrossFade(
firstChild: Column(
mainAxisSize: MainAxisSize.min,
children: widget.children,
),
secondChild: Offstage(),
duration: Duration(milliseconds:800),
crossFadeState: isExpanded ? CrossFadeState.showFirst : CrossFadeState.showSecond,
)*/
AnimatedSwitcher(
duration: Duration(milliseconds:900),
reverseDuration: Duration(milliseconds:500),
switchInCurve: Curves.easeIn,
child: isExpanded ? Column(
mainAxisSize: MainAxisSize.min,
children: widget.children,
) : Offstage(),
),
),
)
], ],
); );
} }
/*AnimatedList animateChildren() {
return AnimatedList(
shrinkWrap: true,
key: listKey,
initialItemCount: listWidgets.length,
itemBuilder: (context, index, animation) {
return slideIt(animation, listWidgets[index]);
},
);
}
Widget slideIt(Animation<double> animation, Widget element) {
return SizeTransition(
sizeFactor: animation,
axis: Axis.vertical,
child: element,
);
}*/
void toggleExpanded() { void toggleExpanded() {
setState(() { setState(() {
this.isExpanded = !this.isExpanded; this.isExpanded = !this.isExpanded;
_color = isExpanded ? Colors.black12 : Colors.transparent;
_opacity = isExpanded ? 1 : 0;
/* if ( isExpanded == false ) {
_removeAllItems();
} else {
//_addAllItems();
listWidgets = List.from(widget.children);
}*/
}); });
} }
/* void _removeAllItems() {
final int itemCount = widget.children.length;
for (var i = 0; i < itemCount; i++) {
Widget itemToRemove = listWidgets[0];
listKey.currentState.removeItem(0,
(BuildContext context, Animation<double> animation) => slideIt(animation, itemToRemove),
duration: const Duration(milliseconds: 250),
);
listWidgets.removeAt(0);
}
}
void _addAllItems() {
final int itemCount = widget.children.length;
for (var i = 0; i < itemCount; i++) {
listKey.currentState.insertItem(0);
listWidgets.insert(0, widget.children[i]);
}
}*/
} }

View File

@ -9,9 +9,9 @@ import 'package:aitrainer_app/view/customer_fitness_page.dart';
import 'package:aitrainer_app/view/customer_goal_page.dart'; import 'package:aitrainer_app/view/customer_goal_page.dart';
import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart';
import 'package:aitrainer_app/view/customer_welcome_page.dart'; import 'package:aitrainer_app/view/customer_welcome_page.dart';
import 'package:aitrainer_app/view/exercise_add_by_plan_page.dart';
import 'package:aitrainer_app/view/exercise_control_page.dart'; 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_execute_page.dart';
import 'package:aitrainer_app/view/exercise_execute_plan_add_page.dart';
import 'package:aitrainer_app/view/exercise_log_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_custom_page.dart';
import 'package:aitrainer_app/view/exercise_plan_custom_detail_add_page.dart'; import 'package:aitrainer_app/view/exercise_plan_custom_detail_add_page.dart';
@ -27,6 +27,8 @@ import 'package:aitrainer_app/view/myexcercise_plan_page.dart';
import 'package:aitrainer_app/view/registration.dart'; import 'package:aitrainer_app/view/registration.dart';
import 'package:aitrainer_app/view/settings.dart'; import 'package:aitrainer_app/view/settings.dart';
import 'package:aitrainer_app/widgets/home.dart'; import 'package:aitrainer_app/widgets/home.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -37,7 +39,7 @@ import 'package:sentry/sentry.dart';
import 'bloc/account/account_bloc.dart'; import 'bloc/account/account_bloc.dart';
import 'bloc/body_development/body_development_bloc.dart'; import 'bloc/body_development/body_development_bloc.dart';
import 'bloc/development_by_muscle/development_by_muscle_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_execute_plan/exercise_execute_plan_bloc.dart';
import 'bloc/exercise_plan/exercise_plan_bloc.dart'; import 'bloc/exercise_plan/exercise_plan_bloc.dart';
import 'bloc/menu/menu_bloc.dart'; import 'bloc/menu/menu_bloc.dart';
import 'bloc/session/session_bloc.dart'; import 'bloc/session/session_bloc.dart';
@ -127,8 +129,8 @@ Future<Null> main() async {
BlocProvider<ExercisePlanBloc>( BlocProvider<ExercisePlanBloc>(
create: (BuildContext context) => ExercisePlanBloc(menuTreeRepository: menuTreeRepository), create: (BuildContext context) => ExercisePlanBloc(menuTreeRepository: menuTreeRepository),
), ),
BlocProvider<ExerciseByPlanBloc>( BlocProvider<ExerciseExecutePlanBloc>(
create: (BuildContext context) => ExerciseByPlanBloc( create: (BuildContext context) => ExerciseExecutePlanBloc(
menuTreeRepository: menuTreeRepository), menuTreeRepository: menuTreeRepository),
), ),
BlocProvider<DevelopmentByMuscleBloc>( BlocProvider<DevelopmentByMuscleBloc>(
@ -155,6 +157,7 @@ class AitrainerApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
final FirebaseAnalytics analytics = FirebaseAnalytics();
return MaterialApp( return MaterialApp(
localizationsDelegates: [ localizationsDelegates: [
// ... app-specific localization delegate[s] here // ... app-specific localization delegate[s] here
@ -205,8 +208,8 @@ class AitrainerApp extends StatelessWidget {
'exerciseLogPage': (context) => ExerciseLogPage(), 'exerciseLogPage': (context) => ExerciseLogPage(),
'exercisePlanCustomPage': (context) => ExercisePlanCustomPage(), 'exercisePlanCustomPage': (context) => ExercisePlanCustomPage(),
'exercisePlanDetailAdd': (context) => ExercisePlanDetailAddPage(), 'exercisePlanDetailAdd': (context) => ExercisePlanDetailAddPage(),
'exerciseByPlanPage': (context) => ExerciseByPlanPage(), 'exerciseExecutePlanPage': (context) => ExerciseExecutePage(),
'exerciseAddByPlanPage': (context) => ExerciseAddByPlanPage(), 'exerciseExecuteAddPage': (context) => ExerciseExecutePlanAddPage(),
'mydevelopmentMusclePage': (context) => MyDevelopmentMusclePage(), 'mydevelopmentMusclePage': (context) => MyDevelopmentMusclePage(),
'mydevelopmentBodyPage': (context) => MyDevelopmentBodyPage(), 'mydevelopmentBodyPage': (context) => MyDevelopmentBodyPage(),
}, },
@ -220,6 +223,9 @@ class AitrainerApp extends StatelessWidget {
bodyText1: TextStyle(fontSize: 14.0), bodyText1: TextStyle(fontSize: 14.0),
) )
), ),
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: analytics),
],
home: AitrainerHome(), home: AitrainerHome(),
); );

View File

@ -18,6 +18,7 @@ class Exercise {
Exercise({this.exerciseTypeId, this.customerId, this.quantity, this.dateAdd}); Exercise({this.exerciseTypeId, this.customerId, this.quantity, this.dateAdd});
Exercise.fromJson(Map json) { Exercise.fromJson(Map json) {
this.exerciseId = json['exerciseId'];
this.exerciseTypeId = json['exerciseTypeId']; this.exerciseTypeId = json['exerciseTypeId'];
this.customerId = json['customerId']; this.customerId = json['customerId'];
this.quantity = json['quantity']; this.quantity = json['quantity'];

View File

@ -5,6 +5,7 @@ class ExercisePlanDetailChange {
static const String delete = "delete"; static const String delete = "delete";
static const String update = "update"; static const String update = "update";
static const String deleted = "deleted"; static const String deleted = "deleted";
static const String saved = "saved";
} }
class ExercisePlanDetail { class ExercisePlanDetail {

View File

@ -134,15 +134,16 @@ class ExercisePlanRepository {
.deleteExercisePlanDetail(exercisePlanDetail.exercisePlanDetailId); .deleteExercisePlanDetail(exercisePlanDetail.exercisePlanDetailId);
exercisePlanDetail.change = ExercisePlanDetailChange.deleted; exercisePlanDetail.change = ExercisePlanDetailChange.deleted;
Cache().deletedMyExercisePlanDetail(exercisePlanDetail); Cache().deletedMyExercisePlanDetail(exercisePlanDetail);
} else if ( exercisePlanDetail.change == ExercisePlanDetailChange.update ) { } else if ( exercisePlanDetail.change == ExercisePlanDetailChange.update ) {
await ExercisePlanApi() await ExercisePlanApi()
.updateExercisePlanDetail(exercisePlanDetail, exercisePlanDetail.exercisePlanDetailId); .updateExercisePlanDetail(exercisePlanDetail, exercisePlanDetail.exercisePlanDetailId);
Cache().updateMyExercisePlanDetail(exercisePlanDetail); Cache().updateMyExercisePlanDetail(exercisePlanDetail);
exercisePlanDetail.change = ExercisePlanDetailChange.saved;
} else if ( exercisePlanDetail.change == ExercisePlanDetailChange.add ) { } else if ( exercisePlanDetail.change == ExercisePlanDetailChange.add ) {
await ExercisePlanApi() await ExercisePlanApi()
.saveExercisePlanDetail(exercisePlanDetail); .saveExercisePlanDetail(exercisePlanDetail);
Cache().addToMyExercisePlanDetails(exercisePlanDetail); Cache().addToMyExercisePlanDetails(exercisePlanDetail);
exercisePlanDetail.change = ExercisePlanDetailChange.saved;
} }
}); });

View File

@ -74,6 +74,10 @@ class ExerciseRepository {
} }
} }
Future<void> deleteExercise(Exercise exercise) async {
await ExerciseApi().deleteExercise(exercise);
}
setCustomer(Customer customer) => this.customer = customer; setCustomer(Customer customer) => this.customer = customer;

View File

@ -44,4 +44,11 @@ class ExerciseApi {
return savedExercise; return savedExercise;
} }
Future<void> deleteExercise(Exercise exercise) async {
int exerciseId = exercise.exerciseId;
print(" ===== delete exercise: " + exerciseId.toString() );
final String response = await _client.post("exercises/" + exerciseId.toString(), "");
return;
}
} }

View File

@ -9,6 +9,7 @@ import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/cache.dart';
import 'package:firebase_core/firebase_core.dart';
//import '../push_notifications.dart'; //import '../push_notifications.dart';
@ -30,13 +31,28 @@ class Session {
await AppLocalizations.delegate.load(AppLanguage().appLocal); await AppLocalizations.delegate.load(AppLanguage().appLocal);
print (" -- Session: fetch token.."); print (" -- Session: fetch token..");
await _fetchToken(_sharedPreferences); await _fetchToken(_sharedPreferences);
await _initializeFlutterFire();
//initDeviceLocale(); //initDeviceLocale();
// Create the initialization Future outside of `build`:
// PushNotificationsManager().init(); // PushNotificationsManager().init();
} }
} }
// Define an async function to initialize FlutterFire
void _initializeFlutterFire() async {
try {
// Wait for Firebase to initialize and set `_initialized` state to true
await Firebase.initializeApp();
} catch (e) {
// Set `_error` state to true if Firebase initialization fails
print("Error initializing Firebase");
}
}
Future<void> initDeviceLocale() async { Future<void> initDeviceLocale() async {
List languages; List languages;
String currentLocale; String currentLocale;

View File

@ -129,11 +129,12 @@ class AccountPage extends StatelessWidget with Trans {
color: Colors.white, color: Colors.white,
onPressed: () => { onPressed: () => {
if ( accountBloc.loggedIn ) { if ( accountBloc.loggedIn ) {
accountBloc.add(AccountLogout()) confirmationDialog( accountBloc ),
} else { } else {
accountBloc.add(AccountLogin()), accountBloc.add(AccountLogin()),
Navigator.of(context).pushNamed('login'), Navigator.of(context).pushNamed('login'),
} }
}, },
), ),
); );
@ -209,4 +210,33 @@ class AccountPage extends StatelessWidget with Trans {
); );
} }
void confirmationDialog(AccountBloc accountBloc) {
showCupertinoDialog(
useRootNavigator: true,
context: context,
//barrierDismissible: false,
builder:(_) => CupertinoAlertDialog(
title: Text(t("Are you sure to logout?")),
content: Column(
children: [
Divider(),
]),
actions: [
FlatButton(
child: Text(t("No")),
onPressed: () => Navigator.pop(context),
),
FlatButton(
child: Text(t("Yes")),
onPressed: () => {
accountBloc.add(AccountLogout()),
Navigator.pop(context),
},
)
],
)
);
}
} }

View File

@ -18,16 +18,7 @@ class CustomerModifyPage extends StatelessWidget with Trans {
setContext(context); setContext(context);
// ignore: close_sinks // ignore: close_sinks
final accountBloc = BlocProvider.of<AccountBloc>(context); final accountBloc = BlocProvider.of<AccountBloc>(context);
// we cannot initialize the translations in the initState
/* genders.forEach((GenderItem element) {
if (element.dbValue == "m") {
element.name = AppLocalizations.of(context).translate("Man");
}
if (element.dbValue == "w") {
element.name = AppLocalizations.of(context).translate("Woman");
}
});
*/
return BlocProvider( return BlocProvider(
create: (context) => CustomerChangeFormBloc(customerRepository: accountBloc.customerRepository), create: (context) => CustomerChangeFormBloc(customerRepository: accountBloc.customerRepository),
child: Builder(builder: (context) { child: Builder(builder: (context) {
@ -82,20 +73,6 @@ class CustomerModifyPage extends StatelessWidget with Trans {
labelText: t('Email'), labelText: t('Email'),
), ),
), ),
/* TextFormField(
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate('Email'),
), */
// initialValue: customerChangingViewModel.customer
// .customer.email,
// onFieldSubmitted: (input) =>
// customerChangingViewModel.customer.setEmail(
// input)
) )
], ],
), ),
@ -113,19 +90,6 @@ class CustomerModifyPage extends StatelessWidget with Trans {
labelText: t('Password (Leave empty if no change)'), labelText: t('Password (Leave empty if no change)'),
), ),
), ),
/*TextFormField(
style: TextStyle(fontSize: 12),
obscureText: true,
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate(
'Password (Leave empty if you don\'t want to change)'),
),
//initialValue: customerChangingViewModel.customer.customer.password,
// onFieldSubmitted: (input) => customerChangingViewModel.customer.setPassword(input)
)*/
) )
], ],
), ),

View File

@ -1,47 +1,63 @@
import 'dart:collection'; import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_control_form_bloc.dart'; import 'package:aitrainer_app/bloc/exercise_control/exercise_control_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/splash.dart'; import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:aitrainer_app/library/numberpicker.dart';
class ExerciseControlPage extends StatefulWidget { class ExerciseControlPage extends StatefulWidget {
_ExerciseControlPage createState() => _ExerciseControlPage(); _ExerciseControlPage createState() => _ExerciseControlPage();
} }
class _ExerciseControlPage extends State<ExerciseControlPage> { class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; LinkedHashMap arguments = ModalRoute.of(context).settings.arguments;
final ExerciseRepository exerciseRepository = arguments['exerciseRepository']; final ExerciseRepository exerciseRepository = arguments['exerciseRepository'];
final double percent = arguments['percent']; final double percent = arguments['percent'];
final bool readonly = arguments['readonly']; final bool readonly = arguments['readonly'];
setContext(context);
return BlocProvider( return BlocProvider(
create: (context) => ExerciseControlFormBloc( create: (context) => ExerciseControlBloc(
exerciseRepository: exerciseRepository, percentToCalculate: percent, readonly: readonly), exerciseRepository: exerciseRepository, percentToCalculate: percent, readonly: readonly)..
child: BlocBuilder<ExerciseControlFormBloc, FormBlocState>(builder: (context, state) { add(ExerciseControlLoad()),
// ignore: close_sinks child:
final exerciseBloc = BlocProvider.of<ExerciseControlFormBloc>(context); BlocConsumer<ExerciseControlBloc, ExerciseControlState>(
if (state is FormBlocLoading) { listener: (context, state) {
if (state is ExerciseControlError) {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange,
content:
Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is ExerciseControlLoading) {
return LoadingDialog(); return LoadingDialog();
} else if (state is FormBlocSuccess) { }
},
builder: (context, state) {
final exerciseBloc = BlocProvider.of<ExerciseControlBloc>(context);
if (state is ExerciseControlReady) {
return getControlForm(exerciseBloc); return getControlForm(exerciseBloc);
} else { } else {
return getControlForm(exerciseBloc); return getControlForm(exerciseBloc);
} }
})); })
);
} }
Form getControlForm(ExerciseControlFormBloc exerciseBloc) { Form getControlForm(ExerciseControlBloc exerciseBloc) {
String exerciseName = AppLanguage().appLocal == Locale("en") String exerciseName = AppLanguage().appLocal == Locale("en")
? exerciseBloc.exerciseRepository.exerciseType.name ? exerciseBloc.exerciseRepository.exerciseType.name
: exerciseBloc.exerciseRepository.exerciseType.nameTranslation; : exerciseBloc.exerciseRepository.exerciseType.nameTranslation;
@ -65,209 +81,162 @@ class _ExerciseControlPage extends State<ExerciseControlPage> {
padding: const EdgeInsets.only(top: 25, left: 25, right: 25), padding: const EdgeInsets.only(top: 25, left: 25, right: 25),
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ child: Column(
Text( mainAxisAlignment: MainAxisAlignment.spaceAround,
exerciseName, crossAxisAlignment: CrossAxisAlignment.start,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange), children: <Widget>[
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: true,
),
FlatButton(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Icon(Icons.question_answer),
Text(AppLocalizations.of(context).translate("Why do you need Exercise Control?"),
style: TextStyle(color: Colors.blueAccent, fontWeight: FontWeight.normal, fontSize: 14)),
Icon(Icons.arrow_forward_ios),
]),
textColor: Colors.blueAccent,
color: Colors.transparent,
onPressed: () => {
//Navigator.of(context).pushNamed('exerciseTypeDescription', arguments: exerciseBloc.exerciseRepository),
},
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text( Text(
AppLocalizations.of(context).translate("Your 1RM:"), exerciseName,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange),
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: true,
), ),
Text( FlatButton(
" " + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
exerciseBloc.initialRMField.value + Icon(Icons.info),
Flexible(
child: Text(t("Why do you need Exercise Control?"),
style:
TextStyle(color: Colors.blueAccent, fontWeight: FontWeight.normal, fontSize: 14)),
),
Icon(Icons.arrow_forward_ios),
]),
textColor: Colors.blueAccent,
color: Colors.transparent,
onPressed: () => {
//Navigator.of(context).pushNamed('exerciseTypeDescription', arguments: exerciseBloc.exerciseRepository),
},
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
t("Your 1RM:"),
),
Text(
" " + " " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit, exerciseBloc.initialRM.toStringAsFixed(0) +
style: TextStyle(fontWeight: FontWeight.bold), " " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
), ),
], Divider(),
), numberPickForm(exerciseBloc, 1),
Divider(), Divider(),
Column( numberPickForm(exerciseBloc, 2),
mainAxisAlignment: MainAxisAlignment.spaceAround, Divider(),
crossAxisAlignment: CrossAxisAlignment.start, numberPickForm(exerciseBloc, 3),
children: [ ]),
Text(
AppLocalizations.of(context).translate("1st Control Exercise:"),
style: TextStyle(),
),
TextFieldBlocBuilder(
readOnly: exerciseBloc.step != 1,
textFieldBloc: exerciseBloc.quantity1Field,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the exercise"),
labelStyle:
TextStyle(fontSize: 12, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: "Please repeat with " +
exerciseBloc.unitQuantity1Field.value +
" " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit +
" " +
exerciseBloc.times +
" times!",
),
),
RaisedButton(
padding: EdgeInsets.all(0),
textColor: Colors.white,
color: exerciseBloc.step == 1 ? Colors.blue : Colors.black26,
focusColor: Colors.blueAccent,
onPressed: () => {exerciseBloc.submit()},
child: Text(
AppLocalizations.of(context).translate("Check"),
style: TextStyle(fontSize: 12),
)),
],
),
Divider(),
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppLocalizations.of(context).translate("2nd Control Exercise:"),
style: TextStyle(),
),
TextFieldBlocBuilder(
readOnly: exerciseBloc.step != 2,
textFieldBloc: exerciseBloc.quantity2Field,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
onChanged: (input) => {
print("Quantity 2 value $input"),
//exerciseBloc.exerciseRepository.setQuantity(double.parse(input)),
//exerciseBloc.exerciseRepository
// .setUnit(exerciseBloc.exerciseRepository.exerciseType.unit)
},
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the exercise"),
labelStyle:
TextStyle(fontSize: 12, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: "Please repeat with " +
exerciseBloc.unitQuantity2Field.value +
" " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit +
" 12 times!",
),
),
RaisedButton(
padding: EdgeInsets.all(0),
textColor: Colors.white,
color: exerciseBloc.step == 2 ? Colors.blue : Colors.black26,
focusColor: Colors.blueAccent,
onPressed: () => {exerciseBloc.submit()},
child: Text(
AppLocalizations.of(context).translate("Check"),
style: TextStyle(fontSize: 12),
)),
],
),
Divider(),
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppLocalizations.of(context).translate("3rd Control Exercise:"),
style: TextStyle(),
),
TextFieldBlocBuilder(
readOnly: exerciseBloc.step != 3,
textFieldBloc: exerciseBloc.quantity3Field,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
onChanged: (input) => {
print("Quantity 3 value $input"),
//exerciseBloc.exerciseRepository.setQuantity(double.parse(input)),
//exerciseBloc.exerciseRepository
// .setUnit(exerciseBloc.exerciseRepository.exerciseType.unit)
},
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the exercise"),
labelStyle:
TextStyle(fontSize: 12, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: "Please repeat with " +
exerciseBloc.unitQuantity3Field.value +
" " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit +
" 12 times!",
),
),
RaisedButton(
padding: EdgeInsets.all(0),
textColor: Colors.white,
color: exerciseBloc.step == 3 ? Colors.blue : Colors.black26,
focusColor: Colors.blueAccent,
onPressed: () => {exerciseBloc.submit()},
child: Text(
AppLocalizations.of(context).translate("Check"),
style: TextStyle(fontSize: 12),
)),
],
),
]),
))), ))),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 1), bottomNavigationBar: BottomNavigator(bottomNavIndex: 1),
), ),
);
}
Widget numberPickForm(ExerciseControlBloc exerciseBloc, int step) {
String strTimes = step == 2 ? exerciseBloc.origQuantity.toString() : "max.";
String textInstruction = "";
textInstruction = t("Please repeat with ") +
exerciseBloc.unitQuantity.toStringAsFixed(0) +
" " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit +
t("hu_with") + " " +
strTimes + " " + t("times!");
String title = step.toString() + ". " + t("Control Exercise:");
List<Widget> listWidgets = [
Text(
title,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
textInstruction,
style: TextStyle(fontSize: 12),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
NumberPicker.horizontal(
highlightSelectedValue: step == exerciseBloc.step,
initialValue: exerciseBloc.quantity.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
onChanged: (value) => {
exerciseBloc.add(ExerciseControlQuantityChange(quantity: value.toDouble(), step: step))
},
listViewHeight: 80,
//decoration: _decoration,
),
RaisedButton(
padding: EdgeInsets.all(0),
textColor: Colors.white,
color: step == exerciseBloc.step ? Colors.blue : Colors.black26,
focusColor: Colors.blueAccent,
onPressed: () => {
exerciseBloc.add(ExerciseControlSubmit(step: step)),
if ( step == 3 ) {
confirmationDialog(exerciseBloc)
}
},
child: Text(
t("Save"),
style: TextStyle(fontSize: 12),
)),
],
),
];
return
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: listWidgets,
); );
} }
String validateNumberInput(input) { void confirmationDialog( ExerciseControlBloc bloc ) {
String error = AppLocalizations.of(context).translate("Please type the right quantity 0-10000");
dynamic rc = (input != null && input.length > 0);
if (!rc) {
return null;
}
Pattern pattern = r'^\d+(?:\.\d+)?$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(input)) {
return error;
}
rc = double.tryParse(input); String unit = t(bloc.exerciseRepository.exerciseType.unit);
if (rc == null) {
return error;
}
if (!(double.parse(input) < 10000 && double.parse(input) > 0)) { showCupertinoDialog(
return error; useRootNavigator: true,
} context: context,
//barrierDismissible: false,
builder:(_) => CupertinoAlertDialog(
title: Text(t("Summary of your test")),
content: Column(
return null; children: [
Text(t("Test") + ": " + bloc.repeats[1].toStringAsFixed(0) + "x" + bloc.repeats[0].toStringAsFixed(0) + " " + unit ,
style: (TextStyle(color: Colors.blue)),),
Divider(),
Text(t("1st Control") + ": " + bloc.repeats[2].toStringAsFixed(0) + "x" + bloc.unitQuantity.toStringAsFixed(0) + " " + unit ,
style: (TextStyle(color: Colors.blue)),),
Text(t("2nd Control") + ": " + bloc.repeats[3].toStringAsFixed(0) + "x" + bloc.unitQuantity.toStringAsFixed(0) + " " + unit ,
style: (TextStyle(color: Colors.blue)),),
Text(t("3rd Control") + ": " + bloc.repeats [4].toStringAsFixed(0) + "x" + bloc.unitQuantity.toStringAsFixed(0) + " " + unit ,
style: (TextStyle(color: Colors.blue)),),
]),
actions: [
FlatButton(
child: Text(t("OK")),
onPressed: () => {
Navigator.of(context).pop(),
Navigator.of(context).pop()
},
)
],
)
);
} }
} }

View File

@ -1,5 +1,5 @@
import 'dart:collection'; import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_by_plan/exercise_by_plan_bloc.dart'; import 'package:aitrainer_app/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart';
import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/library/tree_view.dart';
@ -13,15 +13,15 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class ExerciseByPlanPage extends StatefulWidget { class ExerciseExecutePage extends StatefulWidget {
@override @override
_ExerciseByPlanPage createState() => _ExerciseByPlanPage(); _ExerciseExecutePage createState() => _ExerciseExecutePage();
} }
class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans { class _ExerciseExecutePage extends State<ExerciseExecutePage> with Trans {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
// ignore: close_sinks // ignore: close_sinks
ExerciseByPlanBloc bloc; ExerciseExecutePlanBloc bloc;
@override @override
void initState() { void initState() {
@ -29,7 +29,7 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
/// We require the initializers to run after the loading screen is rendered /// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
BlocProvider.of<ExerciseByPlanBloc>(context).add(ExerciseByPlanLoad()); BlocProvider.of<ExerciseExecutePlanBloc>(context).add(ExerciseByPlanLoad());
}); });
} }
@ -37,7 +37,7 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
Widget build(BuildContext context) { Widget build(BuildContext context) {
LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; LinkedHashMap arguments = ModalRoute.of(context).settings.arguments;
final int customerId = arguments['customerId']; final int customerId = arguments['customerId'];
bloc = BlocProvider.of<ExerciseByPlanBloc>(context); bloc = BlocProvider.of<ExerciseExecutePlanBloc>(context);
bloc.customerId = customerId; bloc.customerId = customerId;
setContext(context); setContext(context);
@ -49,13 +49,13 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: customerId == Cache().userLoggedIn.customerId image: customerId == Cache().userLoggedIn.customerId
? AssetImage('asset/image/WT_light_background.png') ? AssetImage('asset/image/WT_menu_dark.png')
: AssetImage('asset/image/WT_menu_dark.png'), : AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center, alignment: Alignment.center,
), ),
), ),
child: BlocConsumer<ExerciseByPlanBloc, ExerciseByPlanState>( child: BlocConsumer<ExerciseExecutePlanBloc, ExerciseExecutePlanState>(
listener: (context, state) { listener: (context, state) {
if (state is ExerciseByPlanError) { if (state is ExerciseByPlanError) {
Scaffold.of(context).showSnackBar(SnackBar( Scaffold.of(context).showSnackBar(SnackBar(
@ -82,14 +82,14 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
); );
} }
Widget exerciseWidget(ExerciseByPlanBloc bloc) { Widget exerciseWidget(ExerciseExecutePlanBloc bloc) {
return TreeView( return TreeView(
startExpanded: false, startExpanded: false,
children: nodeExercisePlan(bloc), children: nodeExercisePlan(bloc),
); );
} }
List<Widget> nodeExercisePlan(ExerciseByPlanBloc bloc) { List<Widget> nodeExercisePlan(ExerciseExecutePlanBloc bloc) {
List<Widget> exerciseTypes = List(); List<Widget> exerciseTypes = List();
Card explanation = Card( Card explanation = Card(
color: Colors.white38, color: Colors.white38,
@ -141,7 +141,7 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
return exerciseTypes; return exerciseTypes;
} }
List<Widget> _getChildList(List<WorkoutMenuTree> listWorkoutTree, ExerciseByPlanBloc bloc) { List<Widget> _getChildList(List<WorkoutMenuTree> listWorkoutTree, ExerciseExecutePlanBloc bloc) {
List<Widget> list = List(); List<Widget> list = List();
listWorkoutTree.forEach((element) { listWorkoutTree.forEach((element) {
@ -184,7 +184,7 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
IconButton( IconButton(
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
icon: Icon(Icons.description, color: Colors.black12,), icon: Icon(Icons.info, color: Colors.black12,),
onPressed: () { onPressed: () {
}, },
@ -202,11 +202,11 @@ class _ExerciseByPlanPage extends State<ExerciseByPlanPage> with Trans {
return list; return list;
} }
void addExerciseByPlanEvent(ExerciseByPlanBloc bloc, WorkoutMenuTree workoutTree) { void addExerciseByPlanEvent(ExerciseExecutePlanBloc bloc, WorkoutMenuTree workoutTree) {
LinkedHashMap args = LinkedHashMap(); LinkedHashMap args = LinkedHashMap();
args['blocExerciseByPlan'] = bloc; args['blocExerciseByPlan'] = bloc;
args['customerId'] = bloc.customerId; args['customerId'] = bloc.customerId;
args['workoutTree'] = workoutTree; args['workoutTree'] = workoutTree;
Navigator.of(context).pushNamed("exerciseAddByPlanPage", arguments: args); Navigator.of(context).pushNamed("exerciseExecuteAddPage", arguments: args);
} }
} }

View File

@ -1,29 +1,29 @@
import 'dart:collection'; import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_add_by_plan_bloc.dart'; import 'package:aitrainer_app/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart';
import 'package:aitrainer_app/bloc/exercise_by_plan/exercise_by_plan_bloc.dart'; import 'package:aitrainer_app/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/splash.dart'; import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/services.dart'; import 'package:aitrainer_app/library/numberpicker.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class ExerciseAddByPlanPage extends StatefulWidget{ class ExerciseExecutePlanAddPage extends StatefulWidget{
_ExerciseAddByPlanPage createState() => _ExerciseAddByPlanPage(); _ExerciseExecuteAddPage createState() => _ExerciseExecuteAddPage();
} }
class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans { class _ExerciseExecuteAddPage extends State<ExerciseExecutePlanAddPage> with Trans {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; LinkedHashMap arguments = ModalRoute.of(context).settings.arguments;
// ignore: close_sinks // ignore: close_sinks
final ExerciseByPlanBloc bloc = arguments['blocExerciseByPlan']; final ExerciseExecutePlanBloc planBloc = arguments['blocExerciseByPlan'];
final int customerId = arguments['customerId']; final int customerId = arguments['customerId'];
final WorkoutMenuTree workoutTree = arguments['workoutTree']; final WorkoutMenuTree workoutTree = arguments['workoutTree'];
final ExerciseRepository exerciseRepository = ExerciseRepository(); final ExerciseRepository exerciseRepository = ExerciseRepository();
@ -31,18 +31,29 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
ExerciseAddByPlanFormBloc( ExerciseExecutePlanAddBloc(
exerciseRepository: exerciseRepository, exerciseRepository: exerciseRepository,
exercisePlanRepository: bloc.exercisePlanRepository, exercisePlanRepository: planBloc.exercisePlanRepository,
customerId: customerId, customerId: customerId,
workoutTree: workoutTree), workoutTree: workoutTree,
child: BlocBuilder<ExerciseAddByPlanFormBloc, FormBlocState>( planBloc: planBloc),
child: BlocConsumer<ExerciseExecutePlanAddBloc, ExerciseExecutePlanAddState>(
listener: (context, state) {
if (state is ExerciseExecutePlanAddError) {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange,
content:
Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is ExerciseExecutePlanAddLoading) {
return LoadingDialog();
}
},
builder: (context, state) { builder: (context, state) {
// ignore: close_sinks // ignore: close_sinks
final exerciseBloc = BlocProvider.of<ExerciseAddByPlanFormBloc>(context); final exerciseBloc = BlocProvider.of<ExerciseExecutePlanAddBloc>(context);
if ( state is FormBlocLoading ) { if ( state is ExerciseExecutePlanAddLoading ) {
return LoadingDialog(); return LoadingDialog();
} else if ( state is FormBlocSuccess) { } else if ( state is ExerciseExecutePlanAddReady) {
return getControlForm(exerciseBloc); return getControlForm(exerciseBloc);
} else { } else {
return getControlForm(exerciseBloc); return getControlForm(exerciseBloc);
@ -51,7 +62,7 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
)); ));
} }
Form getControlForm( ExerciseAddByPlanFormBloc exerciseBloc) { Form getControlForm( ExerciseExecutePlanAddBloc exerciseBloc) {
String exerciseName = AppLanguage().appLocal == Locale("en") ? String exerciseName = AppLanguage().appLocal == Locale("en") ?
exerciseBloc.exerciseRepository.exerciseType.name : exerciseBloc.exerciseRepository.exerciseType.name :
exerciseBloc.exerciseRepository.exerciseType.nameTranslation; exerciseBloc.exerciseRepository.exerciseType.nameTranslation;
@ -65,7 +76,7 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text("Add Exercise"), Text(t("Save Exercise"), style: TextStyle(fontSize: 18),),
Image.asset( Image.asset(
'asset/image/WT_long_logo.png', 'asset/image/WT_long_logo.png',
fit: BoxFit.cover, fit: BoxFit.cover,
@ -98,6 +109,9 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
padding: const EdgeInsets.only (top: 25, left: 25, right: 25), padding: const EdgeInsets.only (top: 25, left: 25, right: 25),
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
controller: ScrollController(
initialScrollOffset: exerciseBloc.scrollOffset,
),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[ children: <Widget>[
@ -128,7 +142,7 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
); );
} }
List<Column> repeatExercises(ExerciseAddByPlanFormBloc exerciseBloc) { List<Column> repeatExercises(ExerciseExecutePlanAddBloc exerciseBloc) {
List<Column> listColumns = List<Column>(); List<Column> listColumns = List<Column>();
for ( int i = 0; i < exerciseBloc.countSteps; i++) { for ( int i = 0; i < exerciseBloc.countSteps; i++) {
Column col = Column( Column col = Column(
@ -138,8 +152,54 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
Divider(color: Colors.transparent,), Divider(color: Colors.transparent,),
Text(t("Execute the") + " " + (i+1).toString() + t(". set!"), Text(t("Execute the") + " " + (i+1).toString() + t(". set!"),
style: TextStyle(),), style: TextStyle(),),
Divider(color: Colors.transparent,),
Text(t("Please repeat with") + " "+ exerciseBloc.unitQuantity.toStringAsFixed(0) + " " +
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit + " " +
exerciseBloc.exercisePlanRepository.getActualPlanDetail().repeats.toString() + " " + t("times!")),
Row(
children: [
NumberPicker.horizontal(
highlightSelectedValue: (i + 1) == exerciseBloc.step,
initialValue: exerciseBloc.unitQuantity.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
textStyle: TextStyle(fontWeight: FontWeight.bold),
textStyleHighlighted: TextStyle(fontSize: 24, color: Colors.indigo, fontWeight: FontWeight.bold),
onChanged: (value) => {
exerciseBloc.add(ExerciseExecutePlanAddChangeUnitQuantity(quantity: value.toDouble()))
},
listViewHeight: 80,
//decoration: _decoration,
),
Text(exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit),
]
),
TextFieldBlocBuilder( Row(
children: [
NumberPicker.horizontal(
highlightSelectedValue: (i+1) == exerciseBloc.step,
initialValue: exerciseBloc.quantity.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
textStyle: TextStyle(fontWeight: FontWeight.bold),
textStyleHighlighted: TextStyle(fontSize: 24, color: Colors.deepOrange, fontWeight: FontWeight.bold),
onChanged: (value) => {
exerciseBloc.add(ExerciseExecutePlanAddChangeQuantity(quantity: value.toDouble()))
},
listViewHeight: 80,
//decoration: _decoration,
),
Text(t("repeat")),
]
),
/*TextFieldBlocBuilder(
readOnly: exerciseBloc.step != i+1, readOnly: exerciseBloc.step != i+1,
textFieldBloc: exerciseBloc.unitQuantity1Field, textFieldBloc: exerciseBloc.unitQuantity1Field,
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -159,8 +219,8 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
labelStyle: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.normal), labelStyle: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.normal),
labelText: exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit, labelText: exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit,
), ),
), ),*/
TextFieldBlocBuilder( /*TextFieldBlocBuilder(
readOnly: exerciseBloc.step != i+1, readOnly: exerciseBloc.step != i+1,
textFieldBloc: exerciseBloc.quantity1Field, textFieldBloc: exerciseBloc.quantity1Field,
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -183,7 +243,7 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit + " " + exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit + " " +
exerciseBloc.exercisePlanRepository.getActualPlanDetail().repeats.toString() + " " + t("times!"), exerciseBloc.exercisePlanRepository.getActualPlanDetail().repeats.toString() + " " + t("times!"),
), ),
), ),*/
RaisedButton( RaisedButton(
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
@ -194,47 +254,21 @@ class _ExerciseAddByPlanPage extends State<ExerciseAddByPlanPage> with Trans {
{ {
print ("Submit step " + exerciseBloc.step.toString() + " (i) " + i.toString()), print ("Submit step " + exerciseBloc.step.toString() + " (i) " + i.toString()),
if ( exerciseBloc.step == i+1 ) { if ( exerciseBloc.step == i+1 ) {
exerciseBloc.submit() exerciseBloc.add(ExerciseExecutePlanAddSubmit())
}, },
if ( i+1 == exerciseBloc.countSteps) { if ( i+1 == exerciseBloc.countSteps) {
Navigator.of(context).pop() Navigator.of(context).pop()
} }
}, },
child: Text( child: Text(
t("Check"), t("Save"),
style: TextStyle(fontSize: 12),) style: TextStyle(fontSize: 12),)
), ),
Divider(color: Colors.transparent,), Divider(),
], ],
); );
listColumns.add(col); listColumns.add(col);
} }
return listColumns; return listColumns;
} }
String validateNumberInput(input) {
String error = t("Please type the right quantity 0-10000");
dynamic rc = (input != null && input.length > 0);
if (!rc) {
return null;
}
Pattern pattern = r'^\d+(?:\.\d+)?$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(input)) {
return error;
}
rc = double.tryParse(input);
if (rc == null) {
return error;
}
if (!(double.parse(input) < 10000 && double.parse(input) > 0)) {
return error;
}
return null;
}
} }

View File

@ -1,6 +1,10 @@
import 'dart:collection'; import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_log/exercise_log_bloc.dart';
import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/cache.dart';
@ -22,36 +26,68 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; LinkedHashMap arguments = ModalRoute.of(context).settings.arguments;
final ExerciseRepository exerciseRepository = arguments['exerciseRepository']; //final ExerciseRepository exerciseRepository = arguments['exerciseRepository'];
final int customerId = arguments['customerId']; final int customerId = arguments['customerId'];
setContext(context); setContext(context);
return BlocProvider(
create: (context) => ExerciseLogBloc(exerciseRepository: ExerciseRepository())..
add(ExerciseLogLoad()),
child: BlocConsumer<ExerciseLogBloc, ExerciseLogState>(
listener: (context, state) {
if ( state is ExerciseLogLoading ) {
return LoadingDialog();
} else if ( state is ExerciseLogError ) {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange,
content:
Text(state.message, style: TextStyle(color: Colors.white))));
}
},
builder: (context, state) {
final exerciseBloc = BlocProvider.of<ExerciseLogBloc>(context);
if ( state is ExerciseLogReady ) {
return getExerciseLog(customerId, exerciseBloc);
} else {
return getExerciseLog(customerId, exerciseBloc);
}
}
)
);
}
Widget getExerciseLog(int customerId, ExerciseLogBloc exerciseLogBloc) {
return Scaffold( return Scaffold(
appBar: AppBarNav(depth: 1), appBar: AppBarNav(depth: 1),
body: Container( body: Container(
padding: EdgeInsets.all(20), padding: EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: customerId == Cache().userLoggedIn.customerId ? AssetImage('asset/image/WT_light_background.png'): image: customerId == Cache().userLoggedIn.customerId ? AssetImage('asset/image/WT_light_background.png'):
AssetImage('asset/image/WT_menu_dark.png'), AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center, alignment: Alignment.center,
),
), ),
child: exerciseWidget(exerciseRepository, customerId),
), ),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 1), child: exerciseWidget(exerciseLogBloc, customerId),
),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 1),
); );
} }
Widget exerciseWidget(ExerciseRepository exerciseRepository, int customerId) { Widget exerciseWidget(ExerciseLogBloc exerciseLogBloc, int customerId) {
return TreeView( return TreeView(
startExpanded: false, startExpanded: false,
children: _getTreeChildren(exerciseRepository, customerId), children: _getTreeChildren(exerciseLogBloc, customerId),
); );
} }
List<Widget> _getTreeChildren(ExerciseRepository exerciseRepository, int customerId) { List<Widget> _getTreeChildren(ExerciseLogBloc exerciseLogBloc, int customerId) {
final ExerciseRepository exerciseRepository = exerciseLogBloc.exerciseRepository;
if ( customerId == Cache().userLoggedIn.customerId ) { if ( customerId == Cache().userLoggedIn.customerId ) {
exerciseRepository.exerciseList = exerciseRepository.getExerciseList(); exerciseRepository.exerciseList = exerciseRepository.getExerciseList();
} else if ( Cache().getTrainee() != null && customerId == Cache().getTrainee().customerId ) { } else if ( Cache().getTrainee() != null && customerId == Cache().getTrainee().customerId ) {
@ -109,9 +145,9 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
Container( Container(
margin: const EdgeInsets.only(left: 4.0), margin: const EdgeInsets.only(left: 4.0),
child: TreeViewChild( child: TreeViewChild(
startExpanded: true, startExpanded: false,
parent: TreeviewParentWidget(text: origDate), parent: TreeviewParentWidget(text: origDate),
children: _getChildList(listExercises, exerciseRepository), children: _getChildList(listExercises, exerciseRepository, exerciseLogBloc),
) )
) )
); );
@ -134,7 +170,7 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
child: TreeViewChild( child: TreeViewChild(
startExpanded: true, startExpanded: true,
parent: TreeviewParentWidget(text: origDate), parent: TreeviewParentWidget(text: origDate),
children: _getChildList(listExercises, exerciseRepository), children: _getChildList(listExercises, exerciseRepository, exerciseLogBloc),
) )
) )
); );
@ -143,7 +179,7 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
return listWidget; return listWidget;
} }
List<Widget> _getChildList(List<Exercise> listExercises, ExerciseRepository exerciseRepository) { List<Widget> _getChildList(List<Exercise> listExercises, ExerciseRepository exerciseRepository, ExerciseLogBloc exerciseLogBloc) {
List<Widget> list = List(); List<Widget> list = List();
bool isEnglish = AppLanguage().appLocal == Locale('en'); bool isEnglish = AppLanguage().appLocal == Locale('en');
@ -173,18 +209,28 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Icon(Icons.accessibility, color: Colors.black12), Icon(Icons.accessibility, color: Colors.black12),
SizedBox(width: 20), SizedBox(width: 10,),
Flexible( Flexible(
child: fit: FlexFit.tight,
Text( flex: 8,
exerciseName, child: Text(
textAlign: TextAlign.start, exerciseName,
style: TextStyle(fontSize: 12, color: Colors.black),
), style: TextStyle(fontSize: 12, color: Colors.black),
),
), ),
SizedBox(width: 20), Flexible(fit: FlexFit.tight, child: SizedBox(width: 10,)),
Text(labelExercise , style: TextStyle(fontSize: 9, color: Colors.blueAccent.shade700),) , Text(labelExercise , style: TextStyle(fontSize: 9, color: Colors.blueAccent.shade700),) ,
Flexible(fit: FlexFit.tight, child: SizedBox(width: 10,)),
IconButton(
icon: Icon(Icons.delete, color: Colors.black12),
onPressed: () {
confirmationDialog(exerciseLogBloc, exercise);
},
),
]), ]),
) )
), ),
@ -195,4 +241,55 @@ class _ExerciseLogPage extends State<ExerciseLogPage> with Trans, Common {
return list; return list;
} }
void confirmationDialog( ExerciseLogBloc bloc, Exercise exercise ) {
ExerciseType exerciseType = bloc.exerciseRepository.getExerciseTypeById(exercise.exerciseTypeId);
String exerciseName = AppLanguage().appLocal == Locale("en")
? exerciseType.name
: exerciseType.nameTranslation;
String strDate = AppLanguage().appLocal == Locale("en")
? "on the " + DateFormat(DateFormat.YEAR_MONTH_DAY, AppLanguage().appLocal.toString()).format(exercise.dateAdd.toUtc())
: DateFormat(DateFormat.YEAR_MONTH_DAY, AppLanguage().appLocal.toString()).format(exercise.dateAdd.toUtc()) + "-n";
showCupertinoDialog(
useRootNavigator: true,
context: context,
//barrierDismissible: false,
builder:(_) => CupertinoAlertDialog(
title: Text(t("Are you sure to delete this exercise?")),
content: Column(
children: [
Divider(),
Text(t("Exercise") + ": " + exerciseName,
style: (TextStyle(color: Colors.blue)),),
Text(
exercise.quantity.toStringAsFixed(0) + "x" + exercise.unitQuantity.toStringAsFixed(0) + " " + exerciseType.unitQuantityUnit,
style: (TextStyle(color: Colors.deepOrange)),
),
Text(
strDate,
style: (TextStyle(color: Colors.deepOrange)),
),
]),
actions: [
FlatButton(
child: Text(t("No")),
onPressed: () => Navigator.pop(context),
),
FlatButton(
child: Text(t("Yes")),
onPressed: () => {
print("delete exercise: " + exercise.toJson().toString()),
bloc.add(ExerciseLogDelete(exercise: exercise)),
Navigator.pop(context)
},
)
],
)
);
}
} }

View File

@ -1,232 +1,214 @@
import 'dart:collection'; import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_form_bloc.dart'; import 'package:aitrainer_app/bloc/exercise_new/exercise_new_bloc.dart';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:flutter/services.dart'; import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:aitrainer_app/library/numberpicker.dart';
class ExerciseNewPage extends StatefulWidget{ class ExerciseNewPage extends StatefulWidget{
_ExerciseNewPageState createState() => _ExerciseNewPageState(); _ExerciseNewPageState createState() => _ExerciseNewPageState();
} }
class _ExerciseNewPageState extends State<ExerciseNewPage> { class _ExerciseNewPageState extends State<ExerciseNewPage> with Trans{
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ExerciseType exerciseType = ModalRoute.of(context).settings.arguments; final ExerciseType exerciseType = ModalRoute.of(context).settings.arguments;
// ignore: close_sinks // ignore: close_sinks
final menuBloc = BlocProvider.of<MenuBloc>(context); final menuBloc = BlocProvider.of<MenuBloc>(context);
setContext(context);
return BlocProvider( return BlocProvider(
create: (context) => ExerciseFormBloc(exerciseRepository: ExerciseRepository(), menuBloc: menuBloc), create: (context) => ExerciseNewBloc(exerciseRepository: ExerciseRepository(), menuBloc: menuBloc, exerciseType: exerciseType)..
child: Builder(builder: (context) { add(ExerciseNewLoad()),
// ignore: close_sinks child: BlocConsumer<ExerciseNewBloc, ExerciseNewState>(
final exerciseBloc = BlocProvider.of<ExerciseFormBloc>(context); listener: (context, state) {
if ( state is ExerciseNewLoading ) {
return LoadingDialog();
exerciseBloc.exerciseRepository.setExerciseType(exerciseType); } else if ( state is ExerciseNewError ) {
String exerciseName = AppLanguage().appLocal == Locale("en") ? Scaffold.of(context).showSnackBar(SnackBar(
exerciseBloc.exerciseRepository.exerciseType.name : backgroundColor: Colors.orange,
exerciseBloc.exerciseRepository.exerciseType.nameTranslation; content:
Text(state.message, style: TextStyle(color: Colors.white))));
return Form( }
autovalidate: true, },
child: Scaffold( builder: (context, state) {
resizeToAvoidBottomInset: true, final exerciseBloc = BlocProvider.of<ExerciseNewBloc>(context);
appBar: AppBarNav(depth: 1), if ( state is ExerciseNewReady ) {
body: Container( return getExerciseWidget(exerciseBloc, exerciseType);
width: MediaQuery.of(context).size.width, } else {
height: MediaQuery.of(context).size.height, return getExerciseWidget(exerciseBloc, exerciseType);
decoration: BoxDecoration( }
image: DecorationImage( },
image: AssetImage('asset/image/WT_light_background.png'), )
fit: BoxFit.fill,
alignment: Alignment.center,
),
),
child: Container(
padding: const EdgeInsets.only (top: 25, left:25, right: 25),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
Text(AppLocalizations.of(context).translate('Save Exercise'),
style: TextStyle(fontSize: 14, color: Colors.blueAccent)),
Text(exerciseName,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange),
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: true,
),
Divider(color: Colors.transparent,),
FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.description),
Text(AppLocalizations.of(context).translate("Description"),
style: TextStyle(
color: Colors.blueAccent, fontWeight: FontWeight.normal, fontSize: 14 )),
Icon(Icons.arrow_forward_ios),
]),
textColor: Colors.blueAccent,
color: Colors.transparent,
onPressed: () => {
Navigator.of(context).pushNamed('exerciseTypeDescription', arguments: exerciseBloc.exerciseRepository),
},
),
Divider(color: Colors.transparent,),
columnQuantityUnit(exerciseBloc),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
columnQuantity(exerciseBloc),
Divider(),
RaisedButton(
textColor: Colors.white,
color: Colors.deepOrange,
focusColor: Colors.white,
onPressed: () =>
{
confirmationDialog( exerciseBloc ),
},
child: Text(AppLocalizations.of(context).translate("Save"), style: TextStyle(fontSize: 16),)
),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
]),
)
)
),
),
);
})
); );
} }
Column columnQuantityUnit( ExerciseFormBloc bloc ) { Widget getExerciseWidget(ExerciseNewBloc exerciseBloc, ExerciseType exerciseType) {
Column column = Column();
if ( bloc.exerciseRepository.exerciseType != null && exerciseBloc.exerciseRepository.setExerciseType(exerciseType);
bloc.exerciseRepository.exerciseType.unitQuantity == "1") { String exerciseName = AppLanguage().appLocal == Locale("en") ?
column = Column( exerciseBloc.exerciseRepository.exerciseType.name :
children: [ exerciseBloc.exerciseRepository.exerciseType.nameTranslation;
TextFieldBlocBuilder(
textFieldBloc: bloc.unitQuantityField, return Form(
textAlign: TextAlign.center, autovalidate: true,
style: TextStyle(fontSize: 30, child: Scaffold(
color: Colors.lightBlue, resizeToAvoidBottomInset: true,
fontWeight: FontWeight.bold), appBar: AppBarNav(depth: 1),
inputFormatters: [ body: Container(
FilteringTextInputFormatter.allow(RegExp(r"[\d.]")) width: MediaQuery
], .of(context)
onChanged: (input) => .size
{ .width,
print("UnitQuantity value $input"), height: MediaQuery
bloc.exerciseRepository.setUnitQuantity( .of(context)
double.parse(input)) .size
}, .height,
decoration: InputDecoration( decoration: BoxDecoration(
fillColor: Colors.white, image: DecorationImage(
filled: false, image: AssetImage('asset/image/WT_light_background.png'),
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), fit: BoxFit.fill,
hintText: AppLocalizations.of(context).translate("The number of the exercise done with"), alignment: Alignment.center,
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unitQuantityUnit),
), ),
), ),
child: Container(
padding: const EdgeInsets.only (top: 25, left: 25, right: 25),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
/*Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),*/
Text(t('Save Exercise'),
style: TextStyle(fontSize: 14, color: Colors.blueAccent)),
Text(exerciseName,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange),
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: true,
),
//Divider(color: Colors.transparent,),
FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.description),
Text(t("Description"),
style: TextStyle(
color: Colors.blueAccent, fontWeight: FontWeight.normal, fontSize: 14)),
Icon(Icons.arrow_forward_ios),
]),
textColor: Colors.blueAccent,
color: Colors.transparent,
onPressed: () =>
{
Navigator.of(context).pushNamed(
'exerciseTypeDescription', arguments: exerciseBloc.exerciseRepository),
},
),
//Divider(color: Colors.transparent,),
columnQuantityUnit(exerciseBloc),
Divider(color: Colors.transparent,),
columnQuantity(exerciseBloc),
Divider(),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
RaisedButton(
textColor: Colors.white,
color: Colors.deepOrange,
focusColor: Colors.white,
onPressed: () =>
{
confirmationDialog(exerciseBloc),
},
child: Text(t("Save"), style: TextStyle(fontSize: 16),)
),
]),
)
)
),
),
);
}
Row columnQuantityUnit( ExerciseNewBloc bloc ) {
Row row = Row();
if ( bloc.exerciseRepository.exerciseType != null &&
bloc.exerciseRepository.exerciseType.unitQuantity == "1") {
row = Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
NumberPicker.horizontal(
highlightSelectedValue: true,
initialValue: bloc.unitQuantity.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
onChanged: (value) => {
bloc.add(ExerciseNewQuantityUnitChange(quantity: value.toDouble()))
},
listViewHeight: 80,
textStyle: TextStyle(fontSize: 24),
textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold),
//decoration: _decoration,
),
new InkWell( new InkWell(
child: new Text(AppLocalizations.of(context).translate( child: new Text(t(bloc.exerciseRepository.exerciseType.unitQuantityUnit),
bloc.exerciseRepository.exerciseType.unitQuantityUnit),
style: TextStyle(fontSize: 16)), style: TextStyle(fontSize: 16)),
), ),
]); ]);
} }
return column; return row;
} }
Column columnQuantity( ExerciseFormBloc bloc ) { Row columnQuantity( ExerciseNewBloc bloc ) {
Column column = Column( Row row = Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
TextFieldBlocBuilder( NumberPicker.horizontal(
textFieldBloc: bloc.quantityField, highlightSelectedValue: true,
textAlign: TextAlign.center, initialValue: bloc.quantity.toInt(),
style: TextStyle(fontSize: 50, minValue: 0,
color: Colors.deepOrange, maxValue: 200,
fontWeight: FontWeight.bold), step: 1,
inputFormatters: [ onChanged: (value) => {
FilteringTextInputFormatter.allow (RegExp(r"[\d.]")) bloc.add(ExerciseNewQuantityChange(quantity: value.toDouble()))
],
onChanged: (input) =>
{
print ("Quantity value $input"),
bloc.exerciseRepository.setQuantity(double.parse(input)),
bloc.exerciseRepository.setUnit(bloc.exerciseRepository.exerciseType.unit)
}, },
decoration: InputDecoration( listViewHeight: 80,
fillColor: Colors.white, textStyle: TextStyle(fontSize: 24),
filled: false, textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold),
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), //decoration: _decoration,
hintText: AppLocalizations.of(context).translate("The number of the exercise"),
labelStyle: TextStyle(fontSize: 16, color: Colors.deepOrange),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unit),
),
), ),
Text(t(bloc.exerciseRepository.exerciseType.unit),
style: TextStyle(fontSize: 16)),
]); ]);
return column; return row;
} }
String validateNumberInput( input ) { void confirmationDialog( ExerciseNewBloc bloc ) {
String error = AppLocalizations.of(context).translate("Please type the right quantity 0-10000");
dynamic rc = ( input != null && input.length > 0);
if ( ! rc ) {
return null;
}
Pattern pattern = r'^\d+(?:\.\d+)?$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(input)) {
return error;
}
rc = double.tryParse(input);
if ( rc == null ) {
return error;
}
if ( ! ( double.parse(input) < 10000 && double.parse(input) > 0) ) {
return error;
}
return null;
}
void confirmationDialog( ExerciseFormBloc bloc ) {
LinkedHashMap args = LinkedHashMap(); LinkedHashMap args = LinkedHashMap();
print("exercise validated " + bloc.exerciseRepository.exercise.quantity.toString()); print("exercise validated " + bloc.exerciseRepository.exercise.quantity.toString());
if ( bloc.exerciseRepository.exercise.quantity == null) { if ( bloc.exerciseRepository.exercise.quantity == null) {
@ -250,21 +232,21 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> {
context: context, context: context,
//barrierDismissible: false, //barrierDismissible: false,
builder:(_) => CupertinoAlertDialog( builder:(_) => CupertinoAlertDialog(
title: Text(AppLocalizations.of(context).translate("Do you save this exercise with these parameters?")), title: Text(t("Do you save this exercise with these parameters?")),
content: Column( content: Column(
children: [ children: [
Divider(), Divider(),
Text(AppLocalizations.of(context).translate("Exercise") + ": " + Text(t("Exercise") + ": " +
bloc.exerciseRepository.exerciseType.name, bloc.exerciseRepository.exerciseType.name,
style: (TextStyle(color: Colors.blue)),), style: (TextStyle(color: Colors.blue)),),
Text(quantity + " " + Text(quantity + " " +
AppLocalizations.of(context).translate(bloc.exerciseRepository.exerciseType.unit), t(bloc.exerciseRepository.exerciseType.unit),
style: (TextStyle(color: Colors.deepOrange)),), style: (TextStyle(color: Colors.deepOrange)),),
Text(bloc.exerciseRepository.exerciseType.unitQuantity == "1" ? Text(bloc.exerciseRepository.exerciseType.unitQuantity == "1" ?
AppLocalizations.of(context).translate("with") + " " t("with") + " "
+ unitQuantity + " " + unitQuantity + " "
+ AppLocalizations.of(context).translate(bloc.exerciseRepository.exerciseType.unitQuantityUnit) : + t(bloc.exerciseRepository.exerciseType.unitQuantityUnit) :
"", "",
style: (TextStyle(color: Colors.deepOrange)), style: (TextStyle(color: Colors.deepOrange)),
), ),
@ -272,11 +254,11 @@ class _ExerciseNewPageState extends State<ExerciseNewPage> {
]), ]),
actions: [ actions: [
FlatButton( FlatButton(
child: Text(AppLocalizations.of(context).translate("No")), child: Text(t("No")),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
), ),
FlatButton( FlatButton(
child: Text(AppLocalizations.of(context).translate("Yes")), child: Text(t("Yes")),
onPressed: () => { onPressed: () => {
bloc.exerciseRepository.setCustomer(Cache().userLoggedIn), bloc.exerciseRepository.setCustomer(Cache().userLoggedIn),
bloc.exerciseRepository.addExercise(), bloc.exerciseRepository.addExercise(),

View File

@ -1,12 +1,14 @@
import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart'; 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/bloc/exercise_plan_custom_add/exercise_plan_custom_add_bloc.dart';
import 'package:aitrainer_app/library/numberpicker.dart';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart'; import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:flutter/services.dart'; import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -20,130 +22,229 @@ class ExercisePlanDetailAddPage extends StatefulWidget {
class _ExercisePlanDetailAddPage extends State<ExercisePlanDetailAddPage> with Trans { class _ExercisePlanDetailAddPage extends State<ExercisePlanDetailAddPage> with Trans {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
LinkedHashMap args = ModalRoute.of(context).settings.arguments;
// ignore: close_sinks // ignore: close_sinks
final ExercisePlanBloc planBloc = ModalRoute.of(context).settings.arguments; final ExercisePlanBloc planBloc = args['bloc'];
final WorkoutMenuTree workoutMenuTree = args['workoutTreeItem'];
final ExercisePlanRepository exercisePlanRepository = planBloc.exercisePlanRepository; final ExercisePlanRepository exercisePlanRepository = planBloc.exercisePlanRepository;
setContext(context); setContext(context);
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
ExercisePlanCustomerFormBloc(exercisePlanRepository: exercisePlanRepository, planBloc: planBloc), ExercisePlanCustomAddBloc(exercisePlanRepository: exercisePlanRepository, planBloc: planBloc, workoutMenuTree: workoutMenuTree)
child: Builder(builder: (context) { ..add(ExercisePlanCustomAddLoad()),
// ignore: close_sinks child: BlocConsumer<ExercisePlanCustomAddBloc, ExercisePlanCustomAddState>(
final bloc = BlocProvider.of<ExercisePlanCustomerFormBloc>(context); listener: (context, state) {
if (state is ExercisePlanCustomAddLoading) {
return LoadingDialog();
} else if (state is ExercisePlanCustomAddError) {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
}
},
builder: (context, state) {
// ignore: close_sinks
final bloc = BlocProvider.of<ExercisePlanCustomAddBloc>(context);
return getForm(bloc, workoutMenuTree);
},
));
}
String exerciseName = ""; Widget getForm(ExercisePlanCustomAddBloc bloc, WorkoutMenuTree workoutMenuTree) {
if (bloc != null) { String exerciseName = "";
exerciseName = AppLanguage().appLocal == Locale("en") if (bloc != null) {
? bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name exerciseName = AppLanguage().appLocal == Locale("en")
: bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.nameTranslation; ? bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name
} : bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.nameTranslation;
}
return Form( return Form(
autovalidate: true, autovalidate: true,
child: Scaffold( child: Scaffold(
resizeToAvoidBottomInset: true, resizeToAvoidBottomInset: true,
appBar: AppBarNav(depth: 1), appBar: AppBarNav(depth: 1),
body: Container( body: Container(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height, height: MediaQuery.of(context).size.height,
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'), image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.fill, fit: BoxFit.fill,
alignment: Alignment.center, alignment: Alignment.center,
), ),
),
child: Container(
padding: const EdgeInsets.only(top: 25, left: 25, right: 25),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[
Text(AppLocalizations.of(context).translate('Save The Exercise To The Exercise Plan'),
style: TextStyle(fontSize: 14, color: Colors.blueAccent)),
Text(
exerciseName,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange),
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: true,
),
TextFieldBlocBuilder(
textFieldBloc: bloc.serieField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the serie done with"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate("Serie"),
),
),
TextFieldBlocBuilder(
textFieldBloc: bloc.quantityField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText:
AppLocalizations.of(context).translate("The number of the repeats of one serie"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate("Repeats"),
),
),
TextFieldBlocBuilder(
textFieldBloc: bloc.weightField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The weight"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate("Weight"),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
textColor: Colors.white,
color: Colors.red.shade300,
focusColor: Colors.white,
onPressed: () => {
print("Remove " + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name),
bloc.exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.delete,
planBloc.add(ExercisePlanRemoveExercise(
exercisePlanDetail: bloc.exercisePlanRepository.getActualPlanDetail() )),
Navigator.of(context).pop(),
},
child: Text(t(
"Delete")), //Text(AppLocalizations.of(context).translate("Delete"), style: TextStyle(fontSize: 16),)
),
RaisedButton(
textColor: Colors.white,
color: Colors.blueAccent,
focusColor: Colors.white,
onPressed: () => {
bloc.submit(),
Navigator.of(context).pop(),
},
child: Text(t("Save"), style: TextStyle(fontSize: 16),
)),
],
),
]),
))),
), ),
); child: Container(
})); padding: const EdgeInsets.only(top: 25, left: 25, right: 25),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[
Text(t('Save The Exercise To The Exercise Plan'),
style: TextStyle(fontSize: 14, color: Colors.blueAccent)),
Text(
exerciseName,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange),
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: true,
),
Divider(color: Colors.transparent,height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Flexible(
fit: FlexFit.tight,
flex: 8,
child:
Text(t("Serie")),
),
NumberPicker.horizontal(
highlightSelectedValue: true,
initialValue: bloc.serie.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
onChanged: (value) => {
bloc.add(ExercisePlanCustomAddChangeSerie(quantity: value.toDouble()))
},
listViewHeight: 80,
textStyle: TextStyle(fontSize: 24),
textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold),
//decoration: _decoration,
),
]
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Flexible(
fit: FlexFit.tight,
flex: 8,
child:
Text(t("Weight") + " (" + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.unitQuantityUnit + ")"),
),
NumberPicker.horizontal(
highlightSelectedValue: true,
initialValue: bloc.quantityUnit.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
onChanged: (value) => {
bloc.add(ExercisePlanCustomAddChangeQuantityUnit(quantity: value.toDouble()))
},
listViewHeight: 80,
textStyle: TextStyle(fontSize: 24),
textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold),
//decoration: _decoration,
),
]),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Flexible(
fit: FlexFit.tight,
flex: 8,
child:
Text(t("Repeats")),
),
NumberPicker.horizontal(
highlightSelectedValue: true,
initialValue: bloc.quantity.toInt(),
minValue: 0,
maxValue: 200,
step: 1,
onChanged: (value) => {
bloc.add(ExercisePlanCustomAddChangeQuantity(quantity: value.toDouble()))
},
listViewHeight: 80,
textStyle: TextStyle(fontSize: 24),
textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold),
//decoration: _decoration,
),
]),
Divider(),
/*TextFieldBlocBuilder(
textFieldBloc: bloc.serieField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the serie done with"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate("Serie"),
),
),*/
/*TextFieldBlocBuilder(
textFieldBloc: bloc.quantityField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText:
AppLocalizations.of(context).translate("The number of the repeats of one serie"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate("Repeats"),
),
),*/
/*TextFieldBlocBuilder(
textFieldBloc: bloc.weightField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))],
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The weight"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate("Weight"),
),
),*/
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
textColor: Colors.white,
color: Colors.red.shade300,
focusColor: Colors.white,
onPressed: () => {
bloc.add(ExercisePlanCustomAddRemove()),
Navigator.of(context).pop(),
/*print("Remove " + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name),
bloc.exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.delete,
planBloc.add(ExercisePlanRemoveExercise(
exercisePlanDetail: bloc.exercisePlanRepository.getActualPlanDetail() )),
Navigator.of(context).pop(),*/
},
child: Text(t(
"Delete")), //Text(AppLocalizations.of(context).translate("Delete"), style: TextStyle(fontSize: 16),)
),
RaisedButton(
textColor: Colors.white,
color: Colors.blueAccent,
focusColor: Colors.white,
onPressed: () => {
bloc.add(ExercisePlanCustomAddSubmit()),
Navigator.of(context).pop(),
/* bloc.submit(),
Navigator.of(context).pop(),*/
},
child: Text(
t("Save"),
style: TextStyle(fontSize: 16),
)),
],
),
]),
))),
),
);
} }
} }

View File

@ -50,7 +50,7 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> with Trans {
padding: EdgeInsets.all(20), padding: EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: customerId == Cache().userLoggedIn.customerId ? AssetImage('asset/image/WT_light_background.png'): image: customerId == Cache().userLoggedIn.customerId ? AssetImage('asset/image/WT_menu_dark.png'):
AssetImage('asset/image/WT_menu_dark.png'), AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center, alignment: Alignment.center,
@ -157,14 +157,14 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> with Trans {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
IconButton( IconButton(
icon: element.selected ? Icon(Icons.check, color: Colors.greenAccent.shade700,) : Icon(Icons.add, color: Colors.blue.shade400,), icon: element.selected ? Icon(Icons.check, color: Colors.greenAccent.shade700,)
onPressed: () => { : Icon(Icons.add, color: Colors.blue.shade400,),
bloc.add(ExercisePlanUpdateUI(workoutTree: element)), onPressed: () => clickAddDetail(bloc, element),
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc),
},
), ),
SizedBox(width: 20), SizedBox(width: 10),
Flexible( Flexible(
fit: FlexFit.tight,
flex: 8,
child: child:
InkWell( InkWell(
child: child:
@ -173,10 +173,7 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> with Trans {
textAlign: TextAlign.start, textAlign: TextAlign.start,
style: TextStyle(fontSize: 12, color: Colors.black), style: TextStyle(fontSize: 12, color: Colors.black),
), ),
onTap: () => { onTap: () => clickAddDetail(bloc, element),
bloc.add(ExercisePlanUpdateUI(workoutTree: element)),
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc),
},
), ),
), ),
InkWell( InkWell(
@ -186,15 +183,12 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> with Trans {
Text(bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].repeats.toString() + Text(bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].repeats.toString() +
" x " + bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].weightEquation + " x " + bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].weightEquation +
" " + element.exerciseType.unitQuantityUnit, style: TextStyle(fontSize: 9, color: Colors.green),), " " + element.exerciseType.unitQuantityUnit, style: TextStyle(fontSize: 9, color: Colors.green),),
onTap: () => { onTap: () => clickAddDetail(bloc, element),
bloc.add(ExercisePlanUpdateUI(workoutTree: element)),
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc),
},
), ),
IconButton( IconButton(
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
icon: Icon(Icons.description, color: Colors.black12,), icon: Icon(Icons.info, color: Colors.black12,),
onPressed: () { onPressed: () {
}, },
@ -211,4 +205,11 @@ class _ExercisePlanCustomPage extends State<ExercisePlanCustomPage> with Trans {
}); });
return list; return list;
} }
void clickAddDetail(ExercisePlanBloc bloc, WorkoutMenuTree workoutMenuTree) {
final LinkedHashMap args = LinkedHashMap();
args['bloc'] = bloc;
args['workoutTreeItem'] = workoutMenuTree;
Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: args);
}
} }

View File

@ -27,6 +27,7 @@ class _MenuPage extends State<MenuPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
menuBloc = BlocProvider.of<MenuBloc>(context); menuBloc = BlocProvider.of<MenuBloc>(context);
menuBloc.parent = widget.parent;
return Scaffold( return Scaffold(
appBar: AppBarNav(isMenu: true,), appBar: AppBarNav(isMenu: true,),
body: Container( body: Container(

View File

@ -268,7 +268,7 @@ class _MyDevelopmentMuscleState extends State<MyDevelopmentMusclePage> with Comm
show: true, show: true,
bottomTitles: SideTitles( bottomTitles: SideTitles(
showTitles: true, showTitles: true,
textStyle: TextStyle(fontSize: 8, color: Colors.blueGrey), getTextStyles: (_) => TextStyle(fontSize: 8, color: Colors.blueGrey),
getTitles: (double value) { getTitles: (double value) {
var date = new DateTime.fromMillisecondsSinceEpoch(value.toInt()); var date = new DateTime.fromMillisecondsSinceEpoch(value.toInt());
//String strDate = DateFormat('MM.dd.', AppLanguage().appLocal.toString()).format(date); //String strDate = DateFormat('MM.dd.', AppLanguage().appLocal.toString()).format(date);
@ -278,7 +278,7 @@ class _MyDevelopmentMuscleState extends State<MyDevelopmentMusclePage> with Comm
), ),
leftTitles: SideTitles( leftTitles: SideTitles(
showTitles: true, showTitles: true,
textStyle: TextStyle(fontSize: 8, color: Colors.blueGrey), getTextStyles: (_) => TextStyle(fontSize: 8, color: Colors.blueGrey),
interval: bloc.listChartData[element.exerciseTypeId] == null interval: bloc.listChartData[element.exerciseTypeId] == null
? 100 ? 100
: bloc.listChartData[element.exerciseTypeId].interval, : bloc.listChartData[element.exerciseTypeId].interval,

View File

@ -26,10 +26,10 @@ class _MyDevelopmentPage extends State<MyDevelopmentPage> with Trans {
return Scaffold( return Scaffold(
appBar: AppBarNav(depth: 0), appBar: AppBarNav(depth: 0),
body: Container( body: Container(
padding: EdgeInsets.all(20), padding: EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'), image: AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center, alignment: Alignment.center,
), ),
@ -44,61 +44,22 @@ class _MyDevelopmentPage extends State<MyDevelopmentPage> with Trans {
ImageButton( ImageButton(
textAlignment: Alignment.topCenter, textAlignment: Alignment.topCenter,
text: t("My Exercise Logs"), text: t("My Exercise Logs"),
style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, backgroundColor: Colors.black54.withOpacity(0.4)), style: TextStyle(fontSize: 16, color: Colors.orange, fontWeight: FontWeight.bold, backgroundColor: Colors.black54.withOpacity(0.4)),
image: "asset/image/exercise_log.jpg", image: "asset/image/edzesnaplom400400.jpg",
top: 40, top: 150,
left: 15,
onTap:() => this.callBackExerciseLog(exerciseRepository, customerRepository), onTap:() => this.callBackExerciseLog(exerciseRepository, customerRepository),
isLocked: false, isLocked: false,
), ),
/*FlatButton(
padding: EdgeInsets.all(0),
textColor: Colors.white,
color: Colors.black12,
focusColor: Colors.blueAccent,
onPressed: () =>
{
args['exerciseRepository'] = exerciseRepository,
args['customerRepository'] = customerRepository,
args['customerId'] = Cache().userLoggedIn.customerId,
Navigator.of(context).pushNamed('exerciseLogPage',
arguments: args)
},
child: Text(t("My Exercise Logs"),
style: TextStyle(fontSize: 18),)
),*/
/*Stack(
fit: StackFit.passthrough,
overflow: Overflow.clip,
alignment: Alignment.topLeft,
children: [
Image.asset('asset/image/lock.png',
height: 40,
width: 40,
),
FlatButton(
padding: EdgeInsets.all(20),
textColor: Colors.white,
color: Colors.black12,
focusColor: Colors.blueAccent,
onPressed: () =>
{
args['customerId'] = Cache().userLoggedIn.customerId,
Navigator.of(context).pushNamed('mydevelopmentBodyPage',
arguments: args)
},
child: Text(t("My Whole Body Development"),
style: TextStyle(fontSize: 18),)
),
],
),*/
ImageButton( ImageButton(
textAlignment: Alignment.topLeft, textAlignment: Alignment.topLeft,
text: t("My Whole Body Development"), text: t("My Whole Body Development"),
style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, style: TextStyle(fontSize: 16, color: Colors.orange, fontWeight: FontWeight.bold,
backgroundColor: Colors.black54.withOpacity(0.4)), backgroundColor: Colors.black54.withOpacity(0.4)),
image: "asset/menu/3.1.BMI.png", image: "asset/image/testemfejl400x400.jpg",
top: 50, top: 150,
left: 15,
onTap:() => { onTap:() => {
args['customerId'] = Cache().userLoggedIn.customerId, args['customerId'] = Cache().userLoggedIn.customerId,
Navigator.of(context).pushNamed('mydevelopmentBodyPage', Navigator.of(context).pushNamed('mydevelopmentBodyPage',
@ -109,82 +70,39 @@ class _MyDevelopmentPage extends State<MyDevelopmentPage> with Trans {
ImageButton( ImageButton(
textAlignment: Alignment.topLeft, textAlignment: Alignment.topLeft,
text: t("Development Of Muscles"), text: t("Development Of Muscles"),
style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, style: TextStyle(fontSize: 16, color: Colors.orange, fontWeight: FontWeight.bold,
backgroundColor: Colors.black54.withOpacity(0.4)), backgroundColor: Colors.black54.withOpacity(0.4)),
image: "asset/image/development_muscles.jpg", image: "asset/image/izomcsop400400.jpg",
top: 50, top: 120,
left: 10,
onTap:() => { onTap:() => {
Navigator.of(context).pushNamed('mydevelopmentMusclePage', Navigator.of(context).pushNamed('mydevelopmentMusclePage',
arguments: args) arguments: args)
}, },
isLocked: true, isLocked: true,
), ),
/*Stack(
fit: StackFit.passthrough,
overflow: Overflow.clip,
children: [
*//*Image.asset('asset/image/lock.png',
height: 40,
width: 40,
),*//*
FlatButton(
padding: EdgeInsets.all(20),
textColor: Colors.white,
color: Colors.black12,
focusColor: Colors.blueAccent,
onPressed: () =>
{
Navigator.of(context).pushNamed('mydevelopmentMusclePage',
arguments: args)
},
child: Text(t("Development Of Muscles"),
style: TextStyle(fontSize: 18),)
),
]
),*/
ImageButton( ImageButton(
textAlignment: Alignment.topLeft, textAlignment: Alignment.topLeft,
text: t("Predictions"), text: t("Predictions"),
style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, style: TextStyle(fontSize: 16, color: Colors.orange, fontWeight: FontWeight.bold,
backgroundColor: Colors.black54.withOpacity(0.4)), backgroundColor: Colors.black54.withOpacity(0.4)),
image: "asset/menu/2.2.1.1RM.png", image: "asset/menu/2.2.1.1RM.png",
top: 50, top: 150,
onTap:() => { onTap:() => {
}, },
isLocked: true, isLocked: true,
), ),
/*Stack(
fit: StackFit.passthrough,
overflow: Overflow.clip,
children: [
Image.asset('asset/image/lock.png',
height: 40,
width: 40,
),
FlatButton(
padding: EdgeInsets.all(20),
textColor: Colors.white,
color: Colors.black12,
focusColor: Colors.blueAccent,
onPressed: () =>
{
},
child: Text(t("Predictions"),
style: TextStyle(fontSize: 18),)
),
]
),*/
hiddenWidget(customerRepository, exerciseRepository), hiddenWidget(customerRepository, exerciseRepository),
] ]
), ),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
mainAxisSpacing: 20.0, mainAxisSpacing: 15.0,
crossAxisSpacing: 20.0, crossAxisSpacing: 15.0,
childAspectRatio: 1.2, childAspectRatio: 1.0,
), ),
) )
] ]

View File

@ -4,6 +4,7 @@ import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/image_button.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -25,7 +26,7 @@ class _MyExercisePlanPage extends State<MyExercisePlanPage> with Trans {
padding: EdgeInsets.all(20), padding: EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'), image: AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center, alignment: Alignment.center,
), ),
@ -37,97 +38,82 @@ class _MyExercisePlanPage extends State<MyExercisePlanPage> with Trans {
SliverGrid( SliverGrid(
delegate: SliverChildListDelegate( delegate: SliverChildListDelegate(
[ [
FlatButton( ImageButton(
padding: EdgeInsets.all(10), textAlignment: Alignment.topLeft,
textColor: Colors.white, text: t("Execute My Selected Training Plan"),
color: Colors.black12, style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold,
focusColor: Colors.blueAccent, backgroundColor: Colors.black54.withOpacity(0.4)),
onPressed: () => image: "asset/image/exercise_plan_execute.jpg",
{ top: 150,
left: 15,
onTap:() => {
args['customerId'] = Cache().userLoggedIn.customerId, args['customerId'] = Cache().userLoggedIn.customerId,
Navigator.of(context).pushNamed('exerciseByPlanPage', Navigator.of(context).pushNamed('exerciseExecutePlanPage',
arguments: args) arguments: args)
}, },
child: Text(t("Execute My Selected Training Plan"), isLocked: false,
style: TextStyle(fontSize: 18),)
), ),
FlatButton( ImageButton(
padding: EdgeInsets.all(0), textAlignment: Alignment.topLeft,
textColor: Colors.white, text: t("Edit My Custom Plan"),
color: Colors.black12, style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold,
focusColor: Colors.blueAccent, backgroundColor: Colors.black54.withOpacity(0.4)),
onPressed: () => image: "asset/image/exercise_plan_custom.jpg",
{ top: 150,
left: 15,
onTap:() => {
args['exerciseRepository'] = exerciseRepository, args['exerciseRepository'] = exerciseRepository,
args['customerId'] = Cache().userLoggedIn.customerId, args['customerId'] = Cache().userLoggedIn.customerId,
Navigator.of(context).pushNamed('exercisePlanCustomPage', Navigator.of(context).pushNamed('exercisePlanCustomPage',
arguments: args) arguments: args)
}, },
child: Text(t("Edit My Custom Plan"), isLocked: false,
style: TextStyle(fontSize: 18),)
), ),
FlatButton(
padding: EdgeInsets.all(20), ImageButton(
textColor: Colors.white, textAlignment: Alignment.topLeft,
color: Colors.black12, text: t("Suggested Training Plan"),
focusColor: Colors.blueAccent, style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold,
onPressed: () => backgroundColor: Colors.black54.withOpacity(0.4)),
{ image: "asset/image/exercise_plan_suggested.jpg",
top: 150,
left: 10,
onTap:() => {
}, },
child: Text(t("Suggested Training Plan"), isLocked: true,
style: TextStyle(fontSize: 18),)
),
Stack(
fit: StackFit.passthrough,
overflow: Overflow.clip,
alignment: Alignment.topLeft,
children: [
Image.asset('asset/image/lock.png',
height: 40,
width: 40,
),
FlatButton(
padding: EdgeInsets.all(20),
textColor: Colors.white,
color: Colors.black12,
focusColor: Colors.blueAccent,
onPressed: () =>
{
},
child: Text(t("My Special Plan"),
style: TextStyle(fontSize: 18),)
),
],
), ),
Stack( ImageButton(
fit: StackFit.passthrough, textAlignment: Alignment.topLeft,
overflow: Overflow.clip, text: t("My Special Plan"),
children: [ style: TextStyle(fontSize: 16, color: Colors.orange, fontWeight: FontWeight.bold,
Image.asset('asset/image/lock.png', backgroundColor: Colors.black54.withOpacity(0.4)),
height: 40, image: "asset/image/exercise_plan_special.jpg",
width: 40, top: 150,
), left: 10,
FlatButton( onTap:() => {
padding: EdgeInsets.all(20),
textColor: Colors.white,
color: Colors.black12,
focusColor: Colors.blueAccent,
onPressed: () =>
{
}, },
child: Text(t("My Arnold's Plan"), isLocked: true,
style: TextStyle(fontSize: 18),)
),
]
), ),
ImageButton(
textAlignment: Alignment.topLeft,
text: t("My Arnold's Plan"),
style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold,
backgroundColor: Colors.black54.withOpacity(0.4)),
image: "asset/image/exercise_plan_stars.jpg",
top: 120,
left: 10,
onTap:() => {
},
isLocked: true,
),
hiddenPlanWidget(exerciseRepository), hiddenPlanWidget(exerciseRepository),
hiddenTrainingWidget(), hiddenTrainingWidget(),
@ -136,9 +122,9 @@ class _MyExercisePlanPage extends State<MyExercisePlanPage> with Trans {
), ),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
mainAxisSpacing: 20.0, mainAxisSpacing: 15.0,
crossAxisSpacing: 20.0, crossAxisSpacing: 15.0,
childAspectRatio: 1.2, childAspectRatio: 1.0,
), ),
) )
] ]
@ -182,7 +168,7 @@ class _MyExercisePlanPage extends State<MyExercisePlanPage> with Trans {
onPressed: () => onPressed: () =>
{ {
args['customerId'] = Cache().getTrainee().customerId, args['customerId'] = Cache().getTrainee().customerId,
Navigator.of(context).pushNamed('exerciseByPlanPage', Navigator.of(context).pushNamed('exerciseExecutePlanPage',
arguments: args) arguments: args)
}, },
child: Text(t("Execute My Trainee's Training Plan"), child: Text(t("Execute My Trainee's Training Plan"),

View File

@ -108,13 +108,6 @@ class SettingsPage extends StatelessWidget{
) )
), ),
ListTile(
leading: Icon(Icons.get_app),
title: RaisedButton(
child: Text("Check lang", style: TextStyle(fontSize: 12),),
onPressed: () => settingsBloc.add(SettingsGetLanguage()),
)
)
] ]
); );
} }

View File

@ -8,6 +8,7 @@ class ImageButton extends StatelessWidget {
final TextStyle style; final TextStyle style;
final String image; final String image;
final double top; final double top;
final double left;
final double height; final double height;
final double width; final double width;
final bool isShape; final bool isShape;
@ -21,6 +22,7 @@ class ImageButton extends StatelessWidget {
this.style, this.style,
this.image, this.image,
this.top, this.top,
this.left,
this.height, this.height,
this.width, this.width,
this.bloc, this.bloc,
@ -48,20 +50,24 @@ class ImageButton extends StatelessWidget {
Stack( Stack(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
children: [ children: [
this.isLocked? Positioned(
top: 50,
left: 50,
child: this.isLocked?
Image.asset( Image.asset(
'asset/image/lock.png', 'asset/image/lock.png',
height: 40, height: 60,
width: 40, width: 60,
) )
: Container(), : Container(),
]), )]
),
Positioned( Positioned(
top: top, top: top,
left: 10, left: left,
child: Container( child: Container(
height: 100, height: 100,
width: 150, width: 180,
child: InkWell( child: InkWell(
onTap: onTap ?? onTap, onTap: onTap ?? onTap,
child: Text( child: Text(

View File

@ -42,7 +42,7 @@ packages:
name: bloc name: bloc
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.3" version: "6.1.0"
bloc_test: bloc_test:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -154,7 +154,7 @@ packages:
name: code_builder name: code_builder
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.4.1" version: "3.5.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -210,7 +210,7 @@ packages:
name: devicelocale name: devicelocale
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.2" version: "0.3.3"
equatable: equatable:
dependency: "direct main" dependency: "direct main"
description: description:
@ -239,6 +239,76 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.2.1" version: "5.2.1"
firebase:
dependency: transitive
description:
name: firebase
url: "https://pub.dartlang.org"
source: hosted
version: "7.3.2"
firebase_analytics:
dependency: "direct main"
description:
name: firebase_analytics
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.2"
firebase_analytics_platform_interface:
dependency: transitive
description:
name: firebase_analytics_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
firebase_analytics_web:
dependency: transitive
description:
name: firebase_analytics_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
url: "https://pub.dartlang.org"
source: hosted
version: "0.18.1+2"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1+1"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0+1"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -252,7 +322,7 @@ packages:
name: fl_chart name: fl_chart
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.11.1" version: "0.12.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -395,6 +465,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.18" version: "2.1.18"
infinite_listview:
dependency: "direct main"
description:
name: infinite_listview
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1+1"
intl: intl:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -653,7 +730,7 @@ packages:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.12" version: "0.5.12+2"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:

View File

@ -25,12 +25,9 @@ dependencies:
sdk: flutter sdk: flutter
cupertino_icons: ^1.0.0 cupertino_icons: ^1.0.0
devicelocale: ^0.3.2 devicelocale: ^0.3.3
sentry: ^3.0.1 sentry: ^3.0.1
# firebase_messaging: ^6.0.16 flutter_bloc: ^6.0.6
#flutter_local_notifications: ^1.5.0-beta.9
flutter_facebook_login: ^3.0.0
flutter_bloc: ^6.0.5
equatable: ^1.2.5 equatable: ^1.2.5
freezed: ^0.12.1 freezed: ^0.12.1
flutter_form_bloc: ^0.19.0 flutter_form_bloc: ^0.19.0
@ -38,7 +35,13 @@ dependencies:
rainbow_color: ^0.1.1 rainbow_color: ^0.1.1
percent_indicator: ^2.1.7+4 percent_indicator: ^2.1.7+4
gradient_bottom_navigation_bar: ^1.0.0+4 gradient_bottom_navigation_bar: ^1.0.0+4
fl_chart: ^0.11.1 fl_chart: ^0.12.0
infinite_listview: ^1.0.1+1
firebase_core: 0.5.0+1
firebase_analytics: ^6.0.2
firebase_auth: ^0.18.1+2
flutter_facebook_login: ^3.0.0
mockito: ^4.1.1 mockito: ^4.1.1
@ -56,9 +59,9 @@ dev_dependencies:
http: 0.12.1 http: 0.12.1
intl: 0.16.1 intl: 0.16.1
shared_preferences: ^0.5.12 shared_preferences: ^0.5.12+2
flutter_launcher_icons: ^0.8.0 flutter_launcher_icons: ^0.8.1
flutter_icons: flutter_icons:
android: "launcher_icon" android: "launcher_icon"
@ -93,8 +96,14 @@ flutter:
- asset/image/login_fb.png - asset/image/login_fb.png
- asset/image/lock.png - asset/image/lock.png
- asset/image/Congrats_N1.jpg - asset/image/Congrats_N1.jpg
- asset/image/development_muscles.jpg - asset/image/testemfejl400x400.jpg
- asset/image/exercise_log.jpg - asset/image/izomcsop400400.jpg
- asset/image/edzesnaplom400400.jpg
- asset/image/exercise_plan_stars.jpg
- asset/image/exercise_plan_special.jpg
- asset/image/exercise_plan_execute.jpg
- asset/image/exercise_plan_custom.jpg
- asset/image/exercise_plan_suggested.jpg
- asset/menu/1.cardio.png - asset/menu/1.cardio.png
- asset/menu/1.1.aerob.png - asset/menu/1.1.aerob.png
- asset/menu/1.2.anaerob.png - asset/menu/1.2.anaerob.png
@ -119,12 +128,12 @@ flutter:
- asset/menu/3.1.BMI.png - asset/menu/3.1.BMI.png
- asset/menu/3.2.BMR.png - asset/menu/3.2.BMR.png
- asset/menu/3.3.sizes.png - asset/menu/3.3.sizes.png
- asset/menu/cable triceps.png - asset/menu/cable_triceps.png
- asset/menu/Back_pullup.png - asset/menu/Back_pullup.png
- asset/menu/biceps.jpg - asset/menu/biceps.jpg
- asset/menu/calf.png - asset/menu/calf.png
- asset/menu/legpress.jpg - asset/menu/legpress.jpg
- asset/menu/shoulder press.png - asset/menu/shoulder_press.png
- asset/menu/squat.jpg - asset/menu/squat.jpg
- asset/menu/tricdip.jpg - asset/menu/tricdip.jpg
- i18n/en.json - i18n/en.json