wt1.1a flutter_bloc

This commit is contained in:
Bossanyi Tibor 2020-08-17 12:38:47 +02:00
parent 8363d7772f
commit ac1c6be8f3
90 changed files with 4282 additions and 2855 deletions

View File

@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {
def flutterRoot = localProperties.getProperty('flutter.sdk') def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) { if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
} }
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
@ -26,6 +26,12 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android { android {
compileSdkVersion 28 compileSdkVersion 28
@ -46,11 +52,20 @@ android {
versionName flutterVersionName versionName flutterVersionName
} }
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug signingConfig signingConfigs.release
} }
} }
} }
@ -62,4 +77,5 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-analytics:17.2.2' implementation 'com.google.firebase:firebase-analytics:17.2.2'
implementation 'com.facebook.android:facebook-login:[5,6)'
} }

View File

@ -48,5 +48,23 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<meta-data android:name="com.facebook.sdk.ApplicationId"
android:value="@string/app_name"/>
<activity android:name="com.facebook.FacebookActivity"
android:configChanges=
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="WorkoutTest" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
</application> </application>
</manifest> </manifest>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WorkoutTest</string>
<!-- Replace "000000000000" with your Facebook App ID here. -->
<string name="facebook_app_id">584181112271127</string>
<!--
Replace "000000000000" with your Facebook App ID here.
**NOTE**: The scheme needs to start with `fb` and then your ID.
-->
<string name="fb_login_protocol_scheme">fb584181112271127</string>
</resources>

View File

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

4
android/key.properties Normal file
View File

@ -0,0 +1,4 @@
storePassword=tbi6012Andi
keyPassword=tbi6012Andi
keyAlias=key
storeFile=c:/Users/bossa/.ssh/key.jks

View File

@ -0,0 +1 @@
include ':app'

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
asset/icon/icon_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
asset/image/login_fb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

File diff suppressed because one or more lines are too long

View File

@ -23,8 +23,9 @@
"gdpr_text": "", "gdpr_text": "",
"Please log in": "Kérlek jelentkezz be", "Please log in": "Kérlek jelentkezz be",
"Customer does not exist or the password is wrong": "A felhasználónév nem létezik vagy a jelszó rossz.", "Exception: Customer does not exist or the password is wrong": "A felhasználónév nem létezik vagy a jelszó rossz.",
"Please select an exercise": "Válassz ki egy gyakorlatot", "Please select an exercise": "Válassz ki egy gyakorlatot",
"There is an error: during registration:": "Hiba lépett fel a regisztráció során:",
"Customer exists": "Ez az email cím már regisztrálva van", "Customer exists": "Ez az email cím már regisztrálva van",
"Cardio": "Kardió", "Cardio": "Kardió",
"400m": "400m", "400m": "400m",
@ -50,7 +51,10 @@
"BMR": "Alapanyagcsere", "BMR": "Alapanyagcsere",
"Sizes": "Méretek", "Sizes": "Méretek",
"Save Exercise": "gyakorlat mentése", "Save Exercise": "gyakorlat mentése",
"Save": "Save", "Save": "Mentés",
"The number of the exercise": "Írd be a gyakorlat számát",
"The number of the exercise done with": "Írd be, mennyivel csináltad a gyakorlatot",
"Name": "Név", "Name": "Név",
"Exercise": "Gyakorlat", "Exercise": "Gyakorlat",
@ -58,17 +62,23 @@
"Unit": "Egység", "Unit": "Egység",
"Exercise date and time": "A gyakorlat időpontja", "Exercise date and time": "A gyakorlat időpontja",
"Please type the right quantity 0-1000": "Kérlek írj be egy helyes számot 0-10000 között", "Please type the right quantity 0-1000": "Kérlek írj be egy helyes számot 0-10000 között",
"Yes": "Igen",
"No": "Nem",
"with": "",
"Do you save this exercise with these parameters?":"Elmented a gyakorlatot ezekkel az adatokkal?",
"repeat": "ismétlés", "repeat": "ismétlés",
"meter": "meter", "meter": "meter",
"percent": "százalék", "percent": "százalék",
"kg": "kg", "kg": "kg",
"kilogram": "kilogramm",
"lbs": "lbs", "lbs": "lbs",
"second": "másodperc", "second": "másodperc",
"Email": "Email", "Email": "Email",
"Password": "Jelszó", "Password": "Jelszó",
"Password (Leave empty if you don't want to change)":"Jelszó (hagyd üresen, ha nem akarod megváltoztatni)", "OR": "VAGY",
"Password (Leave empty if no change)":"Jelszó (üres, ha nincs változás)",
"First Name": "Keresztnév", "First Name": "Keresztnév",
"Birth Year": "Születési év", "Birth Year": "Születési év",
"Weight": "Tömeg", "Weight": "Tömeg",

View File

@ -0,0 +1,46 @@
import 'dart:async';
import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'account_event.dart';
part 'account_state.dart';
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;
loggedIn = true;
}
}
@override
Stream<AccountState> mapEventToState(
AccountEvent event,
) async* {
try {
if (event is AccountChangeCustomer) {
// route to Customer Change page
yield AccountReady();
} else if (event is AccountLogin) {
//route to Login Page
} else if (event is AccountLogInFinished) {
customerRepository.customer = event.customer;
yield AccountLoggedIn();
} else if (event is AccountLogout) {
await Auth().logout();
customerRepository.customer = null;
loggedIn = false;
yield AccountLoggedOut();
}
} on Exception catch(e) {
yield AccountError(message: e.toString());
}
}
}

View File

@ -0,0 +1,46 @@
part of 'account_bloc.dart';
@immutable
abstract class AccountEvent extends Equatable {
const AccountEvent();
@override
List<Object> get props => [];
}
class AccountChangeCustomer extends AccountEvent {
final Customer customer;
const AccountChangeCustomer({this.customer});
@override
List<Object> get props => [customer];
}
class AccountLogout extends AccountEvent {
final Customer customer;
const AccountLogout({this.customer});
@override
List<Object> get props => [customer];
}
class AccountLogin extends AccountEvent {
final Customer customer;
const AccountLogin({this.customer});
@override
List<Object> get props => [customer];
}
class AccountLogInFinished extends AccountEvent {
final Customer customer;
const AccountLogInFinished({this.customer});
@override
List<Object> get props => [customer];
}

View File

@ -0,0 +1,36 @@
part of 'account_bloc.dart';
@immutable
abstract class AccountState extends Equatable {
const AccountState();
@override
List<Object> get props => [];
}
class AccountInitial extends AccountState {
const AccountInitial();
}
class AccountLoading extends AccountState {
const AccountLoading();
}
class AccountReady extends AccountState {
const AccountReady();
}
class AccountLoggedOut extends AccountState {
const AccountLoggedOut();
}
class AccountLoggedIn extends AccountState {
const AccountLoggedIn();
}
class AccountError extends AccountState {
final String message;
const AccountError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,127 @@
import 'dart:math';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class CustomExerciseFormBloc extends FormBloc<String, String> {
final ExerciseRepository exerciseRepository;
final quantityField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final unitQuantityField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
//1RM calculated Fields
final rmWendlerField = TextFieldBloc( initialValue: "0",);
final rmMcGothlinField = TextFieldBloc( initialValue: "0");
final rmLombardiField = TextFieldBloc( initialValue: "0");
final rmMayhewField = TextFieldBloc( initialValue: "0");
final rmOconnerField = TextFieldBloc( initialValue: "0");
final rmWathenField = TextFieldBloc( initialValue: "0");
final rmAverageField = TextFieldBloc( initialValue: "0");
final rm90Field = TextFieldBloc( initialValue: "0");
final rm80Field = TextFieldBloc( initialValue: "0");
final rm70Field = TextFieldBloc( initialValue: "0");
final rm60Field = TextFieldBloc( initialValue: "0");
final rm50Field = TextFieldBloc( initialValue: "0");
CustomExerciseFormBloc({this.exerciseRepository}) {
addFieldBlocs(fieldBlocs: [
quantityField,
unitQuantityField,
rmWendlerField,
rmMcGothlinField,
rmLombardiField,
rmMayhewField,
rmOconnerField,
rmWathenField,
rmAverageField,
rm90Field,
rm80Field,
rm70Field,
rm60Field,
rm50Field
]);
quantityField.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setQuantity(current.valueToDouble);
calculate1RM();
});
unitQuantityField.onValueChanges(onData: (previous, current) async* {
exerciseRepository.setUnitQuantity(current.valueToDouble);
calculate1RM();
});
}
@override
void onSubmitting() async {
print("on Submitting Custom form");
try {
emitLoading(progress: 30);
// Emit either Loaded or Error
emitSuccess(canSubmitAgain: false);
} on Exception catch (ex) {
emitFailure(failureResponse: ex.toString());
}
}
void calculate1RM() {
double weight = exerciseRepository.exercise.unitQuantity;
double repeat = exerciseRepository.exercise.quantity;
if ( weight == 0 || repeat == 0) {
return;
}
print("Calculating 1RM");
exerciseRepository.rmWendler = weight * repeat * 0.0333 + weight;
rmWendlerField.updateValue(exerciseRepository.rmWendler.toString());
exerciseRepository.rmMcglothlin = 100 * weight / (101.3 - 2.67123 * repeat);
rmMcGothlinField.updateValue(exerciseRepository.rmMcglothlin.toString());
exerciseRepository.rmLombardi = pow(weight * repeat, 0.1);
rmLombardiField.updateValue(exerciseRepository.rmLombardi.toString());
exerciseRepository.rmOconner = weight * (1 + repeat / 40);
rmOconnerField.updateValue(exerciseRepository.rmOconner.toString());
exerciseRepository.rmMayhew =
100 * weight / (52.2 + 41.9 * pow(e, -0.055 * repeat));
rmMayhewField.updateValue(exerciseRepository.rmMayhew.toString());
exerciseRepository.rmWathen =
100 * weight / (48.8 + 53.8 * pow(e, -0.075 * repeat));
rmWathenField.updateValue(exerciseRepository.rmWathen.toString());
double average = (exerciseRepository.rmWendler + exerciseRepository.rmWathen +
exerciseRepository.rmMayhew + exerciseRepository.rmOconner +
exerciseRepository.rmMcglothlin)/5;
rmAverageField.updateValue(average.toString());
rm90Field.updateValue((average*0.9).toString());
rm80Field.updateValue((average*0.8).toString());
rm70Field.updateValue((average*0.7).toString());
rm60Field.updateValue((average*0.6).toString());
rm50Field.updateValue((average*0.5).toString());
}
//@override
Future<void> close() {
quantityField.close();
unitQuantityField.close();
rmWendlerField.close();
rmMcGothlinField.close();
rmLombardiField.close();
rmMayhewField.close();
rmOconnerField.close();
rmWathenField.close();
rmAverageField.close();
rm90Field.close();
rm80Field.close();
rm70Field.close();
rm60Field.close();
rm50Field.close();
return super.close();
}
}

View File

@ -0,0 +1,38 @@
import 'dart:async';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'customer_change_event.dart';
part 'customer_change_state.dart';
class CustomerChangeBloc extends Bloc<CustomerChangeEvent, CustomerChangeState> {
final CustomerRepository customerRepository;
CustomerChangeBloc({this.customerRepository}) : super(CustomerChangeInitial());
@override
Stream<CustomerChangeState> mapEventToState(
CustomerChangeEvent event,
) async* {
try {
if (event is CustomerGoalChange) {
customerRepository.setGoal(event.goal);
yield CustomerDataChanged();
} else if (event is CustomerFitnessChange) {
customerRepository.setFitnessLevel(event.fitness);
yield CustomerDataChanged();
} else if (event is CustomerBodyTypeChange) {
customerRepository.setBodyType(event.bodyType);
yield CustomerDataChanged();
} else if (event is CustomerSave) {
yield CustomerSaving();
await customerRepository.saveCustomer();
yield CustomerSaveSuccess();
}
} on Exception catch(e) {
yield CustomerSaveError(message: e.toString());
}
}
}

View File

@ -0,0 +1,36 @@
part of 'customer_change_bloc.dart';
@immutable
abstract class CustomerChangeEvent extends Equatable {
const CustomerChangeEvent();
@override
List<Object> get props => [];
}
class CustomerGoalChange extends CustomerChangeEvent {
final String goal;
const CustomerGoalChange({this.goal});
@override
List<Object> get props => [goal];
}
class CustomerFitnessChange extends CustomerChangeEvent {
final String fitness;
const CustomerFitnessChange({this.fitness});
@override
List<Object> get props => [fitness];
}
class CustomerBodyTypeChange extends CustomerChangeEvent {
final String bodyType;
const CustomerBodyTypeChange({this.bodyType});
@override
List<Object> get props => [bodyType];
}
class CustomerSave extends CustomerChangeEvent {
const CustomerSave();
}

View File

@ -0,0 +1,32 @@
part of 'customer_change_bloc.dart';
@immutable
abstract class CustomerChangeState extends Equatable {
const CustomerChangeState();
@override
List<Object> get props => [];
}
class CustomerChangeInitial extends CustomerChangeState {
const CustomerChangeInitial();
}
class CustomerSaving extends CustomerChangeState {
const CustomerSaving();
}
class CustomerDataChanged extends CustomerChangeState {
const CustomerDataChanged();
}
class CustomerSaveSuccess extends CustomerChangeState {
const CustomerSaveSuccess();
}
class CustomerSaveError extends CustomerChangeState {
final String message;
const CustomerSaveError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,101 @@
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class CustomerChangeFormBloc extends FormBloc<String, String> {
final CustomerRepository customerRepository;
final emailField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final firstNameField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final nameField = TextFieldBloc();
final passwordField = TextFieldBloc(
validators: [
//FieldBlocValidators.confirmPassword(passwordField),
],
);
final birthYearField = TextFieldBloc();
final weightField = TextFieldBloc();
final genderField = SelectFieldBloc();
final goalField = TextFieldBloc();
CustomerChangeFormBloc({this.customerRepository}) {
addFieldBlocs(fieldBlocs: [
emailField,
firstNameField,
nameField,
passwordField,
birthYearField,
weightField,
genderField,
goalField,
]);
emailField.updateInitialValue(customerRepository.customer.email);
firstNameField.updateInitialValue(customerRepository.customer.firstname);
nameField.updateInitialValue(customerRepository.customer.name);
birthYearField.updateInitialValue(customerRepository.customer.birthYear.toString());
weightField.updateInitialValue(customerRepository.customer.weight.toString());
genderField.updateInitialValue(customerRepository.getGenderByDBValue(customerRepository.sex));
firstNameField.onValueChanges(onData: (previous, current) async* {
customerRepository.setFirstName(current.value);
});
nameField.onValueChanges(onData: (previous, current) async* {
customerRepository.setName(current.value);
});
birthYearField.onValueChanges(onData: (previous, current) async* {
customerRepository.setBirthYear(current.valueToInt);
});
weightField.onValueChanges(onData: (previous, current) async* {
customerRepository.setWeight(current.valueToInt);
});
customerRepository.genders.forEach((element) {
genderField.addItem(element.name);
});
genderField.onValueChanges(onData: (previous, current) async* {
String dbValue = customerRepository.getGenderByName(current.value);
customerRepository.setSex(dbValue);
});
}
@override
void onSubmitting() async {
print("on Submitting Custom form");
try {
emitLoading(progress: 30);
// Emit either Loaded or Error
await customerRepository.saveCustomer();
emitSuccess(canSubmitAgain: false);
} on Exception catch (ex) {
emitFailure(failureResponse: ex.toString());
}
}
@override
Future<void> close() {
emailField.close();
firstNameField.close();
nameField.close();
passwordField.close();
birthYearField.close();
weightField.close();
genderField.close();
return super.close();
}
}

View File

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

View File

@ -0,0 +1,61 @@
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/model/auth.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> {
final AccountBloc accountBloc;
final UserRepository userRepository;
final emailField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final passwordField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
]
);
LoginFormBloc({this.userRepository, this.accountBloc}) {
addFieldBlocs(fieldBlocs: [
emailField,
passwordField
]);
emailField.onValueChanges(onData: (previous, current) async* {
userRepository.setEmail(current.value);
});
passwordField.onValueChanges(onData: (previous, current) async* {
userRepository.setPassword(current.value);
});
}
@override
void onSubmitting() async {
try {
emitLoading(progress: 30);
if ( ! Common.validateEmail(userRepository)) {
emailField.addFieldError(Common.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);
} else {
// Emit either Loaded or Error
await userRepository.getUser();
emitSuccess(canSubmitAgain: false);
accountBloc.add(AccountLogInFinished(customer: Auth().userLoggedIn));
}
} on Exception catch (ex) {
emitFailure(failureResponse: ex.toString());
}
}
}

View File

@ -0,0 +1,51 @@
import 'dart:async';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/repository/menu_tree_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'menu_event.dart';
part 'menu_state.dart';
class MenuBloc extends Bloc<MenuEvent, MenuState> {
final MenuTreeRepository menuTreeRepository;
int parent;
MenuBloc({this.menuTreeRepository}) : super(MenuInitial()) {
parent = 0;
}
@override
Stream<MenuState> mapEventToState(
MenuEvent event,
) async* {
try {
if ( event is MenuCreate ) {
yield MenuLoading();
await menuTreeRepository.createTree();
yield MenuReady();
} else if (event is MenuTreeDown) {
// get child menus or exercises
yield MenuLoading();
parent = event.parent;
menuTreeRepository.getBranch(event.parent);
yield MenuReady();
} else if (event is MenuTreeUp) {
yield MenuLoading();
// get parent menus or exercises
parent = event.parent;
menuTreeRepository.getBranch(parent);
yield MenuReady();
} else if (event is MenuClickExercise) {
yield MenuLoading();
// get exercise page
yield MenuReady();
}
} on Exception catch(ex) {
yield MenuError(message: ex.toString());
}
}
}

View File

@ -0,0 +1,41 @@
part of 'menu_bloc.dart';
@immutable
abstract class MenuEvent extends Equatable {
const MenuEvent();
@override
List<Object> get props => [];
}
class MenuCreate extends MenuEvent {
const MenuCreate();
@override
List<Object> get props => [];
}
class MenuTreeDown extends MenuEvent {
final int parent;
const MenuTreeDown({this.parent});
@override
List<Object> get props => [parent];
}
class MenuTreeUp extends MenuEvent {
final int parent;
const MenuTreeUp({this.parent});
@override
List<Object> get props => [parent];
}
class MenuClickExercise extends MenuEvent {
final int exerciseTypeId;
const MenuClickExercise({this.exerciseTypeId});
@override
List<Object> get props => [exerciseTypeId];
}

View File

