wt1.1c auth->cache, control page, animation base exercise percent

This commit is contained in:
Bossanyi Tibor 2020-09-01 16:47:45 +02:00
parent 18d6994068
commit 6b0892ec09
38 changed files with 1148 additions and 261 deletions

View File

@ -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:"
}

View File

@ -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:"
}

View File

@ -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<double> animation,
}) : super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
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,)
),
);
}
}

View File

@ -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<AccountEvent, AccountState> {
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<AccountEvent, AccountState> {
customerRepository.customer = event.customer;
yield AccountLoggedIn();
} else if (event is AccountLogout) {
await Auth().logout();
await Cache().logout();
customerRepository.customer = null;
loggedIn = false;
yield AccountLoggedOut();

View File

@ -92,9 +92,7 @@ class CustomExerciseFormBloc extends FormBloc<String, String> {
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));

View File

@ -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<String, String> {
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<void> close() {
initialRMField.close();
quantity1Field.close();
unitQuantity1Field.close();
quantity2Field.close();
unitQuantity2Field.close();
quantity3Field.close();
unitQuantity3Field.close();
return super.close();
}
}

View File

@ -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<String, String> {
class LoginFormBloc extends FormBloc<String, String> with Common {
final AccountBloc accountBloc;
final UserRepository userRepository;
@ -40,18 +40,18 @@ class LoginFormBloc extends FormBloc<String, String> {
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());

View File

@ -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<String, String> {
class RegistrationFormBloc extends FormBloc<String, String> with Common {
final AccountBloc accountBloc;
final emailField = TextFieldBloc(
validators: [
@ -38,18 +38,18 @@ class RegistrationFormBloc extends FormBloc<String, String> {
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());

View File

@ -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(),

View File

@ -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<ExerciseType> _exerciseTypes;
List<ExerciseTree> _exerciseTree;
List<Exercise> _exercises;
LinkedHashMap _tree = LinkedHashMap<String, WorkoutTree>();
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<Exercise> exercises ) {
this._exercises = exercises;
}
void setWorkoutTree( LinkedHashMap<String, WorkoutTree> tree) {
this._tree = tree;
}
List<ExerciseType> getExerciseTypes() {
return this._exerciseTypes;
}
@ -137,4 +157,13 @@ class Auth {
List<ExerciseTree> getExerciseTree() {
return this._exerciseTree;
}
List<Exercise> getExercises() {
return this._exercises;
}
LinkedHashMap<String, WorkoutTree> getWorkoutTree() {
return this._tree;
}
}

View File

@ -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;
}
}

View File

@ -1,4 +1,4 @@
class User {
class User {
String email;
String password;
int customerId;
@ -12,4 +12,4 @@ class User {
"username": email,
"password": password,
};
}
}

View File

@ -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);
}

View File

@ -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<Exercise> exerciseList;
double rmWendler = 0;
double rmMcglothlin = 0;
@ -79,10 +81,51 @@ class ExerciseRepository {
this.exerciseType = exerciseType;
}
/*
Future<List<ExerciseRepository>> getExercisesByCustomer( int customerId ) async {
Future<List<Exercise>> 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<Exercise> getExerciseList() {
if ( this.exerciseList == null || this.exerciseList.length == 0 ) {
this.exerciseList = Cache().getExercises();
}
return this.exerciseList;
}
double getBaseExerciseFinishedPercent() {
return 0;
}
void getLastExercise() {
List<Exercise> 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;
}
}

View File

@ -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> exerciseTree = Auth().getExerciseTree();
List<ExerciseTree> 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<ExerciseType> exerciseTypes = Auth().getExerciseTypes();
List<ExerciseType> 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<String, WorkoutTree>();

View File

@ -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<String> 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<String> 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(

View File

@ -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";
}
}
}

View File

@ -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> exerciseTree = json.map((exerciseTree) =>
ExerciseTree.fromJson(exerciseTree)).toList();
Auth().setExerciseTree(exerciseTree);
Cache().setExerciseTree(exerciseTree);
return exerciseTree;
}

View File

@ -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<ExerciseType> exerciseTypes = json.map( (exerciseType) => ExerciseType.fromJson(exerciseType) ).toList();
Auth().setExerciseTypes(exerciseTypes);
Cache().setExerciseTypes(exerciseTypes);
return exerciseTypes;
}

View File

@ -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<String, String> map ) {
String toJson( Map<String, String> 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<ExerciseType> listExerciseType = Auth().getExerciseTypes();
List<ExerciseType> 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<int> 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,}$');

View File

@ -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");
}

View File

@ -7,7 +7,6 @@ import 'package:flutter/cupertino.dart';
// ignore: must_be_immutable
class AccountPage extends StatelessWidget {
// ignore: close_sinks
AccountBloc accountBloc;
@override

View File

@ -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<CustomExercisePage> {
create: (context) =>
CustomExerciseFormBloc(exerciseRepository: ExerciseRepository()),
child: Builder(builder: (context) {
// ignore: close_sinks
final exerciseBloc = BlocProvider.of<CustomExerciseFormBloc>(context);
exerciseBloc.exerciseRepository.setExerciseType(exerciseType);
@ -189,6 +190,7 @@ class _CustomExerciseNewPageState extends State<CustomExercisePage> {
}
SliverGrid gridCalculation(CustomExerciseFormBloc bloc) {
LinkedHashMap args = LinkedHashMap();
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
@ -201,7 +203,7 @@ class _CustomExerciseNewPageState extends State<CustomExercisePage> {
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
)),
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<CustomExercisePage> {
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();
}
}

View File

@ -299,4 +299,4 @@ class CustomerModifyPage extends StatelessWidget{
);
}
}
}

View File

@ -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<ExerciseControlPage> {
@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<ExerciseControlFormBloc, FormBlocState>(
builder: (context, state) {
// ignore: close_sinks
final exerciseBloc = BlocProvider.of<ExerciseControlFormBloc>(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: <Widget>[
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: <Widget>[
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;
}
}

View File

@ -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<ExerciseNewPage> {
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),

View File

@ -26,7 +26,7 @@ class LoginWidget extends StatefulWidget {
State<StatefulWidget> createState() => _LoginWidget();
}
class _LoginWidget extends State<LoginWidget> {
class _LoginWidget extends State<LoginWidget> with Common {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final _formKey = GlobalKey<FormState>();
@ -72,7 +72,7 @@ class _LoginWidget extends State<LoginWidget> {
}
Widget buildLoginForm(LoginFormBloc formBloc, AccountBloc accountBloc) {
final cWidth = Common.mediaSizeWidth(context);
final cWidth = mediaSizeWidth(context);
return Form(
key: _formKey,

View File

@ -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<MenuPage> {
// ignore: close_sinks
MenuBloc menuBloc;
void checkTest() {
}
@override
Widget build(BuildContext context) {
menuBloc = BlocProvider.of<MenuBloc>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
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(

View File

@ -34,13 +34,13 @@ class RegistrationWidget extends StatefulWidget {
State<StatefulWidget> createState() => _RegistrationWidget();
}
class _RegistrationWidget extends State<RegistrationWidget> {
class _RegistrationWidget extends State<RegistrationWidget> with Common {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
final cWidth = Common.mediaSizeWidth(context);
final cWidth = mediaSizeWidth(context);
// ignore: close_sinks
final accountBloc = BlocProvider.of<AccountBloc>(context);
return BlocProvider(

View File

@ -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';

153
lib/widgets/app_bar.dart Normal file
View File

@ -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<AppBarNav> with SingleTickerProviderStateMixin {
Animation<Color> colorAnim;
//Animation<double> 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<double>(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<MenuBloc>(context);
return AppBar(
backgroundColor: Colors.black,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
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,
),
],
);
}
}
}

View File

@ -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<AitrainerHome> {
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);

View File

@ -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(

View File

@ -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,

View File

@ -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:

View File

@ -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

View File

@ -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());
});
}

View File

@ -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<FormBlocEvent>
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);
});
});