From 6b0892ec09ce5a65878c15a12556d27505dcc1be Mon Sep 17 00:00:00 2001 From: Bossanyi Tibor Date: Tue, 1 Sep 2020 16:47:45 +0200 Subject: [PATCH] wt1.1c auth->cache, control page, animation base exercise percent --- i18n/en.json | 11 +- i18n/hu.json | 11 +- lib/animations/test_progress_animation.dart | 29 ++ lib/bloc/account/account_bloc.dart | 8 +- lib/bloc/custom_exercise_form_bloc.dart | 4 +- lib/bloc/exercise_control_form_bloc.dart | 133 ++++++++ lib/bloc/login_form_bloc.dart | 18 +- lib/bloc/registration_form_bloc.dart | 18 +- lib/main.dart | 3 +- lib/model/{auth.dart => cache.dart} | 57 +++- lib/model/exercise_type.dart | 10 + lib/model/user.dart | 4 +- lib/model/workout_tree.dart | 4 +- lib/repository/exercise_repository.dart | 51 +++- lib/repository/menu_tree_repository.dart | 29 +- lib/service/api.dart | 29 +- lib/service/customer_service.dart | 12 +- lib/service/exercise_tree_service.dart | 4 +- lib/service/exercisetype_service.dart | 4 +- lib/util/common.dart | 24 +- lib/util/session.dart | 40 +-- lib/view/account.dart | 1 - lib/view/custom_exercise_page.dart | 73 +++-- lib/view/customer_modify_page.dart | 2 +- lib/view/exercise_control_page.dart | 321 ++++++++++++++++++++ lib/view/exercise_new_page.dart | 4 +- lib/view/login.dart | 4 +- lib/view/menu_page.dart | 43 ++- lib/view/registration.dart | 4 +- lib/view/settings.dart | 2 +- lib/widgets/app_bar.dart | 153 ++++++++++ lib/widgets/home.dart | 6 +- lib/widgets/loading.dart | 4 +- lib/widgets/menu_page_widget.dart | 11 +- pubspec.lock | 173 ++++++++++- pubspec.yaml | 14 +- test/account_bloc_test.dart | 79 ----- test/widget_test.login.dart | 12 +- 38 files changed, 1148 insertions(+), 261 deletions(-) create mode 100644 lib/animations/test_progress_animation.dart create mode 100644 lib/bloc/exercise_control_form_bloc.dart rename lib/model/{auth.dart => cache.dart} (71%) create mode 100644 lib/view/exercise_control_page.dart create mode 100644 lib/widgets/app_bar.dart delete mode 100644 test/account_bloc_test.dart diff --git a/i18n/en.json b/i18n/en.json index 5b6516f..5315f9f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -105,5 +105,14 @@ "Endomorph": "Endomorph", "Mesomorph": "Mesomorph", - "Description": "Description" + "Description": "Description", + "Make your first test": "Make your first test", + "Why do you need Exercise Control?" : "Why do you need Exercise Control?", + + "Your 1RM:":"Your 1RM:", + "Your Real 1RM:":"Your Real 1RM:", + "Check":"Check", + "1st Control Exercise:": "1st Control Exercise:", + "2nd Control Exercise:": "2nd Control Exercise:", + "3rd Control Exercise:": "3rd Control Exercise:" } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index b5e9c4a..debfcca 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -105,6 +105,13 @@ "Endomorph":"Endomorf", "Mesomorph":"Mezomorf", - "Description": "Leírás" - + "Description": "Leírás", + "Make your first test": "Végezd el az első tesztet", + "Why do you need Exercise Control?": "Miért szükséges a kontrollgyakorlat?", + "Your 1RM:":"Maxerőd:", + "Your Real 1RM:":"Ellenőrzött maxerő:", + "Check":"Ellenőrzés", + "1st Control Exercise:": "1. kontrollgyakorlat:", + "2nd Control Exercise:": "2. kontrollgyakorlat:", + "3rd Control Exercise:": "3. kontrollgyakorlat:" } \ No newline at end of file diff --git a/lib/animations/test_progress_animation.dart b/lib/animations/test_progress_animation.dart new file mode 100644 index 0000000..7b1d6cc --- /dev/null +++ b/lib/animations/test_progress_animation.dart @@ -0,0 +1,29 @@ +import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class TestProgress extends AnimatedWidget { + + TestProgress({ + Key key, + @required Animation animation, + }) : super(key: key, listenable: animation); + + @override + Widget build(BuildContext context) { + final animation = listenable as Animation; + + return Transform.scale( + alignment: Alignment.center, + scale: animation.value, + origin: Offset(-5,0), + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.only(left: 0), + child: Icon(Icons.star, color: Colors.yellow,) + ), + ); + } + + +} \ No newline at end of file diff --git a/lib/bloc/account/account_bloc.dart b/lib/bloc/account/account_bloc.dart index e637e96..03779a8 100644 --- a/lib/bloc/account/account_bloc.dart +++ b/lib/bloc/account/account_bloc.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:bloc/bloc.dart'; @@ -14,8 +14,8 @@ class AccountBloc extends Bloc { final CustomerRepository customerRepository; bool loggedIn = false; AccountBloc({this.customerRepository}) : super(AccountInitial()) { - if ( Auth().userLoggedIn != null ) { - customerRepository.customer = Auth().userLoggedIn; + if ( Cache().userLoggedIn != null ) { + customerRepository.customer = Cache().userLoggedIn; loggedIn = true; } } @@ -34,7 +34,7 @@ class AccountBloc extends Bloc { customerRepository.customer = event.customer; yield AccountLoggedIn(); } else if (event is AccountLogout) { - await Auth().logout(); + await Cache().logout(); customerRepository.customer = null; loggedIn = false; yield AccountLoggedOut(); diff --git a/lib/bloc/custom_exercise_form_bloc.dart b/lib/bloc/custom_exercise_form_bloc.dart index 69830db..6f4008c 100644 --- a/lib/bloc/custom_exercise_form_bloc.dart +++ b/lib/bloc/custom_exercise_form_bloc.dart @@ -92,9 +92,7 @@ class CustomExerciseFormBloc extends FormBloc { exerciseRepository.rmWathen = 100 * weight / (48.8 + 53.8 * pow(e, -0.075 * repeat)); rmWathenField.updateValue(exerciseRepository.rmWathen.toStringAsFixed(1)); - double average = (exerciseRepository.rmWendler + exerciseRepository.rmWathen + - exerciseRepository.rmMayhew + exerciseRepository.rmOconner + - exerciseRepository.rmMcglothlin)/4; + double average = (exerciseRepository.rmWendler + exerciseRepository.rmOconner)/2; rmAverageField.updateValue(average.toStringAsFixed(1)); rm90Field.updateValue((average*0.9).toStringAsFixed(1)); rm80Field.updateValue((average*0.8).toStringAsFixed(1)); diff --git a/lib/bloc/exercise_control_form_bloc.dart b/lib/bloc/exercise_control_form_bloc.dart new file mode 100644 index 0000000..efbd8ec --- /dev/null +++ b/lib/bloc/exercise_control_form_bloc.dart @@ -0,0 +1,133 @@ +import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +class ExerciseControlFormBloc extends FormBloc { + final ExerciseRepository exerciseRepository; + int step = 1; + final double percentToCalculate; + + 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}) { + 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); + }); + + unitQuantity1Field.onValueChanges(onData: (previous, current) async* { + exerciseRepository.setUnitQuantity(current.valueToDouble); + }); + quantity2Field.onValueChanges(onData: (previous, current) async* { + exerciseRepository.setQuantity(current.valueToDouble); + }); + + unitQuantity2Field.onValueChanges(onData: (previous, current) async* { + exerciseRepository.setUnitQuantity(current.valueToDouble); + }); + quantity3Field.onValueChanges(onData: (previous, current) async* { + exerciseRepository.setQuantity(current.valueToDouble); + }); + + unitQuantity3Field.onValueChanges(onData: (previous, current) async* { + exerciseRepository.setUnitQuantity(current.valueToDouble); + }); + } + + @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)); + } + + //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 close() { + initialRMField.close(); + quantity1Field.close(); + unitQuantity1Field.close(); + quantity2Field.close(); + unitQuantity2Field.close(); + quantity3Field.close(); + unitQuantity3Field.close(); + + return super.close(); + } +} diff --git a/lib/bloc/login_form_bloc.dart b/lib/bloc/login_form_bloc.dart index 7a720ec..7c9ac78 100644 --- a/lib/bloc/login_form_bloc.dart +++ b/lib/bloc/login_form_bloc.dart @@ -1,10 +1,10 @@ import 'package:aitrainer_app/bloc/account/account_bloc.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; -class LoginFormBloc extends FormBloc { +class LoginFormBloc extends FormBloc with Common { final AccountBloc accountBloc; final UserRepository userRepository; @@ -40,18 +40,18 @@ class LoginFormBloc extends FormBloc { void onSubmitting() async { try { emitLoading(progress: 30); - if ( ! Common.validateEmail(userRepository)) { - emailField.addFieldError(Common.EMAIL_ERROR, isPermanent: true); + if ( ! validateEmail(userRepository)) { + emailField.addFieldError(EMAIL_ERROR, isPermanent: true); - emitFailure(failureResponse: Common.EMAIL_ERROR); - } else if ( ! Common.validatePassword(userRepository)) { - passwordField.addFieldError(Common.PASSWORD_ERROR, isPermanent: true); - emitFailure(failureResponse: Common.PASSWORD_ERROR); + emitFailure(failureResponse: EMAIL_ERROR); + } else if ( ! validatePassword(userRepository)) { + passwordField.addFieldError( PASSWORD_ERROR, isPermanent: true); + emitFailure(failureResponse: PASSWORD_ERROR); } else { // Emit either Loaded or Error await userRepository.getUser(); emitSuccess(canSubmitAgain: false); - accountBloc.add(AccountLogInFinished(customer: Auth().userLoggedIn)); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn)); } } on Exception catch (ex) { emitFailure(failureResponse: ex.toString()); diff --git a/lib/bloc/registration_form_bloc.dart b/lib/bloc/registration_form_bloc.dart index 79c407f..55d5097 100644 --- a/lib/bloc/registration_form_bloc.dart +++ b/lib/bloc/registration_form_bloc.dart @@ -1,10 +1,10 @@ -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'account/account_bloc.dart'; -class RegistrationFormBloc extends FormBloc { +class RegistrationFormBloc extends FormBloc with Common { final AccountBloc accountBloc; final emailField = TextFieldBloc( validators: [ @@ -38,18 +38,18 @@ class RegistrationFormBloc extends FormBloc { void onSubmitting() async { try { emitLoading(progress: 30); - if ( ! Common.validateEmail(userRepository)) { - emailField.addFieldError(Common.EMAIL_ERROR, isPermanent: true); + if ( ! validateEmail(userRepository)) { + emailField.addFieldError(EMAIL_ERROR, isPermanent: true); - emitFailure(failureResponse: Common.EMAIL_ERROR); - } else if ( ! Common.validatePassword(userRepository)) { - passwordField.addFieldError(Common.PASSWORD_ERROR, isPermanent: true); - emitFailure(failureResponse: Common.PASSWORD_ERROR); + emitFailure(failureResponse: EMAIL_ERROR); + } else if ( ! validatePassword(userRepository)) { + passwordField.addFieldError(PASSWORD_ERROR, isPermanent: true); + emitFailure(failureResponse: PASSWORD_ERROR); } else { // Emit either Loaded or Error await userRepository.addUser(); emitSuccess(canSubmitAgain: false); - accountBloc.add(AccountLogInFinished(customer: Auth().userLoggedIn)); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn)); } } on Exception catch (ex) { emitFailure(failureResponse: ex.toString()); diff --git a/lib/main.dart b/lib/main.dart index d4763f3..338231c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,4 @@ import 'dart:async'; - import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/menu_tree_repository.dart'; import 'package:aitrainer_app/util/session.dart'; @@ -10,6 +9,7 @@ import 'package:aitrainer_app/view/customer_fitness_page.dart'; import 'package:aitrainer_app/view/customer_goal_page.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_welcome_page.dart'; +import 'package:aitrainer_app/view/exercise_control_page.dart'; import 'package:aitrainer_app/view/exercise_type_description.dart'; import 'package:aitrainer_app/view/gdpr.dart'; import 'package:aitrainer_app/view/login.dart'; @@ -163,6 +163,7 @@ class AitrainerApp extends StatelessWidget { 'customerWelcomePage': (context) => CustomerWelcomePage(), 'exerciseNewPage': (context) => ExerciseNewPage(), 'exerciseCustomPage': (context) => CustomExercisePage(), + 'exerciseControlPage': (context) => ExerciseControlPage(), 'login': (context) => LoginPage(), 'registration': (context) => RegistrationPage(), 'gdpr': (context) => Gdpr(), diff --git a/lib/model/auth.dart b/lib/model/cache.dart similarity index 71% rename from lib/model/auth.dart rename to lib/model/cache.dart index 14862c3..a22104d 100644 --- a/lib/model/auth.dart +++ b/lib/model/cache.dart @@ -1,5 +1,10 @@ +import 'dart:collection'; + import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/exercise_tree.dart'; +import 'package:aitrainer_app/model/exercise.dart'; +import 'package:aitrainer_app/model/workout_tree.dart'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/service/exercise_tree_service.dart'; import 'package:aitrainer_app/service/exercisetype_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -27,8 +32,8 @@ enum SharePrefsChange { - is_logged_in */ -class Auth { - static final Auth _singleton = Auth._internal(); +class Cache { + static final Cache _singleton = Cache._internal(); // Keys to store and fetch data from SharedPreferences static final String authTokenKey = 'auth_token'; @@ -45,16 +50,20 @@ class Auth { String authToken = ""; Customer userLoggedIn; bool firstLoad = true; + List _exerciseTypes; List _exerciseTree; + List _exercises; + LinkedHashMap _tree = LinkedHashMap(); + List deviceLanguages; String startPage; - factory Auth() { + factory Cache() { return _singleton; } - Auth._internal(); + Cache._internal(); String getAuthToken() { return this.authToken; @@ -101,23 +110,26 @@ class Auth { sharedPreferences = await prefs; DateTime now = DateTime.now(); - sharedPreferences.setString(Auth.lastStoreDateKey, now.toString()); + sharedPreferences.setString(Cache.lastStoreDateKey, now.toString()); + ExerciseRepository exerciseRepository = ExerciseRepository(); if ( type == SharePrefsChange.registration ) { - Auth().startPage = "home"; - sharedPreferences.setInt(Auth.customerIdKey, customerId); - sharedPreferences.setBool(Auth.isRegisteredKey, true); - sharedPreferences.setBool(Auth.isLoggedInKey, true); + Cache().startPage = "home"; + sharedPreferences.setInt(Cache.customerIdKey, customerId); + sharedPreferences.setBool(Cache.isRegisteredKey, true); + sharedPreferences.setBool(Cache.isLoggedInKey, true); await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); + exerciseRepository.getExercisesByCustomer(customerId); } else if ( type == SharePrefsChange.login ) { - Auth().startPage = "home"; - sharedPreferences.setInt(Auth.customerIdKey, customerId); - sharedPreferences.setBool(Auth.isLoggedInKey, true); + Cache().startPage = "home"; + sharedPreferences.setInt(Cache.customerIdKey, customerId); + sharedPreferences.setBool(Cache.isLoggedInKey, true); await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); + exerciseRepository.getExercisesByCustomer(customerId); } else if ( type == SharePrefsChange.logout ) { - sharedPreferences.setBool(Auth.isLoggedInKey, false); - sharedPreferences.setInt(Auth.customerIdKey, 0); + sharedPreferences.setBool(Cache.isLoggedInKey, false); + sharedPreferences.setInt(Cache.customerIdKey, 0); sharedPreferences.setString(authTokenKey, ""); } } @@ -130,6 +142,14 @@ class Auth { this._exerciseTree = exerciseTree; } + void setExercises( List exercises ) { + this._exercises = exercises; + } + + void setWorkoutTree( LinkedHashMap tree) { + this._tree = tree; + } + List getExerciseTypes() { return this._exerciseTypes; } @@ -137,4 +157,13 @@ class Auth { List getExerciseTree() { return this._exerciseTree; } + + List getExercises() { + return this._exercises; + } + + LinkedHashMap getWorkoutTree() { + return this._tree; + } + } \ No newline at end of file diff --git a/lib/model/exercise_type.dart b/lib/model/exercise_type.dart index 133a32c..27890f0 100644 --- a/lib/model/exercise_type.dart +++ b/lib/model/exercise_type.dart @@ -15,6 +15,8 @@ class ExerciseType { String nameTranslation; String descriptionTranslation; + bool is1RM; + ExerciseType({this.name, this.description}); ExerciseType.fromJson(Map json) { @@ -41,4 +43,12 @@ class ExerciseType { "unitQuantityUnit": unitQuantityUnit, "active": active }; + + void set1RM(bool is1RM) { + this.is1RM = is1RM; + } + + bool get1RM() { + return this.is1RM; + } } \ No newline at end of file diff --git a/lib/model/user.dart b/lib/model/user.dart index 0d83fef..14cba3c 100644 --- a/lib/model/user.dart +++ b/lib/model/user.dart @@ -1,4 +1,4 @@ -class User { + class User { String email; String password; int customerId; @@ -12,4 +12,4 @@ class User { "username": email, "password": password, }; -} \ No newline at end of file +} diff --git a/lib/model/workout_tree.dart b/lib/model/workout_tree.dart index 2a94e80..d07bb92 100644 --- a/lib/model/workout_tree.dart +++ b/lib/model/workout_tree.dart @@ -14,6 +14,8 @@ class WorkoutTree { ExerciseType exerciseType; bool base; - WorkoutTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child, this.exerciseTypeId, this.exerciseType, this.base); + bool is1RM; + + WorkoutTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child, this.exerciseTypeId, this.exerciseType, this.base, this.is1RM); } diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart index 4bc949c..10e2b77 100644 --- a/lib/repository/exercise_repository.dart +++ b/lib/repository/exercise_repository.dart @@ -1,3 +1,4 @@ +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; @@ -7,6 +8,7 @@ class ExerciseRepository { Exercise exercise; Customer customer; ExerciseType exerciseType; + List exerciseList; double rmWendler = 0; double rmMcglothlin = 0; @@ -79,10 +81,51 @@ class ExerciseRepository { this.exerciseType = exerciseType; } -/* - Future> getExercisesByCustomer( int customerId ) async { + + Future> getExercisesByCustomer( int customerId ) async { final results = await ExerciseApi().getExercisesByCustomer(customerId); - this.exerciseList = results.map((item) => ExerciseRepository(exercise: item)).toList(); + this.exerciseList = results; + Cache().setExercises(exerciseList); return this.exerciseList; - } */ + } + + List getExerciseList() { + if ( this.exerciseList == null || this.exerciseList.length == 0 ) { + this.exerciseList = Cache().getExercises(); + } + return this.exerciseList; + } + + double getBaseExerciseFinishedPercent() { + return 0; + } + + void getLastExercise() { + List exercises = this.getExerciseList(); + Exercise lastExercise = exercises[0]; + exercises.forEach((element) { + Exercise actualExercise = element; + if ( actualExercise.dateAdd.compareTo(lastExercise.dateAdd) > 0 ) { + lastExercise = actualExercise; + } + }); + this.exercise = lastExercise; + this.customer = Cache().userLoggedIn; + this.exerciseType = getExerciseTypeById(exercise.exerciseTypeId); + return; + } + + ExerciseType getExerciseTypeById(int exerciseTypeId) { + ExerciseType actualExerciseType; + Cache().getExerciseTypes().forEach((element) { + ExerciseType exerciseType = element; + if ( exerciseType.exerciseTypeId == exerciseTypeId) { + actualExerciseType = exerciseType; + } + }); + if ( actualExerciseType == null ) { + throw Exception("Data error, no ExerciseType for exerciseTypeId $exerciseTypeId" ); + } + return actualExerciseType; + } } \ No newline at end of file diff --git a/lib/repository/menu_tree_repository.dart b/lib/repository/menu_tree_repository.dart index 13f835e..4d39a5a 100644 --- a/lib/repository/menu_tree_repository.dart +++ b/lib/repository/menu_tree_repository.dart @@ -1,6 +1,6 @@ import 'dart:collection'; import 'package:aitrainer_app/localization/app_language.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_tree.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/model/workout_tree.dart'; @@ -16,7 +16,7 @@ class MenuTreeRepository { final AppLanguage appLanguage = AppLanguage(); bool isEnglish = appLanguage.appLocal == Locale('en'); - List exerciseTree = Auth().getExerciseTree(); + List exerciseTree = Cache().getExerciseTree(); if ( exerciseTree == null || exerciseTree.length == 0) { await ExerciseTreeApi().getExerciseTree(); } @@ -24,6 +24,10 @@ class MenuTreeRepository { exerciseTree.forEach( (treeItem) async { String treeName = isEnglish ? treeItem.name : treeItem.nameTranslation; String assetImage = 'asset/menu/' + treeItem.imageUrl.substring(7); + bool is1RM = treeItem.name == '1RM' ? true : false; + if ( is1RM == false) { + is1RM = false; //isParent1RM(treeItem.treeId); + } this.tree[treeItem.name] = WorkoutTree( treeItem.treeId, treeItem.parentId, @@ -33,11 +37,12 @@ class MenuTreeRepository { false, 0, null, - false + false, + is1RM ); }); - List exerciseTypes = Auth().getExerciseTypes(); + List exerciseTypes = Cache().getExerciseTypes(); if ( exerciseTypes == null || exerciseTypes.length == 0) { await ExerciseTypeApi().getExerciseTypes(); } @@ -46,6 +51,7 @@ class MenuTreeRepository { String exerciseTypeName = isEnglish ? exerciseType.name : exerciseType.nameTranslation; String assetImage = 'asset/menu/' + exerciseType.imageUrl.substring(7); + bool is1RM = false; //this.isParent1RM(exerciseType.treeId); this.tree[exerciseType.name] = WorkoutTree( exerciseType.exerciseTypeId, exerciseType.treeId, @@ -56,11 +62,24 @@ class MenuTreeRepository { true, exerciseType.exerciseTypeId, exerciseType, - exerciseType.base + exerciseType.base, + is1RM ); }); } + bool isParent1RM(int treeId) { + bool isTreeItem1RM = false; + + for (int i = 0; i < this.tree.length; i++ ) { + WorkoutTree treeItem = this.tree[i]; + if ( treeItem.id == treeId ) { + isTreeItem1RM = treeItem.is1RM; + break; + } + }; + return isTreeItem1RM; + } LinkedHashMap getBranch(int parent) { LinkedHashMap branch = LinkedHashMap(); diff --git a/lib/service/api.dart b/lib/service/api.dart index d9c72a8..e172e96 100644 --- a/lib/service/api.dart +++ b/lib/service/api.dart @@ -1,18 +1,17 @@ import 'dart:convert'; import 'package:aitrainer_app/util/common.dart'; -import 'package:flutter/cupertino.dart'; import 'package:http/http.dart' as http; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; -class APIClient extends ChangeNotifier { +class APIClient with Common { Future get(String endPoint, String param) async { - final url = Auth.getBaseUrl() + endPoint + param; - String authToken = Auth().getAuthToken(); + final url = Cache.getBaseUrl() + endPoint + param; + String authToken = Cache().getAuthToken(); if ( authToken.length == 0 ) { var responseJson = await APIClient.authenticateUser( - Auth.username, - Auth.password + Cache.username, + Cache.password ); authToken = responseJson['token']; } @@ -21,7 +20,6 @@ class APIClient extends ChangeNotifier { 'Content-Type': 'application/json', 'Authorization' : "Bearer " + authToken } ); - notifyListeners(); if(response.statusCode == 200) { return utf8.decode(response.bodyBytes); } else { @@ -30,13 +28,13 @@ class APIClient extends ChangeNotifier { } Future post(String endPoint, String body) async { - final url = Auth.getBaseUrl() + endPoint; + final url = Cache.getBaseUrl() + endPoint; print(" ------------ http/post endpoint $endPoint body $body - url: $url "); - String authToken = Auth().getAuthToken(); + String authToken = Cache().getAuthToken(); if ( authToken.length == 0 ) { var responseJson = await APIClient.authenticateUser( - Auth.username, - Auth.password + Cache.username, + Cache.password ); authToken = responseJson['token']; } @@ -48,14 +46,13 @@ class APIClient extends ChangeNotifier { }, body: body, ); - String decodedResponse = Common.utf8convert(response.body); + String decodedResponse = utf8convert(response.body); print(" ------------ response: " + decodedResponse); - notifyListeners(); return decodedResponse; } static dynamic authenticateUser(String email, String password) async { - var uri = Auth.getBaseUrl() + "authenticate"; + var uri = Cache.getBaseUrl() + "authenticate"; try { final body = '{"username":"$email", "password":"$password"}'; @@ -82,7 +79,7 @@ class APIClient extends ChangeNotifier { } static fetch(var authToken, var endPoint) async { - var uri = Auth.getBaseUrl() + endPoint; + var uri = Cache.getBaseUrl() + endPoint; try { final response = await http.get( diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index b8da324..39f04b6 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/user.dart'; import 'package:aitrainer_app/service/api.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; class CustomerApi { final APIClient _client=new APIClient(); @@ -44,7 +44,7 @@ class CustomerApi { throw new Exception(jsonDecode(responseBody)['error']); } else { customer = Customer.fromJson(jsonDecode(responseBody)); - Auth().afterRegistration(customer); + Cache().afterRegistration(customer); } } on FormatException catch(exception) { throw new Exception(responseBody); @@ -61,7 +61,7 @@ class CustomerApi { Customer customer; try { customer = Customer.fromJson(jsonDecode(responseBody)); - await Auth().afterLogin(customer); + await Cache().afterLogin(customer); } on FormatException catch(exception) { throw new Exception(responseBody); } @@ -76,12 +76,12 @@ class CustomerApi { "customers/"+customerId.toString(), body); Customer customer = Customer.fromJson(jsonDecode(responseBody)); - Auth().afterRegistration(customer); + Cache().afterRegistration(customer); } catch (exception) { print ("Exception: " + exception.toString()); print (" === go to registration "); - Auth().logout(); - Auth().startPage = "registration"; + Cache().logout(); + Cache().startPage = "registration"; } } } \ No newline at end of file diff --git a/lib/service/exercise_tree_service.dart b/lib/service/exercise_tree_service.dart index 943c1b1..55df9ad 100644 --- a/lib/service/exercise_tree_service.dart +++ b/lib/service/exercise_tree_service.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_tree.dart'; import 'api.dart'; @@ -12,7 +12,7 @@ class ExerciseTreeApi { final Iterable json = jsonDecode(body); final List exerciseTree = json.map((exerciseTree) => ExerciseTree.fromJson(exerciseTree)).toList(); - Auth().setExerciseTree(exerciseTree); + Cache().setExerciseTree(exerciseTree); return exerciseTree; } diff --git a/lib/service/exercisetype_service.dart b/lib/service/exercisetype_service.dart index 997839d..38273b6 100644 --- a/lib/service/exercisetype_service.dart +++ b/lib/service/exercisetype_service.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/service/api.dart'; @@ -11,7 +11,7 @@ class ExerciseTypeApi { final body = await _client.get("exercise_type/active", ""); final Iterable json = jsonDecode(body); final List exerciseTypes = json.map( (exerciseType) => ExerciseType.fromJson(exerciseType) ).toList(); - Auth().setExerciseTypes(exerciseTypes); + Cache().setExerciseTypes(exerciseTypes); return exerciseTypes; } diff --git a/lib/util/common.dart b/lib/util/common.dart index 12c664c..b8adb23 100644 --- a/lib/util/common.dart +++ b/lib/util/common.dart @@ -1,20 +1,20 @@ import 'dart:convert'; import 'package:aitrainer_app/localization/app_language.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; -class Common { +mixin Common { - static const EMAIL_ERROR = "Please type a right email address here."; - static const PASSWORD_ERROR = "The password must have at least 8 characters."; + final EMAIL_ERROR = "Please type a right email address here."; + final PASSWORD_ERROR = "The password must have at least 8 characters."; - static String toJson( Map map ) { + String toJson( Map map ) { String rc = "{"; map.forEach((key, value) { rc += "'$key':'$value'"; @@ -23,9 +23,9 @@ class Common { return rc; } - static ExerciseType getExerciseType( int exerciseTypeId ) { + ExerciseType getExerciseType( int exerciseTypeId ) { ExerciseType returnElement = null; - List listExerciseType = Auth().getExerciseTypes(); + List listExerciseType = Cache().getExerciseTypes(); if ( listExerciseType != null ) { for ( var element in listExerciseType ) { if (exerciseTypeId == element.exerciseTypeId) { @@ -37,7 +37,7 @@ class Common { return returnElement; } - static String getDateLocale( DateTime datetime, bool timeDisplay ) { + String getDateLocale( DateTime datetime, bool timeDisplay ) { AppLanguage appLanguage = AppLanguage(); var date = datetime; @@ -49,16 +49,16 @@ class Common { return dateName; } - static String utf8convert(String text) { + String utf8convert(String text) { List bytes = text.toString().codeUnits; return utf8.decode(bytes); } - static double mediaSizeWidth( BuildContext context ) { + double mediaSizeWidth( BuildContext context ) { return MediaQuery.of(context).size.width; } - static bool validateEmail(UserRepository userRepository) { + bool validateEmail(UserRepository userRepository) { final String email = userRepository.user.email; final RegExp _emailRegExp = RegExp( r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$', @@ -66,7 +66,7 @@ class Common { return _emailRegExp.hasMatch(email); } - static bool validatePassword(UserRepository userRepository) { + bool validatePassword(UserRepository userRepository) { final password = userRepository.user.password; final RegExp _passwordRegExp = RegExp(r'^(?=.*[A-Za-z0-9])(?=.*\d)[A-Za-z\d]{7,}$'); diff --git a/lib/util/session.dart b/lib/util/session.dart index bcb95c8..bcbe973 100644 --- a/lib/util/session.dart +++ b/lib/util/session.dart @@ -1,5 +1,6 @@ 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/service/api.dart'; import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/service/exercise_tree_service.dart'; @@ -7,7 +8,7 @@ import 'package:aitrainer_app/service/exercisetype_service.dart'; import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; //import '../push_notifications.dart'; @@ -23,7 +24,7 @@ class Session { _sharedPreferences = await _prefs; - if ( Auth().firstLoad ) { + if ( Cache().firstLoad ) { print (" -- Session: fetch locale.."); await appLanguage.fetchLocale(); @@ -44,7 +45,7 @@ class Session { // Platform messages may fail, so we use a try/catch PlatformException. try { languages = await Devicelocale.preferredLanguages; - Auth().deviceLanguages = languages; + Cache().deviceLanguages = languages; print("device langs " + languages.toString()); } on PlatformException { @@ -64,44 +65,49 @@ class Session { _fetchToken(SharedPreferences prefs) async { var responseJson = await APIClient.authenticateUser( - Auth.username, - Auth.password + Cache.username, + Cache.password ); + int customerId = 0; print("--- Lang: " + appLanguage.appLocal.toString()); if(responseJson['error'] != null) { print("************** Here big error - no authentication"); } else if (responseJson['token'] != null) { - prefs.setString(Auth.authTokenKey, responseJson['token']); - Auth auth = Auth(); - auth.authToken = responseJson['token']; - if (prefs.get(Auth.customerIdKey) == null) { + prefs.setString(Cache.authTokenKey, responseJson['token']); + Cache().authToken = responseJson['token']; + if (prefs.get(Cache.customerIdKey) == null) { print("************** Registration"); // registration //Navigator.of(context).pushNamed('registration'); - prefs.setBool(Auth.isRegisteredKey, true); - Auth().startPage = "registration"; + prefs.setBool(Cache.isRegisteredKey, true); + Cache().startPage = "registration"; } else { DateTime now = DateTime.now(); DateTime lastStoreDate = DateTime.parse( - prefs.get(Auth.lastStoreDateKey)); + prefs.get(Cache.lastStoreDateKey)); DateTime minStoreDate = now.add(Duration(days: -10)); if (lastStoreDate == null || lastStoreDate.difference(minStoreDate) > Duration(days: 10) || - prefs.get(Auth.isLoggedInKey) == null || - prefs.get(Auth.isLoggedInKey) == false) { + prefs.get(Cache.isLoggedInKey) == null || + prefs.get(Cache.isLoggedInKey) == false) { print("************* Login"); //Navigator.of(context).pushNamed('login'); - Auth().startPage = "login"; + Cache().startPage = "login"; } else { print("************** Store SharedPreferences"); // get API customer - await CustomerApi().getCustomer(prefs.getInt(Auth.customerIdKey)); - Auth().startPage = "home"; + customerId = prefs.getInt(Cache.customerIdKey); + await CustomerApi().getCustomer(customerId); + Cache().startPage = "home"; } await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); + if ( customerId > 0) { + ExerciseRepository exerciseRepository = ExerciseRepository(); + exerciseRepository.getExercisesByCustomer(customerId); + } print("--- Session finished"); } diff --git a/lib/view/account.dart b/lib/view/account.dart index e695569..8084fd5 100644 --- a/lib/view/account.dart +++ b/lib/view/account.dart @@ -7,7 +7,6 @@ import 'package:flutter/cupertino.dart'; // ignore: must_be_immutable class AccountPage extends StatelessWidget { - // ignore: close_sinks AccountBloc accountBloc; @override diff --git a/lib/view/custom_exercise_page.dart b/lib/view/custom_exercise_page.dart index 9d1ca51..f2e0102 100644 --- a/lib/view/custom_exercise_page.dart +++ b/lib/view/custom_exercise_page.dart @@ -1,3 +1,5 @@ +import 'dart:collection'; + import 'package:aitrainer_app/bloc/custom_exercise_form_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; @@ -24,7 +26,6 @@ class _CustomExerciseNewPageState extends State { create: (context) => CustomExerciseFormBloc(exerciseRepository: ExerciseRepository()), child: Builder(builder: (context) { - // ignore: close_sinks final exerciseBloc = BlocProvider.of(context); exerciseBloc.exerciseRepository.setExerciseType(exerciseType); @@ -189,6 +190,7 @@ class _CustomExerciseNewPageState extends State { } SliverGrid gridCalculation(CustomExerciseFormBloc bloc) { + LinkedHashMap args = LinkedHashMap(); return SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, @@ -201,7 +203,7 @@ class _CustomExerciseNewPageState extends State { TextFieldBlocBuilder( readOnly: true, textFieldBloc: bloc.rmWendlerField, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), style: TextStyle(color: Colors.deepOrange, fontSize: 12), decoration: InputDecoration( border: InputBorder.none, @@ -211,7 +213,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rmWathenField, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -223,7 +225,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rmOconnerField, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -235,7 +237,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rmMayhewField, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -247,7 +249,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rmAverageField, style: TextStyle(color: Colors.blueAccent, fontSize: 12), @@ -259,7 +261,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm90Field, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -272,7 +274,7 @@ class _CustomExerciseNewPageState extends State { TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm75Field, style: TextStyle(color: Colors.deepOrange, fontSize: 12, fontWeight: FontWeight.bold), @@ -284,7 +286,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm75OconnorField, style: TextStyle(color: Colors.deepOrange, fontSize: 12, fontWeight: FontWeight.bold), @@ -296,7 +298,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm75WendlerField, style: TextStyle(color: Colors.deepOrange, fontSize: 12, fontWeight: FontWeight.bold), @@ -308,7 +310,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm80Field, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -320,7 +322,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm70Field, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -332,7 +334,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm60Field, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -344,7 +346,7 @@ class _CustomExerciseNewPageState extends State { )), TextFieldBlocBuilder( readOnly: true, - padding: EdgeInsets.only(left:30), + padding: EdgeInsets.only(left:10), maxLines: 1, textFieldBloc: bloc.rm50Field, style: TextStyle(color: Colors.deepOrange, fontSize: 12), @@ -353,8 +355,45 @@ class _CustomExerciseNewPageState extends State { fillColor: Colors.white, filled: false, labelText: "1RM 50%: ", - )) - ]) - ); + ) + ), + RaisedButton( + padding: EdgeInsets.all(0), + textColor: Colors.white, + color: Colors.blue, + focusColor: Colors.blueAccent, + onPressed: () => + { + args['exerciseRepository'] = bloc.exerciseRepository, + args['percent'] = 0.75, + Navigator.of(context).pushNamed('exerciseControlPage', + arguments: args) + }, + child: Text("Control with 75%", + style: TextStyle(fontSize: 12),) + ), + RaisedButton( + padding: EdgeInsets.all(0), + textColor: Colors.white, + color: Colors.green, + focusColor: Colors.blueAccent, + onPressed: () => + { + args['exerciseRepository'] = bloc.exerciseRepository, + args['percent'] = 0.5, + Navigator.of(context).pushNamed('exerciseControlPage', + arguments: args ) + }, + child: Text("Control with 50%", + style: TextStyle(fontSize: 12),) + ), + ], + ) + ); + } + + @override + void dispose() { + super.dispose(); } } diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart index 282b695..dfb6c63 100644 --- a/lib/view/customer_modify_page.dart +++ b/lib/view/customer_modify_page.dart @@ -299,4 +299,4 @@ class CustomerModifyPage extends StatelessWidget{ ); } -} \ No newline at end of file +} diff --git a/lib/view/exercise_control_page.dart b/lib/view/exercise_control_page.dart new file mode 100644 index 0000000..963994e --- /dev/null +++ b/lib/view/exercise_control_page.dart @@ -0,0 +1,321 @@ +import 'dart:collection'; + +import 'package:aitrainer_app/bloc/exercise_control_form_bloc.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/widgets/splash.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +class ExerciseControlPage extends StatefulWidget{ + _ExerciseControlPage createState() => _ExerciseControlPage(); +} + +class _ExerciseControlPage extends State { + + @override + Widget build(BuildContext context) { + LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; + final ExerciseRepository exerciseRepository = arguments['exerciseRepository']; + final double percent = arguments['percent']; + + return BlocProvider( + create: (context) => + ExerciseControlFormBloc(exerciseRepository: exerciseRepository, percentToCalculate: percent), + child: BlocBuilder( + builder: (context, state) { + // ignore: close_sinks + final exerciseBloc = BlocProvider.of(context); + if ( state is FormBlocLoading ) { + return LoadingDialog(); + } else if ( state is FormBlocSuccess) { + return getControlForm(exerciseBloc); + } else { + return getControlForm(exerciseBloc); + } + } + )); + } + + Form getControlForm( ExerciseControlFormBloc exerciseBloc) { + String exerciseName = AppLanguage().appLocal == Locale("en") ? + exerciseBloc.exerciseRepository.exerciseType.name : + exerciseBloc.exerciseRepository.exerciseType.nameTranslation; + + return Form( + autovalidate: true, + child: Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + title: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text("1RM Control"), + Image.asset( + 'asset/image/WT_long_logo.png', + fit: BoxFit.cover, + height: 65.0, + ), + ], + ), + leading: IconButton( + icon: Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: Container( + width: MediaQuery + .of(context) + .size + .width, + height: MediaQuery + .of(context) + .size + .height, + 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: [ + Text(exerciseName, + style: TextStyle(fontWeight: FontWeight.bold, + fontSize: 18, + color: Colors.deepOrange), + 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(AppLocalizations.of(context).translate("Your 1RM:"),), + Text(" " + exerciseBloc.initialRMField.value + " " +exerciseBloc.exerciseRepository.exerciseType + .unitQuantityUnit, + style: TextStyle(fontWeight: FontWeight.bold),), + ], + ), + Divider(), + Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + 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: [ + WhitelistingTextInputFormatter (RegExp(r"[\d.]")) + ], + onChanged: (input) => { + print("Quantity 1 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.unitQuantity1Field.value + " " + exerciseBloc.exerciseRepository.exerciseType.unitQuantityUnit + " 12 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: [ + WhitelistingTextInputFormatter (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: [ + WhitelistingTextInputFormatter (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),) + ), + ], + ), + + ]), + ) + ) + ), + ), + ); + } + + + String validateNumberInput(input) { + 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; + } +} diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart index 4c81e9e..2673f43 100644 --- a/lib/view/exercise_new_page.dart +++ b/lib/view/exercise_new_page.dart @@ -1,7 +1,7 @@ import 'package:aitrainer_app/bloc/exercise_form_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:flutter/services.dart'; @@ -283,7 +283,7 @@ class _ExerciseNewPageState extends State { FlatButton( child: Text(AppLocalizations.of(context).translate("Yes")), onPressed: () => { - bloc.exerciseRepository.setCustomer(Auth().userLoggedIn), + bloc.exerciseRepository.setCustomer(Cache().userLoggedIn), bloc.exerciseRepository.addExercise(), Navigator.pop(context), diff --git a/lib/view/login.dart b/lib/view/login.dart index 92202b5..d552e74 100644 --- a/lib/view/login.dart +++ b/lib/view/login.dart @@ -26,7 +26,7 @@ class LoginWidget extends StatefulWidget { State createState() => _LoginWidget(); } -class _LoginWidget extends State { +class _LoginWidget extends State with Common { final GlobalKey _scaffoldKey = new GlobalKey(); final _formKey = GlobalKey(); @@ -72,7 +72,7 @@ class _LoginWidget extends State { } Widget buildLoginForm(LoginFormBloc formBloc, AccountBloc accountBloc) { - final cWidth = Common.mediaSizeWidth(context); + final cWidth = mediaSizeWidth(context); return Form( key: _formKey, diff --git a/lib/view/menu_page.dart b/lib/view/menu_page.dart index f376d82..3d066e4 100644 --- a/lib/view/menu_page.dart +++ b/lib/view/menu_page.dart @@ -1,5 +1,7 @@ + import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; -import 'package:aitrainer_app/localization/app_localization.dart'; + +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/menu_page_widget.dart'; import 'package:flutter/cupertino.dart'; @@ -9,39 +11,30 @@ import 'package:flutter_bloc/flutter_bloc.dart'; // ignore: must_be_immutable -class MenuPage extends StatelessWidget { +class MenuPage extends StatefulWidget { static const routeName = '/menu_page'; int parent; - MenuBloc menuBloc; MenuPage({this.parent}); + _MenuPage createState() => _MenuPage(); +} + +class _MenuPage extends State { + // ignore: close_sinks + MenuBloc menuBloc; + + + + void checkTest() { + + } + @override Widget build(BuildContext context) { menuBloc = BlocProvider.of(context); return Scaffold( - appBar: AppBar( - backgroundColor: Colors.transparent, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(AppLocalizations.of(context).translate("Tests")), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => - { - menuBloc.add(MenuTreeUp(parent: 0)) - }, - ), - ), - + appBar: AppBarNav(), body: Container( decoration: BoxDecoration( image: DecorationImage( diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 74a8cf0..345ba93 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -34,13 +34,13 @@ class RegistrationWidget extends StatefulWidget { State createState() => _RegistrationWidget(); } -class _RegistrationWidget extends State { +class _RegistrationWidget extends State with Common { final GlobalKey _scaffoldKey = new GlobalKey(); final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - final cWidth = Common.mediaSizeWidth(context); + final cWidth = mediaSizeWidth(context); // ignore: close_sinks final accountBloc = BlocProvider.of(context); return BlocProvider( diff --git a/lib/view/settings.dart b/lib/view/settings.dart index 35ceee5..d2db3fd 100644 --- a/lib/view/settings.dart +++ b/lib/view/settings.dart @@ -2,7 +2,7 @@ import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/app_bar.dart b/lib/widgets/app_bar.dart new file mode 100644 index 0000000..38eae78 --- /dev/null +++ b/lib/widgets/app_bar.dart @@ -0,0 +1,153 @@ +import 'dart:async'; + +import 'package:aitrainer_app/animations/test_progress_animation.dart'; +import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; +import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:percent_indicator/linear_percent_indicator.dart'; +import 'package:rainbow_color/rainbow_color.dart'; + + +class AppBarNav extends StatefulWidget implements PreferredSizeWidget { + final MenuBloc menuBloc; + const AppBarNav({this.menuBloc}); + + @override + _AppBarNav createState() => _AppBarNav(); + + @override + Size get preferredSize => const Size.fromHeight(60); +} + +class _AppBarNav extends State with SingleTickerProviderStateMixin { + Animation colorAnim; + //Animation sizeAnim; + AnimationController colorController; + //AnimationController sizeController; + MenuBloc menuBloc; + + + @override + void initState() { + colorController = + AnimationController(duration: Duration(seconds: 4), vsync: this); + //sizeController = + // AnimationController(duration: Duration(seconds: 3), vsync: this); + + /* final curvedAnimation = CurvedAnimation( + parent: sizeController, + curve: Curves.easeIn, + reverseCurve: Curves.easeInOutBack, + ); + + sizeAnim = + Tween(begin: 0, end: 1.5).animate(curvedAnimation) + ..addStatusListener((status) { + if (status == AnimationStatus.completed) { + sizeController.reverse(); + } else if (status == AnimationStatus.dismissed) { + Timer(Duration(seconds: 5), () { + sizeController.forward(); + }); + } + }); */ + colorAnim = RainbowColorTween([Colors.white70, + Colors.greenAccent, + Colors.lightGreen, + Colors.lightGreenAccent, + Colors.yellow, + Colors.yellowAccent, + Colors.orange, + Colors.orangeAccent, + Colors.white70]) + .animate(colorController) + ..addListener(() { setState(() {}); }) + ..addStatusListener((status) { + if (status == AnimationStatus.completed) { + Timer(Duration(seconds: 10), () { + colorController.reset(); + colorController.forward(); + }); + } else if (status == AnimationStatus.dismissed) { + colorController.forward(); + } + }); + colorController.forward(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + menuBloc = BlocProvider.of(context); + return AppBar( + backgroundColor: Colors.black, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + + getAnimatedWidget(), + Image.asset( + 'asset/image/WT_long_logo.png', + fit: BoxFit.cover, + height: 65.0, + ), + ], + ), + leading: IconButton( + icon: Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => + { + menuBloc.add(MenuTreeUp(parent: 0)) + }, + ) + ); + } + + @override + void dispose() { + //sizeController.dispose(); + colorController.dispose(); + super.dispose(); + } + + Widget getAnimatedWidget() { + ExerciseRepository exerciseRepository = ExerciseRepository(); + exerciseRepository.getExerciseList(); + if ( exerciseRepository.exerciseList == null || exerciseRepository.exerciseList.length == 0 ) { + return Stack( + alignment: Alignment.topLeft, + children: [ + Text(AppLocalizations.of(context).translate("Make your first test"), + style: TextStyle(fontSize: 16, color: colorAnim.value, shadows: [Shadow(color: Colors.purple , blurRadius: 15)]), + + ), + //TestProgress(animation: sizeAnim), + ] + ); + } else { + double percent = 0.61;//exerciseRepository.getBaseExerciseFinishedPercent(); + return Stack( + alignment: Alignment.topLeft, + children: [ + LinearPercentIndicator( + width: 120.0, + lineHeight: 14.0, + percent: percent, + center: Text( + (percent * 100).toStringAsFixed(0) + "% finished", + style: new TextStyle(fontSize: 12.0), + ), + trailing: Icon(percent > 0.6 ? Icons.mood : Icons.mood_bad, color: colorAnim.value,), + linearStrokeCap: LinearStrokeCap.roundAll, + backgroundColor: colorAnim.value, + progressColor: Colors.blue, + animation: true, + ), + ], + ); + } + } +} diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 6112843..b613d38 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -1,7 +1,7 @@ import 'package:aitrainer_app/bloc/session/session_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/menu_page.dart'; import 'package:aitrainer_app/view/registration.dart'; @@ -69,9 +69,9 @@ class _HomePageState extends State { return LoadingScreenMain(); } else if (state is SessionReady) { print("ready"); - if (Auth().startPage == 'login') { + if (Cache().startPage == 'login') { return LoginPage(); - } else if (Auth().startPage == 'registration') { + } else if (Cache().startPage == 'registration') { return RegistrationPage(); } else { return MenuPage(parent: 0); diff --git a/lib/widgets/loading.dart b/lib/widgets/loading.dart index 974b39b..2dce838 100644 --- a/lib/widgets/loading.dart +++ b/lib/widgets/loading.dart @@ -6,8 +6,8 @@ class LoadingScreenMain extends StatelessWidget { Widget build(BuildContext context) { Image _backgroundImage = Image.asset('asset/image/WT01_loading_layers.png', fit: BoxFit.cover, - height: double.infinity, - width: double.infinity, + //height: double.infinity, + //width: double.infinity, alignment: Alignment.center, ); return Scaffold( diff --git a/lib/widgets/menu_page_widget.dart b/lib/widgets/menu_page_widget.dart index 6eded68..477f747 100644 --- a/lib/widgets/menu_page_widget.dart +++ b/lib/widgets/menu_page_widget.dart @@ -2,7 +2,7 @@ import 'dart:ui'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; -import 'package:aitrainer_app/model/auth.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/workout_tree.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -75,7 +75,7 @@ class MenuPageWidget extends StatelessWidget { menuBloc.add(MenuTreeDown(parent: workoutTree.id)); } else { menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id)); - if (Auth().userLoggedIn == null) { + if (Cache().userLoggedIn == null) { Scaffold.of(context).showSnackBar(SnackBar( backgroundColor: Colors.orange, content: Text( @@ -85,6 +85,9 @@ class MenuPageWidget extends StatelessWidget { if (workoutTree.exerciseType.name == "Custom") { Navigator.of(context).pushNamed('exerciseCustomPage', arguments: workoutTree.exerciseType); + } else if (workoutTree.exerciseType.name == "Control") { + Navigator.of(context).pushNamed('exerciseControlPage', + arguments: workoutTree.exerciseType); } else { Navigator.of(context).pushNamed('exerciseNewPage', arguments: workoutTree.exerciseType); @@ -122,7 +125,7 @@ class MenuPageWidget extends StatelessWidget { height: 180, errorBuilder: (context, error, stackTrace) { String url = - Auth.mediaUrl + 'images/' + workoutTree.imageName.substring(11); + Cache.mediaUrl + 'images/' + workoutTree.imageName.substring(11); Widget image = FadeInImage.assetNetwork( placeholder: 'asset/image/dots.gif', image: url, @@ -132,7 +135,7 @@ class MenuPageWidget extends StatelessWidget { }, ); } on Exception catch (_) { - String url = Auth.mediaUrl + '/images/' + workoutTree.imageName; + String url = Cache.mediaUrl + '/images/' + workoutTree.imageName; image = FadeInImage.assetNetwork( placeholder: 'asset/image/dots.gif', image: url, diff --git a/pubspec.lock b/pubspec.lock index 8c02be1..e0c822f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,7 +49,7 @@ packages: name: bloc_test url: "https://pub.dartlang.org" source: hosted - version: "7.0.1" + version: "7.0.2" boolean_selector: dependency: transitive description: @@ -57,6 +57,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.11" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.2" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "7.1.0" characters: dependency: transitive description: @@ -71,6 +127,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.3" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" cli_util: dependency: transitive description: @@ -85,6 +148,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "3.4.1" collection: dependency: transitive description: @@ -127,6 +197,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.6" devicelocale: dependency: "direct main" description: @@ -140,7 +217,7 @@ packages: name: equatable url: "https://pub.dartlang.org" source: hosted - version: "1.2.3" + version: "1.2.4" fake_async: dependency: transitive description: @@ -155,6 +232,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.2.1" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.11" flutter: dependency: "direct main" description: flutter @@ -236,6 +320,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.19.1" + freezed: + dependency: "direct main" + description: + name: freezed + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.6" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.0+1" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -248,6 +346,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" html: dependency: transitive description: @@ -304,6 +409,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" json_rpc_2: dependency: transitive description: @@ -401,7 +513,7 @@ packages: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" pedantic: dependency: transitive description: @@ -409,6 +521,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.0" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" petitparser: dependency: transitive description: @@ -450,7 +569,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "4.3.2" + version: "4.3.2+1" pub_semver: dependency: transitive description: @@ -458,6 +577,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.4.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" quiver: dependency: transitive description: @@ -465,6 +591,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + rainbow_color: + dependency: "direct main" + description: + name: rainbow_color + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + rainbow_vis: + dependency: transitive + description: + name: rainbow_vis + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" rxdart: dependency: transitive description: @@ -485,14 +625,14 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.8" + version: "0.5.10" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.2+1" + version: "0.0.2+2" shared_preferences_macos: dependency: transitive description: @@ -547,6 +687,13 @@ packages: description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.6" source_map_stack_trace: dependency: transitive description: @@ -589,6 +736,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" string_scanner: dependency: transitive description: @@ -631,6 +785,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.10" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4559559..a6db8cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,9 +33,12 @@ dependencies: flutter_local_notifications: 1.1.1 flutter_facebook_login: ^3.0.0 flutter_bloc: ^6.0.1 - equatable: ^1.2.3 + equatable: ^1.2.4 + freezed: ^0.11.6 flutter_form_bloc: ^0.19.0 spider_chart: ^0.1.5 + rainbow_color: ^0.1.1 + percent_indicator: ^2.1.5 mockito: ^4.1.1 @@ -46,12 +49,15 @@ dev_dependencies: flutter_driver: sdk: flutter test: any - bloc_test: ^7.0.1 + bloc_test: ^7.0.2 + + build_runner: + http: 0.12.1 - provider: ^4.3.2 + provider: ^4.3.2+1 intl: 0.16.1 - shared_preferences: ^0.5.8 + shared_preferences: ^0.5.10 flutter_launcher_icons: ^0.7.5 diff --git a/test/account_bloc_test.dart b/test/account_bloc_test.dart deleted file mode 100644 index 562111d..0000000 --- a/test/account_bloc_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:aitrainer_app/bloc/account/account_bloc.dart'; -import 'package:aitrainer_app/repository/customer_repository.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart' as test; -import 'package:flutter_test/flutter_test.dart'; - -class MockCustomerRepository extends Mock implements CustomerRepository { - -} - -void main() { - MockCustomerRepository customerRepository; - AccountBloc accountBloc; - - TestWidgetsFlutterBinding.ensureInitialized(); - - test.setUp(() { - customerRepository = MockCustomerRepository(); - accountBloc = AccountBloc(customerRepository: customerRepository); - }); - - test.tearDown(() { - accountBloc?.close(); - }); - - test.test('initial state is correct', () { - expect(accountBloc.state, AccountInitial()); - }); - - group('Account', () { - test.test( - 'emits [loading, logged in] when the customer clicked login', - () { - final expectedResponse = [ - AccountLoading(), - AccountLoggedIn(), - ]; - - //verify(accountBloc.customerRepository.customer == null); - - expectLater( - accountBloc, emitsInOrder(expectedResponse), - ); - - accountBloc.add(AccountLogin()); - }); - }); - - test.test( - 'emits [loading, logged out] when the customer clicked logout', - () { - final expectedResponse = [ - AccountLoading(), - AccountLoggedOut(), - ]; - - expectLater( - accountBloc, emitsInOrder(expectedResponse), - ); - - accountBloc.add(AccountLogout()); - }); - - test.test( - 'emits [loading, logged out] when the customer data changed', - () { - final expectedResponse = [ - AccountLoading(), - AccountReady(), - ]; - - expectLater( - accountBloc, emitsInOrder(expectedResponse), - ); - - accountBloc.add(AccountChangeCustomer()); - }); - -} \ No newline at end of file diff --git a/test/widget_test.login.dart b/test/widget_test.login.dart index 724a47f..59449be 100644 --- a/test/widget_test.login.dart +++ b/test/widget_test.login.dart @@ -21,7 +21,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -class MockUserRepository extends Mock implements UserRepository { +class MockUserRepository extends Mock implements UserRepository { final User user = User(); setEmail(String email) { @@ -43,15 +43,23 @@ class MockUserRepository extends Mock implements UserRepository { class MockLoginBloc extends MockBloc implements LoginFormBloc {} +class MockCommon with Common { + String getEmailError() { + return EMAIL_ERROR; + } +} + void main() { group('LoginScreen', () { MockLoginBloc loginBloc; MockUserRepository userRepository; Widget loginWidget; + MockCommon common; setUp(() { loginBloc = MockLoginBloc(); userRepository = MockUserRepository(); + common = MockCommon(); loginWidget = MaterialApp( @@ -100,7 +108,7 @@ void main() { await tester.pumpAndSettle(); await tester.tap(okButton); await tester.pump(const Duration(milliseconds: 500)); // add delay - final emailErrorFinder = find.text(Common.EMAIL_ERROR); + final emailErrorFinder = find.text(common.getEmailError()); expect(emailErrorFinder, findsOneWidget); }); });