@ -0,0 +1,34 @@
part of 'menu_bloc.dart';
@immutable
abstract class MenuState extends Equatable {
const MenuState();
@override
List<Object> get props => [];
}
class MenuInitial extends MenuState {
const MenuInitial();
}
class MenuLoading extends MenuState {
}
class MenuReady extends MenuState {
final WorkoutTree workoutTree;
const MenuReady({this.workoutTree});
@override
List<Object> get props => [workoutTree];
}
class MenuError extends MenuState {
final String message;
const MenuError({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,61 @@
import 'package:aitrainer_app/model/auth.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> {
final AccountBloc accountBloc;
final emailField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);
final passwordField = TextFieldBloc(
validators: [
FieldBlocValidators.required,
]
);
final UserRepository userRepository;
RegistrationFormBloc({this.userRepository, this.accountBloc}) {
addFieldBlocs(fieldBlocs: [
emailField,
passwordField
]);
emailField.onValueChanges(onData: (previous, current) async* {
userRepository.setEmail(current.value);
});
passwordField.onValueChanges(onData: (previous, current) async* {
userRepository.setPassword(current.value);
});
}
@override
void onSubmitting() async {
try {
emitLoading(progress: 30);
if ( ! Common.validateEmail(userRepository)) {
emailField.addFieldError(Common.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);
} else {
// Emit either Loaded or Error
await userRepository.addUser();
emitSuccess(canSubmitAgain: false);
accountBloc.add(AccountLogInFinished(customer: Auth().userLoggedIn));
}
} on Exception catch (ex) {
emitFailure(failureResponse: ex.toString());
}
}
}

View File

@ -0,0 +1,35 @@
import 'dart:async';
import 'package:aitrainer_app/util/session.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
part 'session_event.dart';
part 'session_state.dart';
class SessionBloc extends Bloc<SessionEvent, SessionState> {
final Session session;
SessionBloc({this.session}) : super(SessionInitial());
@override
Stream<SessionState> mapEventToState(
SessionEvent event,
) async* {
try {
if (event is SessionStart) {
yield SessionLoading();
await session.fetchSessionAndNavigate();
yield SessionReady();
}
} on Exception catch(ex) {
yield SessionFailure(message: ex.toString());
}
}
@override
Future<void> close() async {
await this.close(); super.close();
}
}

View File

@ -0,0 +1,16 @@
part of 'session_bloc.dart';
@immutable
abstract class SessionEvent extends Equatable {
const SessionEvent();
@override
List<Object> get props => [];
}
class SessionStart extends SessionEvent {
const SessionStart();
@override
List<Object> get props => [];
}

View File

@ -0,0 +1,32 @@
part of 'session_bloc.dart';
@immutable
abstract class SessionState extends Equatable {
const SessionState();
@override
List<Object> get props => [];
}
class SessionInitial extends SessionState {
const SessionInitial();
}
class SessionLoading extends SessionState {
const SessionLoading();
}
class SessionReady extends SessionState {
const SessionReady();
}
class SessionFailure extends SessionState {
final String message;
const SessionFailure({this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,55 @@
import 'dart:async';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:meta/meta.dart';
part 'settings_event.dart';
part 'settings_state.dart';
class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
String language;
Locale _locale;
BuildContext context;
SettingsBloc({this.context}) : super(SettingsInitial());
@override
Stream<SettingsState> mapEventToState(
SettingsEvent event,
) async* {
if (event is SettingsChangeLanguage) {
yield SettingsLoading();
await _changeLang( event.language);
yield SettingsReady(_locale);
}
}
Future<void> _changeLang( String lang ) async{
switch ( lang ) {
case "English":
case "Angol":
_locale = Locale('en');
break;
case "Hungarian":
case "Magyar":
_locale = Locale('hu');
break;
}
this.language = lang;
await loadLang();
}
Future<void> loadLang() async{
final AppLanguage appLanguage = AppLanguage();
appLanguage.changeLanguage(_locale);
if ( context != null ) {
AppLocalizations.of(context).setLocale(_locale);
await AppLocalizations.of(context).load();
}
}
}

View File

@ -0,0 +1,13 @@
part of 'settings_bloc.dart';
@immutable
abstract class SettingsEvent extends Equatable {
const SettingsEvent();
@override
List<Object> get props => [];
}
class SettingsChangeLanguage extends SettingsEvent {
final String language;
const SettingsChangeLanguage({this.language});
}

View File

@ -0,0 +1,46 @@
part of 'settings_bloc.dart';
@immutable
abstract class SettingsState extends Equatable {
const SettingsState();
@override
List<Object> get props => [];
}
// ignore: must_be_immutable
class SettingsInitial extends SettingsState {
Locale locale;
SettingsInitial();
setLocale(locale) {
this.locale = locale;
}
@override
List<Object> get props => [locale];
}
class SettingsLoading extends SettingsState {
final Locale locale;
const SettingsLoading({this.locale});
@override
List<Object> get props => [locale];
}
class SettingsReady extends SettingsState {
final Locale locale;
const SettingsReady(this.locale);
@override
List<Object> get props => [locale];
}
class SettingsError extends SettingsState {
final String message;
const SettingsError(this.message);
@override
List<Object> get props => [message];
}

9
lib/library_keys.dart Normal file
View File

@ -0,0 +1,9 @@
import 'package:flutter/widgets.dart';
class LibraryKeys {
// Login
static final loginEmailField = const Key('__loginEmailField__');
static final loginPasswordField = const Key('__loginPasswordField__');
static final loginOKButton = const Key('__loginOKButton__');
}

View File

@ -6,8 +6,9 @@ import 'package:flutter/services.dart';
class AppLocalizations { class AppLocalizations {
Locale locale; Locale locale;
bool isTest;
AppLocalizations(this.locale); AppLocalizations(this.locale, {this.isTest = false});
// Helper method to keep the code in the widgets concise // Helper method to keep the code in the widgets concise
// Localizations are accessed using an InheritedWidget "of" syntax // Localizations are accessed using an InheritedWidget "of" syntax
@ -17,7 +18,7 @@ class AppLocalizations {
// Static member to have a simple access to the delegate from the MaterialApp // Static member to have a simple access to the delegate from the MaterialApp
static const LocalizationsDelegate<AppLocalizations> delegate = static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate(); AppLocalizationsDelegate();
Map<String, String> _localizedStrings; Map<String, String> _localizedStrings;
@ -38,18 +39,24 @@ class AppLocalizations {
return true; return true;
} }
Future<AppLocalizations> loadTest(Locale locale) async {
return AppLocalizations(locale);
}
// This method will be called from every widget which needs a localized text // This method will be called from every widget which needs a localized text
String translate(String key) { String translate(String key) {
if (isTest) return key;
return _localizedStrings[key]; return _localizedStrings[key];
} }
} }
class _AppLocalizationsDelegate class AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> { extends LocalizationsDelegate<AppLocalizations> {
final bool isTest;
// This delegate instance will never change (it doesn't even have fields!) // This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor. // It can provide a constant constructor.
const _AppLocalizationsDelegate(); const AppLocalizationsDelegate({this.isTest = false});
@override @override
bool isSupported(Locale locale) { bool isSupported(Locale locale) {
@ -60,11 +67,16 @@ class _AppLocalizationsDelegate
@override @override
Future<AppLocalizations> load(Locale locale) async { Future<AppLocalizations> load(Locale locale) async {
// AppLocalizations class is where the JSON loading actually runs // AppLocalizations class is where the JSON loading actually runs
AppLocalizations localizations = new AppLocalizations(locale); AppLocalizations localizations = new AppLocalizations(locale, isTest: this.isTest);
await localizations.load(); if (isTest) {
await localizations.loadTest(locale);
} else {
await localizations.load();
}
return localizations; return localizations;
} }
@override @override
bool shouldReload(_AppLocalizationsDelegate old) => false; bool shouldReload(AppLocalizationsDelegate old) => false;
} }

View File

@ -1,33 +1,33 @@
import 'dart:async'; 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';
import 'package:aitrainer_app/view/account.dart'; import 'package:aitrainer_app/view/account.dart';
import 'package:aitrainer_app/view/custom_exercise_page.dart';
import 'package:aitrainer_app/view/customer_bodytype_page.dart'; import 'package:aitrainer_app/view/customer_bodytype_page.dart';
import 'package:aitrainer_app/view/customer_fitness_page.dart'; import 'package:aitrainer_app/view/customer_fitness_page.dart';
import 'package:aitrainer_app/view/customer_goal_page.dart'; import 'package:aitrainer_app/view/customer_goal_page.dart';
import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart';
import 'package:aitrainer_app/view/customer_new_page.dart';
import 'package:aitrainer_app/view/customer_welcome_page.dart'; import 'package:aitrainer_app/view/customer_welcome_page.dart';
import 'package:aitrainer_app/view/gdpr.dart'; import 'package:aitrainer_app/view/gdpr.dart';
import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/login.dart';
import 'package:aitrainer_app/view/exercise_new_page.dart'; import 'package:aitrainer_app/view/exercise_new_page.dart';
import 'package:aitrainer_app/view/exercise_type_modify_page.dart';
import 'package:aitrainer_app/view/exercise_type_new_page.dart';
import 'package:aitrainer_app/view/menu_page.dart'; import 'package:aitrainer_app/view/menu_page.dart';
import 'package:aitrainer_app/view/registration.dart'; import 'package:aitrainer_app/view/registration.dart';
import 'package:aitrainer_app/view/settings.dart'; import 'package:aitrainer_app/view/settings.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
import 'package:aitrainer_app/widgets/home.dart'; import 'package:aitrainer_app/widgets/home.dart';
import 'package:aitrainer_app/widgets/loading.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:aitrainer_app/view/customer_list_page.dart';
import 'package:aitrainer_app/view/exercise_type_list_page.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:sentry/sentry.dart'; import 'package:sentry/sentry.dart';
import 'bloc/account/account_bloc.dart';
import 'bloc/menu/menu_bloc.dart';
import 'bloc/session/session_bloc.dart';
import 'bloc/settings/settings_bloc.dart';
final SentryClient _sentry = new SentryClient(dsn: 'https://5fac40cbfcfb4c15aa80c7a8638d7310@o418565.ingest.sentry.io/5322520'); final SentryClient _sentry = new SentryClient(dsn: 'https://5fac40cbfcfb4c15aa80c7a8638d7310@o418565.ingest.sentry.io/5322520');
@ -95,17 +95,25 @@ Future<Null> main() async {
// - https://www.dartlang.org/articles/libraries/zones // - https://www.dartlang.org/articles/libraries/zones
runZonedGuarded<Future<Null>>(() async { runZonedGuarded<Future<Null>>(() async {
runApp( runApp(
MultiProvider( MultiBlocProvider(
// Initialize the model in the builder. That way, Provider
// can own Models's lifecycle, making sure to call `dispose`
// when not needed anymore.
providers: [ providers: [
ChangeNotifierProvider(create: (context) => ExerciseChangingViewModel(null)), BlocProvider<SessionBloc>(
ChangeNotifierProvider(create: (context) => CustomerChangingViewModel(null)), create: (BuildContext context) => SessionBloc(session: Session()),
),
BlocProvider<MenuBloc>(
create: (BuildContext context) => MenuBloc( menuTreeRepository: MenuTreeRepository()),
),
BlocProvider<SettingsBloc>(
create: (BuildContext context) => SettingsBloc(),
),
BlocProvider<AccountBloc>(
create: (BuildContext context) => AccountBloc(customerRepository: CustomerRepository()),
),
], ],
child: AitrainerApp(), child: AitrainerApp(),
)); )
);
}, (error, stackTrace) async { }, (error, stackTrace) async {
await _reportError(error, stackTrace); await _reportError(error, stackTrace);
}); });
@ -147,18 +155,13 @@ class AitrainerApp extends StatelessWidget {
}, },
routes: { routes: {
'home': (context) => AitrainerHome(), 'home': (context) => AitrainerHome(),
'loading': (context) => LoadingScreenMain(),
'customersPage': (context) => CustomerListPage(),
'customerNewPage': (context) => CustomerNewPage(),
'customerModifyPage': (context) => CustomerModifyPage(), 'customerModifyPage': (context) => CustomerModifyPage(),
'customerGoalPage': (context) => CustomerGoalPage(), 'customerGoalPage': (context) => CustomerGoalPage(),
'customerFitnessPage': (context) => CustomerFitnessPage(), 'customerFitnessPage': (context) => CustomerFitnessPage(),
'customerBodyTypePage': (context) => CustomerBodyTypePage(), 'customerBodyTypePage': (context) => CustomerBodyTypePage(),
'customerWelcomePage': (context) => CustomerWelcomePage(), 'customerWelcomePage': (context) => CustomerWelcomePage(),
'exerciseTypeListPage': (context) => ExerciseTypeListPage(),
'exerciseTypeNewPage': (context) => ExerciseTypeNewPage(),
'exerciseTypeModifyPage': (context) => ExerciseTypeModifyPage(),
'exerciseNewPage': (context) => ExerciseNewPage(), 'exerciseNewPage': (context) => ExerciseNewPage(),
'exerciseCustomPage': (context) => CustomExercisePage(),
'login': (context) => LoginPage(), 'login': (context) => LoginPage(),
'registration': (context) => RegistrationPage(), 'registration': (context) => RegistrationPage(),
'gdpr': (context) => Gdpr(), 'gdpr': (context) => Gdpr(),
@ -166,7 +169,7 @@ class AitrainerApp extends StatelessWidget {
'account': (context) => AccountPage(), 'account': (context) => AccountPage(),
'settings': (context) => SettingsPage(), 'settings': (context) => SettingsPage(),
}, },
initialRoute: 'loading', initialRoute: 'home',
title: 'Aitrainer', title: 'Aitrainer',
theme: ThemeData( theme: ThemeData(
brightness: Brightness.light, brightness: Brightness.light,
@ -176,7 +179,7 @@ class AitrainerApp extends StatelessWidget {
bodyText1: TextStyle(fontSize: 14.0), bodyText1: TextStyle(fontSize: 14.0),
) )
), ),
home: LoadingScreenMain(), home: AitrainerHome(),
); );
} }

View File

@ -1,4 +1,6 @@
import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'package:aitrainer_app/service/exercise_tree_service.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart'; import 'package:aitrainer_app/service/exercisetype_service.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/model/exercise_type.dart';
@ -35,7 +37,8 @@ class Auth {
static final String isRegisteredKey = 'is_registered'; static final String isRegisteredKey = 'is_registered';
static final String isLoggedInKey = 'is_logged_in'; static final String isLoggedInKey = 'is_logged_in';
static final String _baseUrl = 'http://andio.eu:8888/api/'; static final String baseUrl = 'http://aitrainer.info:8888/api/';
static final String mediaUrl = 'https://aitrainer.info:4343/media/';
static final String username = 'bosi'; static final String username = 'bosi';
static final String password = 'andio2009'; static final String password = 'andio2009';
@ -43,7 +46,9 @@ class Auth {
Customer userLoggedIn; Customer userLoggedIn;
bool firstLoad = true; bool firstLoad = true;
List<ExerciseType> _exerciseTypes; List<ExerciseType> _exerciseTypes;
List<ExerciseTree> _exerciseTree;
List deviceLanguages; List deviceLanguages;
String startPage;
factory Auth() { factory Auth() {
return _singleton; return _singleton;
@ -60,7 +65,11 @@ class Auth {
} }
static String getBaseUrl() { static String getBaseUrl() {
return _baseUrl; return baseUrl;
}
static String getMediaUrl() {
return mediaUrl;
} }
afterRegistration(Customer customer) { afterRegistration(Customer customer) {
@ -70,20 +79,19 @@ class Auth {
setPreferences(prefs, SharePrefsChange.registration, customer.customerId); setPreferences(prefs, SharePrefsChange.registration, customer.customerId);
} }
afterLogin(Customer customer) { afterLogin(Customer customer) async {
Future<SharedPreferences> prefs = SharedPreferences.getInstance(); Future<SharedPreferences> prefs = SharedPreferences.getInstance();
userLoggedIn = customer; userLoggedIn = customer;
setPreferences(prefs, SharePrefsChange.login, customer.customerId); await setPreferences(prefs, SharePrefsChange.login, customer.customerId);
} }
logout(){ logout() async {
userLoggedIn = null; userLoggedIn = null;
authToken = ""; authToken = "";
//firstLoad = true; //firstLoad = true;
Future<SharedPreferences> prefs = SharedPreferences.getInstance(); Future<SharedPreferences> prefs = SharedPreferences.getInstance();
setPreferences(prefs, SharePrefsChange.logout, 0); await setPreferences(prefs, SharePrefsChange.logout, 0);
} }
setPreferences(Future<SharedPreferences> prefs, setPreferences(Future<SharedPreferences> prefs,
@ -95,18 +103,21 @@ class Auth {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
sharedPreferences.setString(Auth.lastStoreDateKey, now.toString()); sharedPreferences.setString(Auth.lastStoreDateKey, now.toString());
if ( type == SharePrefsChange.registration ) { if ( type == SharePrefsChange.registration ) {
Auth().startPage = "home";
sharedPreferences.setInt(Auth.customerIdKey, customerId); sharedPreferences.setInt(Auth.customerIdKey, customerId);
sharedPreferences.setBool(Auth.isRegisteredKey, true); sharedPreferences.setBool(Auth.isRegisteredKey, true);
sharedPreferences.setBool(Auth.isLoggedInKey, true); sharedPreferences.setBool(Auth.isLoggedInKey, true);
await ExerciseTypeApi().getExerciseTypes(""); await ExerciseTypeApi().getExerciseTypes();
await ExerciseTreeApi().getExerciseTree();
} else if ( type == SharePrefsChange.login ) { } else if ( type == SharePrefsChange.login ) {
Auth().startPage = "home";
sharedPreferences.setInt(Auth.customerIdKey, customerId); sharedPreferences.setInt(Auth.customerIdKey, customerId);
sharedPreferences.setBool(Auth.isLoggedInKey, true); sharedPreferences.setBool(Auth.isLoggedInKey, true);
await ExerciseTypeApi().getExerciseTypes(""); await ExerciseTypeApi().getExerciseTypes();
await ExerciseTreeApi().getExerciseTree();
} else if ( type == SharePrefsChange.logout ) { } else if ( type == SharePrefsChange.logout ) {
sharedPreferences.setBool(Auth.isLoggedInKey, false); sharedPreferences.setBool(Auth.isLoggedInKey, false);
//sharedPreferences.setInt(Auth.customerIdKey, 0); sharedPreferences.setInt(Auth.customerIdKey, 0);
sharedPreferences.setString(authTokenKey, ""); sharedPreferences.setString(authTokenKey, "");
} }
} }
@ -115,7 +126,15 @@ class Auth {
this._exerciseTypes = exerciseTypes; this._exerciseTypes = exerciseTypes;
} }
void setExerciseTree( List<ExerciseTree> exerciseTree) {
this._exerciseTree = exerciseTree;
}
List<ExerciseType> getExerciseTypes() { List<ExerciseType> getExerciseTypes() {
return this._exerciseTypes; return this._exerciseTypes;
} }
List<ExerciseTree> getExerciseTree() {
return this._exerciseTree;
}
} }

View File

@ -0,0 +1,20 @@
class ExerciseTree {
int treeId;
int parentId;
String name;
String imageUrl;
bool active;
String nameTranslation;
ExerciseTree.fromJson(Map json) {
this.treeId = json['treeId'];
this.name = json['name'];
this.parentId = json['parentId'];
this.imageUrl = json['imageUrl'];
this.active = json['active'];
this.nameTranslation =
json['translations'] != null && (json['translations']).length > 0
? json['translations'][0]['name']
: this.name;
}
}

View File

@ -2,22 +2,32 @@ import 'package:flutter/services.dart';
class ExerciseType { class ExerciseType {
int exerciseTypeId; int exerciseTypeId;
int treeId;
String name; String name;
String description; String description;
BinaryCodec video; BinaryCodec video;
String unit; String unit;
String unitQuantity; String unitQuantity;
String unitQuantityUnit; String unitQuantityUnit;
bool active;
String imageUrl;
String nameTranslation;
String descriptionTranslation;
ExerciseType({this.name, this.description}); ExerciseType({this.name, this.description});
ExerciseType.fromJson(Map json) { ExerciseType.fromJson(Map json) {
this.exerciseTypeId = json['exerciseTypeId']; this.exerciseTypeId = json['exerciseTypeId'];
this.treeId = json['treeId'];
this.name = json['name']; this.name = json['name'];
this.description = json['description']; this.description = json['description'];
this.unit = json['unit']; this.unit = json['unit'];
this.unitQuantity = json['unitQuantity']; this.unitQuantity = json['unitQuantity'];
this.unitQuantityUnit = json['unitQuantityUnit']; this.unitQuantityUnit = json['unitQuantityUnit'];
this.active = json['active'];
this.imageUrl = json['images'][0]['url'];
this.nameTranslation = json['translations'][0]['name'];
this.descriptionTranslation = json['translations'][0]['description'];
} }
Map<String, dynamic> toJson() => Map<String, dynamic> toJson() =>
@ -26,6 +36,7 @@ class ExerciseType {
"description": description, "description": description,
"unit": unit, "unit": unit,
"unitQuantity": unitQuantity, "unitQuantity": unitQuantity,
"unitQuantityUnit": unitQuantityUnit "unitQuantityUnit": unitQuantityUnit,
"active": active
}; };
} }

View File

@ -1,5 +1,7 @@
import 'dart:ui'; import 'dart:ui';
import 'exercise_type.dart';
class WorkoutTree { class WorkoutTree {
int id; int id;
int parent; int parent;
@ -9,7 +11,8 @@ class WorkoutTree {
double fontSize; double fontSize;
bool child; bool child;
int exerciseTypeId; int exerciseTypeId;
ExerciseType exerciseType;
WorkoutTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child, this.exerciseTypeId); WorkoutTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child, this.exerciseTypeId, this.exerciseType);
} }

View File

@ -0,0 +1,143 @@
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/service/customer_service.dart';
class GenderItem {
GenderItem(this.dbValue,this.name);
final String dbValue;
String name;
}
class CustomerRepository {
Customer customer;
//List<CustomerRepository> customerList = List<CustomerRepository>();
bool visibleDetails = false;
List<GenderItem> genders;
CustomerRepository({this.customer}) {
customer = Customer();
genders = [
GenderItem("m", "Man"),
GenderItem("w", "Woman"),
];
}
String getGenderByName(String name) {
String dbValue;
genders.forEach((element) {
if (element.name == name) {
dbValue = element.dbValue;
}
});
return dbValue;
}
String getGenderByDBValue(String dbValue) {
String name;
genders.forEach((element) {
if (element.dbValue == dbValue) {
name = element.name;
}
});
return name;
}
String get name {
return this.customer.name != null ? this.customer.name : "";
}
String get firstName {
return this.customer.firstname != null ? this.customer.firstname : "";
}
String get sex {
return this.customer.sex == "m" ? "Man" : "Woman";
}
int get birthYear {
return this.customer.birthYear;
}
String get goal {
return this.customer.goal;
}
String get fitnessLevel {
return this.customer.fitnessLevel;
}
String get bodyType {
return this.customer.bodyType;
}
setName(String name) {
this.customer.name = name;
}
setFirstName(String firstName) {
this.customer.firstname = firstName;
}
setPassword( String password ) {
this.customer.password = password;
}
setEmail(String email) {
this.customer.email = email;
}
setSex(String sex) {
this.customer.sex = sex;
}
setWeight( int weight) {
this.customer.weight = weight;
}
setBirthYear( int birthYear ) {
this.customer.birthYear = birthYear;
}
setFitnessLevel( String level ) {
this.customer.fitnessLevel = level;
}
setGoal( String goal ) {
this.customer.goal = goal;
}
setBodyType(String bodyType) {
this.customer.bodyType = bodyType;
}
createNew() {
this.customer = Customer();
}
Customer getCustomer() {
return this.customer;
}
void setCustomer ( Customer customer ) {
this.customer = customer;
}
Future<void> addCustomer() async {
final Customer modelCustomer = customer;
await CustomerApi().addCustomer(modelCustomer);
}
Future<void> saveCustomer() async {
final Customer modelCustomer = customer;
await CustomerApi().saveCustomer(modelCustomer);
}
/* Future<List<CustomerRepository>> getCustomers() async {
final results = await CustomerApi().getRealCustomers("");
this.customerList = results.map((item) => CustomerRepository(customer: item)).toList();
return this.customerList;
}
addNewCustomerToList(CustomerRepository customerViewModel) {
customerList.add(customerViewModel);
}*/
}

View File

@ -0,0 +1,88 @@
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/service/exercise_service.dart';
class ExerciseRepository {
Exercise exercise;
Customer customer;
ExerciseType exerciseType;
double rmWendler = 0;
double rmMcglothlin = 0;
double rmLombardi = 0;
double rmMayhew = 0;
double rmOconner = 0;
double rmWathen = 0;
createNew() {
this.exercise = Exercise();
exercise.dateAdd = DateTime.now();
}
setQuantity(double quantity) {
if ( this.exercise == null ) {
this.createNew();
}
this.exercise.quantity = quantity;
}
setUnitQuantity(double unitQuantity) {
if ( this.exercise == null ) {
this.createNew();
}
this.exercise.unitQuantity = unitQuantity;
}
setUnit( String unit) {
if ( this.exercise == null ) {
this.createNew();
}
this.exercise.unit = unit;
}
setDatetimeExercise(DateTime datetimeExercise) {
if ( this.exercise == null ) {
this.createNew();
}
this.exercise.dateAdd = datetimeExercise;
}
double get unitQuantity {
return this.exercise.unitQuantity;
}
double get quantity {
return this.exercise.quantity;
}
Exercise getExercise() {
return this.exercise;
}
Future<void> addExercise() async {
final Exercise modelExercise = this.exercise;
modelExercise.customerId = this.customer.customerId;
modelExercise.exerciseTypeId = this.exerciseType.exerciseTypeId;
await ExerciseApi().addExercise(modelExercise);
}
setCustomer(Customer customer) {
this.customer = customer;
}
setExerciseType( ExerciseType exerciseType) {
this.exerciseType = exerciseType;
}
/*
Future<List<ExerciseRepository>> getExercisesByCustomer( int customerId ) async {
final results = await ExerciseApi().getExercisesByCustomer(customerId);
this.exerciseList = results.map((item) => ExerciseRepository(exercise: item)).toList();
return this.exerciseList;
} */
}

View File

@ -0,0 +1,73 @@
import 'dart:collection';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/service/exercise_tree_service.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart';
import 'package:flutter/material.dart';
class MenuTreeRepository {
final LinkedHashMap tree = LinkedHashMap<String, WorkoutTree>();
Future<void> createTree() async {
final AppLanguage appLanguage = AppLanguage();
bool isEnglish = appLanguage.appLocal == Locale('en');
List<ExerciseTree> exerciseTree = Auth().getExerciseTree();
if ( exerciseTree == null || exerciseTree.length == 0) {
await ExerciseTreeApi().getExerciseTree();
}
exerciseTree.forEach( (treeItem) async {
String treeName = isEnglish ? treeItem.name : treeItem.nameTranslation;
String assetImage = 'asset/menu/' + treeItem.imageUrl.substring(7);
this.tree[treeItem.name] = WorkoutTree(
treeItem.treeId,
treeItem.parentId,
treeName,
assetImage, Colors.white,
32,
false,
0,
null
);
});
List<ExerciseType> exerciseTypes = Auth().getExerciseTypes();
if ( exerciseTypes == null || exerciseTypes.length == 0) {
await ExerciseTypeApi().getExerciseTypes();
}
exerciseTypes.forEach( (exerciseType) {
String exerciseTypeName = isEnglish ?
exerciseType.name : exerciseType.nameTranslation;
String assetImage = 'asset/menu/' + exerciseType.imageUrl.substring(7);
this.tree[exerciseType.name] = WorkoutTree(
exerciseType.exerciseTypeId,
exerciseType.treeId,
exerciseTypeName,
assetImage,
Colors.white,
16,
true,
exerciseType.exerciseTypeId,
exerciseType
);
});
}
LinkedHashMap getBranch(int parent) {
LinkedHashMap branch = LinkedHashMap<String, WorkoutTree>();
tree.forEach((key, value) {
WorkoutTree workoutTree = value as WorkoutTree;
if ( parent == workoutTree.parent) {
branch[key] = value;
}
});
return branch;
}
}

View File

@ -0,0 +1,32 @@
import 'package:aitrainer_app/model/user.dart';
import 'package:aitrainer_app/service/customer_service.dart';
class UserRepository {
User user;
UserRepository() {
this.createNewUser();
}
setEmail(String email) {
this.user.email = email;
}
setPassword(String password) {
this.user.password = password;
}
createNewUser() {
this.user = User();
}
Future<void> addUser() async {
final User modelUser = this.user;
await CustomerApi().addUser(modelUser);
}
Future<void> getUser() async {
final User modelUser = this.user;
await CustomerApi().getUser(modelUser);
}
}

View File

@ -39,8 +39,13 @@ class CustomerApi {
body); body);
Customer customer; Customer customer;
try { try {
customer = Customer.fromJson(jsonDecode(responseBody)); int status = jsonDecode(responseBody)['status'];
Auth().afterRegistration(customer); if ( status != null ) {
throw new Exception(jsonDecode(responseBody)['error']);
} else {
customer = Customer.fromJson(jsonDecode(responseBody));
Auth().afterRegistration(customer);
}
} on FormatException catch(exception) { } on FormatException catch(exception) {
throw new Exception(responseBody); throw new Exception(responseBody);
} }
@ -56,7 +61,7 @@ class CustomerApi {
Customer customer; Customer customer;
try { try {
customer = Customer.fromJson(jsonDecode(responseBody)); customer = Customer.fromJson(jsonDecode(responseBody));
Auth().afterRegistration(customer); await Auth().afterLogin(customer);
} on FormatException catch(exception) { } on FormatException catch(exception) {
throw new Exception(responseBody); throw new Exception(responseBody);
} }
@ -66,10 +71,17 @@ class CustomerApi {
Future<void> getCustomer(int customerId) async { Future<void> getCustomer(int customerId) async {
String body = ""; String body = "";
print(" ===== get the customer by id: " + customerId.toString() ); print(" ===== get the customer by id: " + customerId.toString() );
final String responseBody = await _client.get( try {
"customers/"+customerId.toString(), final String responseBody = await _client.get(
body); "customers/"+customerId.toString(),
Customer customer = Customer.fromJson(jsonDecode(responseBody)); body);
Auth().afterRegistration(customer); Customer customer = Customer.fromJson(jsonDecode(responseBody));
Auth().afterRegistration(customer);
} catch (exception) {
print ("Exception: " + exception.toString());
print (" === go to registration ");
Auth().logout();
Auth().startPage = "registration";
}
} }
} }

