diff --git a/android/app/build.gradle b/android/app/build.gradle index 7fb27d7..65cff7c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) { def flutterRoot = localProperties.getProperty('flutter.sdk') 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') @@ -26,6 +26,12 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 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 { compileSdkVersion 28 @@ -46,11 +52,20 @@ android { versionName flutterVersionName } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig signingConfigs.release } } } @@ -62,4 +77,5 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.google.firebase:firebase-analytics:17.2.2' + implementation 'com.facebook.android:facebook-login:[5,6)' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 55aec36..7f52f63 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -48,5 +48,23 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..432c9c7 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,13 @@ + + + WorkoutTest + + + 584181112271127 + + + fb584181112271127 + \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146..bc24dcf 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME 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 diff --git a/android/key.properties b/android/key.properties new file mode 100644 index 0000000..39725b5 --- /dev/null +++ b/android/key.properties @@ -0,0 +1,4 @@ +storePassword=tbi6012Andi +keyPassword=tbi6012Andi +keyAlias=key +storeFile=c:/Users/bossa/.ssh/key.jks \ No newline at end of file diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/asset/icon/WT_long_logo_1024x500.png b/asset/icon/WT_long_logo_1024x500.png new file mode 100644 index 0000000..732c026 Binary files /dev/null and b/asset/icon/WT_long_logo_1024x500.png differ diff --git a/asset/icon/icon_512.png b/asset/icon/icon_512.png new file mode 100644 index 0000000..3cbab01 Binary files /dev/null and b/asset/icon/icon_512.png differ diff --git a/asset/image/login_fb.png b/asset/image/login_fb.png new file mode 100644 index 0000000..d51e0a9 Binary files /dev/null and b/asset/image/login_fb.png differ diff --git a/i18n/en.json b/i18n/en.json index 7644033..b1c5100 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -23,8 +23,9 @@ "gdpr_text": "Kik vagyunk\nA weboldalunk a https://andio.biz (blogbejegyzések) és https://natur-haztartas.hu webáruház.\n\nCélunk a természetes és natúr életmód népszerűsítése, hogy az vásárlóinkat és érdeklődőinket segítsünk a vegyszermentes és natúr életmód felé vezető úton.\n\nMelyek azok a személyes adatok amiket gyűjtünk, és milyen céllal gyűjtjük azokat\nVásárlás\nMegrendelés esetén elkérjük a következő adatokat:\n\na nevet: hogy azonosítsunk csomagszállítás esetén, illetve a számlára is rákerül a vásárló neve\nemail címet: megrendelés visszaigazoláshoz, a csomag állapotának a közléséhez. Emellett, ha engedélyt kapunk, hírleveleket is küldünk natúr életmód tippekről, ajánlatokról. Ezekről a levelekről bármikor, egy kattintással le tudsz iratkozni, a levél alján.\ncímet: A csomag szállításához fontos a pontos cím. A számlára is ez a cím kerül, de lehetőséged van itt egy másik, csak számlázási címet megadni.\ntelefonszámot: SMS értesítőt küldünk a csomag kiszállítása előtt egy munkanappal. Ha valamilyen más probléma merül fel a csomag összeállításakor, akkor lehetséges, hogy telefonon megkeresünk és tisztázzuk a problémákat (pl. áruhiány, rosszul megadott adatok kijavítása, stb).\nHozzászólások\nHozzászólás beküldésekor a hozzászólási űrlapban megadottakon kívül begyűjtésre kerül a hozzászóló IP címe és a böngészőazonosító karakterlánc a kéretlen tartalmak kiszűrése céljából.\n\nEgy személytelenített, az e-mail címből előállított karakterlánc (hashnek szokás nevezni) kerül továbbításra a Gravatar szolgáltatás felé, ha ez az oldalon használatban van. A Gravatar szolgáltatás feltételei az alábbi címen tekinthetőek meg: https://automattic.com/privacy/. A hozzászólás elfogadása után, a hozzászólásunk tartalma és a profil képünk is megjelenik nyilvánosan.\n\nKapcsolatfelvételi űrlapok\nAmennyiben szeretnél, feliratkozhatsz hírlevelünkre weboldalunkon az ott található regisztrációs űrlap kitöltésével. (pl: https://natur-haztartas.hu/login?back=my-account)\n\nHa kitöltöd a regisztrációs űrlapot és rendelkezésünkre bocsátod személyes adataidat vagy a vevőként történő regisztrációjakor hozzájárultál ahhoz, hogy hírlevelet küldjünk az számodra, e-mailt fogsz kapni tőlünk a megadott e-mail címre.\n\nA regisztrációs űrlapon megadott személyes adatok kezelésére kizárólag a hírlevélnek az e-mail címre történő küldése céljából kerül.\n\nA hírleveleinkben található linkek nyomon követési információkat tartalmaznak, melyek segítségével megállapíthatjuk, hogy különösen mely linkek érdekeltek téged, amelyekre rákattintottál.\n\nA nyomon követési link segítségével a következő adatok tárolása történik a hírlevélkezelő rendszerünkbe: e-mail cím, hírlevél, link, dátum és idő. Az személyes adataidat csak addig tároljuk, amíg a „leiratkozás” linkre kattintva le nem iratkozol a hírlevélről. E link megtalálható minden egyes tőlünk kapott hírlevél alján. Amennyiben leiratkozol személyes adataid késedelem nélkül törlésre kerülnek. Felhívjuk a figyelmed arra, hogy személyes adatok törlését követően nem fogsz tőlünk több hírlevelet vagy kupont kapni.\n\nHa üzenetet küldesz az Andió Facebook messengerére, akkor jogosultak vagyunk az Andió által viszontüzenetet küldeni a Te messengeredre. Ezekről az üzeneteket bármikor leállíthatod, ha a messengerbe beírod a STOP szót és utána megnyomod a „leiratkozok” gombot.\n\nSütik és nyomon követés\nHozzászólás írása után a honlap a megadott nevet, e-mail és web címet sütiben eltárolja. A tárolás csak kényelmi célokat szolgál, hogy a következő hozzászóláskor ne kelljen automatikusan kitölteni. A sütik lejárati ideje 1 év.\n\nHa rendelkezel felhasználói fiókkal és be is vagy jelentkezve erre a honlapra, akkor átmeneti sütiket állítunk be, annak érdekében, hogy megállapítsuk, hogy a böngésző elfogadja-e a sütiket. Ezek a sütik nem tartalmaznak személyes információt, és törlődnek, ahogy bezárjuk a böngészőt.\n\nA honlapra történő bejelentkezéskor több sütit hozunk létre, amely elmenti a bejelentkezési információt és a szerkesztőfelület megjelenítési opcióit. A bejelentkezési sütik két napig érvényesek, a szerkesztőfelület megjelenítési opcióit tároló süti egy évig. Amennyiben az “Emlékezz rám” opciót bejelöljük, a bejelentkezés két hétig folytatódik. Kijelentkezéskor a bejelentkezési sütik eltávolításra kerülnek.\n\nHa a webáruházban jársz, akkor a kosarad tartalmát és a bejelentkezési állapotodat is a süti tartalmazza. Így ha kikapcsolod a sütiket a böngésződben, akkor nem tudsz a honlapon rendelni, így azt ímélen vagy telefonon tudod megtenni.\n\nA Google Inc. („Google”) által biztosított Google Analytics internetes elemző szolgáltatást is igénybe vesszük. A Google Analytics szintén sütiket használ. A sütik által a weboldalhasználattal kapcsolatban létrehozott információk általában egy USA-beli Google-szerverre kerülnek, és ott tárolják őket. Ezt megelőzően a Google lerövidíti az IP-címedet az Európai Unió tagállamain és az Európai Gazdasági Térségről szóló megállapodás részes államain belül. Teljes IP-címet csak kivételes esetekben továbbítanak lerövidítés céljából a Google USA-ban található szervereire. A Google a weboldal üzemeltetője megbízásából ezen információk alapján értékeli az weboldalhasználatod, és weboldalaktivitással kapcsolatos jelentéseket készít, valamint további weboldal- és internethasználattal kapcsolatos szolgáltatásokat nyújt a weboldal üzemeltetője számára. Az böngésződ által a Google Analytics szolgáltatása révén továbbított IP-címet nem kapcsolják össze a Google más adataival. A böngésződ megfelelő beállításainak kiválasztásával elutasíthatod a sütik használatát. Megtilthatod azt is, hogy a Google gyűjtse és feldolgozza a sütik által létrehozott és a weboldalhasználatodra vonatkozó adatokat (beleértve az IP-címet). Ehhez töltsd le és telepítse a következő linken található beépülő böngészőmodult: https://tools.google.com/dlpage/gaoptout?hl=en\n\nA Facebook által biztosított adatelemzést és követést is használjuk. Így ha a honlapon jársz, később találkozhatsz egy hirdetéssel, amely a meglátogatott honlap oldalról szól, mert feltételezzük, hogy érdekel Téged a téma, és még többet szeretnél megtudni róla.\n\nVásárlás után véleményt kérünk a vásárolt termékről a Yotpo.com szolgáltatásán keresztül. Itt megírhatod a véleményed, amely kikerül a termék weboldalára esetleg az Andió facebook oldalára is. A vásárlás után kb. 2 héttel egy emailt kapsz, hogy írd meg a véleményed. Erről a véleménykérő emailről is leiratkozhatsz a levél alján látható leiratkozási linkkel.\n\nNéhány weboldalunkat elemezzük, hogy minél jobban követhető, olvasható és használható legyen. Ez az elemzés a Hotjar.com szolgáltatásával személyes adatok gyűjtése és felhasználása nélkül rögzíti a tevékenységedet az oldalon és a sok látogató össztevékenységét összevetve tudjuk az oldal minőségét javítani.\n\nMás honlapokról származó beágyazott tartalmak\nA honlapon elérhető bejegyzések külső forrásból származó beágyazott tartalmakat (pl. videók, képek, cikkek stb.) használhatnak. A külső forrásból származó beágyazott tartalmak pontosan úgy viselkednek, mintha meglátogattunk volna egy másik honlapot.\n\nEzek a webhelyek lehetséges, hogy adatot gyűjtenek a látogatókról, sütiket vagy harmadik féltől származó követőkódot használnak, figyelik a beágyazott tartalommal kapcsolatos felhasználói viselkedést, ha rendelkezünk felhasználói fiókkal és be vagyunk jelentkezve az oldalra.\n\nKivel osztjuk meg és hová továbbítjuk a felhasználói adatokat\nAz elkért és tárolt adatokat a webtárhelyen adatbázisban őrizzük. A webtárhely tulajdonosa a Magyar Hosting Kft (Székhely: 1132 Budapest, Victor Hugo u. 18, Postacím: ua. Telefon: (1) 700 2323 Web: https://www.mhosting.hu/ )\n\nA nevet és a címet csomagszállítás esetén továbbítjuk a futárszolgálat felé, amely kiszállítja a címedre a csomagot.\n\nBelföldi szállítás esetén: Complexpress Logisztika Kft.,1033 Budapest, Szentendrei út 89-95. Telefon: 06-1-203-4681\n\nKülföldi szállítás esetén: GLS General Logistics Systems Hungary Csomag-Logisztikai Kft. 2351 Alsónémedi GLS Európa u. 2. info@gls-hungary.com.\n\nA vásárláshoz minden esetben számlát adunk ki, amelyet bármikor letölthetsz a webáruház felhasználó fiókodból: https://natur-haztartas.hu/ugyfelfiok. A számlán levő adatokat (név és cím) elküldjük a könyvelőirodának, amelyet törvényi kötelezettségünk 8 évig megőrizni.\n\nA könyvelőiroda: Adókalkulátor Kft. Cím: Budapest, Szigligeti u. 44, 1205 Telefon: (20) 292 1072\n\nMennyi ideig őrizzük a személyes adatot\nHa hozzászólsz egy blogbejegyzéshez, hozzászólás és annak metaadatai nem meghatározható ideig a rendszerben maradnak. Ennek célja, hogy az összes ezt követő bármely hozzászólás általunk megismertté és jóváhagyottá váljon, azaz ne kerüljön fel a moderálandó hozzászólások listájára.\n\nA honlapon regisztrált felhasználók személyes adatai a felhasználói profiljukban is tárolásra kerülnek. (https://natur-haztartas.hu/ugyfelfiok)\n\nA vásárláskor megadott adatokat a törvény szerint 8 év őrizzük.\n\nA hírlevél feliratkozáskor ill. kapcsolati űrlapon megadott adatokat nem töröljük, csak kérésre. Ezt egy kattintással megteheted minden hírlevél alján, ill. az ügyfelszolgálati email címen is kérheted a törlést.\n\nMilyen jogokkal rendelkezik a felhasználó a saját adatai kapcsán\nA weboldalon regisztrált fiók vagy hozzászólás írása esetén kérhető a személyes adatok export fájlban történő megküldése, amely bármilyen adatot tartalmaz, amit korábban a felhasználó rendelkezésünkre bocsátott.\n\nKérhető továbbá, hogy bármilyen korábban megadott személyes adatot töröljük. Ezt megteheted minden hírlevél alján levő leiratkozás gombbal, vagy ügyfélszolgálati elérhetőségeink egyikén. A törlés nem vonatkozik azokra az adatokra, amelyeket adminisztrációs, jogi vagy biztonsági okokból kötelező megőriznünk.\n\nAdathelyesbítés: ha pontatlanok az adataid (név, email cím, szállítási cím), megteheted az ugyfelszolgalat@andio.biz email címen keresztül\n\nKapcsolati adatok\nÜgyfélszolgálat:\n\nemail: ugyfelszolgalat@andio.biz\n\ntelefon munkaidőben: +36 30 580 0499", "Please log in": "Please log in", - "Customer does not exist or the password is wrong": "Customer does not exist or the password is wrong", + "Exception: Customer does not exist or the password is wrong": "Customer does not exist or the password is wrong", "Customer exists": "The email address has been registered already", + "There is an error: during registration:": "There is an error: during registration:", "Please select an exercise": "Please select an exercise", "Cardio": "Cardio", "400m": "400m", @@ -58,17 +59,26 @@ "Unit": "Unit", "Exercise date and time": "Exercise date and time", "Please type the right quantity 0-1000": "Please type the right quantity 0-10000", + "Yes": "Yes", + "No": "No", + "with": "with", + "Do you save this exercise with these parameters?":"Do you save this exercise with these parameters?", + "The number of the exercise": "The number of the exercise", + "The number of the exercise done with": "The number of the exercise done with", + "repeat": "repeat", "meter": "meter", "percent": "percent", "kg": "kg", + "kilogram": "kilogram", "lbs": "lbs", "second": "second", "Email": "Email", "Password": "Password", - "Password (Leave empty if you don't want to change)":"Password (Leave empty if you don't want to change)", + "OR": "OR", + "Password (Leave empty if no change)":"Password (Leave empty if no change)", "First Name": "First Name", "Birth Year": "Birty Year", "Weight": "Weight", diff --git a/i18n/hu.json b/i18n/hu.json index e03577c..a16b510 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -23,8 +23,9 @@ "gdpr_text": "", "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", + "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", "Cardio": "Kardió", "400m": "400m", @@ -50,7 +51,10 @@ "BMR": "Alapanyagcsere", "Sizes": "Méretek", "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", "Exercise": "Gyakorlat", @@ -58,17 +62,23 @@ "Unit": "Egység", "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", + "Yes": "Igen", + "No": "Nem", + "with": "", + "Do you save this exercise with these parameters?":"Elmented a gyakorlatot ezekkel az adatokkal?", "repeat": "ismétlés", "meter": "meter", "percent": "százalék", "kg": "kg", + "kilogram": "kilogramm", "lbs": "lbs", "second": "másodperc", "Email": "Email", "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", "Birth Year": "Születési év", "Weight": "Tömeg", diff --git a/lib/bloc/account/account_bloc.dart b/lib/bloc/account/account_bloc.dart new file mode 100644 index 0000000..e637e96 --- /dev/null +++ b/lib/bloc/account/account_bloc.dart @@ -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 { + final CustomerRepository customerRepository; + bool loggedIn = false; + AccountBloc({this.customerRepository}) : super(AccountInitial()) { + if ( Auth().userLoggedIn != null ) { + customerRepository.customer = Auth().userLoggedIn; + loggedIn = true; + } + } + + @override + Stream 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()); + } + } +} diff --git a/lib/bloc/account/account_event.dart b/lib/bloc/account/account_event.dart new file mode 100644 index 0000000..a433b05 --- /dev/null +++ b/lib/bloc/account/account_event.dart @@ -0,0 +1,46 @@ +part of 'account_bloc.dart'; + +@immutable +abstract class AccountEvent extends Equatable { + const AccountEvent(); + + @override + List get props => []; +} + +class AccountChangeCustomer extends AccountEvent { + final Customer customer; + + const AccountChangeCustomer({this.customer}); + + @override + List get props => [customer]; +} + +class AccountLogout extends AccountEvent { + final Customer customer; + + const AccountLogout({this.customer}); + + @override + List get props => [customer]; + +} + +class AccountLogin extends AccountEvent { + final Customer customer; + + const AccountLogin({this.customer}); + + @override + List get props => [customer]; +} + +class AccountLogInFinished extends AccountEvent { + final Customer customer; + + const AccountLogInFinished({this.customer}); + + @override + List get props => [customer]; +} \ No newline at end of file diff --git a/lib/bloc/account/account_state.dart b/lib/bloc/account/account_state.dart new file mode 100644 index 0000000..8fa858d --- /dev/null +++ b/lib/bloc/account/account_state.dart @@ -0,0 +1,36 @@ +part of 'account_bloc.dart'; + +@immutable +abstract class AccountState extends Equatable { + const AccountState(); + @override + List 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 get props => [message]; +} diff --git a/lib/bloc/custom_exercise_form_bloc.dart b/lib/bloc/custom_exercise_form_bloc.dart new file mode 100644 index 0000000..4f7675a --- /dev/null +++ b/lib/bloc/custom_exercise_form_bloc.dart @@ -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 { + 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 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(); + } +} diff --git a/lib/bloc/customer_change/customer_change_bloc.dart b/lib/bloc/customer_change/customer_change_bloc.dart new file mode 100644 index 0000000..13516ca --- /dev/null +++ b/lib/bloc/customer_change/customer_change_bloc.dart @@ -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 { + final CustomerRepository customerRepository; + CustomerChangeBloc({this.customerRepository}) : super(CustomerChangeInitial()); + + @override + Stream 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()); + } + } +} diff --git a/lib/bloc/customer_change/customer_change_event.dart b/lib/bloc/customer_change/customer_change_event.dart new file mode 100644 index 0000000..7ae94b0 --- /dev/null +++ b/lib/bloc/customer_change/customer_change_event.dart @@ -0,0 +1,36 @@ +part of 'customer_change_bloc.dart'; + +@immutable +abstract class CustomerChangeEvent extends Equatable { + const CustomerChangeEvent(); + @override + List get props => []; +} + +class CustomerGoalChange extends CustomerChangeEvent { + final String goal; + const CustomerGoalChange({this.goal}); + + @override + List get props => [goal]; +} + +class CustomerFitnessChange extends CustomerChangeEvent { + final String fitness; + const CustomerFitnessChange({this.fitness}); + + @override + List get props => [fitness]; +} + +class CustomerBodyTypeChange extends CustomerChangeEvent { + final String bodyType; + const CustomerBodyTypeChange({this.bodyType}); + + @override + List get props => [bodyType]; +} + +class CustomerSave extends CustomerChangeEvent { + const CustomerSave(); +} diff --git a/lib/bloc/customer_change/customer_change_state.dart b/lib/bloc/customer_change/customer_change_state.dart new file mode 100644 index 0000000..5506468 --- /dev/null +++ b/lib/bloc/customer_change/customer_change_state.dart @@ -0,0 +1,32 @@ +part of 'customer_change_bloc.dart'; + +@immutable +abstract class CustomerChangeState extends Equatable { + const CustomerChangeState(); + @override + List 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 get props => [message]; +} diff --git a/lib/bloc/customer_change_form_bloc.dart b/lib/bloc/customer_change_form_bloc.dart new file mode 100644 index 0000000..0ccac4b --- /dev/null +++ b/lib/bloc/customer_change_form_bloc.dart @@ -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 { + 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 close() { + emailField.close(); + firstNameField.close(); + nameField.close(); + passwordField.close(); + birthYearField.close(); + weightField.close(); + genderField.close(); + return super.close(); + } + +} \ No newline at end of file diff --git a/lib/bloc/exercise_form_bloc.dart b/lib/bloc/exercise_form_bloc.dart new file mode 100644 index 0000000..ebb6a3b --- /dev/null +++ b/lib/bloc/exercise_form_bloc.dart @@ -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 { + 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 close() { + unitQuantityField.close(); + unitQuantityUnitField.close(); + return super.close(); + } + +} \ No newline at end of file diff --git a/lib/bloc/login_form_bloc.dart b/lib/bloc/login_form_bloc.dart new file mode 100644 index 0000000..7a720ec --- /dev/null +++ b/lib/bloc/login_form_bloc.dart @@ -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 { + 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()); + + } + } +} diff --git a/lib/bloc/menu/menu_bloc.dart b/lib/bloc/menu/menu_bloc.dart new file mode 100644 index 0000000..630799a --- /dev/null +++ b/lib/bloc/menu/menu_bloc.dart @@ -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 { + final MenuTreeRepository menuTreeRepository; + int parent; + + MenuBloc({this.menuTreeRepository}) : super(MenuInitial()) { + parent = 0; + } + + @override + Stream 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()); + } + } +} diff --git a/lib/bloc/menu/menu_event.dart b/lib/bloc/menu/menu_event.dart new file mode 100644 index 0000000..ac87f26 --- /dev/null +++ b/lib/bloc/menu/menu_event.dart @@ -0,0 +1,41 @@ +part of 'menu_bloc.dart'; + +@immutable +abstract class MenuEvent extends Equatable { + const MenuEvent(); + + @override + List get props => []; +} + +class MenuCreate extends MenuEvent { + const MenuCreate(); + + @override + List get props => []; +} + +class MenuTreeDown extends MenuEvent { + final int parent; + const MenuTreeDown({this.parent}); + + @override + List get props => [parent]; +} + +class MenuTreeUp extends MenuEvent { + final int parent; + const MenuTreeUp({this.parent}); + + @override + List get props => [parent]; +} + +class MenuClickExercise extends MenuEvent { + final int exerciseTypeId; + const MenuClickExercise({this.exerciseTypeId}); + + @override + List get props => [exerciseTypeId]; +} + diff --git a/lib/bloc/menu/menu_state.dart b/lib/bloc/menu/menu_state.dart new file mode 100644 index 0000000..8408f41 --- /dev/null +++ b/lib/bloc/menu/menu_state.dart @@ -0,0 +1,34 @@ +part of 'menu_bloc.dart'; + +@immutable +abstract class MenuState extends Equatable { + const MenuState(); + + @override + List 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 get props => [workoutTree]; +} + +class MenuError extends MenuState { + final String message; + const MenuError({this.message}); + + @override + List get props => [message]; +} diff --git a/lib/bloc/registration_form_bloc.dart b/lib/bloc/registration_form_bloc.dart new file mode 100644 index 0000000..79c407f --- /dev/null +++ b/lib/bloc/registration_form_bloc.dart @@ -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 { + 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()); + + } + } + + +} \ No newline at end of file diff --git a/lib/bloc/session/session_bloc.dart b/lib/bloc/session/session_bloc.dart new file mode 100644 index 0000000..e7136d3 --- /dev/null +++ b/lib/bloc/session/session_bloc.dart @@ -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 { + final Session session; + SessionBloc({this.session}) : super(SessionInitial()); + + @override + Stream 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 close() async { + await this.close(); super.close(); + } +} diff --git a/lib/bloc/session/session_event.dart b/lib/bloc/session/session_event.dart new file mode 100644 index 0000000..04f34a2 --- /dev/null +++ b/lib/bloc/session/session_event.dart @@ -0,0 +1,16 @@ +part of 'session_bloc.dart'; + +@immutable +abstract class SessionEvent extends Equatable { + const SessionEvent(); + + @override + List get props => []; +} + +class SessionStart extends SessionEvent { + const SessionStart(); + + @override + List get props => []; +} diff --git a/lib/bloc/session/session_state.dart b/lib/bloc/session/session_state.dart new file mode 100644 index 0000000..82822c9 --- /dev/null +++ b/lib/bloc/session/session_state.dart @@ -0,0 +1,32 @@ +part of 'session_bloc.dart'; + +@immutable +abstract class SessionState extends Equatable { + const SessionState(); + + @override + List 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 get props => [message]; + +} + + diff --git a/lib/bloc/settings/settings_bloc.dart b/lib/bloc/settings/settings_bloc.dart new file mode 100644 index 0000000..f6d64c6 --- /dev/null +++ b/lib/bloc/settings/settings_bloc.dart @@ -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 { + String language; + Locale _locale; + BuildContext context; + + SettingsBloc({this.context}) : super(SettingsInitial()); + + @override + Stream mapEventToState( + SettingsEvent event, + ) async* { + if (event is SettingsChangeLanguage) { + yield SettingsLoading(); + await _changeLang( event.language); + yield SettingsReady(_locale); + } + } + + Future _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 loadLang() async{ + final AppLanguage appLanguage = AppLanguage(); + appLanguage.changeLanguage(_locale); + if ( context != null ) { + AppLocalizations.of(context).setLocale(_locale); + await AppLocalizations.of(context).load(); + } + } +} diff --git a/lib/bloc/settings/settings_event.dart b/lib/bloc/settings/settings_event.dart new file mode 100644 index 0000000..31a8279 --- /dev/null +++ b/lib/bloc/settings/settings_event.dart @@ -0,0 +1,13 @@ +part of 'settings_bloc.dart'; + +@immutable +abstract class SettingsEvent extends Equatable { + const SettingsEvent(); + @override + List get props => []; +} + +class SettingsChangeLanguage extends SettingsEvent { + final String language; + const SettingsChangeLanguage({this.language}); +} diff --git a/lib/bloc/settings/settings_state.dart b/lib/bloc/settings/settings_state.dart new file mode 100644 index 0000000..6935d0f --- /dev/null +++ b/lib/bloc/settings/settings_state.dart @@ -0,0 +1,46 @@ +part of 'settings_bloc.dart'; + +@immutable +abstract class SettingsState extends Equatable { + const SettingsState(); + + @override + List get props => []; +} + +// ignore: must_be_immutable +class SettingsInitial extends SettingsState { + Locale locale; + SettingsInitial(); + + setLocale(locale) { + this.locale = locale; + } + + @override + List get props => [locale]; +} + +class SettingsLoading extends SettingsState { + final Locale locale; + const SettingsLoading({this.locale}); + + @override + List get props => [locale]; +} + +class SettingsReady extends SettingsState { + final Locale locale; + const SettingsReady(this.locale); + + @override + List get props => [locale]; +} + +class SettingsError extends SettingsState { + final String message; + const SettingsError(this.message); + + @override + List get props => [message]; +} \ No newline at end of file diff --git a/lib/library_keys.dart b/lib/library_keys.dart new file mode 100644 index 0000000..528b334 --- /dev/null +++ b/lib/library_keys.dart @@ -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__'); +} \ No newline at end of file diff --git a/lib/localization/app_localization.dart b/lib/localization/app_localization.dart index afc6a58..181ba08 100644 --- a/lib/localization/app_localization.dart +++ b/lib/localization/app_localization.dart @@ -6,8 +6,9 @@ import 'package:flutter/services.dart'; class AppLocalizations { Locale locale; + bool isTest; - AppLocalizations(this.locale); + AppLocalizations(this.locale, {this.isTest = false}); // Helper method to keep the code in the widgets concise // 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 const LocalizationsDelegate delegate = - _AppLocalizationsDelegate(); + AppLocalizationsDelegate(); Map _localizedStrings; @@ -38,18 +39,24 @@ class AppLocalizations { return true; } + Future loadTest(Locale locale) async { + return AppLocalizations(locale); + } + // This method will be called from every widget which needs a localized text String translate(String key) { + if (isTest) return key; return _localizedStrings[key]; } } -class _AppLocalizationsDelegate +class AppLocalizationsDelegate extends LocalizationsDelegate { + final bool isTest; // This delegate instance will never change (it doesn't even have fields!) // It can provide a constant constructor. - const _AppLocalizationsDelegate(); + const AppLocalizationsDelegate({this.isTest = false}); @override bool isSupported(Locale locale) { @@ -60,11 +67,16 @@ class _AppLocalizationsDelegate @override Future load(Locale locale) async { // AppLocalizations class is where the JSON loading actually runs - AppLocalizations localizations = new AppLocalizations(locale); - await localizations.load(); + AppLocalizations localizations = new AppLocalizations(locale, isTest: this.isTest); + if (isTest) { + await localizations.loadTest(locale); + } else { + await localizations.load(); + } return localizations; } + @override - bool shouldReload(_AppLocalizationsDelegate old) => false; + bool shouldReload(AppLocalizationsDelegate old) => false; } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index f21b3ae..bb8f67a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,33 +1,33 @@ 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/custom_exercise_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_goal_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/gdpr.dart'; import 'package:aitrainer_app/view/login.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/registration.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/loading.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/widgets.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:sentry/sentry.dart'; +import 'bloc/account/account_bloc.dart'; +import 'bloc/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'); @@ -95,17 +95,25 @@ Future main() async { // - https://www.dartlang.org/articles/libraries/zones runZonedGuarded>(() async { runApp( - MultiProvider( - // Initialize the model in the builder. That way, Provider - // can own Models's lifecycle, making sure to call `dispose` - // when not needed anymore. + MultiBlocProvider( providers: [ - ChangeNotifierProvider(create: (context) => ExerciseChangingViewModel(null)), - ChangeNotifierProvider(create: (context) => CustomerChangingViewModel(null)), + BlocProvider( + create: (BuildContext context) => SessionBloc(session: Session()), + ), + BlocProvider( + create: (BuildContext context) => MenuBloc( menuTreeRepository: MenuTreeRepository()), + ), + BlocProvider( + create: (BuildContext context) => SettingsBloc(), + ), + BlocProvider( + create: (BuildContext context) => AccountBloc(customerRepository: CustomerRepository()), + ), ], - child: AitrainerApp(), - )); + child: AitrainerApp(), + ) + ); }, (error, stackTrace) async { await _reportError(error, stackTrace); }); @@ -147,18 +155,13 @@ class AitrainerApp extends StatelessWidget { }, routes: { 'home': (context) => AitrainerHome(), - 'loading': (context) => LoadingScreenMain(), - 'customersPage': (context) => CustomerListPage(), - 'customerNewPage': (context) => CustomerNewPage(), 'customerModifyPage': (context) => CustomerModifyPage(), 'customerGoalPage': (context) => CustomerGoalPage(), 'customerFitnessPage': (context) => CustomerFitnessPage(), 'customerBodyTypePage': (context) => CustomerBodyTypePage(), 'customerWelcomePage': (context) => CustomerWelcomePage(), - 'exerciseTypeListPage': (context) => ExerciseTypeListPage(), - 'exerciseTypeNewPage': (context) => ExerciseTypeNewPage(), - 'exerciseTypeModifyPage': (context) => ExerciseTypeModifyPage(), 'exerciseNewPage': (context) => ExerciseNewPage(), + 'exerciseCustomPage': (context) => CustomExercisePage(), 'login': (context) => LoginPage(), 'registration': (context) => RegistrationPage(), 'gdpr': (context) => Gdpr(), @@ -166,7 +169,7 @@ class AitrainerApp extends StatelessWidget { 'account': (context) => AccountPage(), 'settings': (context) => SettingsPage(), }, - initialRoute: 'loading', + initialRoute: 'home', title: 'Aitrainer', theme: ThemeData( brightness: Brightness.light, @@ -176,7 +179,7 @@ class AitrainerApp extends StatelessWidget { bodyText1: TextStyle(fontSize: 14.0), ) ), - home: LoadingScreenMain(), + home: AitrainerHome(), ); } diff --git a/lib/model/auth.dart b/lib/model/auth.dart index fb920f1..14862c3 100644 --- a/lib/model/auth.dart +++ b/lib/model/auth.dart @@ -1,4 +1,6 @@ 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:shared_preferences/shared_preferences.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; @@ -35,7 +37,8 @@ class Auth { static final String isRegisteredKey = 'is_registered'; 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 password = 'andio2009'; @@ -43,7 +46,9 @@ class Auth { Customer userLoggedIn; bool firstLoad = true; List _exerciseTypes; + List _exerciseTree; List deviceLanguages; + String startPage; factory Auth() { return _singleton; @@ -60,7 +65,11 @@ class Auth { } static String getBaseUrl() { - return _baseUrl; + return baseUrl; + } + + static String getMediaUrl() { + return mediaUrl; } afterRegistration(Customer customer) { @@ -70,20 +79,19 @@ class Auth { setPreferences(prefs, SharePrefsChange.registration, customer.customerId); } - afterLogin(Customer customer) { + afterLogin(Customer customer) async { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; - setPreferences(prefs, SharePrefsChange.login, customer.customerId); + await setPreferences(prefs, SharePrefsChange.login, customer.customerId); } - logout(){ + logout() async { userLoggedIn = null; authToken = ""; //firstLoad = true; Future prefs = SharedPreferences.getInstance(); - setPreferences(prefs, SharePrefsChange.logout, 0); - + await setPreferences(prefs, SharePrefsChange.logout, 0); } setPreferences(Future prefs, @@ -95,18 +103,21 @@ class Auth { DateTime now = DateTime.now(); sharedPreferences.setString(Auth.lastStoreDateKey, now.toString()); if ( type == SharePrefsChange.registration ) { + Auth().startPage = "home"; sharedPreferences.setInt(Auth.customerIdKey, customerId); sharedPreferences.setBool(Auth.isRegisteredKey, true); sharedPreferences.setBool(Auth.isLoggedInKey, true); - await ExerciseTypeApi().getExerciseTypes(""); + await ExerciseTypeApi().getExerciseTypes(); + await ExerciseTreeApi().getExerciseTree(); } else if ( type == SharePrefsChange.login ) { - + Auth().startPage = "home"; sharedPreferences.setInt(Auth.customerIdKey, customerId); sharedPreferences.setBool(Auth.isLoggedInKey, true); - await ExerciseTypeApi().getExerciseTypes(""); + await ExerciseTypeApi().getExerciseTypes(); + await ExerciseTreeApi().getExerciseTree(); } else if ( type == SharePrefsChange.logout ) { sharedPreferences.setBool(Auth.isLoggedInKey, false); - //sharedPreferences.setInt(Auth.customerIdKey, 0); + sharedPreferences.setInt(Auth.customerIdKey, 0); sharedPreferences.setString(authTokenKey, ""); } } @@ -115,7 +126,15 @@ class Auth { this._exerciseTypes = exerciseTypes; } + void setExerciseTree( List exerciseTree) { + this._exerciseTree = exerciseTree; + } + List getExerciseTypes() { return this._exerciseTypes; } + + List getExerciseTree() { + return this._exerciseTree; + } } \ No newline at end of file diff --git a/lib/model/exercise_tree.dart b/lib/model/exercise_tree.dart new file mode 100644 index 0000000..417190e --- /dev/null +++ b/lib/model/exercise_tree.dart @@ -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; + } +} diff --git a/lib/model/exercise_type.dart b/lib/model/exercise_type.dart index 3f11e0b..4301a58 100644 --- a/lib/model/exercise_type.dart +++ b/lib/model/exercise_type.dart @@ -2,22 +2,32 @@ import 'package:flutter/services.dart'; class ExerciseType { int exerciseTypeId; + int treeId; String name; String description; BinaryCodec video; String unit; String unitQuantity; String unitQuantityUnit; + bool active; + String imageUrl; + String nameTranslation; + String descriptionTranslation; ExerciseType({this.name, this.description}); ExerciseType.fromJson(Map json) { this.exerciseTypeId = json['exerciseTypeId']; + this.treeId = json['treeId']; this.name = json['name']; this.description = json['description']; this.unit = json['unit']; this.unitQuantity = json['unitQuantity']; 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 toJson() => @@ -26,6 +36,7 @@ class ExerciseType { "description": description, "unit": unit, "unitQuantity": unitQuantity, - "unitQuantityUnit": unitQuantityUnit + "unitQuantityUnit": unitQuantityUnit, + "active": active }; } \ No newline at end of file diff --git a/lib/model/workout_tree.dart b/lib/model/workout_tree.dart index 48f8253..17b17df 100644 --- a/lib/model/workout_tree.dart +++ b/lib/model/workout_tree.dart @@ -1,5 +1,7 @@ import 'dart:ui'; +import 'exercise_type.dart'; + class WorkoutTree { int id; int parent; @@ -9,7 +11,8 @@ class WorkoutTree { double fontSize; bool child; 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); } diff --git a/lib/repository/customer_repository.dart b/lib/repository/customer_repository.dart new file mode 100644 index 0000000..4226a2c --- /dev/null +++ b/lib/repository/customer_repository.dart @@ -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 customerList = List(); + bool visibleDetails = false; + List 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 addCustomer() async { + final Customer modelCustomer = customer; + await CustomerApi().addCustomer(modelCustomer); + } + + Future saveCustomer() async { + final Customer modelCustomer = customer; + await CustomerApi().saveCustomer(modelCustomer); + } + + /* Future> 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); + }*/ +} diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart new file mode 100644 index 0000000..4bc949c --- /dev/null +++ b/lib/repository/exercise_repository.dart @@ -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 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> getExercisesByCustomer( int customerId ) async { + final results = await ExerciseApi().getExercisesByCustomer(customerId); + this.exerciseList = results.map((item) => ExerciseRepository(exercise: item)).toList(); + return this.exerciseList; + } */ +} \ No newline at end of file diff --git a/lib/repository/menu_tree_repository.dart b/lib/repository/menu_tree_repository.dart new file mode 100644 index 0000000..860cac8 --- /dev/null +++ b/lib/repository/menu_tree_repository.dart @@ -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(); + + Future createTree() async { + + final AppLanguage appLanguage = AppLanguage(); + bool isEnglish = appLanguage.appLocal == Locale('en'); + + List 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 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(); + tree.forEach((key, value) { + WorkoutTree workoutTree = value as WorkoutTree; + if ( parent == workoutTree.parent) { + branch[key] = value; + } + }); + return branch; + } +} \ No newline at end of file diff --git a/lib/repository/user_repository.dart b/lib/repository/user_repository.dart new file mode 100644 index 0000000..4a7a94a --- /dev/null +++ b/lib/repository/user_repository.dart @@ -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 addUser() async { + final User modelUser = this.user; + await CustomerApi().addUser(modelUser); + } + + Future getUser() async { + final User modelUser = this.user; + await CustomerApi().getUser(modelUser); + } +} diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index f0b154c..b8da324 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -39,8 +39,13 @@ class CustomerApi { body); Customer customer; try { - customer = Customer.fromJson(jsonDecode(responseBody)); - Auth().afterRegistration(customer); + int status = jsonDecode(responseBody)['status']; + if ( status != null ) { + throw new Exception(jsonDecode(responseBody)['error']); + } else { + customer = Customer.fromJson(jsonDecode(responseBody)); + Auth().afterRegistration(customer); + } } on FormatException catch(exception) { throw new Exception(responseBody); } @@ -56,7 +61,7 @@ class CustomerApi { Customer customer; try { customer = Customer.fromJson(jsonDecode(responseBody)); - Auth().afterRegistration(customer); + await Auth().afterLogin(customer); } on FormatException catch(exception) { throw new Exception(responseBody); } @@ -66,10 +71,17 @@ class CustomerApi { Future getCustomer(int customerId) async { String body = ""; print(" ===== get the customer by id: " + customerId.toString() ); - final String responseBody = await _client.get( - "customers/"+customerId.toString(), - body); - Customer customer = Customer.fromJson(jsonDecode(responseBody)); - Auth().afterRegistration(customer); + try { + final String responseBody = await _client.get( + "customers/"+customerId.toString(), + body); + Customer customer = Customer.fromJson(jsonDecode(responseBody)); + Auth().afterRegistration(customer); + } catch (exception) { + print ("Exception: " + exception.toString()); + print (" === go to registration "); + Auth().logout(); + Auth().startPage = "registration"; + } } } \ No newline at end of file diff --git a/lib/service/exercise_tree_service.dart b/lib/service/exercise_tree_service.dart new file mode 100644 index 0000000..943c1b1 --- /dev/null +++ b/lib/service/exercise_tree_service.dart @@ -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> getExerciseTree() async { + final body = await _client.get("exercise_tree", ""); + final Iterable json = jsonDecode(body); + final List exerciseTree = json.map((exerciseTree) => + ExerciseTree.fromJson(exerciseTree)).toList(); + Auth().setExerciseTree(exerciseTree); + return exerciseTree; + } + +} diff --git a/lib/service/exercisetype_service.dart b/lib/service/exercisetype_service.dart index d7178e4..997839d 100644 --- a/lib/service/exercisetype_service.dart +++ b/lib/service/exercisetype_service.dart @@ -7,8 +7,8 @@ import 'package:aitrainer_app/service/api.dart'; class ExerciseTypeApi { final APIClient _client=new APIClient(); - Future> getExerciseTypes(String param) async { - final body = await _client.get("exercise_type", param); + Future> getExerciseTypes() async { + final body = await _client.get("exercise_type/active", ""); final Iterable json = jsonDecode(body); final List exerciseTypes = json.map( (exerciseType) => ExerciseType.fromJson(exerciseType) ).toList(); Auth().setExerciseTypes(exerciseTypes); diff --git a/lib/util/common.dart b/lib/util/common.dart index e1ec8fd..12c664c 100644 --- a/lib/util/common.dart +++ b/lib/util/common.dart @@ -3,10 +3,16 @@ import 'dart:convert'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/auth.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; class Common { + 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 map ) { String rc = "{"; @@ -47,4 +53,26 @@ class Common { List bytes = text.toString().codeUnits; 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); + } + + } \ No newline at end of file diff --git a/lib/util/loading_screen.dart b/lib/util/loading_screen.dart deleted file mode 100644 index 619d04f..0000000 --- a/lib/util/loading_screen.dart +++ /dev/null @@ -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 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(); -} - diff --git a/lib/util/loading_screen_state.dart b/lib/util/loading_screen_state.dart deleted file mode 100644 index 3ce3534..0000000 --- a/lib/util/loading_screen_state.dart +++ /dev/null @@ -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 { - - /// 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: [ - /// 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: [ - new Expanded( - flex: 3, - child: new Container( - child: new Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - new Padding( - padding: const EdgeInsets.only(top: 30.0), - ), - widget.title, - ], - )), - ), - Expanded( - flex: 1, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - /// Loader Animation Widget - CircularProgressIndicator( - valueColor: new AlwaysStoppedAnimation( - widget.loaderColor), - ), - Padding( - padding: const EdgeInsets.only(top: 20.0), - ), - Text(getMessage, style: widget.styleTextUnderTheLoader), - ], - ), - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/lib/util/menu_tests.dart b/lib/util/menu_tests.dart deleted file mode 100644 index c53e126..0000000 --- a/lib/util/menu_tests.dart +++ /dev/null @@ -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; - } - - -} \ No newline at end of file diff --git a/lib/util/message_state.dart b/lib/util/message_state.dart deleted file mode 100644 index 2f83923..0000000 --- a/lib/util/message_state.dart +++ /dev/null @@ -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 extends State { - /// 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; -} diff --git a/lib/util/session.dart b/lib/util/session.dart index 880a5b8..f62a256 100644 --- a/lib/util/session.dart +++ b/lib/util/session.dart @@ -1,6 +1,8 @@ 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/customer_service.dart'; +import 'package:aitrainer_app/service/exercise_tree_service.dart'; import 'package:aitrainer_app/service/exercisetype_service.dart'; import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/services.dart'; @@ -12,18 +14,24 @@ import '../push_notifications.dart'; class Session { Future _prefs = SharedPreferences.getInstance(); - Auth _auth = Auth(); + SharedPreferences _sharedPreferences; final AppLanguage appLanguage = AppLanguage(); - fetchSessionAndNavigate(Function callback ) async { + fetchSessionAndNavigate( ) async { + print (" -- Session: await prefs.."); _sharedPreferences = await _prefs; - if ( _auth.firstLoad ) { - _fetchToken(_sharedPreferences, callback); + if ( Auth().firstLoad ) { + + print (" -- Session: fetch locale.."); + await appLanguage.fetchLocale(); + await AppLocalizations.delegate.load(appLanguage.appLocal); + print (" -- Session: fetch token.."); + await _fetchToken(_sharedPreferences); initDeviceLocale(); - appLanguage.fetchLocale(); + PushNotificationsManager().init(); } @@ -53,7 +61,7 @@ class Session { /* Auth flow of the user, see auth.dart */ - _fetchToken(SharedPreferences prefs, Function callback) async { + _fetchToken(SharedPreferences prefs) async { var responseJson = await APIClient.authenticateUser( Auth.username, @@ -71,6 +79,7 @@ class Session { // registration //Navigator.of(context).pushNamed('registration'); prefs.setBool(Auth.isRegisteredKey, true); + Auth().startPage = "registration"; } else { DateTime now = DateTime.now(); DateTime lastStoreDate = DateTime.parse( @@ -83,16 +92,17 @@ class Session { prefs.get(Auth.isLoggedInKey) == false) { print("************* Login"); //Navigator.of(context).pushNamed('login'); - + Auth().startPage = "login"; } else { print("************** Store SharedPreferences"); // get API customer await CustomerApi().getCustomer(prefs.getInt(Auth.customerIdKey)); + Auth().startPage = "home"; } - await ExerciseTypeApi().getExerciseTypes(""); - print("--- Session finished, call callback "); - callback(); + await ExerciseTypeApi().getExerciseTypes(); + await ExerciseTreeApi().getExerciseTree(); + print("--- Session finished"); } } diff --git a/lib/view/account.dart b/lib/view/account.dart index 46039d9..e695569 100644 --- a/lib/view/account.dart +++ b/lib/view/account.dart @@ -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/model/auth.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:flutter_bloc/flutter_bloc.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:provider/provider.dart'; - -class AccountPage extends StatefulWidget{ - _AccountPagePageState _state; - - _AccountPagePageState createState() { - _state = new _AccountPagePageState(); - return _state; - } - - State getState() { - return _state; - } -} - -class _AccountPagePageState extends State { - final UserViewModel user = UserViewModel(); - final AppLanguage appLanguage = AppLanguage(); - final Future _prefs = SharedPreferences.getInstance(); - final BottomNavigator bottomNav = BottomNavigator(); - Future> _exercises; - ExerciseChangingViewModel exerciseChangingViewModel; - - - - @override - void initState() { - exerciseChangingViewModel = Provider.of(context, listen: false); - super.initState(); - } +// ignore: must_be_immutable +class AccountPage extends StatelessWidget { + // ignore: close_sinks + AccountBloc accountBloc; @override Widget build(BuildContext context) { - return Consumer( - builder: (context, model, child ) { - if ( model.customer == null ) { - CustomerViewModel customerViewModel = CustomerViewModel(); - model.customer = customerViewModel; - if ( model.customer.getCustomer() == null ) { - model.customer.setCustomer(Auth().userLoggedIn); + accountBloc = BlocProvider.of(context); + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context).translate('Account')), + backgroundColor: Colors.transparent, + ), + 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( + 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( - 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: [ - 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) - ); - }); + ), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 2)); } - ListTile loginOut( CustomerChangingViewModel model ) { + ListView accountWidget(BuildContext context, String customerName, AccountBloc accountBloc) { + return ListView(padding: EdgeInsets.only(top: 135), children: [ + 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(); String text = "Logout"; Color buttonColor = Colors.orange; - if ( model.customer.getCustomer() == null ) { + if ( accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer.email == null) { text = "Login"; buttonColor = Colors.blue; } @@ -149,25 +117,20 @@ class _AccountPagePageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(AppLocalizations.of(context).translate(text), - style: TextStyle( - color: buttonColor - )), + style: TextStyle( + color: buttonColor + )), Icon(Icons.arrow_forward_ios), ]), textColor: buttonColor, color: Colors.white, onPressed: () => { - setState(() { - if ( model.customer.getCustomer() == null ) { - print("Login"); - Navigator.of(context).pushNamed("login", arguments: widget._state); - } else { - print("Logout"); - Auth().logout(); - model.customer.setCustomer(null); - } - - }) + if ( accountBloc.loggedIn ) { + accountBloc.add(AccountLogout()) + } else { + accountBloc.add(AccountLogin()), + Navigator.of(context).pushNamed('login'), + } }, ), ); @@ -175,7 +138,7 @@ class _AccountPagePageState extends State { return element; } - ListTile exercises( ExerciseChangingViewModel model ) { + /* ListTile exercises( ExerciseChangingViewModel model ) { ListTile element = ListTile(); if ( Auth().userLoggedIn == null ) { return element; @@ -202,7 +165,8 @@ class _AccountPagePageState extends State { return element; } - +*/ + /* Widget getExercises( ExerciseChangingViewModel model ) { List exercises = model.exerciseList; @@ -262,5 +226,5 @@ class _AccountPagePageState extends State { return element; - } -} \ No newline at end of file + } */ +} diff --git a/lib/view/custom_exercise_page.dart b/lib/view/custom_exercise_page.dart new file mode 100644 index 0000000..820f8eb --- /dev/null +++ b/lib/view/custom_exercise_page.dart @@ -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 { + final GlobalKey _scaffoldKey = new GlobalKey(); + + @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(context); + exerciseBloc.exerciseRepository.setExerciseType(exerciseType); + + return Scaffold( + key: _scaffoldKey, + resizeToAvoidBottomInset: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + title: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + 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( + 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%: ", + )) + ]) + ); + } +} diff --git a/lib/view/customer_bodytype_page.dart b/lib/view/customer_bodytype_page.dart index a3dcba8..70c8f0f 100644 --- a/lib/view/customer_bodytype_page.dart +++ b/lib/view/customer_bodytype_page.dart @@ -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/viewmodel/customer_changing_view_model.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; // ignore: must_be_immutable -class CustomerBodyTypePage extends StatefulWidget{ +class CustomerBodyTypePage extends StatefulWidget { _CustomerBodyTypePageState _state; _CustomerBodyTypePageState createState() { @@ -13,7 +15,6 @@ class CustomerBodyTypePage extends StatefulWidget{ } class BodyTypeItem { - static String endomorph = "endomorph"; static String ectomorph = "ectomorph"; static String mesomorph = "mesomorph"; @@ -23,15 +24,15 @@ class _CustomerBodyTypePageState extends State { String selected; @override Widget build(BuildContext context) { - final CustomerChangingViewModel changingViewModel = ModalRoute.of(context).settings.arguments; - final double cWidth = MediaQuery.of(context).size.width*0.75; + final CustomerRepository customerRepository = + ModalRoute.of(context).settings.arguments; + final double cWidth = MediaQuery.of(context).size.width * 0.75; return Scaffold( appBar: AppBar( title: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Image.asset( 'asset/image/WT_long_logo.png', fit: BoxFit.cover, @@ -40,151 +41,160 @@ class _CustomerBodyTypePageState extends State { ], ), backgroundColor: Colors.transparent, - ), - body: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_light_background.png'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), + ), + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_light_background.png'), + fit: BoxFit.cover, + alignment: Alignment.center, ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Divider(), - Wrap( - //runAlignment: WrapAlignment.center, - alignment: WrapAlignment.center, + ), + child: BlocProvider( + create: (context) => + CustomerChangeBloc(customerRepository: customerRepository), + child: Builder(builder: (context) { + // ignore: close_sinks + CustomerChangeBloc changeBloc = + BlocProvider.of(context); + + return Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - AppLocalizations.of(context).translate("Your Body Type"), - textAlign: TextAlign.center, - style: TextStyle(color: Colors.orange, - fontSize: 42, fontFamily: 'Arial', - fontWeight: FontWeight.w900 ),) - ] - ), - - - Divider(), - FlatButton( - child: Container( - width: cWidth, - child: Column( + Divider(), + Wrap( + //runAlignment: WrapAlignment.center, + alignment: WrapAlignment.center, children: [ - Text(AppLocalizations.of(context).translate("Endomorph"), - textWidthBasis: TextWidthBasis.longestLine, - style: TextStyle(color: Colors.blue, - fontSize: 32, fontFamily: 'Arial', - fontWeight: FontWeight.w900 )), - - ], - ) - ), - padding: EdgeInsets.all(10.0), - shape: getShape(changingViewModel, BodyTypeItem.endomorph ), - onPressed:() => - { - setState((){ - selected = BodyTypeItem.endomorph; - changingViewModel.customer.setBodyType(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, + Text( + AppLocalizations.of(context) + .translate("Your Body Type"), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.orange, + fontSize: 42, + fontFamily: 'Arial', + fontWeight: FontWeight.w900), + ) + ]), + Divider(), + FlatButton( + child: Container( + width: cWidth, + child: Column( + children: [ + Text( + AppLocalizations.of(context) + .translate("Endomorph"), + textWidthBasis: TextWidthBasis.longestLine, + style: TextStyle( + color: Colors.blue, + fontSize: 32, + fontFamily: 'Arial', + fontWeight: FontWeight.w900)), + ], + )), + padding: EdgeInsets.all(10.0), + shape: getShape( + customerRepository, BodyTypeItem.endomorph), + onPressed: () => { + setState(() { + 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 ), - ], - ), - ), - padding: EdgeInsets.all(10.0), - shape: getShape(changingViewModel, BodyTypeItem.ectomorph ), - - onPressed:() => - { - setState((){ - selected = BodyTypeItem.ectomorph; - changingViewModel.customer.setBodyType(selected); - print(selected); - }), - - } - ), - Divider(), - FlatButton( - child: Container( - width: cWidth, - child: Column( - children: [ - InkWell( - child: Text(AppLocalizations.of(context).translate("Mesomorph"), - style: TextStyle(color: Colors.blue, - fontSize: 32, fontFamily: 'Arial', - fontWeight: FontWeight.w900 ),), - highlightColor: Colors.white, + onPressed: () => { + setState(() { + selected = BodyTypeItem.ectomorph; + changeBloc.add(CustomerBodyTypeChange(bodyType: selected)); + print(selected); + }), + }), + Divider(), + FlatButton( + child: Container( + width: cWidth, + child: Column( + children: [ + InkWell( + child: Text( + AppLocalizations.of(context) + .translate("Mesomorph"), + 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)); - ], - ), - ), - padding: EdgeInsets.all(10.0), - shape: getShape(changingViewModel, BodyTypeItem.mesomorph ), - onPressed:() => - { - setState((){ - selected = BodyTypeItem.mesomorph; - changingViewModel.customer.setBodyType(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("customerWelcomePage", arguments: changingViewModel) - }, - ) - ], - ), - ), - ); + print(selected); + }), + }), + Divider(), + RaisedButton( + color: Colors.orange, + textColor: Colors.white, + child: InkWell( + child: Text( + AppLocalizations.of(context).translate("Next"))), + onPressed: () => { + changeBloc.add(CustomerSave()), + Navigator.of(context).pop(), + Navigator.of(context).pushNamed("customerWelcomePage", arguments: customerRepository) + }, + ) + ], + ); + })), + )); } - dynamic getShape( CustomerChangingViewModel changingViewModel, String fitnessLevel ) { - String selected = changingViewModel.customer.bodyType; - dynamic returnCode = ( selected == fitnessLevel ) ? - RoundedRectangleBorder( - side: BorderSide(width: 4, color: Colors.orange), - ) - : - RoundedRectangleBorder( - side: BorderSide(width: 1, color: Colors.blue), - ); + dynamic getShape(CustomerRepository customerRepository, String fitnessLevel) { + String selected = customerRepository.bodyType; + dynamic returnCode = (selected == fitnessLevel) + ? RoundedRectangleBorder( + side: BorderSide(width: 4, color: Colors.orange), + ) + : RoundedRectangleBorder( + side: BorderSide(width: 1, color: Colors.blue), + ); //return return returnCode; } -} \ No newline at end of file +} diff --git a/lib/view/customer_fitness_page.dart b/lib/view/customer_fitness_page.dart index 22d7156..e71bc77 100644 --- a/lib/view/customer_fitness_page.dart +++ b/lib/view/customer_fitness_page.dart @@ -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/viewmodel/customer_changing_view_model.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; // ignore: must_be_immutable -class CustomerFitnessPage extends StatefulWidget{ +class CustomerFitnessPage extends StatefulWidget { _CustomerFitnessPageState _state; _CustomerFitnessPageState createState() { @@ -29,15 +31,15 @@ class _CustomerFitnessPageState extends State { @override Widget build(BuildContext context) { - final double cWidth = MediaQuery.of(context).size.width*0.75; - final CustomerChangingViewModel changingViewModel = ModalRoute.of(context).settings.arguments; - selected = changingViewModel.customer.fitnessLevel; + final double cWidth = MediaQuery.of(context).size.width * 0.75; + final CustomerRepository customerRepository = + ModalRoute.of(context).settings.arguments; + selected = customerRepository.customer.fitnessLevel; return Scaffold( appBar: AppBar( title: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Image.asset( 'asset/image/WT_long_logo.png', fit: BoxFit.cover, @@ -46,206 +48,243 @@ class _CustomerFitnessPageState extends State { ], ), backgroundColor: Colors.transparent, - ), - body: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Container( + ), + body: BlocProvider( + create: (context) => + CustomerChangeBloc(customerRepository: customerRepository), + child: Builder(builder: (context) { + // ignore: close_sinks + CustomerChangeBloc changeBloc = + BlocProvider.of(context); - padding: EdgeInsets.only(bottom: 200), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_light_background.png'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), - ), - child: Column( - 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 ),) - ] + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + padding: EdgeInsets.only(bottom: 200), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_light_background.png'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), ), - - - Divider(), - FlatButton( - child: Container( - width: cWidth, - child: Column( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Divider(), + Wrap( + //runAlignment: WrapAlignment.center, + alignment: WrapAlignment.center, children: [ - Text(AppLocalizations.of(context).translate("Beginner"), - textWidthBasis: TextWidthBasis.longestLine, - style: TextStyle(color: Colors.blue, - fontSize: 32, fontFamily: 'Arial', - fontWeight: FontWeight.w900 )), - Text(AppLocalizations.of(context).translate("I am beginner"), - style: TextStyle(color: Colors.black, - fontSize: 20, fontFamily: 'Arial', - fontWeight: FontWeight.w100 ),), - ], - ) - ), - padding: EdgeInsets.all(10.0), - shape: getShape(changingViewModel, FitnessItem.beginner ), - onPressed:() => - { - setState((){ - selected = FitnessItem.beginner; - changingViewModel.customer.setFitnessLevel(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, + Text( + AppLocalizations.of(context) + .translate("Your Fitness State"), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.orange, + fontSize: 42, + fontFamily: 'Arial', + fontWeight: FontWeight.w900), + ) + ]), + Divider(), + FlatButton( + child: Container( + width: cWidth, + child: Column( + children: [ + Text( + AppLocalizations.of(context) + .translate("Beginner"), + textWidthBasis: + TextWidthBasis.longestLine, + style: TextStyle( + color: Colors.blue, + fontSize: 32, + fontFamily: 'Arial', + fontWeight: FontWeight.w900)), + Text( + AppLocalizations.of(context) + .translate("I am beginner"), + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontFamily: 'Arial', + fontWeight: FontWeight.w100), + ), + ], + )), + 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"), - style: TextStyle(color: Colors.black, - fontSize: 20, fontFamily: 'Arial', - fontWeight: FontWeight.w100 ),), - highlightColor: Colors.white, + ), + padding: EdgeInsets.all(10.0), + shape: getShape( + customerRepository, FitnessItem.intermediate), + onPressed: () => { + 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(changingViewModel, FitnessItem.intermediate ), - - onPressed:() => - { - setState((){ - selected = FitnessItem.intermediate; - changingViewModel.customer.setFitnessLevel(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, + ), + padding: EdgeInsets.all(10.0), + shape: getShape( + customerRepository, FitnessItem.advanced), + onPressed: () => { + setState(() { + selected = FitnessItem.advanced; + changeBloc.add(CustomerFitnessChange(fitness: selected)); + print(selected); + }), + }), + Divider(), + FlatButton( + 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, + ), + ], ), - 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(changingViewModel, FitnessItem.advanced ), - onPressed:() => - { - setState((){ - selected = FitnessItem.advanced; - changingViewModel.customer.setFitnessLevel(selected); - print(selected); - }), - - } - ), - Divider(), - FlatButton( - 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) - }, - ) - ], - ), - ), - ) - ); + ), + padding: EdgeInsets.all(10.0), + shape: getShape( + customerRepository, FitnessItem.professional), + onPressed: () => { + setState(() { + selected = FitnessItem.professional; + changeBloc.add(CustomerFitnessChange(fitness: selected)); + print(selected); + }), + }), + Divider(), + RaisedButton( + color: Colors.orange, + textColor: Colors.white, + child: InkWell( + child: Text(AppLocalizations.of(context) + .translate("Next"))), + onPressed: () => { + changeBloc.add(CustomerSave()), + Navigator.of(context).pop(), + Navigator.of(context).pushNamed( + "customerBodyTypePage", + arguments: customerRepository) + }, + ) + ], + ), + ), + ); + }))); } - dynamic getShape( CustomerChangingViewModel changingViewModel, String fitnessLevel ) { - String selected = changingViewModel.customer.fitnessLevel; - dynamic returnCode = ( selected == fitnessLevel ) ? - RoundedRectangleBorder( - side: BorderSide(width: 4, color: Colors.orange), - ) - : - RoundedRectangleBorder( - side: BorderSide(width: 1, color: Colors.blue), - ); + dynamic getShape(CustomerRepository customerRepository, String fitnessLevel) { + String selected = customerRepository.fitnessLevel; + dynamic returnCode = (selected == fitnessLevel) + ? RoundedRectangleBorder( + side: BorderSide(width: 4, color: Colors.orange), + ) + : RoundedRectangleBorder( + side: BorderSide(width: 1, color: Colors.blue), + ); //return return returnCode; } - -} \ No newline at end of file +} diff --git a/lib/view/customer_goal_page.dart b/lib/view/customer_goal_page.dart index 30320cb..18a8a01 100644 --- a/lib/view/customer_goal_page.dart +++ b/lib/view/customer_goal_page.dart @@ -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/viewmodel/customer_changing_view_model.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; -// ignore: must_be_immutable -class CustomerGoalPage extends StatefulWidget{ - _CustomerGoalPageState _state; - - _CustomerGoalPageState createState() { - _state = _CustomerGoalPageState(); - return _state; - } -} - -class GoalsItem{ +class GoalsItem { static String muscle = "gain_muscle"; static String weight = "weight_loss"; } -class _CustomerGoalPageState extends State { - String selected; +// ignore: must_be_immutable +class CustomerGoalPage extends StatefulWidget { - initState() { - super.initState(); - } + @override + State createState() => _CustomerGoalPage(); +} + + +class _CustomerGoalPage extends State { + String selected; @override Widget build(BuildContext context) { - final double cWidth = MediaQuery.of(context).size.width*0.75; - final CustomerChangingViewModel changingViewModel = ModalRoute.of(context).settings.arguments; - selected = changingViewModel.customer.goal; + final double cWidth = MediaQuery.of(context).size.width * 0.75; + final CustomerRepository customerRepository = + ModalRoute.of(context).settings.arguments; + return Scaffold( appBar: AppBar( title: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Image.asset( 'asset/image/WT_long_logo.png', fit: BoxFit.cover, @@ -44,116 +40,138 @@ class _CustomerGoalPageState extends State { ], ), backgroundColor: Colors.transparent, - ), - body: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_light_background.png'), - fit: BoxFit.cover, - - alignment: Alignment.center, - ), + ), + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_light_background.png'), + fit: BoxFit.cover, + 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(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 ) { - String selectedGoal = changingViewModel.customer.goal; - dynamic returnCode = ( selectedGoal == goal ) ? - RoundedRectangleBorder( - side: BorderSide(width: 4, color: Colors.red), - ) - : null; + dynamic getShape(CustomerChangeBloc customerBloc, String goal) { + String selectedGoal = customerBloc.customerRepository.goal; + dynamic returnCode = (selectedGoal == goal) + ? RoundedRectangleBorder( + side: BorderSide(width: 4, color: Colors.red), + ) + : null; //return return returnCode; } - -} \ No newline at end of file +} diff --git a/lib/view/customer_list_page.dart b/lib/view/customer_list_page.dart deleted file mode 100644 index fe8eba0..0000000 --- a/lib/view/customer_list_page.dart +++ /dev/null @@ -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 { - //final TextEditingController _controller = TextEditingController(); - Future> _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>( - 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: [ - 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, - ) - - ); - } - -} \ No newline at end of file diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart index d2c274a..e7d0990 100644 --- a/lib/view/customer_modify_page.dart +++ b/lib/view/customer_modify_page.dart @@ -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/viewmodel/customer_changing_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.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 -class CustomerModifyPage extends StatefulWidget{ - _CustomerModifyPageState _state; - - _CustomerModifyPageState createState() { - _state = _CustomerModifyPageState(); - return _state; - } -} - -class GenderItem { - GenderItem(this.dbValue,this.name); - final String dbValue; - String name; -} - -class _CustomerModifyPageState extends State { +class CustomerModifyPage extends StatelessWidget{ final _formKey = GlobalKey(); - GenderItem selectedGender; - List genders; - @override - void initState() { - super.initState(); - genders = [ - GenderItem("m", "Man"), - GenderItem("w", "Woman"), - ]; - selectedGender = genders[0]; - - } @override Widget build(BuildContext context) { - //final CustomerViewModel model = CustomerViewModel(); - //model.customer = Auth().userLoggedIn; - //final CustomerChangingViewModel customerChangeModel = - // CustomerChangingViewModel(model); - CustomerChangingViewModel customerChangingViewModel = Provider.of(context, listen: false); - customerChangingViewModel.customer.customer.sex = selectedGender.dbValue; - + // ignore: close_sinks + final accountBloc = BlocProvider.of(context); // we cannot initialize the translations in the initState - genders.forEach((GenderItem element) { - if ( element.dbValue == "m") { +/* genders.forEach((GenderItem element) { + if (element.dbValue == "m") { element.name = AppLocalizations.of(context).translate("Man"); } - if ( element.dbValue == "w") { + if (element.dbValue == "w") { 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(context); - return Scaffold( - resizeToAvoidBottomInset: true, - appBar: AppBar( - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("Profil"), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - 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, + return Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Profil"), + Image.asset( + 'asset/image/WT_long_logo.png', + fit: BoxFit.cover, + height: 65.0, + ), + ], ), + //title: Text(AppLocalizations.of(context).translate('Settings')), + backgroundColor: Colors.transparent, ), - child: Form( - key: _formKey, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - padding: EdgeInsets.only(top: 40, left: 25, right: 45, bottom:100), + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_light_background.png'), + 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( - alignment: Alignment.center, + child: Container( + alignment: Alignment.center, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ - Expanded( - child: TextFormField( - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - fillColor: Colors.white24, - filled: true, - labelText: AppLocalizations.of(context).translate('Email'), - ), - initialValue: customerChangingViewModel.customer.customer.email, - onFieldSubmitted: (input) => customerChangingViewModel.customer.setEmail(input) - ) - ) - ], - ), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ + Expanded( + child: + TextFieldBlocBuilder( + key: LibraryKeys.loginEmailField, + style: TextStyle(fontSize: 12), + textFieldBloc: customerBloc.emailField, + decoration: InputDecoration( + fillColor: Colors.white24, + filled: true, + labelText: AppLocalizations.of(context) + .translate('Email'), + ), + ), - Expanded( - child: TextFormField( - style: TextStyle(fontSize: 12), - obscureText: true, - decoration: InputDecoration( - fillColor: Colors.white24, - filled: true, - labelText: AppLocalizations.of(context).translate('Password (Leave empty if you don\'t want to change)' ), + /* TextFormField( + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + fillColor: Colors.white24, + filled: true, + labelText: AppLocalizations.of(context) + .translate('Email'), + ), */ + // initialValue: customerChangingViewModel.customer + // .customer.email, + // onFieldSubmitted: (input) => + // customerChangingViewModel.customer.setEmail( + // input) + ) + ], + ), + + 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, - onFieldSubmitted: (input) => customerChangingViewModel.customer.setPassword(input) + /*TextFormField( + style: TextStyle(fontSize: 12), + obscureText: true, + decoration: InputDecoration( + fillColor: Colors.white24, + filled: true, + labelText: AppLocalizations.of(context) + .translate( + 'Password (Leave empty if you don\'t want to change)'), + ), + //initialValue: customerChangingViewModel.customer.customer.password, + // onFieldSubmitted: (input) => customerChangingViewModel.customer.setPassword(input) + )*/ ) - ) - ], - ), - 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('Name'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + + Expanded( + child: TextFieldBlocBuilder( + 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( - child: TextFormField( - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - fillColor: Colors.white24, - filled: true, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ - 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( - child: TextFormField( - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - fillColor: Colors.white24, - filled: true, - labelText: AppLocalizations.of(context).translate('Birth Year'), - ), - keyboardType: TextInputType.number, - inputFormatters: [ - WhitelistingTextInputFormatter.digitsOnly + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + + Expanded( + child: TextFieldBlocBuilder( + style: TextStyle(fontSize: 12), + textFieldBloc: customerBloc.birthYearField, + inputFormatters: [ + FilteringTextInputFormatter(RegExp(r"[\d]"), allow: true) ], - initialValue: customerChangingViewModel.customer.customer.birthYear.toString(), - onFieldSubmitted: (input) => customerChangingViewModel.customer.setBirthYear(int.parse(input)) - ) - ) - ], - ), - 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'), + decoration: InputDecoration( + fillColor: Colors.white24, + filled: true, + labelText: AppLocalizations.of(context) + .translate('Birth Year'), + ), ), - inputFormatters: [ - WhitelistingTextInputFormatter.digitsOnly - ], - initialValue: customerChangingViewModel.customer.customer.weight.toString(), - keyboardType: TextInputType.number, - onFieldSubmitted: (input) => customerChangingViewModel.customer.setWeight(int.parse(input)), ) - ) - ], - ), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ - Expanded( - child: DropdownButtonHideUnderline( + Expanded( + 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( - hint: Text(AppLocalizations.of(context).translate('Select a gender')), - style: TextStyle(fontSize: 12, color: Colors.black), + hint: Text( + AppLocalizations.of(context).translate( + 'Select a gender')), + style: TextStyle( + fontSize: 12, color: Colors.black), focusColor: Colors.white24, - value: selectedGender, - items: genders.map((GenderItem gender){ + // value: selectedGender, + items: genders.map((GenderItem gender) { return DropdownMenuItem( value: gender, child: Text(gender.name) ); }).toList(), - onChanged:(GenderItem gender) => { - setState(() { - selectedGender = gender; - customerChangingViewModel.customer.setSex(gender.dbValue); + onChanged: (GenderItem gender) => { + // setState(() { + // selectedGender = gender; + // customerChangingViewModel.customer.setSex(gender.dbValue); - print ("Gender " + gender.name); - }) - //model.customer.sex = - }, + print ("Gender " + gender.name); + //}) + //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) - }, - ) - ) - ], - ), - ], + ), ), - ), - ), - ) - ) + ) + ) + ); + }) ); } + } \ No newline at end of file diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart index dd6c7c4..9cb47d9 100644 --- a/lib/view/exercise_new_page.dart +++ b/lib/view/exercise_new_page.dart @@ -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/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:intl/intl.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -//import 'package:datetime_picker_formfield/datetime_picker_formfield.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; class ExerciseNewPage extends StatefulWidget{ _ExerciseNewPageState createState() => _ExerciseNewPageState(); } -class _ExerciseNewPageState extends State { - final List excluded = [43,44]; - final _formKey = GlobalKey(); +class _ExerciseNewPageState extends State { @override Widget build(BuildContext context) { + final ExerciseType exerciseType = ModalRoute.of(context).settings.arguments; - return Consumer( - builder: (context, model, child ) { - String exerciseName = ""; - String customerName = ""; - if ( model != null ) { - 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"; + return BlocProvider( + create: (context) => ExerciseFormBloc(exerciseRepository: ExerciseRepository()), + child: Builder(builder: (context) { + // ignore: close_sinks + final exerciseBloc = BlocProvider.of(context); - exerciseName = model != null && - model.exerciseType != null - ? model.exerciseType.name - : "Please select an exercise"; - } + exerciseBloc.exerciseRepository.setExerciseType(exerciseType); + String exerciseName = exerciseBloc.exerciseRepository.exerciseType.name; - AppLanguage appLanguage = AppLanguage(); - 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, + return Form( autovalidate: true, child: Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.deepOrange), - onPressed: () => { - Navigator.of(context).pop() - }, + backgroundColor: Colors.transparent, + title: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + 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( - decoration: BoxDecoration( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( image: DecorationImage( - image: AssetImage('asset/image/WT_login.png'), - fit: BoxFit.cover, - //height: double.infinity, - //width: double.infinity, - alignment: Alignment.center, + image: AssetImage('asset/image/WT_light_background.png'), + fit: BoxFit.fill, + alignment: Alignment.center, ), ), child: Container( - padding: const EdgeInsets.only (top: 65, left:25, right: 100), - child: Column( + padding: const EdgeInsets.only (top: 25, left:25, right: 25), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - columnQuantityUnit(model), - columnQuantity(model), + Divider(color: Colors.transparent,), + Divider(color: Colors.transparent,), + Divider(color: Colors.transparent,), - Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - new InkWell( - child: new Text(dateName, - style: TextStyle( fontSize: 16,color: Colors.blue)), - ), - ButtonTheme( - minWidth: 30.0, - height: 30.0, - child: FlatButton( + Text(AppLocalizations.of(context).translate(exerciseName) + " " + + AppLocalizations.of(context).translate('Save Exercise'), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.deepOrange)), + Divider(color: Colors.transparent,), + Divider(color: Colors.transparent,), + Divider(color: Colors.transparent,), + columnQuantityUnit(exerciseBloc), + Divider(color: Colors.transparent,), + Divider(color: Colors.transparent,), + Divider(color: Colors.transparent,), - padding: EdgeInsets.only(bottom: 0), - color: Colors.transparent, - splashColor: Colors.black26, - child: Row( - children: [ + columnQuantity(exerciseBloc), + Divider(), - 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( textColor: Colors.white, color: Colors.deepOrange, focusColor: Colors.white, onPressed: () => { - if (_formKey.currentState.validate()) { - //model = ExerciseChangingViewModel(model.exerciseViewModel), - - if ( ! excluded.contains(model.exerciseType.exerciseTypeId) ) { - model.addExercise(), - }, - Navigator.pop(context), - } + confirmationDialog( exerciseBloc ), }, - 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(); - if ( model.exerciseType != null && model.exerciseType.unitQuantity == "1") { + if ( bloc.exerciseRepository.exerciseType != null && + bloc.exerciseRepository.exerciseType.unitQuantity == "1") { column = Column( 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, textAlign: TextAlign.center, - initialValue: "0", + initialValue: "", style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), @@ -155,18 +158,19 @@ class _ExerciseNewPageState extends State { return validateNumberInput(input); }, inputFormatters: [ - WhitelistingTextInputFormatter(RegExp(r"[\d.]")) + FilteringTextInputFormatter(RegExp(r"[\d.]")) + //WhitelistingTextInputFormatter(RegExp(r"[\d.]")) ], onChanged: (input) => { print ("UnitQuantity value $input"), - model.exerciseViewModel.setUnitQuantity( + bloc.exerciseRepository.setUnitQuantity( double.parse(input)) }, - ), + ), */ new InkWell( child: new Text(AppLocalizations.of(context).translate( - model.exerciseType.unitQuantityUnit), + bloc.exerciseRepository.exerciseType.unitQuantityUnit), style: TextStyle(fontSize: 16)), ), @@ -175,16 +179,48 @@ class _ExerciseNewPageState extends State { return column; } - Column columnQuantity( ExerciseChangingViewModel model) { - Column column = Column(); - - column = Column( + Column columnQuantity( ExerciseFormBloc bloc ) { + Column column = Column( 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, textAlign: TextAlign.center, - initialValue: "0", - style: TextStyle(fontSize: 60, + initialValue: "", + style: TextStyle(fontSize: 50, color: Colors.deepOrange, fontWeight: FontWeight.bold), validator: (input) { @@ -195,17 +231,12 @@ class _ExerciseNewPageState extends State { ], onChanged: (input) => { - print ("Quantity value $input"), - model.exerciseViewModel.setQuantity( - double.parse(input)), - model.exerciseViewModel.setUnit(model.exerciseType.unit) + print ("Quantity value $input"), + bloc.exerciseRepository.setQuantity(double.parse(input)), + bloc.exerciseRepository.setUnit(bloc.exerciseRepository.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; } + + 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), + }, + ) + ], + ) + ); + } } diff --git a/lib/view/exercise_type_list_page.dart b/lib/view/exercise_type_list_page.dart deleted file mode 100644 index ce19ebb..0000000 --- a/lib/view/exercise_type_list_page.dart +++ /dev/null @@ -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> _exerciseTypes; - final _exerciseTypeViewModel = ExerciseTypeChangingViewModel(null); - - // Push the page and remove everything else - navigateToPage(BuildContext context, String page) { - Navigator.of(context).pushNamedAndRemoveUntil(page, (Route 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>( - 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, - ) - ); - } -} \ No newline at end of file diff --git a/lib/view/exercise_type_modify_page.dart b/lib/view/exercise_type_modify_page.dart deleted file mode 100644 index 2ba415a..0000000 --- a/lib/view/exercise_type_modify_page.dart +++ /dev/null @@ -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 { - final _formKey = GlobalKey(); - - @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: [ - 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, - ) - ); - } -} diff --git a/lib/view/exercise_type_new_page.dart b/lib/view/exercise_type_new_page.dart deleted file mode 100644 index c568428..0000000 --- a/lib/view/exercise_type_new_page.dart +++ /dev/null @@ -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(); - - @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: [ - 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, - ) - ); - } -} \ No newline at end of file diff --git a/lib/view/login.dart b/lib/view/login.dart index 0136261..88e4372 100644 --- a/lib/view/login.dart +++ b/lib/view/login.dart @@ -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/model/auth.dart'; -import 'package:aitrainer_app/view/account.dart'; -import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; -import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart'; -import 'package:aitrainer_app/viewmodel/user_view_model.dart'; +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.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{ - _LoginPageState createState() => _LoginPageState(); +class LoginPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return LoginWidget(); + } } -class _LoginPageState extends State { +class LoginWidget extends StatefulWidget { + LoginWidget(); + + @override + State createState() => _LoginWidget(); +} + +class _LoginWidget extends State { final GlobalKey _scaffoldKey = new GlobalKey(); - final UserViewModel user = UserViewModel(); - final bool _obscureText = true; final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - UserChangingViewModel model = UserChangingViewModel(user); - CustomerChangingViewModel customerChangingViewModel = Provider.of(context, listen: false); - user.createNew(); - Future customer; - final State stateAccount = ModalRoute.of(context).settings.arguments; - - return Scaffold( - key: _scaffoldKey, - body: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_login.png'), - fit: BoxFit.cover, - //height: double.infinity, - //width: double.infinity, - alignment: Alignment.center, - ), - ), - child: Form( - key: _formKey, + final accountBloc = BlocProvider.of(context); + return BlocProvider( + create: (context) => LoginFormBloc( + userRepository: UserRepository(), + accountBloc: accountBloc + ), + child: Builder(builder: (context) { + final loginBloc = BlocProvider.of(context); + return Scaffold( + key: _scaffoldKey, + body: FormBlocListener( + onSubmitting: (context, state) { + LoadingDialog.show(context); + }, + onSuccess: (context, state) { + LoadingDialog.hide(context); + Navigator.of(context).pushNamed('home'); + }, + onFailure: (context, state) { + LoadingDialog.hide(context); + showInSnackBar(state.failureResponse); + }, child: Container( - padding: const EdgeInsets.only (left: 25, right: 100), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Spacer(flex: 4), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - new InkWell( - child: new Text( - 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: [ 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: [ - 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), - ]) + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_login.png'), + fit: BoxFit.cover, + //height: double.infinity, + //width: double.infinity, + alignment: Alignment.center, + ), + ), + child: buildLoginForm(loginBloc, accountBloc), ), ), - ) + ); + })); + } + + 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: [ + 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: [ + 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: [ + 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) { - _scaffoldKey.currentState.showSnackBar( - SnackBar( - backgroundColor: Colors.orange, - content: Text( - AppLocalizations.of(context).translate("Customer does not exist or the password is wrong") + " " + error, - style: TextStyle(color: Colors.white)) - ) - ); + _scaffoldKey.currentState.showSnackBar(SnackBar( + backgroundColor: Colors.orange, + content: Text(error, style: TextStyle(color: Colors.white)))); } -} \ No newline at end of file + + Future _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; + } + } +} diff --git a/lib/view/menu_page.dart b/lib/view/menu_page.dart index c2f8a4c..f376d82 100644 --- a/lib/view/menu_page.dart +++ b/lib/view/menu_page.dart @@ -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/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/menu_page_widget.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'dart:collection'; -import 'package:provider/provider.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + // ignore: must_be_immutable -class MenuPage extends StatefulWidget { - _MenuPageState _state; +class MenuPage extends StatelessWidget { static const routeName = '/menu_page'; int parent; + MenuBloc menuBloc; MenuPage({this.parent}); - @override - _MenuPageState createState() { - _state = new _MenuPageState(); - return _state; - } - -} - -class _MenuPageState extends State { - final BottomNavigator bottomNav = BottomNavigator(); - @override Widget build(BuildContext context) { - final MenuTests menu = MenuTests(context); + menuBloc = BlocProvider.of(context); return Scaffold( - appBar: AppBar( - backgroundColor: Colors.transparent, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(AppLocalizations.of(context).translate("Tests")), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => { - 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, - ), + appBar: AppBar( + backgroundColor: Colors.transparent, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(AppLocalizations.of(context).translate("Tests")), + Image.asset( + 'asset/image/WT_long_logo.png', + fit: BoxFit.cover, + height: 65.0, ), - child: CustomScrollView( - scrollDirection: Axis.vertical, - slivers: [ - 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( + 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) ); } - - SliverList buildMenuColumn(int parent, BuildContext context, MenuTests menu) { - LinkedHashMap tree = menu.getMenuItems(); - List _columnChildren = List(); - ExerciseType exerciseType; - ExerciseChangingViewModel model = Provider.of(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; - } - } + +class LoadingMenuDialog extends StatefulWidget { + + LoadingMenuDialog({Key key}) : super(key: key); + + @override + State createState() => _LoadingMenuDialog(); +} + +class _LoadingMenuDialog extends State { + @override + void initState() { + super.initState(); + + /// We require the initializers to run after the loading screen is rendered + SchedulerBinding.instance.addPostFrameCallback((_) { + BlocProvider.of(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, + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 70572e8..946ba4f 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -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/model/auth.dart'; -import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart'; -import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart'; -import 'package:aitrainer_app/viewmodel/user_view_model.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.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{ - _RegistrationPageState createState() => _RegistrationPageState(); +import '../library_keys.dart'; + +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 { - final GlobalKey _scaffoldKey = new GlobalKey(); - final UserViewModel user = UserViewModel(); - bool _obscureText = true; +class RegistrationWidget extends StatefulWidget { + final UserRepository userRepository; + final CustomerRepository customerRepository; + RegistrationWidget({this.userRepository, this.customerRepository}); + + @override + State createState() => _RegistrationWidget(); +} + +class _RegistrationWidget extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - UserChangingViewModel model = UserChangingViewModel(user); - CustomerChangingViewModel customerChangingViewModel = Provider.of(context, listen: false); - user.createNew(); + final cWidth = Common.mediaSizeWidth(context); + // ignore: close_sinks + final accountBloc = BlocProvider.of(context); + return BlocProvider( + create: (context) => RegistrationFormBloc( + userRepository: UserRepository(), + accountBloc: accountBloc), + child: Builder(builder: (context) { + // ignore: close_sinks + final registrationBloc = BlocProvider.of(context); - return Scaffold( - key: _scaffoldKey, - body: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_login.png'), - fit: BoxFit.cover, - //height: double.infinity, - //width: double.infinity, - alignment: Alignment.center, - ), - ), - child: Form( - key: _formKey, - child: Container( - padding: const EdgeInsets.only (left:25, right: 100), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Spacer(flex:4), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - new InkWell( - child: new Text(AppLocalizations.of(context).translate('SignUp'), - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), - ), - ], - ), + return Scaffold( + key: _scaffoldKey, + body: FormBlocListener( + onSubmitting: (context, state) { + LoadingDialog.show(context); + }, + onSuccess: (context, state) { + LoadingDialog.hide(context); + Navigator.of(context).pushNamed('customerModifyPage'); + }, + onFailure: (context, state) { + LoadingDialog.hide(context); + showInSnackBar(state.failureResponse); + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_login.png'), + fit: BoxFit.cover, + //height: double.infinity, + //width: double.infinity, + alignment: Alignment.center, + ), + ), + child: Form( + 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: [ + //Spacer(flex:4), - 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: [ new FlatButton( - child: Image.asset('asset/image/WT_OK.png', - width: 100, - height:100 + FlatButton( + child: new Image.asset( + 'asset/image/login_fb.png', + width: cWidth * .85, + ), + onPressed: () => { + _fbLogin(), + print("Login with FB"), + }, ), - onPressed:() => { - if (_formKey.currentState.validate()) { - model = UserChangingViewModel(user), - model.addUser().then((_) => - { - Navigator.of(context).pushNamed("customerModifyPage",), - customerChangingViewModel.customer.setCustomer(Auth().userLoggedIn), - }).catchError(( error, stackTrace )=> showInSnackBar() + Text(AppLocalizations.of(context).translate("OR")), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + new InkWell( + child: new Text( + AppLocalizations.of(context) + .translate('SignUp'), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24)), ), - - } - }), - ]), - Spacer(flex:2), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - 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), - ]) - ), - ), - ), - - ); + ], + ), + Divider(), + TextFieldBlocBuilder( + key: LibraryKeys.loginEmailField, + textFieldBloc: registrationBloc.emailField, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + labelText: 'Email', + ), + ), + Divider( + color: Colors.transparent, + ), + TextFieldBlocBuilder( + key: LibraryKeys.loginPasswordField, + textFieldBloc: registrationBloc.passwordField, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + labelText: 'Password', + ), + suffixButton: SuffixButton.obscureText, + ), + Divider( + color: Colors.transparent, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + new FlatButton( + child: Image.asset( + 'asset/image/WT_OK.png', + width: 100, + height: 100), + onPressed: () => { + registrationBloc.add(SubmitFormBloc()) + }), + ]), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + 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() { - _scaffoldKey.currentState.showSnackBar( - SnackBar( - backgroundColor: Colors.orange, - content: Text( - AppLocalizations.of(context).translate("Customer exists"), - style: TextStyle(color: Colors.white)) - ) - ); + void showInSnackBar(String error) { + _scaffoldKey.currentState.showSnackBar(SnackBar( + backgroundColor: Colors.orange, + content: Text( + AppLocalizations.of(context) + .translate("There is an error: during registration:") + + " " + + error, + style: TextStyle(color: Colors.white)))); } -} \ No newline at end of file + + Future _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; + } + } +} diff --git a/lib/view/settings.dart b/lib/view/settings.dart index 27f4adf..cb4e1ff 100644 --- a/lib/view/settings.dart +++ b/lib/view/settings.dart @@ -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_localization.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; +import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -class SettingsPage extends StatefulWidget{ - _SettingsPageState _state; - - _SettingsPageState createState() { - _state = new _SettingsPageState(); - return _state; - } -} - -class _SettingsPageState extends State { - final AppLanguage appLanguage = AppLanguage(); - Locale _locale; - - final _formKey = GlobalKey(); +class SettingsPage extends StatelessWidget{ @override Widget build(BuildContext context) { - BottomNavigator bottomNav = BottomNavigator(); - _locale = appLanguage.appLocal; + // ignore: close_sinks + SettingsBloc settingsBloc = BlocProvider.of(context); + settingsBloc.context = context; return Scaffold( appBar: AppBar( title: Row( @@ -50,54 +41,60 @@ class _SettingsPageState extends State { ), ), child: Form( - key: _formKey, - child: - ListView( + child: BlocConsumer( + listener: (context, state) { + 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), children: [ ListTile( leading: Icon(Icons.language), - subtitle: Text(AppLocalizations.of(context).translate("Change App Language")), + subtitle: Text( + AppLocalizations.of(context).translate( + "Change App Language")), 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")] - .map>((String value) { + + value: state.props[0] == Locale('en') + ? AppLocalizations.of(context).translate( + "English") + : AppLocalizations.of(context).translate( + "Hungarian"), + items: [AppLocalizations.of(context).translate( + "English"), AppLocalizations.of(context) + .translate("Hungarian") + ] + .map>((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).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(); - - }); - } } \ No newline at end of file diff --git a/lib/viewmodel/customer_changing_view_model.dart b/lib/viewmodel/customer_changing_view_model.dart deleted file mode 100644 index b0d7e0c..0000000 --- a/lib/viewmodel/customer_changing_view_model.dart +++ /dev/null @@ -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 customerList = List(); - - CustomerChangingViewModel(customer) { - this.customer = customer; - } - - Future addCustomer() async { - this.customer = customer; - final Customer modelCustomer = customer.getCustomer(); - await CustomerApi().addCustomer(modelCustomer); - } - - Future saveCustomer() async { - //this.customer = customer; - final Customer modelCustomer = customer.getCustomer(); - await CustomerApi().saveCustomer(modelCustomer); - } - - Future> 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); - } -} \ No newline at end of file diff --git a/lib/viewmodel/customer_view_model.dart b/lib/viewmodel/customer_view_model.dart deleted file mode 100644 index 4e8546c..0000000 --- a/lib/viewmodel/customer_view_model.dart +++ /dev/null @@ -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; - } -} diff --git a/lib/viewmodel/exercise_changing_view_model.dart b/lib/viewmodel/exercise_changing_view_model.dart deleted file mode 100644 index 0b3df88..0000000 --- a/lib/viewmodel/exercise_changing_view_model.dart +++ /dev/null @@ -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 exerciseList = List(); - - 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> getExercisesByCustomer( int customerId ) async { - final results = await ExerciseApi().getExercisesByCustomer(customerId); - this.exerciseList = results.map((item) => ExerciseViewModel(exercise: item)).toList(); - notifyListeners(); - return this.exerciseList; - } - -} \ No newline at end of file diff --git a/lib/viewmodel/exercise_type_changing_view_model.dart b/lib/viewmodel/exercise_type_changing_view_model.dart deleted file mode 100644 index 3c53aca..0000000 --- a/lib/viewmodel/exercise_type_changing_view_model.dart +++ /dev/null @@ -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 exerciseTypeList = List(); - - ExerciseTypeChangingViewModel(exerciseType) { - this.exerciseType = exerciseType; - } - - Future addExerciseType() async { - this.exerciseType = exerciseType; - final ExerciseType modelExerciseType = exerciseType.getExerciseType(); - await ExerciseTypeApi().addExerciseType(modelExerciseType); - } - - Future saveExerciseType() async { - this.exerciseType = exerciseType; - final ExerciseType modelExerciseType = exerciseType.getExerciseType(); - await ExerciseTypeApi().saveExerciseType(modelExerciseType); - } - - Future> 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); - } -} \ No newline at end of file diff --git a/lib/viewmodel/exercise_type_view_model.dart b/lib/viewmodel/exercise_type_view_model.dart deleted file mode 100644 index f666366..0000000 --- a/lib/viewmodel/exercise_type_view_model.dart +++ /dev/null @@ -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(); - } -} \ No newline at end of file diff --git a/lib/viewmodel/exercise_view_model.dart b/lib/viewmodel/exercise_view_model.dart deleted file mode 100644 index 35fb91d..0000000 --- a/lib/viewmodel/exercise_view_model.dart +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/lib/viewmodel/user_changing_view_model.dart b/lib/viewmodel/user_changing_view_model.dart deleted file mode 100644 index bfc4b9d..0000000 --- a/lib/viewmodel/user_changing_view_model.dart +++ /dev/null @@ -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 addUser() async { - final User modelUser = userViewModel.getUser(); - await CustomerApi().addUser(modelUser); - } - - Future getUser() async { - final User modelUser = userViewModel.getUser(); - await CustomerApi().getUser(modelUser); - } -} \ No newline at end of file diff --git a/lib/viewmodel/user_view_model.dart b/lib/viewmodel/user_view_model.dart deleted file mode 100644 index 20586d3..0000000 --- a/lib/viewmodel/user_view_model.dart +++ /dev/null @@ -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(); - } -} diff --git a/lib/widgets/bottom_nav.dart b/lib/widgets/bottom_nav.dart index 31099fd..bf0c86a 100644 --- a/lib/widgets/bottom_nav.dart +++ b/lib/widgets/bottom_nav.dart @@ -1,15 +1,35 @@ import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:flutter/material.dart'; -class BottomNavigator { - BottomNavigationBar buildBottomNavigator(BuildContext context, State state) { +class BottomNavigator extends StatefulWidget { + int bottomNavIndex = 0; + BottomNavigator({this.bottomNavIndex}) { + this.bottomNavIndex = bottomNavIndex; + } + + @override + _NawDrawerWidget createState() => _NawDrawerWidget(); +} + +class _NawDrawerWidget extends State { + + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BottomNavigationBar( - currentIndex: 0, // this will be set when a new tab is tapped - backgroundColor: Colors.black12, + currentIndex: widget.bottomNavIndex, // this will be set when a new tab is tapped + backgroundColor: Colors.transparent, selectedItemColor: Colors.yellow, unselectedItemColor: Colors.lightGreen, - type: BottomNavigationBarType.shifting, + //type: BottomNavigationBarType.shifting, showSelectedLabels: true, + showUnselectedLabels: true, items: [ BottomNavigationBarItem( backgroundColor: Colors.black12, @@ -18,23 +38,29 @@ class BottomNavigator { title: new Text(AppLocalizations.of(context).translate("Home")), ), 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,), title: new Text(AppLocalizations.of(context).translate("Events")), ), + BottomNavigationBarItem( + backgroundColor: Colors.black12, icon: Icon(Icons.person, color: Colors.lightGreen,), activeIcon: new Icon(Icons.person, color: Colors.yellow,), title: Text(AppLocalizations.of(context).translate("Account")) ), BottomNavigationBarItem( + backgroundColor: Colors.black12, icon: Icon(Icons.settings, color: Colors.lightGreen), activeIcon: new Icon(Icons.settings, color: Colors.yellow,), title: Text(AppLocalizations.of(context).translate("Settings")) ) ], onTap:(index) { - // ignore: invalid_use_of_protected_member + setState(() { + widget.bottomNavIndex = index; + }); switch (index) { case 0: Navigator.of(context).pop(); @@ -59,4 +85,6 @@ class BottomNavigator { } ); } + + } \ No newline at end of file diff --git a/lib/widgets/customer_list_widget.dart b/lib/widgets/customer_list_widget.dart deleted file mode 100644 index 332783b..0000000 --- a/lib/widgets/customer_list_widget.dart +++ /dev/null @@ -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 customers; - - CustomerListWidget({this.customers}); - - @override - _CustomerListWidget createState() => _CustomerListWidget(); - -} - -class _CustomerListWidget extends State { - - - @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: [ - 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(context, listen: false).setCustomer(customer.customer), - Navigator.pop(context) - }, - ), - ], - ), - ), - ), - onTap: () { setState( () { - customer.visibleDetails = customer.visibleDetails ? false : true; - }); - }, - ); - }, - ); - } -} \ No newline at end of file diff --git a/lib/widgets/datetime_picker.dart b/lib/widgets/datetime_picker.dart deleted file mode 100644 index 2f5b5d6..0000000 --- a/lib/widgets/datetime_picker.dart +++ /dev/null @@ -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 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()); - } -} \ No newline at end of file diff --git a/lib/widgets/exercise_type_list_widget.dart b/lib/widgets/exercise_type_list_widget.dart deleted file mode 100644 index 9e3cb3d..0000000 --- a/lib/widgets/exercise_type_list_widget.dart +++ /dev/null @@ -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 exerciseTypes; - ExerciseTypeListWidget({this.exerciseTypes}); - - @override - _ExerciseTypeListWidgetState createState() => _ExerciseTypeListWidgetState(); -} - -class _ExerciseTypeListWidgetState extends State { - //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 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: [ - 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(context, listen: false).setExerciseType(exerciseType.exerciseType), - }, - ), - ], - ), - ), - ), - onTap: () { setState( () { - exerciseType.visible = true; - }); - }, - ); - }, - ); - } -} diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 35fe2ed..6112843 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -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_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/widgets/bottom_nav.dart'; +import 'package:aitrainer_app/view/registration.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; -import 'bottom_nav.dart'; -import 'nav_drawer.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'loading.dart'; + class AitrainerHome extends StatefulWidget { _HomePageState _state; @@ -15,10 +20,6 @@ class AitrainerHome extends StatefulWidget { _state = new _HomePageState(); return _state; } - - void callback() { - _state.setLangNoContext(); - } } class _HomePageState extends State { @@ -28,30 +29,61 @@ class _HomePageState extends State { @override void initState() { super.initState(); - } - @override - Widget build(BuildContext context) { - MenuPage menu = MenuPage(); - 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"); + /// We require the initializers to run after the loading screen is rendered + SchedulerBinding.instance.addPostFrameCallback((_) { + runDelayedEvent(); }); } + Future runDelayedEvent() async { + await Future.delayed(Duration(seconds: 3), () async { + if ( context != null) { + // ignore: close_sinks + SessionBloc sessionBloc = BlocProvider.of(context); + if (sessionBloc.state != SessionReady()) { + sessionBloc.add(SessionStart()); + // ignore: close_sinks + SettingsBloc settingsBloc = BlocProvider.of(context); + settingsBloc.loadLang(); + } + } + }); + } + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + body: Container( + child: BlocConsumer( + 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), + ); + } } diff --git a/lib/widgets/loading.dart b/lib/widgets/loading.dart index 0e50540..974b39b 100644 --- a/lib/widgets/loading.dart +++ b/lib/widgets/loading.dart @@ -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/material.dart'; -import 'package:aitrainer_app/util/loading_screen.dart'; - -class LoadingScreenMain extends StatefulWidget { - @override - LoadingScreenMainState createState() => LoadingScreenMainState(); -} - -class LoadingScreenMainState extends State { +class LoadingScreenMain extends StatelessWidget { @override Widget build(BuildContext context) { Image _backgroundImage = Image.asset('asset/image/WT01_loading_layers.png', @@ -20,34 +10,60 @@ class LoadingScreenMainState extends State { width: double.infinity, alignment: Alignment.center, ); - dynamic _timer = [TimeMessages.timer]; return Scaffold( - body: LoadingScreen( - initializers: _timer, - navigateToWidget: AitrainerHome(), - loaderColor: Colors.yellow, - image: _backgroundImage, - backgroundColor: Colors.black, - styleTextUnderTheLoader: TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.bold, - color: Colors.lightGreenAccent), - ) + backgroundColor: Colors.white, + body: new InkWell( + child: new Stack( + fit: StackFit.expand, + children: [ + /// Paint the area where the inner widgets are loaded with the + /// background to keep consistency with the screen background + new Container( + decoration: BoxDecoration(color: Colors.white), + ), + /// Render the background image + new Container( + child: _backgroundImage, + ), + /// Render the Title widget, loader and messages below each other + new Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + new Expanded( + flex: 3, + child: new Container( + child: new Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + new Padding( + padding: const EdgeInsets.only(top: 30.0), + ), + + ], + )), + ), + Expanded( + flex: 1, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + /// Loader Animation Widget + CircularProgressIndicator( + valueColor: new AlwaysStoppedAnimation( + 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); - }); - //} - } } diff --git a/lib/widgets/menu_page_widget.dart b/lib/widgets/menu_page_widget.dart new file mode 100644 index 0000000..8e5eff8 --- /dev/null +++ b/lib/widgets/menu_page_widget.dart @@ -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(context); + + return CustomScrollView( + scrollDirection: Axis.vertical, + slivers: [ + buildMenuColumn(parent, context, menuBloc) + ] + ); + } + + SliverList buildMenuColumn(int parent, BuildContext context, MenuBloc menuBloc) { + final List _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; + } +} \ No newline at end of file diff --git a/lib/widgets/nav_drawer.dart b/lib/widgets/nav_drawer.dart deleted file mode 100644 index 5dd8c79..0000000 --- a/lib/widgets/nav_drawer.dart +++ /dev/null @@ -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 { - 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: [ - 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() */ - }; - -} \ No newline at end of file diff --git a/lib/widgets/splash.dart b/lib/widgets/splash.dart new file mode 100644 index 0000000..99f2cc2 --- /dev/null +++ b/lib/widgets/splash.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +class LoadingDialog extends StatelessWidget { + static void show(BuildContext context, {Key key}) => showDialog( + 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, + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 03e3c90..0f8c960 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "5.0.0" + version: "7.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.39.12" + version: "0.39.17" archive: dependency: transitive description: @@ -35,7 +35,21 @@ packages: name: async url: "https://pub.dartlang.org" 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: dependency: transitive description: @@ -43,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" charcode: dependency: transitive description: @@ -50,13 +71,27 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" + version: "1.14.13" convert: dependency: transitive description: @@ -70,21 +105,21 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "0.13.11" + version: "0.14.0" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.5" csslib: dependency: transitive description: name: csslib url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.16.2" cupertino_icons: dependency: "direct main" description: @@ -92,13 +127,6 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: "direct main" description: @@ -106,13 +134,27 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "5.2.1" firebase_messaging: dependency: "direct main" description: @@ -125,18 +167,39 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_datetime_picker: - dependency: "direct dev" + flutter_bloc: + dependency: "direct main" description: - name: flutter_datetime_picker + name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "1.3.8" + version: "6.0.1" flutter_driver: dependency: "direct dev" description: flutter source: sdk 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: dependency: "direct dev" description: @@ -168,6 +231,18 @@ packages: description: flutter source: sdk 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: dependency: transitive description: flutter @@ -214,7 +289,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.12" + version: "2.1.14" intl: dependency: "direct dev" description: @@ -242,7 +317,7 @@ packages: name: json_rpc_2 url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.1" logging: dependency: transitive description: @@ -256,7 +331,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.8" meta: dependency: transitive description: @@ -270,7 +345,7 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.6+3" + version: "0.9.7" mockito: dependency: "direct main" description: @@ -278,13 +353,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.1.1" - multi_server_socket: + nested: dependency: transitive description: - name: multi_server_socket + name: nested url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "0.0.4" node_interop: dependency: transitive description: @@ -319,7 +394,21 @@ packages: name: path url: "https://pub.dartlang.org" 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: dependency: transitive description: @@ -333,7 +422,7 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "3.0.4" platform: dependency: transitive description: @@ -361,14 +450,14 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "3.0.12" + version: "3.0.13" provider: dependency: "direct dev" description: name: provider url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "4.3.2" pub_semver: dependency: transitive description: @@ -383,6 +472,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.24.1" sentry: dependency: "direct main" description: @@ -396,14 +492,42 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" 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: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "0.7.7" + version: "0.7.8" shelf_packages_handler: dependency: transitive description: @@ -451,13 +575,20 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.9.5" stream_channel: dependency: transitive description: @@ -492,28 +623,28 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.14.4" + version: "1.15.2" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.17" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "0.3.10" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.2.0" usage: dependency: transitive description: @@ -521,6 +652,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.4.2" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" vector_math: dependency: transitive description: @@ -534,7 +672,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.2.0" vm_service_client: dependency: transitive description: @@ -570,13 +708,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.7.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.6.1" + version: "4.2.0" yaml: dependency: transitive description: @@ -585,5 +730,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.7.0 <3.0.0" - flutter: ">=1.12.13+hotfix.6 <2.0.0" + dart: ">=2.9.0-14.0.dev <3.0.0" + flutter: ">=1.16.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 59d94a6..cde5a4d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.0+1 +version: 1.1.0+12 environment: sdk: ">=2.7.0 <3.0.0" @@ -31,6 +31,11 @@ dependencies: sentry: ^3.0.1 firebase_messaging: ^6.0.16 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 @@ -41,15 +46,14 @@ dev_dependencies: flutter_driver: sdk: flutter test: any + bloc_test: ^7.0.1 http: 0.12.1 - provider: ^3.2.0 + provider: ^4.3.2 intl: 0.16.1 - flutter_datetime_picker: ^1.3.8 - datetime_picker_formfield: ^1.0.0 - shared_preferences: ^0.4.1 + shared_preferences: ^0.5.8 - flutter_launcher_icons: "^0.7.3" + flutter_launcher_icons: ^0.7.5 flutter_icons: android: "launcher_icon" @@ -81,6 +85,7 @@ flutter: - asset/image/WT_gain_muscle.png - asset/image/WT_weight_loss.png - asset/image/WT_welcome.png + - asset/image/login_fb.png - asset/menu/1.cardio.png - asset/menu/1.1.aerob.png - asset/menu/1.2.anaerob.png diff --git a/test/account_bloc_test.dart b/test/account_bloc_test.dart new file mode 100644 index 0000000..562111d --- /dev/null +++ b/test/account_bloc_test.dart @@ -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()); + }); + +} \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index ec3fb30..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -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); - }); -} diff --git a/test/widget_test.login.dart b/test/widget_test.login.dart new file mode 100644 index 0000000..724a47f --- /dev/null +++ b/test/widget_test.login.dart @@ -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 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 + 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); + }); + }); +} diff --git a/test_driver/app.dart b/test_driver/app.dart new file mode 100644 index 0000000..b3698db --- /dev/null +++ b/test_driver/app.dart @@ -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(); +} \ No newline at end of file diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart new file mode 100644 index 0000000..eab3403 --- /dev/null +++ b/test_driver/app_test.dart @@ -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', () { + + }); +} \ No newline at end of file