View File

@ -0,0 +1,19 @@
import 'dart:convert';
import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'api.dart';
class ExerciseTreeApi {
final APIClient _client = new APIClient();
Future<List<ExerciseTree>> getExerciseTree() async {
final body = await _client.get("exercise_tree", "");
final Iterable json = jsonDecode(body);
final List<ExerciseTree> exerciseTree = json.map((exerciseTree) =>
ExerciseTree.fromJson(exerciseTree)).toList();
Auth().setExerciseTree(exerciseTree);
return exerciseTree;
}
}

View File

@ -7,8 +7,8 @@ import 'package:aitrainer_app/service/api.dart';
class ExerciseTypeApi { class ExerciseTypeApi {
final APIClient _client=new APIClient(); final APIClient _client=new APIClient();
Future<List<ExerciseType>> getExerciseTypes(String param) async { Future<List<ExerciseType>> getExerciseTypes() async {
final body = await _client.get("exercise_type", param); final body = await _client.get("exercise_type/active", "");
final Iterable json = jsonDecode(body); final Iterable json = jsonDecode(body);
final List<ExerciseType> exerciseTypes = json.map( (exerciseType) => ExerciseType.fromJson(exerciseType) ).toList(); final List<ExerciseType> exerciseTypes = json.map( (exerciseType) => ExerciseType.fromJson(exerciseType) ).toList();
Auth().setExerciseTypes(exerciseTypes); Auth().setExerciseTypes(exerciseTypes);

View File

@ -3,10 +3,16 @@ import 'dart:convert';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/auth.dart'; import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_type.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'; import 'package:intl/intl.dart';
class Common { class Common {
static const EMAIL_ERROR = "Please type a right email address here.";
static const PASSWORD_ERROR = "The password must have at least 8 characters.";
static String toJson( Map<String, String> map ) { static String toJson( Map<String, String> map ) {
String rc = "{"; String rc = "{";
@ -47,4 +53,26 @@ class Common {
List<int> bytes = text.toString().codeUnits; List<int> bytes = text.toString().codeUnits;
return utf8.decode(bytes); return utf8.decode(bytes);
} }
static double mediaSizeWidth( BuildContext context ) {
return MediaQuery.of(context).size.width;
}
static 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-]+)*$',
);
return _emailRegExp.hasMatch(email);
}
static bool validatePassword(UserRepository userRepository) {
final password = userRepository.user.password;
final RegExp _passwordRegExp =
RegExp(r'^(?=.*[A-Za-z0-9])(?=.*\d)[A-Za-z\d]{7,}$');
return _passwordRegExp.hasMatch(password);
}
} }

View File

@ -1,61 +0,0 @@
import 'package:aitrainer_app/util/loading_screen_state.dart';
import 'package:flutter/material.dart';
/// Loading Screen Widget that updates the screen once all inistializer methods
/// are called
// ignore: must_be_immutable
class LoadingScreen extends StatefulWidget {
/// List of methods that are called once the Loading Screen is rendered
/// for the first time. These are the methods that can update the messages
/// that are shown under the loading symbol
final List<dynamic> initializers;
/// The name of the application that is shown at the top of the loading screen
RichText title = RichText(text: TextSpan(text: 'AI Trainer'));
//final Text title;
/// The background colour which is used as a filler when the image doesn't
/// occupy the full screen
final Color backgroundColor;
/// The styling that is used with the text (messages) that are displayed under
/// the loader symbol
final TextStyle styleTextUnderTheLoader;
/// The Layout/Scaffold Widget that is loaded once all the initializer methods
/// have been executed
final dynamic navigateToWidget;
/// The colour that is used for the loader symbol
final Color loaderColor;
/// The image widget that is used as a background cover to the loading screen
final Image image;
/// The message that is displayed on the first load of the widget
final String initialMessage;
/// Constructor for the LoadingScreen widget with all the required
/// initializers
LoadingScreen(
{this.initializers,
this.navigateToWidget,
this.loaderColor,
this.image,
//this.title = Text("Welcome"),
this.backgroundColor = Colors.white,
this.styleTextUnderTheLoader = const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black),
this.initialMessage})
// The Widget depends on the initializers and navigateToWidget to have a
// valid value. Thus we assert that the values passed are valid and
// not null
: assert(initializers != null && initializers.length > 0),
assert(navigateToWidget != null);
/// Bind the Widget to the custom State object
@override
LoadingScreenState createState() => LoadingScreenState();
}

View File

@ -1,129 +0,0 @@
import 'dart:core';
import 'dart:async';
import 'package:aitrainer_app/util/loading_screen.dart';
import 'package:aitrainer_app/widgets/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:aitrainer_app/util/message_state.dart';
/// The custom state that is used by the Loading Screen widget to handle the
/// messages that are provided by the initializer methods.
///
/// Note: Although the class is not exported from the package as not required by
/// the implementers using the package, the protected metatag is added to make
/// the code clearer.
@protected
class LoadingScreenState extends MessageState<LoadingScreen> {
/// Initialise the state
@override
void initState() {
super.initState();
/// If the LoadingScreen widget has an initial message set, then the default
/// message in the MessageState class needs to be updated
if (widget.initialMessage != null) {
initialMessage = widget.initialMessage;
}
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance.addPostFrameCallback((_) {
runInitTasks();
});
}
/// This method calls the initializers and once they complete redirects to
/// the widget provided in navigateAfterInit
@protected
Future runInitTasks() async {
print(" ----- runInitTasks");
/// Run each initializer method sequentially
Future.forEach(widget.initializers, (init) => init(this, callbackFunction)).whenComplete(() {
// When all the initializers has been called and terminated their
// execution. The screen is navigated to the next scaffolding widget
if (widget.navigateToWidget is String) {
// It's fairly safe to assume this is using the in-built material
// named route component
print(" ----- navigate to " + widget.navigateToWidget);
Navigator.of(context).pushReplacementNamed(widget.navigateToWidget);
} else if (widget.navigateToWidget is Widget) {
Navigator.of(context).pushReplacement(new MaterialPageRoute(
builder: (BuildContext context) => widget.navigateToWidget));
print(" ----- navigate to main ");
} else {
throw new ArgumentError(
'widget.navigateAfterSeconds must either be a String or Widget');
}
});
}
void callbackFunction() {
print("Call Home callback if widget");
if (widget.navigateToWidget is Widget) {
AitrainerHome home = widget.navigateToWidget as AitrainerHome;
home.callback();
}
}
/// Render the LoadingScreen widget
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: widget.backgroundColor,
body: new InkWell(
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
/// Paint the area where the inner widgets are loaded with the
/// background to keep consistency with the screen background
new Container(
decoration: BoxDecoration(color: widget.backgroundColor),
),
/// Render the background image
new Container(
child: widget.image,
),
/// Render the Title widget, loader and messages below each other
new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
flex: 3,
child: new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 30.0),
),
widget.title,
],
)),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
/// Loader Animation Widget
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
widget.loaderColor),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
),
Text(getMessage, style: widget.styleTextUnderTheLoader),
],
),
),
],
),
],
),
),
);
}
}

View File

@ -1,93 +0,0 @@
import 'dart:collection';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:flutter/material.dart';
class MenuTests {
LinkedHashMap tree = LinkedHashMap();
MenuTests(BuildContext context) {
this.tree['Cardio']= WorkoutTree(1, 0, AppLocalizations.of(context).translate("Cardio"),
'asset/menu/1.cardio.png',
Colors.white, 32, false,0);
this.tree['Aerobic']= WorkoutTree(2, 1, AppLocalizations.of(context).translate("Aerobic"),
'asset/menu/1.1.aerob.png',
Colors.white, 32, false,0);
this.tree['Cooper']= WorkoutTree(21, 2, AppLocalizations.of(context).translate("Cooper"),
'asset/menu/1.1.1.cooper.png',
Colors.white, 32, true,30);
this.tree['Anaerobic']= WorkoutTree(3, 1, AppLocalizations.of(context).translate("Anaerobic"),
'asset/menu/1.2.anaerob.png',
Colors.white, 32, false,0);
this.tree['300m']= WorkoutTree(22, 3, "300m",
'asset/menu/1.2.1.300m.png',
Colors.white, 32, true,31);
this.tree['400m']= WorkoutTree(24, 3, "400m",
'asset/menu/1.2.2.400m.png',
Colors.white, 32, true,32);
this.tree['Strength']= WorkoutTree(4, 0, AppLocalizations.of(context).translate("Strength"),
'asset/menu/2.strength.png',
Colors.white, 32, false,0);
this.tree['Endurance']= WorkoutTree(5, 4, AppLocalizations.of(context).translate("Endurance"),
'asset/menu/2.1.endurance.png',
Colors.white, 36, false,0);
this.tree['Pullups']= WorkoutTree(6, 5, AppLocalizations.of(context).translate("Pull Ups"),
'asset/menu/2.1.1.pull-ups.png',
Colors.white, 32, true,38);
this.tree['Pushups']= WorkoutTree(7, 5, AppLocalizations.of(context).translate("Pushups"),
'asset/menu/2.1.2.pushup.png',
Colors.white, 32, true,33);
this.tree['Situps']= WorkoutTree(10, 5, AppLocalizations.of(context).translate("Sit-ups"),
'asset/menu/2.1.3.sit-ups.png',
Colors.white, 32, true,36);
this.tree['Squats']= WorkoutTree(11, 5, AppLocalizations.of(context).translate("Squats"),
'asset/menu/2.1.4.squats.png',
Colors.white, 32, true,35);
this.tree['TimedPushups']= WorkoutTree(12, 5, AppLocalizations.of(context).translate("Timed Pushups"),
'asset/menu/2.1.5.timedpushup.png',
Colors.white, 32, true,34);
this.tree['Core']= WorkoutTree(43, 5, AppLocalizations.of(context).translate("Core"),
'asset/menu/2.1.6.core.png',
Colors.white, 32, true,45);
this.tree['1RM']= WorkoutTree(8, 4, AppLocalizations.of(context).translate("1RM"),
'asset/menu/2.2.1.1RM.png',
Colors.white, 32, false,0);
this.tree['Chestpress']= WorkoutTree(13, 8, AppLocalizations.of(context).translate("Chest Press"),
'asset/menu/2.2.1.1.chestpress.png',
Colors.white, 32, true,37);
this.tree['PullUps1rm']= WorkoutTree(14, 8, AppLocalizations.of(context).translate("Pull Ups"),
'asset/menu/2.2.1.2.pullups.png',
Colors.white, 32, true, 38);
this.tree['Biceps']= WorkoutTree(15, 8, AppLocalizations.of(context).translate("Biceps"),
'asset/menu/2.2.1.3.biceps.png',
Colors.white, 32, true, 39);
this.tree['Triceps']= WorkoutTree(16, 8, AppLocalizations.of(context).translate("Triceps"),
'asset/menu/2.2.1.4.triceps.png',
Colors.white, 32, true, 40);
this.tree['Shoulders']= WorkoutTree(17, 8, AppLocalizations.of(context).translate("Shoulders"),
'asset/menu/2.2.1.5.shoulders.png',
Colors.white, 32, true, 41);
this.tree['BodyCompositions']= WorkoutTree(9, 0, AppLocalizations.of(context).translate("Body Compositions"),
'asset/menu/3.bcs1.png',
Colors.white, 32, false,0);
this.tree['BMI']= WorkoutTree(18, 9, AppLocalizations.of(context).translate("BMI"),
'asset/menu/3.1.BMI.png',
Colors.white, 32, true,42);
this.tree['BMR']= WorkoutTree(19, 9, AppLocalizations.of(context).translate("BMR"),
'asset/menu/3.2.BMR.png',
Colors.white, 32, true, 43);
this.tree['Sizes']= WorkoutTree(20, 9, AppLocalizations.of(context).translate("Sizes"),
'asset/menu/3.3.sizes.png',
Colors.white, 32, true, 44);
}
LinkedHashMap getMenuItems() {
return this.tree;
}
}

View File

@ -1,32 +0,0 @@
import 'package:flutter/widgets.dart';
/// An extension class to the Flutter standard State. The class provides getter
/// and setters for updating the message section of the loading screen
///
/// Note: The class is marked as abstract to avoid IDE issues that expects
/// protected methods to be overloaded
abstract class MessageState<T extends StatefulWidget> extends State<T> {
/// The state variable that will hold the latest message that needs to be
/// displayed.
///
/// Note: Although Flutter standard allow member variables to be used from
/// instance object reference, this is not a best practice with OOP. OOP
/// design proposes that member variables should be accessed through getter
/// and setter methods.
@protected
String _message = 'Loading . . .';
/// The member variable is set as protected this it is not exposed to the
/// widget state class. As a workaround a protected setter is set so it is
/// not used outside the package
@protected
set initialMessage(String message) => _message = message;
/// Setter for the message variable
set setMessage(String message) => setState(() {
_message = message;
});
/// Getter for the message variable
String get getMessage => _message;
}

View File

@ -1,6 +1,8 @@
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/service/api.dart'; import 'package:aitrainer_app/service/api.dart';
import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/service/customer_service.dart';
import 'package:aitrainer_app/service/exercise_tree_service.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart'; import 'package:aitrainer_app/service/exercisetype_service.dart';
import 'package:devicelocale/devicelocale.dart'; import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -12,18 +14,24 @@ import '../push_notifications.dart';
class Session { class Session {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance(); Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Auth _auth = Auth();
SharedPreferences _sharedPreferences; SharedPreferences _sharedPreferences;
final AppLanguage appLanguage = AppLanguage(); final AppLanguage appLanguage = AppLanguage();
fetchSessionAndNavigate(Function callback ) async { fetchSessionAndNavigate( ) async {
print (" -- Session: await prefs..");
_sharedPreferences = await _prefs; _sharedPreferences = await _prefs;
if ( _auth.firstLoad ) { if ( Auth().firstLoad ) {
_fetchToken(_sharedPreferences, callback);
print (" -- Session: fetch locale..");
await appLanguage.fetchLocale();
await AppLocalizations.delegate.load(appLanguage.appLocal);
print (" -- Session: fetch token..");
await _fetchToken(_sharedPreferences);
initDeviceLocale(); initDeviceLocale();
appLanguage.fetchLocale();
PushNotificationsManager().init(); PushNotificationsManager().init();
} }
@ -53,7 +61,7 @@ class Session {
/* /*
Auth flow of the user, see auth.dart Auth flow of the user, see auth.dart
*/ */
_fetchToken(SharedPreferences prefs, Function callback) async { _fetchToken(SharedPreferences prefs) async {
var responseJson = await APIClient.authenticateUser( var responseJson = await APIClient.authenticateUser(
Auth.username, Auth.username,
@ -71,6 +79,7 @@ class Session {
// registration // registration
//Navigator.of(context).pushNamed('registration'); //Navigator.of(context).pushNamed('registration');
prefs.setBool(Auth.isRegisteredKey, true); prefs.setBool(Auth.isRegisteredKey, true);
Auth().startPage = "registration";
} else { } else {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
DateTime lastStoreDate = DateTime.parse( DateTime lastStoreDate = DateTime.parse(
@ -83,16 +92,17 @@ class Session {
prefs.get(Auth.isLoggedInKey) == false) { prefs.get(Auth.isLoggedInKey) == false) {
print("************* Login"); print("************* Login");
//Navigator.of(context).pushNamed('login'); //Navigator.of(context).pushNamed('login');
Auth().startPage = "login";
} else { } else {
print("************** Store SharedPreferences"); print("************** Store SharedPreferences");
// get API customer // get API customer
await CustomerApi().getCustomer(prefs.getInt(Auth.customerIdKey)); await CustomerApi().getCustomer(prefs.getInt(Auth.customerIdKey));
Auth().startPage = "home";
} }
await ExerciseTypeApi().getExerciseTypes(""); await ExerciseTypeApi().getExerciseTypes();
print("--- Session finished, call callback "); await ExerciseTreeApi().getExerciseTree();
callback(); print("--- Session finished");
} }
} }

View File

@ -1,142 +1,110 @@
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/auth.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/customer_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_view_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:aitrainer_app/viewmodel/user_view_model.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class AccountPage extends StatefulWidget{ class AccountPage extends StatelessWidget {
_AccountPagePageState _state; // ignore: close_sinks
AccountBloc accountBloc;
_AccountPagePageState createState() {
_state = new _AccountPagePageState();
return _state;
}
State getState() {
return _state;
}
}
class _AccountPagePageState extends State<AccountPage> {
final UserViewModel user = UserViewModel();
final AppLanguage appLanguage = AppLanguage();
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final BottomNavigator bottomNav = BottomNavigator();
Future<List<ExerciseViewModel>> _exercises;
ExerciseChangingViewModel exerciseChangingViewModel;
@override
void initState() {
exerciseChangingViewModel = Provider.of<ExerciseChangingViewModel>(context, listen: false);
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<CustomerChangingViewModel>( accountBloc = BlocProvider.of<AccountBloc>(context);
builder: (context, model, child ) { return Scaffold(
if ( model.customer == null ) { appBar: AppBar(
CustomerViewModel customerViewModel = CustomerViewModel(); title: Text(AppLocalizations.of(context).translate('Account')),
model.customer = customerViewModel; backgroundColor: Colors.transparent,
if ( model.customer.getCustomer() == null ) { ),
model.customer.setCustomer(Auth().userLoggedIn); body: Container(
foregroundDecoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_long_logo.png'),
alignment: Alignment.topRight,
),
),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: BlocConsumer<AccountBloc, AccountState>(
listener: (context, state) {
if (state is AccountError) {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange,
content:
Text(state.message, style: TextStyle(color: Colors.white))));
} else if (state is AccountLoading) {
}
},
builder: (context, state) {
if ( state is AccountInitial ) {
String customerName = accountBloc.customerRepository.firstName +
" " + accountBloc.customerRepository.name;
return accountWidget(context, customerName, accountBloc);
} else if ( state is AccountLoggedIn ) {
String customerName = accountBloc.customerRepository.firstName +
" " + accountBloc.customerRepository.name;
return accountWidget(context, customerName, accountBloc);
} else if ( state is AccountLoggedOut ) {
String customerName = "";
return accountWidget(context, customerName, accountBloc);
} else if ( state is AccountReady ) {
String customerName = accountBloc.customerRepository.firstName +
" " + accountBloc.customerRepository.name;
return accountWidget(context, customerName, accountBloc);
} else {
return accountWidget(context, "", accountBloc);
}
} }
}
if ( Auth().userLoggedIn != null ) {
_exercises =
exerciseChangingViewModel.getExercisesByCustomer(
Auth().userLoggedIn.customerId);
}
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).translate('Account')),
backgroundColor: Colors.transparent,
), ),
body: Container( ),
foregroundDecoration: BoxDecoration( bottomNavigationBar: BottomNavigator(bottomNavIndex: 2));
image: DecorationImage(
image: AssetImage('asset/image/WT_long_logo.png'),
alignment: Alignment.topRight,
),
),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child:
ListView(
padding: EdgeInsets.only(top: 135),
children: <Widget>[
ListTile(
leading: Icon(Icons.perm_identity),
subtitle: Text(
AppLocalizations.of(context).translate("Profile")),
title: FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(Auth().userLoggedIn != null ?
Auth().userLoggedIn.name + " " +
Auth().userLoggedIn.firstname : "",
style: TextStyle(color: Colors.blue)),
Icon(Icons.arrow_forward_ios),
]),
textColor: Colors.grey,
color: Colors.white,
onPressed: () => {
if (model.customer.getCustomer() != null) {
Navigator.of(context).pushNamed(
'customerModifyPage'),
print("Profile"),
}
},
),
),
ListTile(
leading: Icon(Icons.language),
title: Text(appLanguage.appLocal == Locale('en') ?
AppLocalizations.of(context).translate("English") :
AppLocalizations.of(context).translate("Hungarian")),
subtitle: Text(AppLocalizations.of(context).translate(
"Selected Language")),
),
loginOut( model ),
exercises(exerciseChangingViewModel),
]
)
),
bottomNavigationBar: bottomNav.buildBottomNavigator(
context, widget._state)
);
});
} }
ListTile loginOut( CustomerChangingViewModel model ) { ListView accountWidget(BuildContext context, String customerName, AccountBloc accountBloc) {
return ListView(padding: EdgeInsets.only(top: 135), children: <Widget>[
ListTile(
leading: Icon(Icons.perm_identity),
subtitle:
Text(AppLocalizations.of(context).translate("Profile")),
title: FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(customerName,
style: TextStyle(color: Colors.blue)),
Icon(Icons.arrow_forward_ios),
]),
textColor: Colors.grey,
color: Colors.white,
onPressed: () => {
if (accountBloc.customerRepository.customer != null) {
Navigator.of(context).pushNamed('customerModifyPage'),
print("Profile"),
}
},
),
),
loginOut( context, accountBloc ),
//exercises(exerciseChangingViewModel),
]);
}
ListTile loginOut( BuildContext context, AccountBloc accountBloc ) {
ListTile element = ListTile(); ListTile element = ListTile();
String text = "Logout"; String text = "Logout";
Color buttonColor = Colors.orange; Color buttonColor = Colors.orange;
if ( model.customer.getCustomer() == null ) { if ( accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer.email == null) {
text = "Login"; text = "Login";
buttonColor = Colors.blue; buttonColor = Colors.blue;
} }
@ -149,25 +117,20 @@ class _AccountPagePageState extends State<AccountPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(AppLocalizations.of(context).translate(text), Text(AppLocalizations.of(context).translate(text),
style: TextStyle( style: TextStyle(
color: buttonColor color: buttonColor
)), )),
Icon(Icons.arrow_forward_ios), Icon(Icons.arrow_forward_ios),
]), ]),
textColor: buttonColor, textColor: buttonColor,
color: Colors.white, color: Colors.white,
onPressed: () => { onPressed: () => {
setState(() { if ( accountBloc.loggedIn ) {
if ( model.customer.getCustomer() == null ) { accountBloc.add(AccountLogout())
print("Login"); } else {
Navigator.of(context).pushNamed("login", arguments: widget._state); accountBloc.add(AccountLogin()),
} else { Navigator.of(context).pushNamed('login'),
print("Logout"); }
Auth().logout();
model.customer.setCustomer(null);
}
})
}, },
), ),
); );
@ -175,7 +138,7 @@ class _AccountPagePageState extends State<AccountPage> {
return element; return element;
} }
ListTile exercises( ExerciseChangingViewModel model ) { /* ListTile exercises( ExerciseChangingViewModel model ) {
ListTile element = ListTile(); ListTile element = ListTile();
if ( Auth().userLoggedIn == null ) { if ( Auth().userLoggedIn == null ) {
return element; return element;
@ -202,7 +165,8 @@ class _AccountPagePageState extends State<AccountPage> {
return element; return element;
} }
*/
/*
Widget getExercises( ExerciseChangingViewModel model ) { Widget getExercises( ExerciseChangingViewModel model ) {
List<ExerciseViewModel> exercises = model.exerciseList; List<ExerciseViewModel> exercises = model.exerciseList;
@ -262,5 +226,5 @@ class _AccountPagePageState extends State<AccountPage> {
return element; return element;
} } */
} }

View File

@ -0,0 +1,346 @@
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';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class CustomExercisePage extends StatefulWidget {
_CustomExerciseNewPageState createState() => _CustomExerciseNewPageState();
}
class _CustomExerciseNewPageState extends State<CustomExercisePage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
final ExerciseType exerciseType = ModalRoute.of(context).settings.arguments;
return BlocProvider(
create: (context) =>
CustomExerciseFormBloc(exerciseRepository: ExerciseRepository()),
child: Builder(builder: (context) {
// ignore: close_sinks
final exerciseBloc = BlocProvider.of<CustomExerciseFormBloc>(context);
exerciseBloc.exerciseRepository.setExerciseType(exerciseType);
return Scaffold(
key: _scaffoldKey,
resizeToAvoidBottomInset: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
title: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
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: FormBlocListener<CustomExerciseFormBloc, String, String>(
onSubmitting: (context, state) {
LoadingDialog.show(context);
},
onSuccess: (context, state) {
LoadingDialog.hide(context);
},
onFailure: (context, state) {
LoadingDialog.hide(context);
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange,
content: Text(state.failureResponse,
style: TextStyle(color: Colors.white))));
},
child: 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: CustomScrollView(
scrollDirection: Axis.vertical,
slivers: [
SliverList(
delegate: SliverChildListDelegate(
[
Container(
padding: EdgeInsets.only(top:20,left:25, right:25),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Custom Exercise",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.deepOrange)),
columnQuantityUnit(exerciseBloc),
columnQuantity(exerciseBloc),
]
)
),
]
),
),
gridCalculation(exerciseBloc)
]
)
)
)
);
}));
}
Column columnQuantityUnit(CustomExerciseFormBloc bloc) {
Column column = Column();
if (bloc.exerciseRepository.exerciseType != null &&
bloc.exerciseRepository.exerciseType.unitQuantity == "1") {
column = Column(children: [
TextFieldBlocBuilder(
textFieldBloc: bloc.unitQuantityField,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.lightBlue,
fontWeight: FontWeight.bold),
inputFormatters: [
FilteringTextInputFormatter(RegExp(r"[\d.]"), allow: true)
],
onChanged: (input) => {
print("UnitQuantity value $input"),
bloc.exerciseRepository.setUnitQuantity(double.parse(input))
},
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 done with"),
labelStyle: TextStyle(fontSize: 12, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unitQuantityUnit),
),
),
new InkWell(
child: new Text(
AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unitQuantityUnit),
style: TextStyle(fontSize: 12)),
),
]);
}
;
return column;
}
Column columnQuantity(CustomExerciseFormBloc bloc) {
Column column = Column(children: [
TextFieldBlocBuilder(
textFieldBloc: bloc.quantityField,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.deepOrange,
fontWeight: FontWeight.bold),
inputFormatters: [
FilteringTextInputFormatter(RegExp(r"[\d.]"), allow: true)
],
onChanged: (input) => {
print("Quantity value $input"),
bloc.exerciseRepository.setQuantity(double.parse(input)),
bloc.exerciseRepository
.setUnit(bloc.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),
labelText: AppLocalizations.of(context)
.translate(bloc.exerciseRepository.exerciseType.unit),
),
),
]);
return column;
}
SliverGrid gridCalculation(CustomExerciseFormBloc bloc) {
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildListDelegate(
[
TextFieldBlocBuilder(
isEnabled: false,
textFieldBloc: bloc.rmWendlerField,
padding: EdgeInsets.only(left:30),
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM by Wendler: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
textFieldBloc: bloc.rmMcGothlinField,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM by McGlothin: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rmLombardiField,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM by Lambordini: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rmWathenField,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM by Wahten: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rmOconnerField,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM by O'Conner: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rmMayhewField,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM by Mayhew: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rmAverageField,
style: TextStyle(color: Colors.blueAccent, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM Average: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rm90Field,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM 90%: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rm80Field,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM 80%: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rm70Field,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM 70%: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rm60Field,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM 60%: ",
)),
TextFieldBlocBuilder(
isEnabled: false,
padding: EdgeInsets.only(left:30),
maxLines: 1,
textFieldBloc: bloc.rm50Field,
style: TextStyle(color: Colors.deepOrange, fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.white,
filled: false,
labelText: "1RM 50%: ",
))
])
);
}
}

View File

@ -1,9 +1,11 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class CustomerBodyTypePage extends StatefulWidget{ class CustomerBodyTypePage extends StatefulWidget {
_CustomerBodyTypePageState _state; _CustomerBodyTypePageState _state;
_CustomerBodyTypePageState createState() { _CustomerBodyTypePageState createState() {
@ -13,7 +15,6 @@ class CustomerBodyTypePage extends StatefulWidget{
} }
class BodyTypeItem { class BodyTypeItem {
static String endomorph = "endomorph"; static String endomorph = "endomorph";
static String ectomorph = "ectomorph"; static String ectomorph = "ectomorph";
static String mesomorph = "mesomorph"; static String mesomorph = "mesomorph";
@ -23,15 +24,15 @@ class _CustomerBodyTypePageState extends State<CustomerBodyTypePage> {
String selected; String selected;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final CustomerChangingViewModel changingViewModel = ModalRoute.of(context).settings.arguments; final CustomerRepository customerRepository =
final double cWidth = MediaQuery.of(context).size.width*0.75; ModalRoute.of(context).settings.arguments;
final double cWidth = MediaQuery.of(context).size.width * 0.75;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Image.asset( Image.asset(
'asset/image/WT_long_logo.png', 'asset/image/WT_long_logo.png',
fit: BoxFit.cover, fit: BoxFit.cover,
@ -40,150 +41,159 @@ class _CustomerBodyTypePageState extends State<CustomerBodyTypePage> {
], ],
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
), ),
body: Container( body: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'), image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center, alignment: Alignment.center,
),
), ),
child: Column( ),
mainAxisAlignment: MainAxisAlignment.center, child: BlocProvider(
children: [ create: (context) =>
Divider(), CustomerChangeBloc(customerRepository: customerRepository),
Wrap( child: Builder(builder: (context) {
//runAlignment: WrapAlignment.center, // ignore: close_sinks
alignment: WrapAlignment.center, CustomerChangeBloc changeBloc =
BlocProvider.of<CustomerChangeBloc>(context);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Divider(),
AppLocalizations.of(context).translate("Your Body Type"), Wrap(
textAlign: TextAlign.center, //runAlignment: WrapAlignment.center,
style: TextStyle(color: Colors.orange, alignment: WrapAlignment.center,
fontSize: 42, fontFamily: 'Arial',
fontWeight: FontWeight.w900 ),)
]
),
Divider(),
FlatButton(
child: Container(
width: cWidth,
child: Column(
children: [ children: [
Text(AppLocalizations.of(context).translate("Endomorph"), Text(
textWidthBasis: TextWidthBasis.longestLine, AppLocalizations.of(context)
style: TextStyle(color: Colors.blue, .translate("Your Body Type"),
fontSize: 32, fontFamily: 'Arial', textAlign: TextAlign.center,
fontWeight: FontWeight.w900 )), style: TextStyle(
color: Colors.orange,
], fontSize: 42,
) fontFamily: 'Arial',
), fontWeight: FontWeight.w900),
padding: EdgeInsets.all(10.0), )
shape: getShape(changingViewModel, BodyTypeItem.endomorph ), ]),
onPressed:() => Divider(),
{ FlatButton(
setState((){ child: Container(
selected = BodyTypeItem.endomorph; width: cWidth,
changingViewModel.customer.setBodyType(selected); child: Column(
print(selected); children: [
}), Text(
AppLocalizations.of(context)
} .translate("Endomorph"),
textWidthBasis: TextWidthBasis.longestLine,
), style: TextStyle(
Divider(), color: Colors.blue,
FlatButton( fontSize: 32,
child: Container( fontFamily: 'Arial',
width: cWidth, fontWeight: FontWeight.w900)),
child: Column( ],
children: [ )),
InkWell( padding: EdgeInsets.all(10.0),
child: Text(AppLocalizations.of(context).translate("Ectomorph"), shape: getShape(
style: TextStyle(color: Colors.blue, customerRepository, BodyTypeItem.endomorph),
fontSize: 32, fontFamily: 'Arial', onPressed: () => {
fontWeight: FontWeight.w900 ),), setState(() {
highlightColor: Colors.white, selected = BodyTypeItem.endomorph;
changeBloc.add(CustomerBodyTypeChange(bodyType: selected));
print(selected);
}),
}),
Divider(),
FlatButton(
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("Ectomorph"),
style: TextStyle(
color: Colors.blue,
fontSize: 32,
fontFamily: 'Arial',
fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
],
),
), ),
padding: EdgeInsets.all(10.0),
shape: getShape(customerRepository, BodyTypeItem.ectomorph ),
], onPressed: () => {
), setState(() {
), selected = BodyTypeItem.ectomorph;
padding: EdgeInsets.all(10.0), changeBloc.add(CustomerBodyTypeChange(bodyType: selected));
shape: getShape(changingViewModel, BodyTypeItem.ectomorph ), print(selected);
}),
onPressed:() => }),
{ Divider(),
setState((){ FlatButton(
selected = BodyTypeItem.ectomorph; child: Container(
changingViewModel.customer.setBodyType(selected); width: cWidth,
print(selected); child: Column(
}), children: [
InkWell(
} child: Text(
), AppLocalizations.of(context)
Divider(), .translate("Mesomorph"),
FlatButton( style: TextStyle(
child: Container( color: Colors.blue,
width: cWidth, fontSize: 32,
child: Column( fontFamily: 'Arial',
children: [ fontWeight: FontWeight.w900),
InkWell( ),
child: Text(AppLocalizations.of(context).translate("Mesomorph"), highlightColor: Colors.white,
style: TextStyle(color: Colors.blue, ),
fontSize: 32, fontFamily: 'Arial', ],
fontWeight: FontWeight.w900 ),), ),
highlightColor: Colors.white,
), ),
padding: EdgeInsets.all(10.0),
shape: getShape(customerRepository, BodyTypeItem.mesomorph ),
onPressed: () => {
setState(() {
selected = BodyTypeItem.mesomorph;
changeBloc.add(CustomerBodyTypeChange(bodyType: selected));
], print(selected);
), }),
), }),
padding: EdgeInsets.all(10.0), Divider(),
shape: getShape(changingViewModel, BodyTypeItem.mesomorph ), RaisedButton(
onPressed:() => color: Colors.orange,
{ textColor: Colors.white,
setState((){ child: InkWell(
selected = BodyTypeItem.mesomorph; child: Text(
changingViewModel.customer.setBodyType(selected); AppLocalizations.of(context).translate("Next"))),
print(selected); onPressed: () => {
}), changeBloc.add(CustomerSave()),
Navigator.of(context).pop(),
} Navigator.of(context).pushNamed("customerWelcomePage", arguments: customerRepository)
), },
)
Divider(), ],
RaisedButton( );
})),
color: Colors.orange, ));
textColor: Colors.white,
child: InkWell(
child: Text(AppLocalizations.of(context).translate("Next"))),
onPressed: () => {
changingViewModel.saveCustomer(),
Navigator.of(context).pop(),
Navigator.of(context).pushNamed("customerWelcomePage", arguments: changingViewModel)
},
)
],
),
),
);
} }
dynamic getShape( CustomerChangingViewModel changingViewModel, String fitnessLevel ) { dynamic getShape(CustomerRepository customerRepository, String fitnessLevel) {
String selected = changingViewModel.customer.bodyType; String selected = customerRepository.bodyType;
dynamic returnCode = ( selected == fitnessLevel ) ? dynamic returnCode = (selected == fitnessLevel)
RoundedRectangleBorder( ? RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.orange), side: BorderSide(width: 4, color: Colors.orange),
) )
: : RoundedRectangleBorder(
RoundedRectangleBorder( side: BorderSide(width: 1, color: Colors.blue),
side: BorderSide(width: 1, color: Colors.blue), );
);
//return //return
return returnCode; return returnCode;
} }

View File

@ -1,11 +1,13 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class CustomerFitnessPage extends StatefulWidget{ class CustomerFitnessPage extends StatefulWidget {
_CustomerFitnessPageState _state; _CustomerFitnessPageState _state;
_CustomerFitnessPageState createState() { _CustomerFitnessPageState createState() {
@ -29,15 +31,15 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double cWidth = MediaQuery.of(context).size.width*0.75; final double cWidth = MediaQuery.of(context).size.width * 0.75;
final CustomerChangingViewModel changingViewModel = ModalRoute.of(context).settings.arguments; final CustomerRepository customerRepository =
selected = changingViewModel.customer.fitnessLevel; ModalRoute.of(context).settings.arguments;
selected = customerRepository.customer.fitnessLevel;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Image.asset( Image.asset(
'asset/image/WT_long_logo.png', 'asset/image/WT_long_logo.png',
fit: BoxFit.cover, fit: BoxFit.cover,
@ -46,206 +48,243 @@ class _CustomerFitnessPageState extends State<CustomerFitnessPage> {
], ],
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
), ),
body: SingleChildScrollView( body: BlocProvider(
scrollDirection: Axis.vertical, create: (context) =>
child: Container( CustomerChangeBloc(customerRepository: customerRepository),
child: Builder(builder: (context) {
// ignore: close_sinks
CustomerChangeBloc changeBloc =
BlocProvider.of<CustomerChangeBloc>(context);
padding: EdgeInsets.only(bottom: 200), return SingleChildScrollView(
decoration: BoxDecoration( scrollDirection: Axis.vertical,
image: DecorationImage( child: Container(
image: AssetImage('asset/image/WT_light_background.png'), padding: EdgeInsets.only(bottom: 200),
fit: BoxFit.cover, decoration: BoxDecoration(
alignment: Alignment.center, image: DecorationImage(
), image: AssetImage('asset/image/WT_light_background.png'),
), fit: BoxFit.cover,
child: Column( alignment: Alignment.center,
mainAxisAlignment: MainAxisAlignment.center, ),
children: [
Divider(),
Wrap(
//runAlignment: WrapAlignment.center,
alignment: WrapAlignment.center,
children: [
Text(
AppLocalizations.of(context).translate("Your Fitness State"),
textAlign: TextAlign.center,
style: TextStyle(color: Colors.orange,
fontSize: 42, fontFamily: 'Arial',
fontWeight: FontWeight.w900 ),)
]
), ),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
Divider(), children: [
FlatButton( Divider(),
child: Container( Wrap(
width: cWidth, //runAlignment: WrapAlignment.center,
child: Column( alignment: WrapAlignment.center,
children: [ children: [
Text(AppLocalizations.of(context).translate("Beginner"), Text(
textWidthBasis: TextWidthBasis.longestLine, AppLocalizations.of(context)
style: TextStyle(color: Colors.blue, .translate("Your Fitness State"),
fontSize: 32, fontFamily: 'Arial', textAlign: TextAlign.center,
fontWeight: FontWeight.w900 )), style: TextStyle(
Text(AppLocalizations.of(context).translate("I am beginner"), color: Colors.orange,
style: TextStyle(color: Colors.black, fontSize: 42,
fontSize: 20, fontFamily: 'Arial', fontFamily: 'Arial',
fontWeight: FontWeight.w100 ),), fontWeight: FontWeight.w900),
], )
) ]),
), Divider(),
padding: EdgeInsets.all(10.0), FlatButton(
shape: getShape(changingViewModel, FitnessItem.beginner ), child: Container(
onPressed:() => width: cWidth,
{ child: Column(
setState((){ children: [
selected = FitnessItem.beginner; Text(
changingViewModel.customer.setFitnessLevel(selected); AppLocalizations.of(context)
print(selected); .translate("Beginner"),
}), textWidthBasis:
TextWidthBasis.longestLine,
} style: TextStyle(
color: Colors.blue,
), fontSize: 32,
Divider(), fontFamily: 'Arial',
FlatButton( fontWeight: FontWeight.w900)),
child: Container( Text(
width: cWidth, AppLocalizations.of(context)
child: Column( .translate("I am beginner"),
children: [ style: TextStyle(
InkWell( color: Colors.black,
child: Text(AppLocalizations.of(context).translate("Intermediate"), fontSize: 20,
style: TextStyle(color: Colors.blue, fontFamily: 'Arial',
fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w100),
fontWeight: FontWeight.w900 ),), ),
highlightColor: Colors.white, ],
)),
padding: EdgeInsets.all(10.0),
shape: getShape(
customerRepository, FitnessItem.beginner),
onPressed: () => {
setState(() {
selected = FitnessItem.beginner;
changeBloc.add(CustomerFitnessChange(fitness: selected));
print(selected);
}),
}),
Divider(),
FlatButton(
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("Intermediate"),
style: TextStyle(
color: Colors.blue,
fontSize: 32,
fontFamily: 'Arial',
fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("I am intermediate"),
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontFamily: 'Arial',
fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
), ),
InkWell( ),
child: Text(AppLocalizations.of(context).translate("I am intermediate"), padding: EdgeInsets.all(10.0),
style: TextStyle(color: Colors.black, shape: getShape(
fontSize: 20, fontFamily: 'Arial', customerRepository, FitnessItem.intermediate),
fontWeight: FontWeight.w100 ),), onPressed: () => {
highlightColor: Colors.white, setState(() {
selected = FitnessItem.intermediate;
changeBloc.add(CustomerFitnessChange(fitness: selected));
print(selected);
}),
}),
Divider(),
FlatButton(
child: Container(
width: cWidth,
child: Column(
children: [
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("Advanced"),
style: TextStyle(
color: Colors.blue,
fontSize: 32,
fontFamily: 'Arial',
fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("I am advanced"),
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontFamily: 'Arial',
fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
), ),
], ),
), padding: EdgeInsets.all(10.0),
), shape: getShape(
padding: EdgeInsets.all(10.0), customerRepository, FitnessItem.advanced),
shape: getShape(changingViewModel, FitnessItem.intermediate ), onPressed: () => {
setState(() {
onPressed:() => selected = FitnessItem.advanced;
{ changeBloc.add(CustomerFitnessChange(fitness: selected));
setState((){ print(selected);
selected = FitnessItem.intermediate; }),
changingViewModel.customer.setFitnessLevel(selected); }),
print(selected); Divider(),
}), FlatButton(
child: Container(
} width: cWidth,
), child: Column(
Divider(), children: [
FlatButton( InkWell(
child: Container( child: Text(
width: cWidth, AppLocalizations.of(context)
child: Column( .translate("Professional"),
children: [ style: TextStyle(
InkWell( color: Colors.blue,
child: Text(AppLocalizations.of(context).translate("Advanced"), fontSize: 32,
style: TextStyle(color: Colors.blue, fontFamily: 'Arial',
fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900),
fontWeight: FontWeight.w900 ),), ),
highlightColor: Colors.white, highlightColor: Colors.white,
),
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("I am professional"),
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontFamily: 'Arial',
fontWeight: FontWeight.w100),
),
highlightColor: Colors.white,
),
],
), ),
InkWell( ),
child: Text(AppLocalizations.of(context).translate("I am advanced"), padding: EdgeInsets.all(10.0),
style: TextStyle(color: Colors.black, shape: getShape(
fontSize: 20, fontFamily: 'Arial', customerRepository, FitnessItem.professional),
fontWeight: FontWeight.w100 ),), onPressed: () => {
highlightColor: Colors.white, setState(() {
), selected = FitnessItem.professional;
], changeBloc.add(CustomerFitnessChange(fitness: selected));
), print(selected);
), }),
padding: EdgeInsets.all(10.0), }),
shape: getShape(changingViewModel, FitnessItem.advanced ), Divider(),
onPressed:() => RaisedButton(
{ color: Colors.orange,
setState((){ textColor: Colors.white,
selected = FitnessItem.advanced; child: InkWell(
changingViewModel.customer.setFitnessLevel(selected); child: Text(AppLocalizations.of(context)
print(selected); .translate("Next"))),
}), onPressed: () => {
changeBloc.add(CustomerSave()),
} Navigator.of(context).pop(),
), Navigator.of(context).pushNamed(
Divider(), "customerBodyTypePage",
FlatButton( arguments: customerRepository)
child: Container( },
width: cWidth, )
child: Column( ],
children: [ ),
InkWell( ),
child: Text(AppLocalizations.of(context).translate("Professional"), );
style: TextStyle(color: Colors.blue, })));
fontSize: 32, fontFamily: 'Arial',
fontWeight: FontWeight.w900 ),),
highlightColor: Colors.white,
),
InkWell(
child: Text(AppLocalizations.of(context).translate("I am professional"),
style: TextStyle(color: Colors.black,
fontSize: 20, fontFamily: 'Arial',
fontWeight: FontWeight.w100 ),),
highlightColor: Colors.white,
),
],
),
),
padding: EdgeInsets.all(10.0),
shape: getShape(changingViewModel, FitnessItem.professional ),
onPressed:() =>
{
setState((){
selected = FitnessItem.professional;
changingViewModel.customer.setFitnessLevel(selected);
print(selected);
}),
}
),
Divider(),
RaisedButton(
color: Colors.orange,
textColor: Colors.white,
child: InkWell(
child: Text(AppLocalizations.of(context).translate("Next"))),
onPressed: () => {
changingViewModel.saveCustomer(),
Navigator.of(context).pop(),
Navigator.of(context).pushNamed("customerBodyTypePage", arguments: changingViewModel)
},
)
],
),
),
)
);
} }
dynamic getShape( CustomerChangingViewModel changingViewModel, String fitnessLevel ) { dynamic getShape(CustomerRepository customerRepository, String fitnessLevel) {
String selected = changingViewModel.customer.fitnessLevel; String selected = customerRepository.fitnessLevel;
dynamic returnCode = ( selected == fitnessLevel ) ? dynamic returnCode = (selected == fitnessLevel)
RoundedRectangleBorder( ? RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.orange), side: BorderSide(width: 4, color: Colors.orange),
) )
: : RoundedRectangleBorder(
RoundedRectangleBorder( side: BorderSide(width: 1, color: Colors.blue),
side: BorderSide(width: 1, color: Colors.blue), );
);
//return //return
return returnCode; return returnCode;
} }
} }

View File

@ -1,41 +1,37 @@
import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
// ignore: must_be_immutable class GoalsItem {
class CustomerGoalPage extends StatefulWidget{
_CustomerGoalPageState _state;
_CustomerGoalPageState createState() {
_state = _CustomerGoalPageState();
return _state;
}
}
class GoalsItem{
static String muscle = "gain_muscle"; static String muscle = "gain_muscle";
static String weight = "weight_loss"; static String weight = "weight_loss";
} }
class _CustomerGoalPageState extends State<CustomerGoalPage> { // ignore: must_be_immutable
String selected; class CustomerGoalPage extends StatefulWidget {
initState() { @override
super.initState(); State<StatefulWidget> createState() => _CustomerGoalPage();
} }
class _CustomerGoalPage extends State<CustomerGoalPage> {
String selected;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double cWidth = MediaQuery.of(context).size.width*0.75; final double cWidth = MediaQuery.of(context).size.width * 0.75;
final CustomerChangingViewModel changingViewModel = ModalRoute.of(context).settings.arguments; final CustomerRepository customerRepository =
selected = changingViewModel.customer.goal; ModalRoute.of(context).settings.arguments;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Image.asset( Image.asset(
'asset/image/WT_long_logo.png', 'asset/image/WT_long_logo.png',
fit: BoxFit.cover, fit: BoxFit.cover,
@ -44,116 +40,138 @@ class _CustomerGoalPageState extends State<CustomerGoalPage> {
], ],
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
), ),
body: Container( body: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'), image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.center,
alignment: Alignment.center,
),
), ),
height: double.infinity,
width: double.infinity,
child: SingleChildScrollView(
child: Center(
child: Column(
children: [
Divider(),
InkWell(
child: Text(AppLocalizations.of(context).translate("Set Your Goals"),
style: TextStyle(color: Colors.orange,
fontSize: 50, fontFamily: 'Arial',
fontWeight: FontWeight.w900 ),),
highlightColor: Colors.white,
),
Stack(
alignment: Alignment.bottomLeft,
overflow: Overflow.visible,
children: [
FlatButton(
child: Image.asset("asset/image/WT_gain_muscle.png", height: 180,),
padding: EdgeInsets.all(0.0),
shape: getShape(changingViewModel, GoalsItem.muscle ),
onPressed:() =>
{
print("gain muscle"),
setState((){
selected = GoalsItem.muscle;
changingViewModel.customer.setGoal(GoalsItem.muscle);
}),
}
),
InkWell(
child: Text(AppLocalizations.of(context).translate("Gain Muscle"),
style: TextStyle(color: Colors.white,
fontSize: 32, fontFamily: 'Arial',
fontWeight: FontWeight.w900 ),),
highlightColor: Colors.white,
)
]
),
Divider(),
Stack(
alignment: Alignment.bottomLeft,
overflow: Overflow.visible,
children: [
FlatButton(
child: Image.asset("asset/image/WT_weight_loss.png", height: 180,),
padding: EdgeInsets.all(0.0),
shape: getShape(changingViewModel, GoalsItem.weight ),
onPressed:() =>
{
print("weight_loss"),
setState((){
selected = GoalsItem.weight;
changingViewModel.customer.setGoal(GoalsItem.weight);
}),
}
),
InkWell(
child: Text(AppLocalizations.of(context).translate("Loose Weight"),
style: TextStyle(color: Colors.white,
fontSize: 32, fontFamily: 'Arial',
fontWeight: FontWeight.w900 ),),
highlightColor: Colors.white,
)
]
),
Divider(),
RaisedButton(
color: Colors.orange,
textColor: Colors.white,
child: InkWell(
child: Text(AppLocalizations.of(context).translate("Next"))),
onPressed: () => {
changingViewModel.saveCustomer(),
Navigator.of(context).pop(),
Navigator.of(context).pushNamed("customerFitnessPage", arguments: changingViewModel)
},
)
],
),
)
)
), ),
); height: double.infinity,
width: double.infinity,
child: BlocProvider(
create: (context) =>
CustomerChangeBloc(customerRepository: customerRepository),
child: Builder(builder: (context) {
CustomerChangeBloc changeBloc =
BlocProvider.of<CustomerChangeBloc>(context);
return SingleChildScrollView(
child: Center(
child: Column(
children: [
Divider(),
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("Set Your Goals"),
style: TextStyle(
color: Colors.orange,
fontSize: 50,
fontFamily: 'Arial',
fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
),
Stack(
alignment: Alignment.bottomLeft,
overflow: Overflow.visible,
children: [
FlatButton(
child: Image.asset(
"asset/image/WT_gain_muscle.png",
height: 180,
),
padding: EdgeInsets.all(0.0),
shape: getShape(changeBloc, GoalsItem.muscle),
onPressed: () => {
print("gain muscle"),
setState((){
selected = GoalsItem.muscle;
changeBloc.add(CustomerGoalChange(goal: GoalsItem.muscle));
}),
}),
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("Gain Muscle"),
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontFamily: 'Arial',
fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
)
]),
Divider(),
Stack(
alignment: Alignment.bottomLeft,
overflow: Overflow.visible,
children: [
FlatButton(
child: Image.asset(
"asset/image/WT_weight_loss.png",
height: 180,
),
padding: EdgeInsets.all(0.0),
shape: getShape(changeBloc, GoalsItem.weight),
onPressed: () => {
print("weight_loss"),
setState((){
selected = GoalsItem.muscle;
changeBloc.add(CustomerGoalChange(goal: GoalsItem.weight));
}),
}),
InkWell(
child: Text(
AppLocalizations.of(context)
.translate("Loose Weight"),
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontFamily: 'Arial',
fontWeight: FontWeight.w900),
),
highlightColor: Colors.white,
)
]),
Divider(),
RaisedButton(
color: Colors.orange,
textColor: Colors.white,
child: InkWell(
child: Text(
AppLocalizations.of(context).translate("Next"))),
onPressed: () => {
//changingViewModel.saveCustomer(),
changeBloc.add(CustomerSave()),
Navigator.of(context).pop(),
Navigator.of(context).pushNamed("customerFitnessPage",
arguments: changeBloc.customerRepository)
},
)
],
),
));
}),
),
));
} }
dynamic getShape( CustomerChangingViewModel changingViewModel, String goal ) { dynamic getShape(CustomerChangeBloc customerBloc, String goal) {
String selectedGoal = changingViewModel.customer.goal; String selectedGoal = customerBloc.customerRepository.goal;
dynamic returnCode = ( selectedGoal == goal ) ? dynamic returnCode = (selectedGoal == goal)
RoundedRectangleBorder( ? RoundedRectangleBorder(
side: BorderSide(width: 4, color: Colors.red), side: BorderSide(width: 4, color: Colors.red),
) )
: null; : null;
//return //return
return returnCode; return returnCode;
} }
} }

View File

@ -1,93 +0,0 @@
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/customer_view_model.dart';
import 'package:aitrainer_app/widgets/nav_drawer.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:aitrainer_app/widgets/customer_list_widget.dart';
class CustomerListPage extends StatefulWidget{
_CustomerListPageState createState() => _CustomerListPageState();
}
class _CustomerListPageState extends State<CustomerListPage> {
//final TextEditingController _controller = TextEditingController();
Future<List<CustomerViewModel>> _customers;
final _customerViewModel = CustomerChangingViewModel(null);
@override
void initState() {
super.initState();
_customers = _customerViewModel.getCustomers();
}
@override
Widget build(BuildContext context) {
//final customerViewModel = CustomerChangingViewModel(null);
return Scaffold(
drawer: NavDrawer(),
appBar: AppBar(
title: Text("Real customers")
),
body: Center(
child: FutureBuilder<List<CustomerViewModel>>(
future: _customers,
builder: (context, snapshot) {
if (snapshot.hasData) {
return CustomerListWidget(customers: _customerViewModel.customerList);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
/* body: Container(
padding: EdgeInsets.all(10),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(10)
),
child: TextField(
controller: _controller,
onSubmitted: (value) {
if(value.isNotEmpty) {
customerViewModel.getCustomers();
_controller.clear();
}
},
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
hintText: "Search",
hintStyle: TextStyle(color: Colors.white),
border: InputBorder.none
),
),
),
Expanded(
child: CustomerListWidget(customers: customerViewModel.customers)),
]),
), */
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pushNamed(
context,
'customerNewPage',
),
child: Icon(Icons.add,),
mini: true,
)
);
}
}

View File

@ -1,278 +1,302 @@
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/bloc/customer_change_form_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import '../library_keys.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class CustomerModifyPage extends StatefulWidget{ class CustomerModifyPage extends StatelessWidget{
_CustomerModifyPageState _state;
_CustomerModifyPageState createState() {
_state = _CustomerModifyPageState();
return _state;
}
}
class GenderItem {
GenderItem(this.dbValue,this.name);
final String dbValue;
String name;
}
class _CustomerModifyPageState extends State<CustomerModifyPage> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
GenderItem selectedGender;
List<GenderItem> genders;
@override
void initState() {
super.initState();
genders = [
GenderItem("m", "Man"),
GenderItem("w", "Woman"),
];
selectedGender = genders[0];
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
//final CustomerViewModel model = CustomerViewModel(); // ignore: close_sinks
//model.customer = Auth().userLoggedIn; final accountBloc = BlocProvider.of<AccountBloc>(context);
//final CustomerChangingViewModel customerChangeModel =
// CustomerChangingViewModel(model);
CustomerChangingViewModel customerChangingViewModel = Provider.of<CustomerChangingViewModel>(context, listen: false);
customerChangingViewModel.customer.customer.sex = selectedGender.dbValue;
// we cannot initialize the translations in the initState // we cannot initialize the translations in the initState
genders.forEach((GenderItem element) { /* genders.forEach((GenderItem element) {
if ( element.dbValue == "m") { if (element.dbValue == "m") {
element.name = AppLocalizations.of(context).translate("Man"); element.name = AppLocalizations.of(context).translate("Man");
} }
if ( element.dbValue == "w") { if (element.dbValue == "w") {
element.name = AppLocalizations.of(context).translate("Woman"); element.name = AppLocalizations.of(context).translate("Woman");
} }
}); });
*/
return BlocProvider(
create: (context) =>
CustomerChangeFormBloc(customerRepository: accountBloc.customerRepository),
child: Builder(builder: (context) {
// ignore: close_sinks
final customerBloc = BlocProvider.of<CustomerChangeFormBloc>(context);
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: true, resizeToAvoidBottomInset: true,
appBar: AppBar( appBar: AppBar(
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text("Profil"), Text("Profil"),
Image.asset( Image.asset(
'asset/image/WT_long_logo.png', 'asset/image/WT_long_logo.png',
fit: BoxFit.cover, fit: BoxFit.cover,
height: 65.0, height: 65.0,
), ),
], ],
),
//title: Text(AppLocalizations.of(context).translate('Settings')),
backgroundColor: Colors.transparent,
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.cover,
alignment: Alignment.center,
), ),
//title: Text(AppLocalizations.of(context).translate('Settings')),
backgroundColor: Colors.transparent,
), ),
child: Form( body: Container(
key: _formKey, decoration: BoxDecoration(
child: SingleChildScrollView( image: DecorationImage(
scrollDirection: Axis.vertical, image: AssetImage('asset/image/WT_light_background.png'),
padding: EdgeInsets.only(top: 40, left: 25, right: 45, bottom:100), fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: Form(
key: _formKey,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(
top: 40, left: 25, right: 45, bottom: 100),
child: Container( child: Container(
alignment: Alignment.center, alignment: Alignment.center,
child: Column( child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded( Expanded(
child: TextFormField( child:
style: TextStyle(fontSize: 12), TextFieldBlocBuilder(
decoration: InputDecoration( key: LibraryKeys.loginEmailField,
fillColor: Colors.white24, style: TextStyle(fontSize: 12),
filled: true, textFieldBloc: customerBloc.emailField,
labelText: AppLocalizations.of(context).translate('Email'), decoration: InputDecoration(
), fillColor: Colors.white24,
initialValue: customerChangingViewModel.customer.customer.email, filled: true,
onFieldSubmitted: (input) => customerChangingViewModel.customer.setEmail(input) labelText: AppLocalizations.of(context)
) .translate('Email'),
) ),
], ),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded( /* TextFormField(
child: TextFormField( style: TextStyle(fontSize: 12),
style: TextStyle(fontSize: 12), decoration: InputDecoration(
obscureText: true, fillColor: Colors.white24,
decoration: InputDecoration( filled: true,
fillColor: Colors.white24, labelText: AppLocalizations.of(context)
filled: true, .translate('Email'),
labelText: AppLocalizations.of(context).translate('Password (Leave empty if you don\'t want to change)' ), ), */
// initialValue: customerChangingViewModel.customer
// .customer.email,
// onFieldSubmitted: (input) =>
// customerChangingViewModel.customer.setEmail(
// input)
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child:
TextFieldBlocBuilder(
style: TextStyle(fontSize: 12),
textFieldBloc: customerBloc.passwordField,
suffixButton: SuffixButton.obscureText,
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate('Password (Leave empty if no change)'),
),
), ),
initialValue: customerChangingViewModel.customer.customer.password, /*TextFormField(
onFieldSubmitted: (input) => customerChangingViewModel.customer.setPassword(input) style: TextStyle(fontSize: 12),
obscureText: true,
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate(
'Password (Leave empty if you don\'t want to change)'),
),
//initialValue: customerChangingViewModel.customer.customer.password,
// onFieldSubmitted: (input) => customerChangingViewModel.customer.setPassword(input)
)*/
) )
) ],
], ),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded( Row(
child: TextFormField( mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle(fontSize: 12), children: [
decoration: InputDecoration(
fillColor: Colors.white24, Expanded(
filled: true, child: TextFieldBlocBuilder(
labelText: AppLocalizations.of(context).translate('Name'), style: TextStyle(fontSize: 12),
textFieldBloc: customerBloc.nameField,
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate('Name'),
),
), ),
initialValue: customerChangingViewModel.customer.customer.name,
onFieldSubmitted: (input) => customerChangingViewModel.customer.setName(input)
) )
) ],
], ),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded( Row(
child: TextFormField( mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle(fontSize: 12), children: [
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context).translate('First Name'), Expanded(
child: TextFieldBlocBuilder(
style: TextStyle(fontSize: 12),
textFieldBloc: customerBloc.firstNameField,
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate('First Name'),
),
), ),
keyboardType: TextInputType.emailAddress,
initialValue: customerChangingViewModel.customer.customer.firstname,
onFieldSubmitted: (input) => customerChangingViewModel.customer.setFirstName(input)
) )
) ],
], ),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded( Row(
child: TextFormField( mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle(fontSize: 12), children: [
decoration: InputDecoration(
fillColor: Colors.white24, Expanded(
filled: true, child: TextFieldBlocBuilder(
labelText: AppLocalizations.of(context).translate('Birth Year'), style: TextStyle(fontSize: 12),
), textFieldBloc: customerBloc.birthYearField,
keyboardType: TextInputType.number, inputFormatters: [
inputFormatters: <TextInputFormatter>[ FilteringTextInputFormatter(RegExp(r"[\d]"), allow: true)
WhitelistingTextInputFormatter.digitsOnly
], ],
initialValue: customerChangingViewModel.customer.customer.birthYear.toString(), decoration: InputDecoration(
onFieldSubmitted: (input) => customerChangingViewModel.customer.setBirthYear(int.parse(input)) fillColor: Colors.white24,
) filled: true,
) labelText: AppLocalizations.of(context)
], .translate('Birth Year'),
), ),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextFormField(
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context).translate('Weight'),
), ),
inputFormatters: <TextInputFormatter>[
WhitelistingTextInputFormatter.digitsOnly
],
initialValue: customerChangingViewModel.customer.customer.weight.toString(),
keyboardType: TextInputType.number,
onFieldSubmitted: (input) => customerChangingViewModel.customer.setWeight(int.parse(input)),
) )
) ],
], ),
), Row(
Divider(), mainAxisAlignment: MainAxisAlignment.spaceBetween,
Row( children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded( Expanded(
child: DropdownButtonHideUnderline( child: TextFieldBlocBuilder(
style: TextStyle(fontSize: 12),
textFieldBloc: customerBloc.weightField,
inputFormatters: [
FilteringTextInputFormatter(RegExp(r"[\d]"), allow: true)
],
decoration: InputDecoration(
fillColor: Colors.white24,
filled: true,
labelText: AppLocalizations.of(context)
.translate('Weight'),
),
),
)
],
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: DropdownFieldBlocBuilder(
selectFieldBloc: customerBloc.genderField,
itemBuilder: (context, item) => item,
decoration: InputDecoration(
labelText: AppLocalizations.of(context).translate(
'Select a gender'),
)
),
)
/* child: DropdownButtonHideUnderline(
child: DropdownButton<GenderItem>( child: DropdownButton<GenderItem>(
hint: Text(AppLocalizations.of(context).translate('Select a gender')), hint: Text(
style: TextStyle(fontSize: 12, color: Colors.black), AppLocalizations.of(context).translate(
'Select a gender')),
style: TextStyle(
fontSize: 12, color: Colors.black),
focusColor: Colors.white24, focusColor: Colors.white24,
value: selectedGender, // value: selectedGender,
items: genders.map((GenderItem gender){ items: genders.map((GenderItem gender) {
return DropdownMenuItem<GenderItem>( return DropdownMenuItem<GenderItem>(
value: gender, value: gender,
child: Text(gender.name) child: Text(gender.name)
); );
}).toList(), }).toList(),
onChanged:(GenderItem gender) => { onChanged: (GenderItem gender) => {
setState(() { // setState(() {
selectedGender = gender; // selectedGender = gender;
customerChangingViewModel.customer.setSex(gender.dbValue); // customerChangingViewModel.customer.setSex(gender.dbValue);
print ("Gender " + gender.name); print ("Gender " + gender.name);
}) //})
//model.customer.sex = //model.customer.sex =
}, },
)
) */
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: RaisedButton(
color: Colors.orange,
textColor: Colors.white,
child: InkWell(
child: Text(
AppLocalizations.of(context).translate(
"Next"))),
onPressed: () =>
{
customerBloc.add(SubmitFormBloc()),
Navigator.of(context).pushNamed("customerGoalPage", arguments: customerBloc.customerRepository)
},
) )
) )
) ],
),
], ],
), ),
Row( ),
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: RaisedButton(
color: Colors.orange,
textColor: Colors.white,
child: InkWell(
child: Text(AppLocalizations.of(context).translate("Next"))),
onPressed: () => {
customerChangingViewModel.saveCustomer(),
Navigator.of(context).pushNamed("customerGoalPage", arguments: customerChangingViewModel)
},
)
)
],
),
],
), ),
), )
), )
) );
) })
); );
} }
} }

View File

@ -1,153 +1,156 @@
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/bloc/exercise_form_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart'; import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:intl/intl.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart';
//import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
class ExerciseNewPage extends StatefulWidget{ class ExerciseNewPage extends StatefulWidget{
_ExerciseNewPageState createState() => _ExerciseNewPageState(); _ExerciseNewPageState createState() => _ExerciseNewPageState();
} }
class _ExerciseNewPageState extends State { class _ExerciseNewPageState extends State<ExerciseNewPage> {
final List excluded = [43,44];
final _formKey = GlobalKey<FormState>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ExerciseType exerciseType = ModalRoute.of(context).settings.arguments;
return Consumer<ExerciseChangingViewModel>( return BlocProvider(
builder: (context, model, child ) { create: (context) => ExerciseFormBloc(exerciseRepository: ExerciseRepository()),
String exerciseName = ""; child: Builder(builder: (context) {
String customerName = ""; // ignore: close_sinks
if ( model != null ) { final exerciseBloc = BlocProvider.of<ExerciseFormBloc>(context);
if ( model.exerciseViewModel == null ) {
model.createNewModel();
}
model.exerciseViewModel.createNew();
customerName = model != null && model.customer != null
? model.customer.name + " " +
model.customer.firstname
: "Please select a customer";
exerciseName = model != null && exerciseBloc.exerciseRepository.setExerciseType(exerciseType);
model.exerciseType != null String exerciseName = exerciseBloc.exerciseRepository.exerciseType.name;
? model.exerciseType.name
: "Please select an exercise";
}
AppLanguage appLanguage = AppLanguage(); return Form(
var date = DateTime.now();
String dateName = DateFormat(DateFormat.YEAR_MONTH_DAY, appLanguage.appLocal.toString()).format(date.toUtc()) +
" " +DateFormat(DateFormat.HOUR_MINUTE, appLanguage.appLocal.toString()).format(date.toUtc());
return Form(
key: _formKey,
autovalidate: true, autovalidate: true,
child: Scaffold( child: Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: true,
appBar: AppBar( appBar: AppBar(
leading: IconButton( backgroundColor: Colors.transparent,
icon: Icon(Icons.arrow_back, color: Colors.deepOrange), title: Row(
onPressed: () => { mainAxisAlignment: MainAxisAlignment.end,
Navigator.of(context).pop() children: <Widget>[
}, 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(),
), ),
title: Text(AppLocalizations.of(context).translate(exerciseName) + " " +
AppLocalizations.of(context).translate('Save Exercise'),
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange)),
backgroundColor: Colors.white70,
), ),
body: Container( body: Container(
decoration: BoxDecoration( width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('asset/image/WT_login.png'), image: AssetImage('asset/image/WT_light_background.png'),
fit: BoxFit.cover, fit: BoxFit.fill,
//height: double.infinity, alignment: Alignment.center,
//width: double.infinity,
alignment: Alignment.center,
), ),
), ),
child: Container( child: Container(
padding: const EdgeInsets.only (top: 65, left:25, right: 100), padding: const EdgeInsets.only (top: 25, left:25, right: 25),
child: Column( child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[ children: <Widget>[
columnQuantityUnit(model), Divider(color: Colors.transparent,),
columnQuantity(model), Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
Column( Text(AppLocalizations.of(context).translate(exerciseName) + " " +
children: [ AppLocalizations.of(context).translate('Save Exercise'),
Row( style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange)),
mainAxisAlignment: MainAxisAlignment.center, Divider(color: Colors.transparent,),
children: [ Divider(color: Colors.transparent,),
new InkWell( Divider(color: Colors.transparent,),
child: new Text(dateName, columnQuantityUnit(exerciseBloc),
style: TextStyle( fontSize: 16,color: Colors.blue)), Divider(color: Colors.transparent,),
), Divider(color: Colors.transparent,),
ButtonTheme( Divider(color: Colors.transparent,),
minWidth: 30.0,
height: 30.0,
child: FlatButton(
padding: EdgeInsets.only(bottom: 0), columnQuantity(exerciseBloc),
color: Colors.transparent, Divider(),
splashColor: Colors.black26,
child: Row(
children: [
Icon(Icons.arrow_forward_ios, color: Colors.orange,)
]),
onPressed:() { print("date change");},
)),
],
),
new InkWell(
child: new Text(AppLocalizations.of(context).translate('Exercise date and time'),
style: TextStyle( fontSize: 16)),
),
]),
RaisedButton( RaisedButton(
textColor: Colors.white, textColor: Colors.white,
color: Colors.deepOrange, color: Colors.deepOrange,
focusColor: Colors.white, focusColor: Colors.white,
onPressed: () => onPressed: () =>
{ {
if (_formKey.currentState.validate()) { confirmationDialog( exerciseBloc ),
//model = ExerciseChangingViewModel(model.exerciseViewModel),
if ( ! excluded.contains(model.exerciseType.exerciseTypeId) ) {
model.addExercise(),
},
Navigator.pop(context),
}
}, },
child: Text("Save", style: TextStyle(fontSize: 16),) child: Text(AppLocalizations.of(context).translate("Save"), style: TextStyle(fontSize: 16),)
), ),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
Divider(color: Colors.transparent,),
]), ]),
)
) )
), ),
), ),
); );
}); })
);
} }
Column columnQuantityUnit( ExerciseChangingViewModel model) { Column columnQuantityUnit( ExerciseFormBloc bloc ) {
Column column = Column(); Column column = Column();
if ( model.exerciseType != null && model.exerciseType.unitQuantity == "1") { if ( bloc.exerciseRepository.exerciseType != null &&
bloc.exerciseRepository.exerciseType.unitQuantity == "1") {
column = Column( column = Column(
children: [ children: [
TextFormField( TextFieldBlocBuilder(
textFieldBloc: bloc.unitQuantityField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30,
color: Colors.lightBlue,
fontWeight: FontWeight.bold),
inputFormatters: [
FilteringTextInputFormatter(RegExp(r"[\d.]"), allow: true)
],
onChanged: (input) => {
print ("UnitQuantity value $input"),
bloc.exerciseRepository.setUnitQuantity(
double.parse(input))
},
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the exercise done with"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unitQuantityUnit),
),
),
/*TextFormField(
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the exercise done with"),
labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unitQuantityUnit),
),
autovalidate: true, autovalidate: true,
textAlign: TextAlign.center, textAlign: TextAlign.center,
initialValue: "0", initialValue: "",
style: TextStyle(fontSize: 30, style: TextStyle(fontSize: 30,
color: Colors.lightBlue, color: Colors.lightBlue,
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),
@ -155,18 +158,19 @@ class _ExerciseNewPageState extends State {
return validateNumberInput(input); return validateNumberInput(input);
}, },
inputFormatters: [ inputFormatters: [
WhitelistingTextInputFormatter(RegExp(r"[\d.]")) FilteringTextInputFormatter(RegExp(r"[\d.]"))
//WhitelistingTextInputFormatter(RegExp(r"[\d.]"))
], ],
onChanged: (input) => { onChanged: (input) => {
print ("UnitQuantity value $input"), print ("UnitQuantity value $input"),
model.exerciseViewModel.setUnitQuantity( bloc.exerciseRepository.setUnitQuantity(
double.parse(input)) double.parse(input))
}, },
), ), */
new InkWell( new InkWell(
child: new Text(AppLocalizations.of(context).translate( child: new Text(AppLocalizations.of(context).translate(
model.exerciseType.unitQuantityUnit), bloc.exerciseRepository.exerciseType.unitQuantityUnit),
style: TextStyle(fontSize: 16)), style: TextStyle(fontSize: 16)),
), ),
@ -175,16 +179,48 @@ class _ExerciseNewPageState extends State {
return column; return column;
} }
Column columnQuantity( ExerciseChangingViewModel model) { Column columnQuantity( ExerciseFormBloc bloc ) {
Column column = Column(); Column column = Column(
column = Column(
children: [ children: [
TextFormField( TextFieldBlocBuilder(
textFieldBloc: bloc.quantityField,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 50,
color: Colors.deepOrange,
fontWeight: FontWeight.bold),
inputFormatters: [
FilteringTextInputFormatter(RegExp(r"[\d.]"), allow: true)
],
onChanged: (input) =>
{
print ("Quantity value $input"),
bloc.exerciseRepository.setQuantity(double.parse(input)),
bloc.exerciseRepository.setUnit(bloc.exerciseRepository.exerciseType.unit)
},
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
hintText: AppLocalizations.of(context).translate("The number of the exercise"),
labelStyle: TextStyle(fontSize: 16, color: Colors.deepOrange),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unit),
),
),
/* TextFormField(
decoration: InputDecoration(
fillColor: Colors.white,
filled: false,
hintText: AppLocalizations.of(context).translate("The number of the exercise"),
hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100),
labelStyle: TextStyle(fontSize: 16, color: Colors.deepOrange),
labelText: AppLocalizations.of(context).translate(
bloc.exerciseRepository.exerciseType.unit),
),
autovalidate: true, autovalidate: true,
textAlign: TextAlign.center, textAlign: TextAlign.center,
initialValue: "0", initialValue: "",
style: TextStyle(fontSize: 60, style: TextStyle(fontSize: 50,
color: Colors.deepOrange, color: Colors.deepOrange,
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),
validator: (input) { validator: (input) {
@ -195,17 +231,12 @@ class _ExerciseNewPageState extends State {
], ],
onChanged: (input) => onChanged: (input) =>
{ {
print ("Quantity value $input"), print ("Quantity value $input"),
model.exerciseViewModel.setQuantity( bloc.exerciseRepository.setQuantity(double.parse(input)),
double.parse(input)), bloc.exerciseRepository.setUnit(bloc.exerciseRepository.exerciseType.unit)
model.exerciseViewModel.setUnit(model.exerciseType.unit)
} }
), ),*/
new InkWell(
child: new Text(AppLocalizations.of(context).translate(model.exerciseType.unit),
style: TextStyle(fontSize: 16)),
),
]); ]);
@ -238,4 +269,65 @@ class _ExerciseNewPageState extends State {
return null; return null;
} }
void confirmationDialog( ExerciseFormBloc bloc ) {
print("exercise validated " + bloc.exerciseRepository.exercise.quantity.toString());
if ( bloc.exerciseRepository.exercise.quantity == null) {
return;
}
String quantity = bloc.exerciseRepository.exercise.quantity % 1 == 0?
bloc.exerciseRepository.exercise.quantity.round().toString() :
bloc.exerciseRepository.exercise.quantity.toString();
String unitQuantity = bloc.exerciseRepository.exercise.unitQuantity % 1 == 0?
bloc.exerciseRepository.exercise.unitQuantity.round().toString() :
bloc.exerciseRepository.exercise.unitQuantity.toString();
showCupertinoDialog(
useRootNavigator: true,
context: context,
barrierDismissible: false,
builder:(_) => CupertinoAlertDialog(
title: Text(AppLocalizations.of(context).translate("Do you save this exercise with these parameters?")),
content: Column(
children: [
Divider(),
Text(AppLocalizations.of(context).translate("Exercise") + ": " +
AppLocalizations.of(context).translate(bloc.exerciseRepository.exerciseType.name),
style: (TextStyle(color: Colors.blue)),),
Text(quantity + " " +
AppLocalizations.of(context).translate(bloc.exerciseRepository.exerciseType.unit),
style: (TextStyle(color: Colors.deepOrange)),),
Text(bloc.exerciseRepository.exerciseType.unitQuantity == "1" ?
AppLocalizations.of(context).translate("with") + " "
+ unitQuantity + " "
+ AppLocalizations.of(context).translate(bloc.exerciseRepository.exerciseType.unitQuantityUnit) :
"",
style: (TextStyle(color: Colors.deepOrange)),
),
]),
actions: [
FlatButton(
child: Text(AppLocalizations.of(context).translate("No")),
onPressed: () => Navigator.pop(context),
),
FlatButton(
child: Text(AppLocalizations.of(context).translate("Yes")),
onPressed: () => {
bloc.exerciseRepository.setCustomer(Auth().userLoggedIn),
bloc.exerciseRepository.addExercise(),
Navigator.pop(context),
Navigator.pop(context),
},
)
],
)
);
}
} }

View File

@ -1,59 +0,0 @@
import 'package:aitrainer_app/viewmodel/exercise_type_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_type_view_model.dart';
import 'package:aitrainer_app/widgets/exercise_type_list_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:aitrainer_app/widgets/nav_drawer.dart';
class ExerciseTypeListPage extends StatefulWidget{
_ExerciseTypeListPageState createState() => _ExerciseTypeListPageState();
}
class _ExerciseTypeListPageState extends State {
Future<List<ExerciseTypeViewModel>> _exerciseTypes;
final _exerciseTypeViewModel = ExerciseTypeChangingViewModel(null);
// Push the page and remove everything else
navigateToPage(BuildContext context, String page) {
Navigator.of(context).pushNamedAndRemoveUntil(page, (Route<dynamic> route) => false);
}
@override
void initState() {
super.initState();
_exerciseTypes = _exerciseTypeViewModel.getExerciseTypes();
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: NavDrawer(),
appBar: AppBar(
title: Text('Exercises'),
),
body: Center(
child: FutureBuilder<List<ExerciseTypeViewModel>>(
future: _exerciseTypes,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ExerciseTypeListWidget(exerciseTypes: _exerciseTypeViewModel.exerciseTypeList);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pushNamed(
context,
'exerciseTypeNewPage',
),
child: Icon(Icons.add,),
mini: true,
)
);
}
}

View File

@ -1,67 +0,0 @@
import 'package:aitrainer_app/viewmodel/exercise_type_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_type_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:aitrainer_app/widgets/nav_drawer.dart';
// ignore: must_be_immutable
class ExerciseTypeModifyPage extends StatefulWidget{
ExerciseTypeViewModel exerciseTypeViewModel;
ExerciseTypeModifyPage({this.exerciseTypeViewModel});
_ExerciseTypeModifyPageState createState() => _ExerciseTypeModifyPageState();
}
class _ExerciseTypeModifyPageState extends State<ExerciseTypeModifyPage> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
final ExerciseTypeViewModel exerciseType = ModalRoute.of(context).settings.arguments;
ExerciseTypeChangingViewModel changeModel;
return Scaffold(
drawer: NavDrawer(),
appBar: AppBar(
title: Text('Modify "' + exerciseType.name + '"' ),
),
body: Center(
child: Form(
key:_formKey,
child: Column(
children: <Widget>[
TextFormField(
initialValue: exerciseType.name,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Exercise',
),
validator: (input) => input.length == 0 ? "Please type the name of the exercise" : null,
onChanged: (input) => exerciseType.setName(input),
) ,
TextFormField(
initialValue: exerciseType.description,
minLines: 4,
maxLines: 10,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Description',
),
onChanged: (input) => exerciseType.setDescription(input),
)
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => {
if (_formKey.currentState.validate()) {
changeModel = ExerciseTypeChangingViewModel(exerciseType),
changeModel.saveExerciseType(),
Navigator.pop(context),
}
},
child: Icon(Icons.save,),
mini: true,
)
);
}
}

View File

@ -1,64 +0,0 @@
import 'package:aitrainer_app/viewmodel/exercise_type_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_type_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:aitrainer_app/widgets/nav_drawer.dart';
class ExerciseTypeNewPage extends StatefulWidget{
_ExerciseTypeNewPageState createState() => _ExerciseTypeNewPageState();
}
class _ExerciseTypeNewPageState extends State {
final ExerciseTypeViewModel exerciseType = ExerciseTypeViewModel();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
ExerciseTypeChangingViewModel model;
exerciseType.createNew();
return Scaffold(
drawer: NavDrawer(),
appBar: AppBar(
title: Text('New exercise'),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Exercise',
),
validator: (input) => input.length == 0 ? "Please type the name of the exercise" : null,
onChanged: (input) => exerciseType.setName(input),
) ,
TextFormField(
minLines: 4,
maxLines: 10,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Description',
),
onChanged: (input) => exerciseType.setDescription(input),
)
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => {
if (_formKey.currentState.validate()) {
model = ExerciseTypeChangingViewModel(exerciseType),
model.addExerciseType(),
model.addNewExercise(exerciseType),
Navigator.pop(context),
}
},
child: Icon(Icons.save,),
mini: true,
)
);
}
}

View File

@ -1,162 +1,201 @@
import 'package:aitrainer_app/bloc/login_form_bloc.dart';
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/auth.dart'; import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:aitrainer_app/view/account.dart'; import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; import 'package:aitrainer_app/widgets/splash.dart';
import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/user_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_facebook_login/flutter_facebook_login.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import '../library_keys.dart';
class LoginPage extends StatefulWidget{ class LoginPage extends StatelessWidget {
_LoginPageState createState() => _LoginPageState(); @override
Widget build(BuildContext context) {
return LoginWidget();
}
} }
class _LoginPageState extends State<LoginPage> { class LoginWidget extends StatefulWidget {
LoginWidget();
@override
State<StatefulWidget> createState() => _LoginWidget();
}
class _LoginWidget extends State<LoginWidget> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final UserViewModel user = UserViewModel();
final bool _obscureText = true;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
UserChangingViewModel model = UserChangingViewModel(user); final accountBloc = BlocProvider.of<AccountBloc>(context);
CustomerChangingViewModel customerChangingViewModel = Provider.of<CustomerChangingViewModel>(context, listen: false); return BlocProvider(
user.createNew(); create: (context) => LoginFormBloc(
Future<dynamic> customer; userRepository: UserRepository(),
final State<AccountPage> stateAccount = ModalRoute.of(context).settings.arguments; accountBloc: accountBloc
),
return Scaffold( child: Builder(builder: (context) {
key: _scaffoldKey, final loginBloc = BlocProvider.of<LoginFormBloc>(context);
body: Container( return Scaffold(
decoration: BoxDecoration( key: _scaffoldKey,
image: DecorationImage( body: FormBlocListener<LoginFormBloc, String, String>(
image: AssetImage('asset/image/WT_login.png'), onSubmitting: (context, state) {
fit: BoxFit.cover, LoadingDialog.show(context);
//height: double.infinity, },
//width: double.infinity, onSuccess: (context, state) {
alignment: Alignment.center, LoadingDialog.hide(context);
), Navigator.of(context).pushNamed('home');
), },
child: Form( onFailure: (context, state) {
key: _formKey, LoadingDialog.hide(context);
showInSnackBar(state.failureResponse);
},
child: Container( child: Container(
padding: const EdgeInsets.only (left: 25, right: 100), decoration: BoxDecoration(
child: Column( image: DecorationImage(
mainAxisAlignment: MainAxisAlignment.spaceAround, image: AssetImage('asset/image/WT_login.png'),
children: <Widget>[ fit: BoxFit.cover,
Spacer(flex: 4), //height: double.infinity,
Row( //width: double.infinity,
mainAxisAlignment: MainAxisAlignment.start, alignment: Alignment.center,
children: [ ),
new InkWell( ),
child: new Text( child: buildLoginForm(loginBloc, accountBloc),
AppLocalizations.of(context).translate(
'Login'),
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 24)),
),
],
),
TextFormField(
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
labelText: 'Email',
),
validator: (String input) {
RegExp exp = new RegExp(r"[\w._]+\@[\w._]+.[a-z]+",
caseSensitive: false,
multiLine: false,);
String ret = exp.hasMatch(input) == true ?
null :
AppLocalizations.of(context).translate(
'Please type an email address');
return ret;
},
onChanged: (input) => user.setEmail(input),
),
Spacer(flex: 1),
new TextFormField(
decoration: const InputDecoration(
filled: true,
labelText: "Password",
fillColor: Colors.white,
focusColor: Colors.white,
),
validator: (val) => val.length < 6
? AppLocalizations.of(context).translate(
'Password too short')
: null,
obscureText: _obscureText,
onChanged: (input) => user.setPassword(input),
),
Spacer(flex: 1),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ new FlatButton(
child: Image.asset('asset/image/WT_OK.png',
width: 100,
height: 100
),
onPressed: () =>
{
if (_formKey.currentState.validate()) {
model = UserChangingViewModel(user),
model.getUser().then((_) =>
{
if ( stateAccount != null ) {
stateAccount.setState(() {
print("update account");
}),
},
customerChangingViewModel.customer.setCustomer(Auth().userLoggedIn),
Navigator.pop(context),
}).catchError(( error, stackTrace )=> showInSnackBar(error)
),
}
}),
]),
Spacer(flex: 2),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new InkWell(
child: new Text(
AppLocalizations.of(context).translate(
'SignUp')),
onTap: () =>
Navigator.of(context).pushNamed(
'registration'),
),
Spacer(flex: 1),
new InkWell(
child: new Text(
AppLocalizations.of(context).translate(
'Privacy')),
onTap: () =>
Navigator.of(context).pushNamed('gdpr'),
),
Spacer(flex: 2),
]),
Spacer(flex: 2),
])
), ),
), ),
) );
}));
}
Widget buildLoginForm(LoginFormBloc formBloc, AccountBloc accountBloc) {
final cWidth = Common.mediaSizeWidth(context);
return Form(
key: _formKey,
child: Container(
padding: const EdgeInsets.only(left: 25, right: 100),
child:
ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 120.0),
children: <Widget>[
FlatButton(
child: new Image.asset(
'asset/image/login_fb.png',
width: cWidth * .85,
),
onPressed: () => {
_fbLogin(),
print("Login with FB"),
},
),
Text(AppLocalizations.of(context).translate("OR")),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
new InkWell(
child: new Text(
AppLocalizations.of(context).translate('Login'),
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 24)),
),
],
),
Divider(),
TextFieldBlocBuilder(
key: LibraryKeys.loginEmailField,
textFieldBloc: formBloc.emailField,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
labelText: 'Email',
),
),
Divider(
color: Colors.transparent,
),
TextFieldBlocBuilder(
key: LibraryKeys.loginPasswordField,
textFieldBloc: formBloc.passwordField,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
labelText: 'Password',
),
suffixButton: SuffixButton.obscureText,
),
Divider(
color: Colors.transparent,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new FlatButton(
key: LibraryKeys.loginOKButton,
child: Image.asset('asset/image/WT_OK.png',
width: 100, height: 100),
onPressed: () => {
formBloc.add(SubmitFormBloc())
}),
]),
Divider(
color: Colors.transparent,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new InkWell(
child: new Text(
AppLocalizations.of(context).translate('SignUp')),
onTap: () =>
Navigator.of(context).pushNamed('registration'),
),
Spacer(flex: 1),
new InkWell(
child: new Text(
AppLocalizations.of(context).translate('Privacy')),
onTap: () => Navigator.of(context).pushNamed('gdpr'),
),
Spacer(flex: 2),
]),
])),
); );
} }
void showInSnackBar(String error) { void showInSnackBar(String error) {
_scaffoldKey.currentState.showSnackBar( _scaffoldKey.currentState.showSnackBar(SnackBar(
SnackBar( backgroundColor: Colors.orange,
backgroundColor: Colors.orange, content: Text(error, style: TextStyle(color: Colors.white))));
content: Text( }
AppLocalizations.of(context).translate("Customer does not exist or the password is wrong") + " " + error,
style: TextStyle(color: Colors.white)) Future<Null> _fbLogin() async {
) final FacebookLogin facebookSignIn = new FacebookLogin();
); final FacebookLoginResult result = await facebookSignIn.logIn(['email']);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
final FacebookAccessToken accessToken = result.accessToken;
showInSnackBar('''
Logged in!
Token: ${accessToken.token}
User id: ${accessToken.userId}
Expires: ${accessToken.expires}
Permissions: ${accessToken.permissions}
Declined permissions: ${accessToken.declinedPermissions}
''');
break;
case FacebookLoginStatus.cancelledByUser:
showInSnackBar('Login cancelled by the user.');
break;
case FacebookLoginStatus.error:
showInSnackBar('Something went wrong with the login process.\n'
'Here\'s the error Facebook gave us: ${result.errorMessage}');
break;
}
} }
} }

View File

@ -1,161 +1,115 @@
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/util/menu_tests.dart';
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/menu_page_widget.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:collection'; import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class MenuPage extends StatefulWidget { class MenuPage extends StatelessWidget {
_MenuPageState _state;
static const routeName = '/menu_page'; static const routeName = '/menu_page';
int parent; int parent;
MenuBloc menuBloc;
MenuPage({this.parent}); MenuPage({this.parent});
@override
_MenuPageState createState() {
_state = new _MenuPageState();
return _state;
}
}
class _MenuPageState extends State<MenuPage> {
final BottomNavigator bottomNav = BottomNavigator();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final MenuTests menu = MenuTests(context); menuBloc = BlocProvider.of<MenuBloc>(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text(AppLocalizations.of(context).translate("Tests")), Text(AppLocalizations.of(context).translate("Tests")),
Image.asset( Image.asset(
'asset/image/WT_long_logo.png', 'asset/image/WT_long_logo.png',
fit: BoxFit.cover, fit: BoxFit.cover,
height: 65.0, height: 65.0,
),
],
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => {
this.setState(() {
widget.parent = 0;
},
)},
),
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.fill,
alignment: Alignment.center,
),
), ),
child: CustomScrollView( ],
scrollDirection: Axis.vertical,
slivers: <Widget>[
buildMenuColumn(widget.parent, context, menu)
]
)
), ),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () =>
{
menuBloc.add(MenuTreeUp(parent: 0))
},
),
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_menu_dark.png'),
fit: BoxFit.fill,
alignment: Alignment.center,
),
),
child: BlocConsumer<MenuBloc, MenuState>(
listener: (context, state) {
if (state is MenuError) {
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.orange,
content: Text("error", style: TextStyle(color: Colors.white))));
} else if ( state is MenuLoading ) {
return MenuPageWidget();
}
},
// ignore: missing_return
builder: (context, state) {
if ( state is MenuInitial ) {
return LoadingMenuDialog();
} else if (state is MenuReady ) {
return MenuPageWidget();
} else if ( state is MenuLoading ) {
return LoadingMenuDialog();
}
}
)
),
bottomNavigationBar: BottomNavigator(bottomNavIndex: 0)
);
}
}
class LoadingMenuDialog extends StatefulWidget {
LoadingMenuDialog({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() => _LoadingMenuDialog();
}
class _LoadingMenuDialog extends State<LoadingMenuDialog> {
@override
void initState() {
super.initState();
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance.addPostFrameCallback((_) {
BlocProvider.of<MenuBloc>(context).add(MenuCreate());
});
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: EdgeInsets.all(12.0),
child: CircularProgressIndicator(),
color: Colors.transparent,
),
),
),
); );
} }
SliverList buildMenuColumn(int parent, BuildContext context, MenuTests menu) {
LinkedHashMap tree = menu.getMenuItems();
List<Widget> _columnChildren = List();
ExerciseType exerciseType;
ExerciseChangingViewModel model = Provider.of<ExerciseChangingViewModel>(context, listen: false);
tree.forEach((treeName, value) {
WorkoutTree workoutTree = value as WorkoutTree;
if ( workoutTree.parent == parent ) {
_columnChildren.add(
Container(
padding: EdgeInsets.only(top: 16.0),
child: Center(
child: Stack(
alignment: Alignment.bottomLeft,
overflow: Overflow.visible,
children: [
FlatButton(
child: _getButtonImage(workoutTree),
padding: EdgeInsets.all(0.0),
onPressed:() =>
{
print("Hi!, Menu clicked " + workoutTree.id.toString()),
if ( workoutTree.child == false ) {
this.setState(() {
widget.parent = workoutTree.id;
},
),
} else {
exerciseType = Common.getExerciseType(workoutTree.exerciseTypeId),
model.setExerciseType(exerciseType),
model.setCustomer(Auth().userLoggedIn),
if ( Auth().userLoggedIn == null ) {
Scaffold.of(context)
. showSnackBar(
SnackBar(
backgroundColor: Colors.orange,
content: Text(
AppLocalizations.of(context).translate('Please log in'),
style: TextStyle(color: Colors.white))
))
} else {
Navigator.of(context).pushNamed('exerciseNewPage'),
}
}
}
),
InkWell(
child: Text(workoutTree.name, style: TextStyle(color: workoutTree.color, fontSize: workoutTree.fontSize, fontFamily: 'Arial', fontWeight: FontWeight.w900 ),),
highlightColor: workoutTree.color,
)]))));
}
});
//_columnChildren.add(Spacer(flex: 3));
SliverList sliverList =
SliverList(
delegate: SliverChildListDelegate(
_columnChildren
)
);
return sliverList;
}
dynamic _getButtonImage(WorkoutTree workoutTree) {
dynamic image;
if ( workoutTree.imageName.startsWith("http") ) {
image = FadeInImage.assetNetwork(
image: workoutTree.imageName,
placeholder: 'asset/image/dots.gif',
//imageScale: 0.1,
height: 180,
placeholderScale: 0.1,
);
} else {
image = Image.asset(workoutTree.imageName, height: 180,);
}
return image;
}
} }

View File

@ -1,142 +1,216 @@
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
import 'package:aitrainer_app/bloc/registration_form_bloc.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/auth.dart'; import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart'; import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:aitrainer_app/viewmodel/user_view_model.dart'; import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_facebook_login/flutter_facebook_login.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
class RegistrationPage extends StatefulWidget{ import '../library_keys.dart';
_RegistrationPageState createState() => _RegistrationPageState();
class RegistrationPage extends StatelessWidget {
final UserRepository userRepository = UserRepository();
final CustomerRepository customerRepository = CustomerRepository();
@override
Widget build(BuildContext context) {
return RegistrationWidget(
userRepository: userRepository, customerRepository: customerRepository);
}
} }
class _RegistrationPageState extends State<RegistrationPage> { class RegistrationWidget extends StatefulWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final UserRepository userRepository;
final UserViewModel user = UserViewModel(); final CustomerRepository customerRepository;
bool _obscureText = true;
RegistrationWidget({this.userRepository, this.customerRepository});
@override
State<StatefulWidget> createState() => _RegistrationWidget();
}
class _RegistrationWidget extends State<RegistrationWidget> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
UserChangingViewModel model = UserChangingViewModel(user); final cWidth = Common.mediaSizeWidth(context);
CustomerChangingViewModel customerChangingViewModel = Provider.of<CustomerChangingViewModel>(context, listen: false); // ignore: close_sinks
user.createNew(); final accountBloc = BlocProvider.of<AccountBloc>(context);
return BlocProvider(
create: (context) => RegistrationFormBloc(
userRepository: UserRepository(),
accountBloc: accountBloc),
child: Builder(builder: (context) {
// ignore: close_sinks
final registrationBloc = BlocProvider.of<RegistrationFormBloc>(context);
return Scaffold( return Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
body: Container( body: FormBlocListener<RegistrationFormBloc, String, String>(
decoration: BoxDecoration( onSubmitting: (context, state) {
image: DecorationImage( LoadingDialog.show(context);
image: AssetImage('asset/image/WT_login.png'), },
fit: BoxFit.cover, onSuccess: (context, state) {
//height: double.infinity, LoadingDialog.hide(context);
//width: double.infinity, Navigator.of(context).pushNamed('customerModifyPage');
alignment: Alignment.center, },
), onFailure: (context, state) {
), LoadingDialog.hide(context);
child: Form( showInSnackBar(state.failureResponse);
key: _formKey, },
child: Container( child: Container(
padding: const EdgeInsets.only (left:25, right: 100), decoration: BoxDecoration(
child: Column( image: DecorationImage(
mainAxisAlignment: MainAxisAlignment.spaceAround, image: AssetImage('asset/image/WT_login.png'),
children: <Widget>[ fit: BoxFit.cover,
Spacer(flex:4), //height: double.infinity,
Row( //width: double.infinity,
mainAxisAlignment: MainAxisAlignment.start, alignment: Alignment.center,
children: [ ),
new InkWell( ),
child: new Text(AppLocalizations.of(context).translate('SignUp'), child: Form(
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), key: _formKey,
), child: Container(
], padding: const EdgeInsets.only(left: 25, right: 100),
), child: ListView(
shrinkWrap: false,
padding: EdgeInsets.only(top: 120.0),
//mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//Spacer(flex:4),
TextFormField( FlatButton(
decoration: InputDecoration( child: new Image.asset(
fillColor: Colors.white, 'asset/image/login_fb.png',
filled:true, width: cWidth * .85,
labelText: 'Email', ),
), onPressed: () => {
validator: (String input) { _fbLogin(),
RegExp exp = new RegExp(r"[\w._]+\@[\w._]+.[a-z]+", print("Login with FB"),
caseSensitive: false, },
multiLine: false,);
String ret = exp.hasMatch(input) == true ?
null:
AppLocalizations.of(context).translate('Please type an email address');
return ret;
},
onChanged: (input) => user.setEmail(input),
),
Spacer(flex:1),
new TextFormField(
decoration: const InputDecoration(
filled:true,
labelText: "Password",
fillColor: Colors.white,
focusColor: Colors.white,
),
validator: (val) => val.length < 6 ? AppLocalizations.of(context).translate('Password too short') : null,
obscureText: _obscureText,
onChanged: (input) => user.setPassword(input),
),
Spacer(flex:1),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ new FlatButton(
child: Image.asset('asset/image/WT_OK.png',
width: 100,
height:100
), ),
onPressed:() => { Text(AppLocalizations.of(context).translate("OR")),
if (_formKey.currentState.validate()) { Divider(),
model = UserChangingViewModel(user), Row(
model.addUser().then((_) => mainAxisAlignment: MainAxisAlignment.start,
{ children: [
Navigator.of(context).pushNamed("customerModifyPage",), new InkWell(
customerChangingViewModel.customer.setCustomer(Auth().userLoggedIn), child: new Text(
}).catchError(( error, stackTrace )=> showInSnackBar() AppLocalizations.of(context)
.translate('SignUp'),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24)),
), ),
],
} ),
}), Divider(),
]), TextFieldBlocBuilder(
Spacer(flex:2), key: LibraryKeys.loginEmailField,
Row( textFieldBloc: registrationBloc.emailField,
mainAxisAlignment: MainAxisAlignment.spaceAround, decoration: InputDecoration(
children: <Widget>[ fillColor: Colors.white,
new InkWell( filled: true,
child: new Text(AppLocalizations.of(context).translate('Login')), labelText: 'Email',
onTap: () => Navigator.of(context).pushNamed('login'), ),
), ),
Spacer(flex:1), Divider(
new InkWell( color: Colors.transparent,
child: new Text(AppLocalizations.of(context).translate('Privacy')), ),
onTap: () => Navigator.of(context).pushNamed('gdpr'), TextFieldBlocBuilder(
), key: LibraryKeys.loginPasswordField,
Spacer(flex:2), textFieldBloc: registrationBloc.passwordField,
]), decoration: InputDecoration(
Spacer(flex:2), fillColor: Colors.white,
]) filled: true,
), labelText: 'Password',
), ),
), suffixButton: SuffixButton.obscureText,
),
); Divider(
color: Colors.transparent,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new FlatButton(
child: Image.asset(
'asset/image/WT_OK.png',
width: 100,
height: 100),
onPressed: () => {
registrationBloc.add(SubmitFormBloc())
}),
]),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: <Widget>[
new InkWell(
child: new Text(AppLocalizations.of(context)
.translate('Login')),
onTap: () => Navigator.of(context)
.pushNamed('login'),
),
Spacer(flex: 1),
new InkWell(
child: new Text(AppLocalizations.of(context)
.translate('Privacy')),
onTap: () =>
Navigator.of(context).pushNamed('gdpr'),
),
Spacer(flex: 2),
]),
//Spacer(flex:2),
])),
),
),
),
);
}));
} }
void showInSnackBar() { void showInSnackBar(String error) {
_scaffoldKey.currentState.showSnackBar( _scaffoldKey.currentState.showSnackBar(SnackBar(
SnackBar( backgroundColor: Colors.orange,
backgroundColor: Colors.orange, content: Text(
content: Text( AppLocalizations.of(context)
AppLocalizations.of(context).translate("Customer exists"), .translate("There is an error: during registration:") +
style: TextStyle(color: Colors.white)) " " +
) error,
); style: TextStyle(color: Colors.white))));
}
Future<Null> _fbLogin() async {
final FacebookLogin facebookSignIn = new FacebookLogin();
final FacebookLoginResult result = await facebookSignIn.logIn(['email']);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
final FacebookAccessToken accessToken = result.accessToken;
showInSnackBar('''
Logged in!
Token: ${accessToken.token}
User id: ${accessToken.userId}
Expires: ${accessToken.expires}
Permissions: ${accessToken.permissions}
Declined permissions: ${accessToken.declinedPermissions}
''');
break;
case FacebookLoginStatus.cancelledByUser:
showInSnackBar('Login cancelled by the user.');
break;
case FacebookLoginStatus.error:
showInSnackBar('Something went wrong with the login process.\n'
'Here\'s the error Facebook gave us: ${result.errorMessage}');
break;
}
} }
} }

View File

@ -1,30 +1,21 @@
import 'package:aitrainer_app/bloc/settings/settings_bloc.dart';
import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart';
import 'package:aitrainer_app/widgets/splash.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SettingsPage extends StatefulWidget{ class SettingsPage extends StatelessWidget{
_SettingsPageState _state;
_SettingsPageState createState() {
_state = new _SettingsPageState();
return _state;
}
}
class _SettingsPageState extends State<SettingsPage> {
final AppLanguage appLanguage = AppLanguage();
Locale _locale;
final _formKey = GlobalKey<FormState>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
BottomNavigator bottomNav = BottomNavigator(); // ignore: close_sinks
_locale = appLanguage.appLocal; SettingsBloc settingsBloc = BlocProvider.of<SettingsBloc>(context);
settingsBloc.context = context;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Row( title: Row(
@ -50,54 +41,60 @@ class _SettingsPageState extends State<SettingsPage> {
), ),
), ),
child: Form( child: Form(
key: _formKey, child: BlocConsumer<SettingsBloc, SettingsState>(
child: listener: (context, state) {
ListView( if ( state is SettingsError ) {
}
},
// ignore: missing_return
builder: (context, state) {
if ( state is SettingsLoading ) {
return LoadingDialog();
} else if ( state is SettingsReady || state is SettingsInitial) {
return ListView(
padding: EdgeInsets.only(top: 150), padding: EdgeInsets.only(top: 150),
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
leading: Icon(Icons.language), leading: Icon(Icons.language),
subtitle: Text(AppLocalizations.of(context).translate("Change App Language")), subtitle: Text(
AppLocalizations.of(context).translate(
"Change App Language")),
title: DropdownButton( title: DropdownButton(
value: _locale == Locale('en') ? AppLocalizations.of(context).translate("English") : AppLocalizations.of(context).translate("Hungarian"),
items: [AppLocalizations.of(context).translate("English"), AppLocalizations.of(context).translate("Hungarian")] value: state.props[0] == Locale('en')
.map<DropdownMenuItem<String>>((String value) { ? AppLocalizations.of(context).translate(
"English")
: AppLocalizations.of(context).translate(
"Hungarian"),
items: [AppLocalizations.of(context).translate(
"English"), AppLocalizations.of(context)
.translate("Hungarian")
]
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: value, value: value,
child: Text(value), child: Text(value),
); );
}).toList(), }).toList(),
onChanged:(String lang) => _changeLanguage(lang), onChanged: (String lang) =>
settingsBloc.add(
SettingsChangeLanguage(language: lang)
),
) )
), ),
] ]
);
} else {
return Container();
}
}
), ),
), ),
), ),
bottomNavigationBar: bottomNav.buildBottomNavigator(context, widget._state) bottomNavigationBar: BottomNavigator(bottomNavIndex: 3)
); );
} }
_changeLanguage( String lang ) {
setState(() {
switch ( lang ) {
case "English":
case "Angol":
_locale = Locale('en');
break;
case "Hungarian":
case "Magyar":
_locale = Locale('hu');
break;
}
appLanguage.changeLanguage(_locale);
AppLocalizations.of(context).setLocale(_locale);
AppLocalizations.of(context).load();
});
}
} }

View File

@ -1,37 +0,0 @@
import 'package:aitrainer_app/service/customer_service.dart';
import 'package:flutter/cupertino.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'customer_view_model.dart';
class CustomerChangingViewModel extends ChangeNotifier {
CustomerViewModel customer = CustomerViewModel();
List<CustomerViewModel> customerList = List<CustomerViewModel>();
CustomerChangingViewModel(customer) {
this.customer = customer;
}
Future<void> addCustomer() async {
this.customer = customer;
final Customer modelCustomer = customer.getCustomer();
await CustomerApi().addCustomer(modelCustomer);
}
Future<void> saveCustomer() async {
//this.customer = customer;
final Customer modelCustomer = customer.getCustomer();
await CustomerApi().saveCustomer(modelCustomer);
}
Future<List<CustomerViewModel>> getCustomers() async {
final results = await CustomerApi().getRealCustomers("");
this.customerList = results.map((item) => CustomerViewModel(customer: item)).toList();
notifyListeners();
return this.customerList;
}
addNewCustomerToList(CustomerViewModel customerViewModel) {
customerList.add(customerViewModel);
}
}

View File

@ -1,88 +0,0 @@
import 'package:aitrainer_app/model/customer.dart';
class CustomerViewModel {
Customer customer;
bool visibleDetails = false;
CustomerViewModel({this.customer});
String get name {
return this.customer.name;
}
String get firstName {
return this.customer.firstname;
}
String get sex {
return this.customer.sex == "m" ? "Man" : "Woman";
}
int get birthYear {
return this.customer.birthYear;
}
String get goal {
return this.customer.goal;
}
String get fitnessLevel {
return this.customer.fitnessLevel;
}
String get bodyType {
return this.customer.bodyType;
}
setName(String name) {
this.customer.name = name;
}
setFirstName(String firstName) {
this.customer.firstname = firstName;
}
setPassword( String password ) {
this.customer.password = password;
}
setEmail(String email) {
this.customer.email = email;
}
setSex(String sex) {
this.customer.sex = sex;
}
setWeight( int weight) {
this.customer.weight = weight;
}
setBirthYear( int birthYear ) {
this.customer.birthYear = birthYear;
}
setFitnessLevel( String level ) {
this.customer.fitnessLevel = level;
}
setGoal( String goal ) {
this.customer.goal = goal;
}
setBodyType(String bodyType) {
this.customer.bodyType = bodyType;
}
createNew() {
this.customer = Customer();
}
Customer getCustomer() {
return this.customer;
}
void setCustomer ( Customer customer ) {
this.customer = customer;
}
}

View File

@ -1,56 +0,0 @@
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/service/exercise_service.dart';
import 'package:flutter/cupertino.dart';
import 'exercise_view_model.dart';
class ExerciseChangingViewModel with ChangeNotifier {
Customer customer;
ExerciseType exerciseType;
List<ExerciseViewModel> exerciseList = List<ExerciseViewModel>();
ExerciseViewModel exerciseViewModel = ExerciseViewModel();
ExerciseChangingViewModel(exerciseViewModel) {
this.exerciseViewModel = exerciseViewModel;
}
int quantity;
setCustomer(Customer customer) {
this.customer = customer;
notifyListeners();
}
setExerciseType( ExerciseType exerciseType) {
this.exerciseType = exerciseType;
notifyListeners();
}
setQuantity(int quantity) {
this.quantity = quantity;
}
addExercise() async {
// this.exerciseViewModel = exerciseViewModel;
final Exercise modelExercise = exerciseViewModel.getExercise();
modelExercise.customerId = this.customer.customerId;
modelExercise.exerciseTypeId = this.exerciseType.exerciseTypeId;
await ExerciseApi().addExercise(modelExercise);
}
createNewModel() {
exerciseViewModel = ExerciseViewModel();
exerciseViewModel.createNew();
}
Future<List<ExerciseViewModel>> getExercisesByCustomer( int customerId ) async {
final results = await ExerciseApi().getExercisesByCustomer(customerId);
this.exerciseList = results.map((item) => ExerciseViewModel(exercise: item)).toList();
notifyListeners();
return this.exerciseList;
}
}

View File

@ -1,36 +0,0 @@
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/viewmodel/exercise_type_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart';
class ExerciseTypeChangingViewModel extends ChangeNotifier {
ExerciseTypeViewModel exerciseType = ExerciseTypeViewModel();
List<ExerciseTypeViewModel> exerciseTypeList = List<ExerciseTypeViewModel>();
ExerciseTypeChangingViewModel(exerciseType) {
this.exerciseType = exerciseType;
}
Future<void> addExerciseType() async {
this.exerciseType = exerciseType;
final ExerciseType modelExerciseType = exerciseType.getExerciseType();
await ExerciseTypeApi().addExerciseType(modelExerciseType);
}
Future<void> saveExerciseType() async {
this.exerciseType = exerciseType;
final ExerciseType modelExerciseType = exerciseType.getExerciseType();
await ExerciseTypeApi().saveExerciseType(modelExerciseType);
}
Future<List<ExerciseTypeViewModel>> getExerciseTypes() async {
final results = await ExerciseTypeApi().getExerciseTypes("");
this.exerciseTypeList = results.map((item) => ExerciseTypeViewModel( exerciseType: item) ).toList();
notifyListeners();
return this.exerciseTypeList;
}
addNewExercise(ExerciseTypeViewModel exerciseTypeViewModel) {
exerciseTypeList.add(exerciseTypeViewModel);
}
}

View File

@ -1,36 +0,0 @@
import 'package:aitrainer_app/model/exercise_type.dart';
class ExerciseTypeViewModel {
ExerciseType exerciseType;
bool visible = false;
ExerciseTypeViewModel( {this.exerciseType} );
String get name {
return this.exerciseType.name;
}
setName(String name) {
this.exerciseType.name = name;
}
String get description {
return this.exerciseType.description;
}
setDescription(String description) {
this.exerciseType.description = description;
}
int get exerciseTypeId {
return this.exerciseType.exerciseTypeId;
}
ExerciseType getExerciseType() {
return this.exerciseType;
}
createNew() {
this.exerciseType = ExerciseType();
}
}

View File

@ -1,31 +0,0 @@
import 'package:aitrainer_app/model/exercise.dart';
class ExerciseViewModel {
Exercise exercise;
ExerciseViewModel({this.exercise});
createNew() {
this.exercise = Exercise();
exercise.dateAdd = DateTime.now();
}
setQuantity(double quantity) {
this.exercise.quantity = quantity;
}
setUnitQuantity(double unitQuantity) {
this.exercise.unitQuantity = unitQuantity;
}
setUnit( String unit) {
this.exercise.unit = unit;
}
setDatetimeExercise(DateTime datetimeExercise) {
this.exercise.dateAdd = datetimeExercise;
}
Exercise getExercise() {
return this.exercise;
}
}

View File

@ -1,22 +0,0 @@
import 'package:aitrainer_app/model/user.dart';
import 'package:aitrainer_app/service/customer_service.dart';
import 'package:aitrainer_app/viewmodel/user_view_model.dart';
import 'package:flutter/cupertino.dart';
class UserChangingViewModel extends ChangeNotifier {
UserViewModel userViewModel = UserViewModel();
UserChangingViewModel(userViewModel) {
this.userViewModel = userViewModel;
}
Future<void> addUser() async {
final User modelUser = userViewModel.getUser();
await CustomerApi().addUser(modelUser);
}
Future<void> getUser() async {
final User modelUser = userViewModel.getUser();
await CustomerApi().getUser(modelUser);
}
}

View File

@ -1,23 +0,0 @@
import 'package:aitrainer_app/model/user.dart';
class UserViewModel {
User user;
UserViewModel({this.user});
setEmail(String email) {
this.user.email = email;
}
setPassword(String password) {
this.user.password = password;
}
User getUser() {
return this.user;
}
createNew() {
this.user = User();
}
}

View File

@ -1,15 +1,35 @@
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class BottomNavigator { class BottomNavigator extends StatefulWidget {
BottomNavigationBar buildBottomNavigator(BuildContext context, State state) { int bottomNavIndex = 0;
BottomNavigator({this.bottomNavIndex}) {
this.bottomNavIndex = bottomNavIndex;
}
@override
_NawDrawerWidget createState() => _NawDrawerWidget();
}
class _NawDrawerWidget extends State<BottomNavigator> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return BottomNavigationBar( return BottomNavigationBar(
currentIndex: 0, // this will be set when a new tab is tapped currentIndex: widget.bottomNavIndex, // this will be set when a new tab is tapped
backgroundColor: Colors.black12, backgroundColor: Colors.transparent,
selectedItemColor: Colors.yellow, selectedItemColor: Colors.yellow,
unselectedItemColor: Colors.lightGreen, unselectedItemColor: Colors.lightGreen,
type: BottomNavigationBarType.shifting, //type: BottomNavigationBarType.shifting,
showSelectedLabels: true, showSelectedLabels: true,
showUnselectedLabels: true,
items: [ items: [
BottomNavigationBarItem( BottomNavigationBarItem(
backgroundColor: Colors.black12, backgroundColor: Colors.black12,
@ -18,23 +38,29 @@ class BottomNavigator {
title: new Text(AppLocalizations.of(context).translate("Home")), title: new Text(AppLocalizations.of(context).translate("Home")),
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: new Icon(Icons.event, color: Colors.lightGreen), backgroundColor: Colors.black12,
icon: new Icon(Icons.confirmation_number, color: Colors.lightGreen),
activeIcon: new Icon(Icons.event, color: Colors.yellow,), activeIcon: new Icon(Icons.event, color: Colors.yellow,),
title: new Text(AppLocalizations.of(context).translate("Events")), title: new Text(AppLocalizations.of(context).translate("Events")),
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
backgroundColor: Colors.black12,
icon: Icon(Icons.person, color: Colors.lightGreen,), icon: Icon(Icons.person, color: Colors.lightGreen,),
activeIcon: new Icon(Icons.person, color: Colors.yellow,), activeIcon: new Icon(Icons.person, color: Colors.yellow,),
title: Text(AppLocalizations.of(context).translate("Account")) title: Text(AppLocalizations.of(context).translate("Account"))
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
backgroundColor: Colors.black12,
icon: Icon(Icons.settings, color: Colors.lightGreen), icon: Icon(Icons.settings, color: Colors.lightGreen),
activeIcon: new Icon(Icons.settings, color: Colors.yellow,), activeIcon: new Icon(Icons.settings, color: Colors.yellow,),
title: Text(AppLocalizations.of(context).translate("Settings")) title: Text(AppLocalizations.of(context).translate("Settings"))
) )
], ],
onTap:(index) { onTap:(index) {
// ignore: invalid_use_of_protected_member setState(() {
widget.bottomNavIndex = index;
});
switch (index) { switch (index) {
case 0: case 0:
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -59,4 +85,6 @@ class BottomNavigator {
} }
); );
} }
} }

View File

@ -1,67 +0,0 @@
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:aitrainer_app/viewmodel/customer_view_model.dart';
import 'package:provider/provider.dart';
class CustomerListWidget extends StatefulWidget {
static const routeName = '/customer_list';
final List<CustomerViewModel> customers;
CustomerListWidget({this.customers});
@override
_CustomerListWidget createState() => _CustomerListWidget();
}
class _CustomerListWidget extends State<CustomerListWidget> {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.customers.length,
itemBuilder: (context, index) {
final customer = widget.customers[index];
return ListTile(
contentPadding: EdgeInsets.all(10),
leading:Icon(Icons.accessibility),
title: Text(customer.name + " " + customer.firstName),
subtitle:
Container(
child: Visibility(
visible: customer.visibleDetails,
child: Row(
children: <Widget>[
Text(customer.birthYear.toString() + " years, " + customer.sex),
new RaisedButton(
child: new Text('Modify'),
color: Color.fromRGBO(244, 122, 22, 0.9),
onPressed: () => {
},
),
new RaisedButton(
child: new Text('Select'),
color: Colors.blueGrey,
onPressed: () => {
Provider.of<ExerciseChangingViewModel>(context, listen: false).setCustomer(customer.customer),
Navigator.pop(context)
},
),
],
),
),
),
onTap: () { setState( () {
customer.visibleDetails = customer.visibleDetails ? false : true;
});
},
);
},
);
}
}

View File

@ -1,65 +0,0 @@
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
class CustomPicker extends CommonPickerModel {
String digits(int value, int length) {
return '$value'.padLeft(length, "0");
}
CustomPicker({DateTime currentTime, LocaleType locale}) : super(locale: locale) {
this.currentTime = currentTime ?? DateTime.now();
this.setLeftIndex(this.currentTime.hour);
this.setMiddleIndex(this.currentTime.minute);
this.setRightIndex(this.currentTime.second);
}
@override
String leftStringAtIndex(int index) {
if (index >= 0 && index < 24) {
return this.digits(index, 2);
} else {
return null;
}
}
@override
String middleStringAtIndex(int index) {
if (index >= 0 && index < 60) {
return this.digits(index, 2);
} else {
return null;
}
}
@override
String rightStringAtIndex(int index) {
if (index >= 0 && index < 60) {
return this.digits(index, 2);
} else {
return null;
}
}
@override
String leftDivider() {
return "|";
}
@override
String rightDivider() {
return "|";
}
@override
List<int> layoutProportions() {
return [1, 2, 1];
}
@override
DateTime finalTime() {
return currentTime.isUtc
? DateTime.utc(currentTime.year, currentTime.month, currentTime.day,
this.currentLeftIndex(), this.currentMiddleIndex(), this.currentRightIndex())
: DateTime(currentTime.year, currentTime.month, currentTime.day, this.currentLeftIndex(),
this.currentMiddleIndex(), this.currentRightIndex());
}
}

View File

@ -1,75 +0,0 @@
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/exercise_type_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ExerciseTypeListWidget extends StatefulWidget {
final List<ExerciseTypeViewModel> exerciseTypes;
ExerciseTypeListWidget({this.exerciseTypes});
@override
_ExerciseTypeListWidgetState createState() => _ExerciseTypeListWidgetState();
}
class _ExerciseTypeListWidgetState extends State<ExerciseTypeListWidget> {
//static const routeName = '/exercise_type_list';
bool visible = false;
// Push the page and remove everything else
navigateToPage(BuildContext context, String page) {
Navigator.of(context).pushNamedAndRemoveUntil(page, (Route<dynamic> route) => false);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.exerciseTypes.length,
itemBuilder: (context, index) {
final exerciseType = widget.exerciseTypes[index];
return ListTile(
contentPadding: EdgeInsets.all(10),
leading: Icon(Icons.directions_run),
title: Text(exerciseType.name),
subtitle:
Container(
child: Visibility(
visible: exerciseType.visible,
child: Column(
children: <Widget>[
Text(exerciseType.description),
new RaisedButton(
child: new Text('Modify'),
color: Color.fromRGBO(244, 122, 22, 0.9),
onPressed: () => {
Navigator.pushNamed(
context,
'exerciseTypeModifyPage',
arguments: exerciseType
)
},
),
new RaisedButton(
child: new Text('Select'),
color: Colors.blueGrey,
onPressed: () => {
Navigator.pop(context),
Provider.of<ExerciseChangingViewModel>(context, listen: false).setExerciseType(exerciseType.exerciseType),
},
),
],
),
),
),
onTap: () { setState( () {
exerciseType.visible = true;
});
},
);
},
);
}
}

View File

@ -1,12 +1,17 @@
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/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/view/login.dart';
import 'package:aitrainer_app/view/menu_page.dart'; import 'package:aitrainer_app/view/menu_page.dart';
import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/view/registration.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'bottom_nav.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'nav_drawer.dart'; import 'loading.dart';
class AitrainerHome extends StatefulWidget { class AitrainerHome extends StatefulWidget {
_HomePageState _state; _HomePageState _state;
@ -15,10 +20,6 @@ class AitrainerHome extends StatefulWidget {
_state = new _HomePageState(); _state = new _HomePageState();
return _state; return _state;
} }
void callback() {
_state.setLangNoContext();
}
} }
class _HomePageState extends State<AitrainerHome> { class _HomePageState extends State<AitrainerHome> {
@ -28,30 +29,61 @@ class _HomePageState extends State<AitrainerHome> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
}
@override /// We require the initializers to run after the loading screen is rendered
Widget build(BuildContext context) { SchedulerBinding.instance.addPostFrameCallback((_) {
MenuPage menu = MenuPage(); runDelayedEvent();
BottomNavigator bottomNav = BottomNavigator();
return Scaffold(
key: _scaffoldKey,
drawer: NavDrawer(),
body:Container(
child: MenuPage(parent: 0),
),
bottomNavigationBar: bottomNav.buildBottomNavigator(context, widget._state)
);
}
void setLangNoContext() {
print("--- Callback ");
setState(() {
final AppLanguage appLanguage = AppLanguage();
AppLocalizations.of(context).setLocale(appLanguage.appLocal);
AppLocalizations.of(context).load();
print("--- Lang for context reloaded");
}); });
} }
Future runDelayedEvent() async {
await Future.delayed(Duration(seconds: 3), () async {
if ( context != null) {
// ignore: close_sinks
SessionBloc sessionBloc = BlocProvider.of<SessionBloc>(context);
if (sessionBloc.state != SessionReady()) {
sessionBloc.add(SessionStart());
// ignore: close_sinks
SettingsBloc settingsBloc = BlocProvider.of<SettingsBloc>(context);
settingsBloc.loadLang();
}
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Container(
child: BlocConsumer<SessionBloc, SessionState>(
listener: (context, state) {
if ( state is SessionFailure) {
}
},
builder: (context, state) {
if (state is SessionInitial) {
return LoadingScreenMain();
} else if (state is SessionLoading) {
print("loading");
return LoadingScreenMain();
} else if (state is SessionReady) {
print("ready");
if (Auth().startPage == 'login') {
return LoginPage();
} else if (Auth().startPage == 'registration') {
return RegistrationPage();
} else {
return MenuPage(parent: 0);
}
} else {
print("else");
return MenuPage(parent: 0);
}
}
),
),
//bottomNavigationBar: BottomNavigator(bottomNavIndex: 0),
);
}
} }

View File

@ -1,17 +1,7 @@
import 'package:aitrainer_app/util/message_state.dart';
import 'package:aitrainer_app/util/session.dart';
import 'package:aitrainer_app/widgets/home.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:aitrainer_app/util/loading_screen.dart';
class LoadingScreenMain extends StatefulWidget {
@override
LoadingScreenMainState createState() => LoadingScreenMainState();
}
class LoadingScreenMainState extends State<LoadingScreenMain> {
class LoadingScreenMain extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Image _backgroundImage = Image.asset('asset/image/WT01_loading_layers.png', Image _backgroundImage = Image.asset('asset/image/WT01_loading_layers.png',
@ -20,34 +10,60 @@ class LoadingScreenMainState extends State<LoadingScreenMain> {
width: double.infinity, width: double.infinity,
alignment: Alignment.center, alignment: Alignment.center,
); );
dynamic _timer = <dynamic>[TimeMessages.timer];
return Scaffold( return Scaffold(
body: LoadingScreen( backgroundColor: Colors.white,
initializers: _timer, body: new InkWell(
navigateToWidget: AitrainerHome(), child: new Stack(
loaderColor: Colors.yellow, fit: StackFit.expand,
image: _backgroundImage, children: <Widget>[
backgroundColor: Colors.black, /// Paint the area where the inner widgets are loaded with the
styleTextUnderTheLoader: TextStyle( /// background to keep consistency with the screen background
fontSize: 14.0, new Container(
fontWeight: FontWeight.bold, decoration: BoxDecoration(color: Colors.white),
color: Colors.lightGreenAccent), ),
) /// Render the background image
new Container(
child: _backgroundImage,
),
/// Render the Title widget, loader and messages below each other
new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
flex: 3,
child: new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 30.0),
),
],
)),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
/// Loader Animation Widget
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
Colors.lightGreenAccent),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
),
//Text(getMessage, style: widget.styleTextUnderTheLoader),
],
),
),
],
),
],
),
),
); );
} }
}
class TimeMessages {
static Future timer(MessageState state, Function callback) async {
//while (true) {
await Future.delayed(Duration(seconds: 2), () {
state.setMessage = DateTime.now().toIso8601String();
print("---- TimeMessages initializer");
Session session = Session();
session.fetchSessionAndNavigate(callback);
});
//}
}
} }

View File

@ -0,0 +1,136 @@
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/workout_tree.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// ignore: must_be_immutable
class MenuPageWidget extends StatelessWidget {
int parent;
MenuPageWidget({this.parent});
@override
Widget build(BuildContext context) {
MenuBloc menuBloc = BlocProvider.of<MenuBloc>(context);
return CustomScrollView(
scrollDirection: Axis.vertical,
slivers: <Widget>[
buildMenuColumn(parent, context, menuBloc)
]
);
}
SliverList buildMenuColumn(int parent, BuildContext context, MenuBloc menuBloc) {
final List<Widget> _columnChildren = List();
menuBloc.menuTreeRepository.getBranch(menuBloc.parent).forEach((treeName, value) {
WorkoutTree workoutTree = value as WorkoutTree;
_columnChildren.add(
Container(
padding: EdgeInsets.only(top: 16.0),
child: Center(
child: Stack(
alignment: Alignment.bottomLeft,
overflow: Overflow.visible,
children: [
FlatButton(
child: _getButtonImage(workoutTree),
padding: EdgeInsets.all(0.0),
onPressed:() =>
{
print("Hi!, Menu clicked " + workoutTree.id.toString()),
if ( workoutTree.child == false ) {
menuBloc.add(MenuTreeDown(parent: workoutTree.id)),
} else {
menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id)),
if ( Auth().userLoggedIn == null ) {
Scaffold.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.orange,
content: Text(
AppLocalizations.of(context).translate('Please log in'),
style: TextStyle(color: Colors.white))
))
} else {
if ( workoutTree.exerciseType.name == "Custom") {
Navigator.of(context).pushNamed(
'exerciseCustomPage',
arguments: workoutTree.exerciseType),
} else {
Navigator.of(context).pushNamed(
'exerciseNewPage',
arguments: workoutTree.exerciseType),
}
}
}
}
),
InkWell(
child: Text(workoutTree.name, style: TextStyle(color: workoutTree.color, fontSize: workoutTree.fontSize, fontFamily: 'Arial', fontWeight: FontWeight.w900 ),),
highlightColor: workoutTree.color,
)]
)
)
)
);
});
//_columnChildren.add(Spacer(flex: 3));
SliverList sliverList =
SliverList(
delegate: SliverChildListDelegate(
_columnChildren
)
);
return sliverList;
}
dynamic _getButtonImage(WorkoutTree workoutTree) {
dynamic image;
/*String url = workoutTree.imageName;
if ( workoutTree.imageName.startsWith("https") ) {
image = FadeInImage.assetNetwork(
placeholder: 'asset/image/dots.gif',
image: url,
height: 180,
);
} else {
image = Image.asset(workoutTree.imageName, height: 180,);
}*/
try {
image = Image.asset(
workoutTree.imageName,
height: 180,
errorBuilder: (context, error, stackTrace) {
String url = Auth.mediaUrl + 'images/' + workoutTree.imageName.substring(11);
Widget image = FadeInImage.assetNetwork(
placeholder: 'asset/image/dots.gif',
image: url,
height: 180,
);
return image;
},
);
} on Exception catch(_) {
String url = Auth.mediaUrl + '/images/' + workoutTree.imageName;
image = FadeInImage.assetNetwork(
placeholder: 'asset/image/dots.gif',
image: url,
height: 180,
);
}
return image;
}
}

View File

@ -1,91 +0,0 @@
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:flutter/material.dart';
class NavDrawer extends StatefulWidget {
@override
_NawDrawerWidget createState() => _NawDrawerWidget();
}
class _NawDrawerWidget extends State<NavDrawer> {
final Auth auth = Auth();
final AppLanguage appLanguage = AppLanguage();
Locale _locale;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text(
AppLocalizations.of(context).translate('Customers And Exercises'),
style: TextStyle(color: Colors.blue, fontSize: 25),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text( AppLocalizations.of(context).translate('Home')),
onTap: () => Navigator.of(context).pushNamed('home'),
),
ListTile(
leading: Icon(Icons.people),
title: Text( AppLocalizations.of(context).translate('Customers')),
onTap: () => Navigator.of(context).pushNamed('customersPage'),
),
ListTile(
leading: Icon(Icons.directions_run),
title: Text(AppLocalizations.of(context).translate('Exercises')),
onTap: () =>
Navigator.of(context).pushNamed('exerciseTypeListPage'),
),
ListTile(
leading: Icon(Icons.arrow_upward),
title: Text(AppLocalizations.of(context).translate("TRAINING!")),
onTap: () => Navigator.of(context).pushNamed('exerciseNewPage'),
),
ListTile(
leading: Icon(Icons.perm_identity),
title: Text(AppLocalizations.of(context).translate('Login')),
onTap: () => Navigator.of(context).pushNamed('login'),
),
ListTile(
leading: Icon(Icons.cancel),
title: Text(AppLocalizations.of(context).translate('Logout')),
onTap: () =>
{
auth.logout(),
Navigator.of(context).pushNamed('home'),
}
),
ListTile(
leading: Icon(Icons.hearing),
title: Text(AppLocalizations.of(context).translate('Change Language')),
onTap: () => _tapped(),
),
],
),
);
}
void _tapped() => {
/* _locale = Locale("hu"),
appLanguage.changeLanguage(_locale),
AppLocalizations.of(context).setLocale(_locale),
AppLocalizations.of(context).load() */
};
}

32
lib/widgets/splash.dart Normal file
View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
class LoadingDialog extends StatelessWidget {
static void show(BuildContext context, {Key key}) => showDialog<void>(
context: context,
useRootNavigator: false,
barrierDismissible: false,
builder: (_) => LoadingDialog(key: key),
).then((_) => FocusScope.of(context).requestFocus(FocusNode()));
static void hide(BuildContext context) => Navigator.pop(context);
LoadingDialog({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: EdgeInsets.all(12.0),
child: CircularProgressIndicator(backgroundColor: Colors.transparent,),
color: Colors.transparent,
),
),
),
);
}
}

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared name: _fe_analyzer_shared
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.0.0" version: "7.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.39.12" version: "0.39.17"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@ -35,7 +35,21 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.1" version: "2.4.2"
bloc:
dependency: transitive
description:
name: bloc
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.1"
bloc_test:
dependency: "direct dev"
description:
name: bloc_test
url: "https://pub.dartlang.org"
source: hosted
version: "7.0.1"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -43,6 +57,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -50,13 +71,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.3" version: "1.1.3"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.14.12" version: "1.14.13"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -70,21 +105,21 @@ packages:
name: coverage name: coverage
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.11" version: "0.14.0"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.4" version: "2.1.5"
csslib: csslib:
dependency: transitive dependency: transitive
description: description:
name: csslib name: csslib
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.16.1" version: "0.16.2"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -92,13 +127,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.3" version: "0.1.3"
datetime_picker_formfield:
dependency: "direct dev"
description:
name: datetime_picker_formfield
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
devicelocale: devicelocale:
dependency: "direct main" dependency: "direct main"
description: description:
@ -106,13 +134,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.1" version: "0.3.1"
equatable:
dependency: "direct main"
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.3"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
file: file:
dependency: transitive dependency: transitive
description: description:
name: file name: file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.0" version: "5.2.1"
firebase_messaging: firebase_messaging:
dependency: "direct main" dependency: "direct main"
description: description:
@ -125,18 +167,39 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_datetime_picker: flutter_bloc:
dependency: "direct dev" dependency: "direct main"
description: description:
name: flutter_datetime_picker name: flutter_bloc
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.8" version: "6.0.1"
flutter_driver: flutter_driver:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_facebook_login:
dependency: "direct main"
description:
name: flutter_facebook_login
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
flutter_form_bloc:
dependency: "direct main"
description:
name: flutter_form_bloc
url: "https://pub.dartlang.org"
source: hosted
version: "0.19.0"
flutter_keyboard_visibility:
dependency: transitive
description:
name: flutter_keyboard_visibility
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
flutter_launcher_icons: flutter_launcher_icons:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -168,6 +231,18 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
form_bloc:
dependency: transitive
description:
name: form_bloc
url: "https://pub.dartlang.org"
source: hosted
version: "0.19.1"
fuchsia_remote_debug_protocol: fuchsia_remote_debug_protocol:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -214,7 +289,7 @@ packages:
name: image name: image
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.12" version: "2.1.14"
intl: intl:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -242,7 +317,7 @@ packages:
name: json_rpc_2 name: json_rpc_2
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.2.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -256,7 +331,7 @@ packages:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.6" version: "0.12.8"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -270,7 +345,7 @@ packages:
name: mime name: mime
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.6+3" version: "0.9.7"
mockito: mockito:
dependency: "direct main" dependency: "direct main"
description: description:
@ -278,13 +353,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.1.1" version: "4.1.1"
multi_server_socket: nested:
dependency: transitive dependency: transitive
description: description:
name: multi_server_socket name: nested
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "0.0.4"
node_interop: node_interop:
dependency: transitive dependency: transitive
description: description:
@ -319,7 +394,21 @@ packages:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.4" version: "1.7.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
pedantic: pedantic:
dependency: transitive dependency: transitive
description: description:
@ -333,7 +422,7 @@ packages:
name: petitparser name: petitparser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.0" version: "3.0.4"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -361,14 +450,14 @@ packages:
name: process name: process
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.12" version: "3.0.13"
provider: provider:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: provider name: provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.0" version: "4.3.2"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@ -383,6 +472,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.3" version: "2.1.3"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.24.1"
sentry: sentry:
dependency: "direct main" dependency: "direct main"
description: description:
@ -396,14 +492,42 @@ packages:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.3" version: "0.5.8"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+10"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
name: shelf name: shelf
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.7" version: "0.7.8"
shelf_packages_handler: shelf_packages_handler:
dependency: transitive dependency: transitive
description: description:
@ -451,13 +575,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.7.0"
spider_chart:
dependency: "direct main"
description:
name: spider_chart
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.3" version: "1.9.5"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -492,28 +623,28 @@ packages:
name: test name: test
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.14.4" version: "1.15.2"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.15" version: "0.2.17"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.4" version: "0.3.10"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.6" version: "1.2.0"
usage: usage:
dependency: transitive dependency: transitive
description: description:
@ -521,6 +652,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.4.2" version: "3.4.2"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -534,7 +672,7 @@ packages:
name: vm_service name: vm_service
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.1.0" version: "4.2.0"
vm_service_client: vm_service_client:
dependency: transitive dependency: transitive
description: description:
@ -570,13 +708,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.3" version: "0.7.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
xml: xml:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.6.1" version: "4.2.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@ -585,5 +730,5 @@ packages:
source: hosted source: hosted
version: "2.2.1" version: "2.2.1"
sdks: sdks:
dart: ">=2.7.0 <3.0.0" dart: ">=2.9.0-14.0.dev <3.0.0"
flutter: ">=1.12.13+hotfix.6 <2.0.0" flutter: ">=1.16.0 <2.0.0"

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.1.0+1 version: 1.1.0+12
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.7.0 <3.0.0"
@ -31,6 +31,11 @@ dependencies:
sentry: ^3.0.1 sentry: ^3.0.1
firebase_messaging: ^6.0.16 firebase_messaging: ^6.0.16
flutter_local_notifications: 1.1.1 flutter_local_notifications: 1.1.1
flutter_facebook_login: ^3.0.0
flutter_bloc: ^6.0.1
equatable: ^1.2.3
flutter_form_bloc: ^0.19.0
spider_chart: ^0.1.5
mockito: ^4.1.1 mockito: ^4.1.1
@ -41,15 +46,14 @@ dev_dependencies:
flutter_driver: flutter_driver:
sdk: flutter sdk: flutter
test: any test: any
bloc_test: ^7.0.1
http: 0.12.1 http: 0.12.1
provider: ^3.2.0 provider: ^4.3.2
intl: 0.16.1 intl: 0.16.1
flutter_datetime_picker: ^1.3.8 shared_preferences: ^0.5.8
datetime_picker_formfield: ^1.0.0
shared_preferences: ^0.4.1
flutter_launcher_icons: "^0.7.3" flutter_launcher_icons: ^0.7.5
flutter_icons: flutter_icons:
android: "launcher_icon" android: "launcher_icon"
@ -81,6 +85,7 @@ flutter:
- asset/image/WT_gain_muscle.png - asset/image/WT_gain_muscle.png
- asset/image/WT_weight_loss.png - asset/image/WT_weight_loss.png
- asset/image/WT_welcome.png - asset/image/WT_welcome.png
- asset/image/login_fb.png
- asset/menu/1.cardio.png - asset/menu/1.cardio.png
- asset/menu/1.1.aerob.png - asset/menu/1.1.aerob.png
- asset/menu/1.2.anaerob.png - asset/menu/1.2.anaerob.png

View File

@ -0,0 +1,79 @@
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

@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:aitrainer_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(AitrainerApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

107
test/widget_test.login.dart Normal file
View File

@ -0,0 +1,107 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:aitrainer_app/bloc/login_form_bloc.dart';
import 'package:aitrainer_app/library_keys.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/user.dart';
import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/view/login.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
class MockUserRepository extends Mock implements UserRepository {
final User user = User();
setEmail(String email) {
this.user.email = email;
}
setPassword(String password) {
this.user.password = password;
}
Future<void> getUser() async {
if ( this.user.email == "sw@andio.biz" && this.user.password == "PalKataPeter1") {
//OK
} else {
throw new Exception("Customer does not exist");
}
}
}
class MockLoginBloc extends MockBloc<FormBlocEvent>
implements LoginFormBloc {}
void main() {
group('LoginScreen', () {
MockLoginBloc loginBloc;
MockUserRepository userRepository;
Widget loginWidget;
setUp(() {
loginBloc = MockLoginBloc();
userRepository = MockUserRepository();
loginWidget =
MaterialApp(
home: LoginPage(),
localizationsDelegates: [
AppLocalizationsDelegate(isTest: true),
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
routes: {
'home': (context) => LoginPage(),
}
);
});
tearDown(() {
loginBloc.close();
});
testWidgets('Display login page', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(loginWidget);
await tester.pumpAndSettle();
expect(find.byKey(LibraryKeys.loginEmailField), findsOneWidget);
expect(find.byKey(LibraryKeys.loginPasswordField), findsOneWidget);
expect(find.byKey(LibraryKeys.loginOKButton), findsOneWidget);
});
testWidgets('Add zero length email', (WidgetTester tester) async {
await tester.pumpWidget(loginWidget);
await tester.pumpAndSettle();
var emailField = find.byKey(LibraryKeys.loginEmailField);
var pwdField = find.byKey(LibraryKeys.loginPasswordField);
var okButton = find.byKey(LibraryKeys.loginOKButton);
expect(emailField, findsOneWidget);
expect(pwdField, findsOneWidget);
expect(okButton, findsOneWidget);
await tester.tap(emailField);
await tester.enterText(emailField, "sw");
await tester.enterText(pwdField, "123");
await tester.pumpAndSettle();
await tester.tap(okButton);
await tester.pump(const Duration(milliseconds: 500)); // add delay
final emailErrorFinder = find.text(Common.EMAIL_ERROR);
expect(emailErrorFinder, findsOneWidget);
});
});
}

11
test_driver/app.dart Normal file
View File

@ -0,0 +1,11 @@
import 'package:flutter_driver/driver_extension.dart';
import 'package:aitrainer_app/main.dart' as app;
void main() {
// This line enables the extension.
enableFlutterDriverExtension();
// Call the `main()` function of the app, or call `runApp` with
// any widget you are interested in testing.
app.main();
}

View File

@ -0,0 +1,9 @@
// Imports the Flutter Driver API.
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('Login App', () {
});
}