diff --git a/asset/WT01_loading_layers.png b/asset/image/WT01_loading_layers.png
similarity index 100%
rename from asset/WT01_loading_layers.png
rename to asset/image/WT01_loading_layers.png
diff --git a/asset/image/WT_OK.png b/asset/image/WT_OK.png
new file mode 100644
index 0000000..e791af5
Binary files /dev/null and b/asset/image/WT_OK.png differ
diff --git a/asset/image/WT_gain_muscle.png b/asset/image/WT_gain_muscle.png
new file mode 100644
index 0000000..c6c6c82
Binary files /dev/null and b/asset/image/WT_gain_muscle.png differ
diff --git a/asset/image/WT_light_background.png b/asset/image/WT_light_background.png
new file mode 100644
index 0000000..836a7b7
Binary files /dev/null and b/asset/image/WT_light_background.png differ
diff --git a/asset/image/WT_login.png b/asset/image/WT_login.png
new file mode 100644
index 0000000..ab6e431
Binary files /dev/null and b/asset/image/WT_login.png differ
diff --git a/asset/image/WT_long_logo.png b/asset/image/WT_long_logo.png
new file mode 100644
index 0000000..356c244
Binary files /dev/null and b/asset/image/WT_long_logo.png differ
diff --git a/asset/image/WT_menu.png b/asset/image/WT_menu.png
new file mode 100644
index 0000000..9599510
Binary files /dev/null and b/asset/image/WT_menu.png differ
diff --git a/asset/image/WT_menu_backround.png b/asset/image/WT_menu_backround.png
new file mode 100644
index 0000000..92b944d
Binary files /dev/null and b/asset/image/WT_menu_backround.png differ
diff --git a/asset/image/WT_menu_dark.png b/asset/image/WT_menu_dark.png
new file mode 100644
index 0000000..6f74804
Binary files /dev/null and b/asset/image/WT_menu_dark.png differ
diff --git a/asset/image/WT_menu_welcome.png b/asset/image/WT_menu_welcome.png
new file mode 100644
index 0000000..5463667
Binary files /dev/null and b/asset/image/WT_menu_welcome.png differ
diff --git a/asset/image/WT_weight_loss.png b/asset/image/WT_weight_loss.png
new file mode 100644
index 0000000..9506018
Binary files /dev/null and b/asset/image/WT_weight_loss.png differ
diff --git a/asset/image/dots.gif b/asset/image/dots.gif
new file mode 100644
index 0000000..bce1400
Binary files /dev/null and b/asset/image/dots.gif differ
diff --git a/asset/menu/1.1.1.cooper.png b/asset/menu/1.1.1.cooper.png
new file mode 100644
index 0000000..12ae85d
Binary files /dev/null and b/asset/menu/1.1.1.cooper.png differ
diff --git a/asset/menu/1.1.aerob.png b/asset/menu/1.1.aerob.png
new file mode 100644
index 0000000..326672b
Binary files /dev/null and b/asset/menu/1.1.aerob.png differ
diff --git a/asset/menu/1.2.1.300m.png b/asset/menu/1.2.1.300m.png
new file mode 100644
index 0000000..509b604
Binary files /dev/null and b/asset/menu/1.2.1.300m.png differ
diff --git a/asset/menu/1.2.2.400m.png b/asset/menu/1.2.2.400m.png
new file mode 100644
index 0000000..9279f05
Binary files /dev/null and b/asset/menu/1.2.2.400m.png differ
diff --git a/asset/menu/1.2.anaerob.png b/asset/menu/1.2.anaerob.png
new file mode 100644
index 0000000..2f6566a
Binary files /dev/null and b/asset/menu/1.2.anaerob.png differ
diff --git a/asset/menu/1.cardio.png b/asset/menu/1.cardio.png
new file mode 100644
index 0000000..9098704
Binary files /dev/null and b/asset/menu/1.cardio.png differ
diff --git a/asset/menu/2.1.1.pull-ups.png b/asset/menu/2.1.1.pull-ups.png
new file mode 100644
index 0000000..1f3f8de
Binary files /dev/null and b/asset/menu/2.1.1.pull-ups.png differ
diff --git a/asset/menu/2.1.2.pushup.png b/asset/menu/2.1.2.pushup.png
new file mode 100644
index 0000000..0fe26f3
Binary files /dev/null and b/asset/menu/2.1.2.pushup.png differ
diff --git a/asset/menu/2.1.3.sit-ups.png b/asset/menu/2.1.3.sit-ups.png
new file mode 100644
index 0000000..aa3dfe6
Binary files /dev/null and b/asset/menu/2.1.3.sit-ups.png differ
diff --git a/asset/menu/2.1.4.squats.png b/asset/menu/2.1.4.squats.png
new file mode 100644
index 0000000..fbfe705
Binary files /dev/null and b/asset/menu/2.1.4.squats.png differ
diff --git a/asset/menu/2.1.5.timedpushup.png b/asset/menu/2.1.5.timedpushup.png
new file mode 100644
index 0000000..be77466
Binary files /dev/null and b/asset/menu/2.1.5.timedpushup.png differ
diff --git a/asset/menu/2.1.6.core.png b/asset/menu/2.1.6.core.png
new file mode 100644
index 0000000..335f007
Binary files /dev/null and b/asset/menu/2.1.6.core.png differ
diff --git a/asset/menu/2.1.endurance.png b/asset/menu/2.1.endurance.png
new file mode 100644
index 0000000..0e59701
Binary files /dev/null and b/asset/menu/2.1.endurance.png differ
diff --git a/asset/menu/2.2.1.1.chestpress.png b/asset/menu/2.2.1.1.chestpress.png
new file mode 100644
index 0000000..035b19c
Binary files /dev/null and b/asset/menu/2.2.1.1.chestpress.png differ
diff --git a/asset/menu/2.2.1.1RM.png b/asset/menu/2.2.1.1RM.png
new file mode 100644
index 0000000..ac947f1
Binary files /dev/null and b/asset/menu/2.2.1.1RM.png differ
diff --git a/asset/menu/2.2.1.2.pullups.png b/asset/menu/2.2.1.2.pullups.png
new file mode 100644
index 0000000..17180f1
Binary files /dev/null and b/asset/menu/2.2.1.2.pullups.png differ
diff --git a/asset/menu/2.2.1.3.biceps.png b/asset/menu/2.2.1.3.biceps.png
new file mode 100644
index 0000000..93285fb
Binary files /dev/null and b/asset/menu/2.2.1.3.biceps.png differ
diff --git a/asset/menu/2.2.1.4.triceps.png b/asset/menu/2.2.1.4.triceps.png
new file mode 100644
index 0000000..46fc684
Binary files /dev/null and b/asset/menu/2.2.1.4.triceps.png differ
diff --git a/asset/menu/2.2.1.5.shoulders.png b/asset/menu/2.2.1.5.shoulders.png
new file mode 100644
index 0000000..af10f6d
Binary files /dev/null and b/asset/menu/2.2.1.5.shoulders.png differ
diff --git a/asset/menu/2.strength.png b/asset/menu/2.strength.png
new file mode 100644
index 0000000..f5b1589
Binary files /dev/null and b/asset/menu/2.strength.png differ
diff --git a/asset/menu/3.1.BMI.png b/asset/menu/3.1.BMI.png
new file mode 100644
index 0000000..473fd39
Binary files /dev/null and b/asset/menu/3.1.BMI.png differ
diff --git a/asset/menu/3.2.BMR.png b/asset/menu/3.2.BMR.png
new file mode 100644
index 0000000..93b6e83
Binary files /dev/null and b/asset/menu/3.2.BMR.png differ
diff --git a/asset/menu/3.3.sizes.png b/asset/menu/3.3.sizes.png
new file mode 100644
index 0000000..3d99831
Binary files /dev/null and b/asset/menu/3.3.sizes.png differ
diff --git a/asset/menu/3.bcs1.png b/asset/menu/3.bcs1.png
new file mode 100644
index 0000000..48d84a2
Binary files /dev/null and b/asset/menu/3.bcs1.png differ
diff --git a/i18n/en.json b/i18n/en.json
new file mode 100644
index 0000000..704918a
--- /dev/null
+++ b/i18n/en.json
@@ -0,0 +1,79 @@
+{
+ "Customers And Exercises": "Customers And Exercises",
+ "Home": "Home",
+ "Customers": "Customers",
+ "Exercises": "Exercises",
+ "TRAINING!": "TRAINING!",
+ "Login": "Login",
+ "Logout": "Logout",
+ "Change Language": "Change Language",
+ "Password too short": "Password too short",
+ "Please type an email address": "Please type an email address",
+ "SignUp": "SignUp",
+ "Privacy": "Privacy",
+ "Change App Language": "Change App Language",
+ "English": "English",
+ "Hungarian": "Hungarian",
+ "Events": "Events",
+ "Account": "Account",
+ "Settings": "Settings",
+ "Profile": "Profile",
+ "Selected Language": "Selected Language",
+ "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 select an exercise": "Please select an exercise",
+ "Cardio": "Cardio",
+ "400m": "400m",
+ "300m": "300m",
+ "Aerobic": "Aerobic",
+ "Anaerobic": "Anaerobic",
+ "Cooper": "Cooper",
+ "Strength": "Strength",
+ "Endurance": "Strength Endurance",
+ "Pushups": "Pushups",
+ "Timed Pushups": "Timed Pushups",
+ "Core": "Core",
+ "Squats": "Squats",
+ "Sit-ups": "Sit-ups",
+ "1RM": "1RM",
+ "Chest Press": "Chest Press",
+ "Pull Ups": "Pull Ups",
+ "Biceps": "Biceps",
+ "Triceps": "Triceps",
+ "Shoulders": "Shoulders",
+ "Body Compositions": "Body Compositions",
+ "BMI": "BMI",
+ "BMR": "BMR",
+ "Sizes": "Sizes",
+ "Save Exercise": "save Exercise",
+ "Save": "Save",
+
+ "Name": "Name",
+ "Exercise": "Exercise",
+ "Quantity": "Quantity",
+ "Unit": "Unit",
+ "Exercise date and time": "Exercise date and time",
+ "Please type the right quantity 0-1000": "Please type the right quantity 0-10000",
+
+ "repeat": "repeat",
+ "meter": "meter",
+ "percent": "percent",
+ "kg": "kg",
+ "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)",
+ "First Name": "First Name",
+ "Birth Year": "Birty Year",
+ "Weight": "Weight",
+ "Gender": "Gender",
+ "Man": "Man",
+ "Woman": "Woman",
+ "Next": "Next",
+ "Select a gender": "Select a gender",
+
+ "Goal Settings": "Goal Settings"
+
+}
\ No newline at end of file
diff --git a/i18n/hu.json b/i18n/hu.json
new file mode 100644
index 0000000..cba22fb
--- /dev/null
+++ b/i18n/hu.json
@@ -0,0 +1,79 @@
+{
+ "Customers And Exercises": "Ügyfelek és gyakorlatok",
+ "Home": "Főoldal",
+ "Customers": "Ügyfelek",
+ "Exercises": "Gyakorlatok",
+ "TRAINING!": "EDZÉS!",
+ "Login": "Bejelentkezés",
+ "Logout": "Kijelentkezés",
+ "Change Language": "Nyelv",
+ "Password too short": "A jelszó min. 6 karakterből álljon",
+ "Please type an email address": "Kérlek írj be egy email címet",
+ "SignUp": "Regisztráció",
+ "Privacy": "Adatkezelés",
+ "Change App Language": "Nyelvválasztás",
+ "English": "Angol",
+ "Hungarian": "Magyar",
+ "Events": "Események",
+ "Account": "Fiók",
+ "Settings": "Beállítások",
+ "Profile": "Személyes adatok",
+ "Selected Language": "Választott nyelv",
+ "gdpr_text": "",
+
+ "Please select an exercise": "Válassz ki egy gyakorlatot",
+ "Cardio": "Kardió",
+ "400m": "400m",
+ "300m": "300m",
+ "Aerobic": "Aerob",
+ "Anaerobic": "Anaerob",
+ "Cooper": "Cooper teszt",
+ "Strength": "Erő",
+ "Endurance": "Erő állóképesség",
+ "Pushups": "Fekvőtámasz",
+ "Timed Pushups": "Fekvőtámasz időre",
+ "Core": "Core (plank)",
+ "Squats": "Guggolás",
+ "Sit-ups": "Felülés",
+ "1RM": "1RM - Maxerő",
+ "Chest Press": "Fekvenyomás",
+ "Pull Ups": "Húzódszkodás",
+ "Biceps": "Bicepsz",
+ "Triceps": "Tricepsz",
+ "Shoulders": "Vállak",
+ "Body Compositions": "Testformálás",
+ "BMI": "Testtömegindex",
+ "BMR": "Alapanyagcsere érték",
+ "Sizes": "Méretek",
+ "Save Exercise": "gyakorlat mentése",
+ "Save": "Save",
+
+ "Name": "Név",
+ "Exercise": "Gyakorlat",
+ "Quantity": "Mennyiség",
+ "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",
+
+ "repeat": "ismétlés",
+ "meter": "meter",
+ "percent": "százalék",
+ "kg": "kg",
+ "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)",
+ "First Name": "Keresztnév",
+ "Birth Year": "Születési év",
+ "Weight": "Tömeg",
+ "Gender": "Nemed",
+ "Man": "Férfi",
+ "Woman": "Nő",
+ "Next": "Tovább",
+ "Select a gender": "Válaszd ki a nemet",
+
+ "Goal Settings": "Cél"
+
+}
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 35ce4af..bc5d837 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -29,8 +29,6 @@
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad
diff --git a/lib/localization/app_language.dart b/lib/localization/app_language.dart
new file mode 100644
index 0000000..9ebf61e
--- /dev/null
+++ b/lib/localization/app_language.dart
@@ -0,0 +1,55 @@
+import 'package:flutter/cupertino.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+
+class AppLanguage extends ChangeNotifier {
+ static final AppLanguage _singleton = AppLanguage._internal();
+
+ Locale _appLocale = Locale('en');
+
+ factory AppLanguage() {
+ return _singleton;
+ }
+
+ AppLanguage._internal();
+
+ static Future getInstance() async {
+ return _singleton;
+ }
+
+ Locale get appLocal => _appLocale ?? Locale("en");
+
+ fetchLocale() async {
+ var prefs = await SharedPreferences.getInstance();
+ if (prefs.getString('language_code') == null) {
+ _appLocale = Locale('en');
+ }
+ _appLocale = Locale(prefs.getString('language_code'));
+ print(" ---- Fetched lang: " + _appLocale.toString());
+ }
+
+ getLocale(SharedPreferences prefs) {
+ if (prefs.getString('language_code') == null) {
+ _appLocale = Locale('en');
+ }
+ _appLocale = Locale(prefs.getString('language_code'));
+ }
+
+
+ void changeLanguage(Locale type) async {
+ var prefs = await SharedPreferences.getInstance();
+ if (_appLocale == type) {
+ return;
+ }
+ if (type == Locale("hu")) {
+ _appLocale = Locale("hu");
+ await prefs.setString('language_code', 'hu');
+ await prefs.setString('countryCode', 'HU');
+ } else {
+ _appLocale = Locale("en");
+ await prefs.setString('language_code', 'en');
+ await prefs.setString('countryCode', 'US');
+ }
+ print(" ---- Stored lang: " + _appLocale.toString());
+ notifyListeners();
+ }
+}
diff --git a/lib/localization/app_localization.dart b/lib/localization/app_localization.dart
new file mode 100644
index 0000000..afc6a58
--- /dev/null
+++ b/lib/localization/app_localization.dart
@@ -0,0 +1,70 @@
+import 'dart:convert';
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
+
+class AppLocalizations {
+ Locale locale;
+
+ AppLocalizations(this.locale);
+
+ // Helper method to keep the code in the widgets concise
+ // Localizations are accessed using an InheritedWidget "of" syntax
+ static AppLocalizations of(BuildContext context) {
+ return Localizations.of(context, AppLocalizations);
+ }
+
+ // Static member to have a simple access to the delegate from the MaterialApp
+ static const LocalizationsDelegate delegate =
+ _AppLocalizationsDelegate();
+
+ Map _localizedStrings;
+
+ setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+ Future load() async {
+ // Load the language JSON file from the "lang" folder
+ String jsonString =
+ await rootBundle.loadString('i18n/${locale.languageCode}.json');
+ Map jsonMap = json.decode(jsonString);
+
+ _localizedStrings = jsonMap.map((key, value) {
+ return MapEntry(key, value.toString());
+ });
+
+ return true;
+ }
+
+ // This method will be called from every widget which needs a localized text
+ String translate(String key) {
+ return _localizedStrings[key];
+ }
+
+}
+
+class _AppLocalizationsDelegate
+ extends LocalizationsDelegate {
+ // This delegate instance will never change (it doesn't even have fields!)
+ // It can provide a constant constructor.
+ const _AppLocalizationsDelegate();
+
+ @override
+ bool isSupported(Locale locale) {
+ // Include all of your supported language codes here
+ return ['en', 'hu'].contains(locale.languageCode);
+ }
+
+ @override
+ Future load(Locale locale) async {
+ // AppLocalizations class is where the JSON loading actually runs
+ AppLocalizations localizations = new AppLocalizations(locale);
+ await localizations.load();
+ return localizations;
+ }
+
+ @override
+ bool shouldReload(_AppLocalizationsDelegate old) => false;
+}
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
index 9320d2b..b28d024 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,33 +1,43 @@
-import 'package:aitrainer_app/model/auth.dart';
-import 'package:aitrainer_app/service/api.dart';
-import 'package:aitrainer_app/service/customer_service.dart';
+import 'package:aitrainer_app/view/account.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:aitrainer_app/widgets/nav_drawer.dart';
+import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
-import 'package:shared_preferences/shared_preferences.dart';
+import 'package:aitrainer_app/localization/app_localization.dart';
void main() {
- 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.
- providers: [
- ChangeNotifierProvider(create: (context) => ExerciseChangingViewModel(null)),
- ],
- child: AitrainerApp(),
+ 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.
+ providers: [
+ ChangeNotifierProvider(create: (context) => ExerciseChangingViewModel(null)),
+ ChangeNotifierProvider(create: (context) => CustomerChangingViewModel(null)),
+ ],
+
+ child: AitrainerApp(),
),
);
}
@@ -36,308 +46,54 @@ class AitrainerApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return MaterialApp(
localizationsDelegates: [
// ... app-specific localization delegate[s] here
- GlobalMaterialLocalizations.delegate,
+ AppLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
- const Locale('en'), // English
- const Locale('hu'), // Hungarian
+ const Locale('en', "US"), // English
+ const Locale('hu', "HU"), // Hungarian
// ... other locales the app supports
],
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(),
'login': (context) => LoginPage(),
'registration': (context) => RegistrationPage(),
+ 'gdpr': (context) => Gdpr(),
+ 'menu_page': (context) => MenuPage(),
+ 'account': (context) => AccountPage(),
+ 'settings': (context) => SettingsPage(),
},
- initialRoute: 'home',
- title: 'Aitrainer Demo',
+ initialRoute: 'loading',
+ title: 'Aitrainer',
theme: ThemeData(
- primarySwatch: Colors.green,
- ),
- home: AitrainerHome(),
- );
- }
-}
-
-class AitrainerHome extends StatefulWidget {
- static final String routeName = 'home';
-
-
- @override
- State createState() {
- return new _HomePageState();
- }
-}
-
-class _HomePageState extends State {
- GlobalKey _scaffoldKey = new GlobalKey();
- Future _prefs = SharedPreferences.getInstance();
- Auth _auth = Auth();
- SharedPreferences _sharedPreferences;
- var _authToken;
-
-
- @override
- void initState() {
- super.initState();
- _fetchSessionAndNavigate();
- }
-
- _fetchSessionAndNavigate() async {
- _sharedPreferences = await _prefs;
- String authToken = Auth.getToken(_sharedPreferences);
- var customerId = _sharedPreferences.getInt(Auth.customerIdKey);
-
- if ( _auth.firstLoad ) {
- _fetchToken(_sharedPreferences);
- }
-
- }
-
- /*
- Auth flow of the user, see auth.dart
- */
- _fetchToken(SharedPreferences prefs) async {
- var responseJson = await APIClient.authenticateUser(
- Auth.username,
- Auth.password
- );
-
- if(responseJson['error'] != null) {
- showSnackBar(_scaffoldKey, responseJson['error']);
- print("************** Here big error - no authentication");
- } else if (responseJson['token'] != null) {
- prefs.setString(Auth.authTokenKey, responseJson['token']);
- Auth auth = Auth();
- auth.authToken = responseJson['token'];
- if ( prefs.get(Auth.customerIdKey) == null ) {
- print("************** Registration");
- // registration
- Navigator.of(context).pushNamed('registration');
- prefs.setBool(Auth.isRegisteredKey, true);
- } else {
- DateTime now = DateTime.now();
- DateTime lastStoreDate = DateTime.parse( prefs.get(Auth.lastStoreDateKey) );
- DateTime minStoreDate = now.add(Duration(days: -10));
-
- if ( lastStoreDate == null ||
- lastStoreDate.difference(minStoreDate) > Duration(days: 10) ||
- prefs.get(Auth.isLoggedInKey) == null ||
- prefs.get(Auth.isLoggedInKey) == false ) {
- print("************* Login");
- Navigator.of(context).pushNamed('login');
-
- } else {
- print("************** Store SharedPreferences");
- // get API customer
- await CustomerApi().getCustomer( prefs.getInt(Auth.customerIdKey));
-
- }
- }
-
- setState(() {
- _auth.firstLoad = false;
- _authToken = auth.authToken;
- _sharedPreferences.setString(Auth.authTokenKey, _authToken);
- });
-
- }
- }
-
-
- static showSnackBar(GlobalKey scaffoldKey, String message) {
- scaffoldKey.currentState.showSnackBar(
- new SnackBar(
- content: new Text(message ?? 'You are offline'),
+ brightness: Brightness.light,
+ //primarySwatch: Colors.transparent,
+ fontFamily: 'Arial',
+ textTheme: TextTheme(
+ bodyText1: TextStyle(fontSize: 14.0),
)
- );
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- key: _scaffoldKey,
- drawer: NavDrawer(),
- appBar: AppBar(
- title: Text('Home'),
- backgroundColor: Colors.transparent
),
- body: Image.asset('asset/WT01_loading_layers.png',
- fit: BoxFit.fill,
- height: double.infinity,
- width: double.infinity,
- alignment: Alignment.center,
- ),
-
+ home: LoadingScreenMain(),
);
}
}
-/*
-class CustomerScreen extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- drawer: NavDrawer(),
- appBar: AppBar(
- title: Text('Customers'),
- ),
- body: Center(
- child: CustomerListPage(),
- ),
- );
- }
-}
-*/
-/* class AitrainerApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: "AITRAINER customers",
- home:
- ChangeNotifierProvider(
- create: (context) => CustomerListViewModel(),
- child: CustomerListPage(),
- )
- );
- }
-}
-
-*/
-
-/*
-// #docregion MyApp
-class MyApp extends StatelessWidget {
- // #docregion build
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: 'Startup Name Generator',
- theme: ThemeData( // Add the 3 lines from here...
- primaryColor: Colors.white,
- ),
- home: RandomWords(),
- );
- }
-// #enddocregion build
-}
-// #enddocregion MyApp
-
-// #docregion RWS-var
-class RandomWordsState extends State {
- final _suggestions = [];
- final _biggerFont = const TextStyle(fontSize: 18.0);
- final Set _saved = Set();
-
- // #enddocregion RWS-var
-
- // #docregion _buildSuggestions
- Widget _buildSuggestions() {
- return ListView.builder(
- padding: const EdgeInsets.all(16.0),
- itemBuilder: /*1*/ (context, i) {
- if (i.isOdd) return Divider(); /*2*/
-
- final index = i ~/ 2; /*3*/
- if (index >= _suggestions.length) {
- _suggestions.addAll(generateWordPairs().take(10)); /*4*/
- }
- return _buildRow(_suggestions[index]);
- });
- }
- // #enddocregion _buildSuggestions
-
- // #docregion _buildRow
- Widget _buildRow(WordPair pair) {
- final bool alreadySaved = _saved.contains(pair);
- return ListTile(
- title: Text(
- pair.asPascalCase,
- style: _biggerFont,
- ),
- trailing: Icon( // Add the lines from here...
- alreadySaved ? Icons.favorite : Icons.favorite_border,
- color: alreadySaved ? Colors.red : null,
- ),
- onTap: () { // Add 9 lines from here...
- setState(() {
- if (alreadySaved) {
- _saved.remove(pair);
- } else {
- _saved.add(pair);
- }
- });
- },
- );
- }
- // #enddocregion _buildRow
-
- // #docregion RWS-build
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Startup Name Generator'),
- actions: [ // Add 3 lines from here...
- IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
- ],
- ),
- body: _buildSuggestions(),
- );
- }
-
- void _pushSaved() {
- Navigator.of(context).push(
- MaterialPageRoute( // Add 20 lines from here...
- builder: (BuildContext context) {
- final Iterable tiles = _saved.map(
- (WordPair pair) {
- return ListTile(
- title: Text(
- pair.asPascalCase,
- style: _biggerFont,
- ),
- );
- },
- );
- final List divided = ListTile
- .divideTiles(
- context: context,
- tiles: tiles,
- ).toList();
- return Scaffold( // Add 6 lines from here...
- appBar: AppBar(
- title: Text('Saved Suggestions'),
- ),
- body: ListView(children: divided),
- );
- },
-
- ),
-
- );
- }
-// #enddocregion RWS-build
-// #docregion RWS-var
-}
-// #enddocregion RWS-var
-
-class RandomWords extends StatefulWidget {
- @override
- RandomWordsState createState() => new RandomWordsState();
-}
-
-*/
diff --git a/lib/model/auth.dart b/lib/model/auth.dart
index ddf3ebc..14e1c31 100644
--- a/lib/model/auth.dart
+++ b/lib/model/auth.dart
@@ -1,5 +1,7 @@
import 'package:aitrainer_app/model/customer.dart';
+import 'package:aitrainer_app/service/exercisetype_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
+import 'package:aitrainer_app/model/exercise_type.dart';
enum SharePrefsChange {
login,
@@ -40,6 +42,7 @@ class Auth {
String authToken = "";
Customer userLoggedIn;
bool firstLoad = true;
+ List _exerciseTypes;
factory Auth() {
return _singleton;
@@ -79,6 +82,7 @@ class Auth {
//firstLoad = true;
Future prefs = SharedPreferences.getInstance();
setPreferences(prefs, SharePrefsChange.logout, 0);
+
}
setPreferences(Future prefs,
@@ -93,13 +97,24 @@ class Auth {
sharedPreferences.setInt(Auth.customerIdKey, customerId);
sharedPreferences.setBool(Auth.isRegisteredKey, true);
sharedPreferences.setBool(Auth.isLoggedInKey, true);
+ await ExerciseTypeApi().getExerciseTypes("");
} else if ( type == SharePrefsChange.login ) {
sharedPreferences.setInt(Auth.customerIdKey, customerId);
sharedPreferences.setBool(Auth.isLoggedInKey, true);
- } else if ( type == SharePrefsChange.login ) {
+ await ExerciseTypeApi().getExerciseTypes("");
+ } else if ( type == SharePrefsChange.logout ) {
sharedPreferences.setBool(Auth.isLoggedInKey, false);
- sharedPreferences.setInt(Auth.customerIdKey, 0);
+ //sharedPreferences.setInt(Auth.customerIdKey, 0);
+ sharedPreferences.setString(authTokenKey, "");
}
}
+
+ void setExerciseTypes( List exerciseTypes) {
+ this._exerciseTypes = exerciseTypes;
+ }
+
+ List getExerciseTypes() {
+ return this._exerciseTypes;
+ }
}
\ No newline at end of file
diff --git a/lib/model/customer.dart b/lib/model/customer.dart
index f725a5e..8dad01f 100644
--- a/lib/model/customer.dart
+++ b/lib/model/customer.dart
@@ -7,18 +7,46 @@ class Customer {
String active;
int customerId;
String password;
+ int birthYear;
+ int weight;
+ String goal;
+ String fitnessLevel;
+ String bodyType;
+ int admin;
+ int dataPolicyAllowed;
- Customer({this.customerId, this.name, this.firstName, this.email, this.sex, this.age, this.active, this.password});
+ Customer({this.customerId,
+ this.name,
+ this.firstName,
+ this.email,
+ this.sex,
+ this.age,
+ this.active,
+ this.password,
+ this.birthYear,
+ this.bodyType,
+ this.fitnessLevel,
+ this.goal,
+ this.weight,
+ this.admin,
+ this.dataPolicyAllowed
+ });
Customer.fromJson(Map json) {
- this.customerId = json['customer_id'];
+ this.customerId = json['customerId'];
this.name = json['name'];
this.firstName = json['firstname'];
this.email = json['email'];
this.sex = json['sex'];
this.age = json['age'];
this.active = json['active'];
+ this.birthYear = json['birthYear'];
+ this.bodyType = json['bodyType'];
+ this.fitnessLevel = json['fitnessLevel'];
+ this.goal = json['goal'];
+ this.weight = json['weight'];
+ this.admin = json['admin'];
}
Map toJson() =>
@@ -30,5 +58,12 @@ class Customer {
"sex": sex,
"active": 'Y',
"password": password,
+ "birthYear": birthYear,
+ "bodyType": bodyType,
+ "fitnessLevel": fitnessLevel,
+ "goal": goal,
+ "weight": weight,
+ "admin": admin,
+ "dataPolicyAllowed": dataPolicyAllowed,
};
-}
\ No newline at end of file
+}
diff --git a/lib/model/exercise.dart b/lib/model/exercise.dart
index ee232ae..3e16abb 100644
--- a/lib/model/exercise.dart
+++ b/lib/model/exercise.dart
@@ -4,18 +4,22 @@ class Exercise {
int exerciseId;
int exerciseTypeId;
int customerId;
- int quantity;
- DateTime datetimeExercise;
+ double quantity;
+ String unit;
+ double unitQuantity;
+ DateTime dateAdd;
- Exercise({this.exerciseTypeId, this.customerId, this.quantity, this.datetimeExercise});
+ Exercise({this.exerciseTypeId, this.customerId, this.quantity, this.dateAdd});
Exercise.fromJson(Map json) {
this.exerciseTypeId = json['exerciseTypeId'];
this.customerId = json['customerId'];
this.quantity = json['quantity'];
- this.datetimeExercise = json['datetimeExercise'];
+ this.unit = json['unit'];
+ this.unitQuantity = json['unitQuantity'];
+ this.dateAdd = DateTime.parse( json['dateAdd'] );
}
Map toJson() =>
@@ -23,7 +27,8 @@ class Exercise {
"exerciseTypeId": exerciseTypeId,
"customerId": customerId,
"quantity": quantity,
-
- "datetimeExercise": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.datetimeExercise),
+ "unit": unit,
+ "unitQuantity": unitQuantity,
+ "dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd),
};
}
\ No newline at end of file
diff --git a/lib/model/exercise_type.dart b/lib/model/exercise_type.dart
index 58ea476..3f11e0b 100644
--- a/lib/model/exercise_type.dart
+++ b/lib/model/exercise_type.dart
@@ -5,6 +5,9 @@ class ExerciseType {
String name;
String description;
BinaryCodec video;
+ String unit;
+ String unitQuantity;
+ String unitQuantityUnit;
ExerciseType({this.name, this.description});
@@ -12,11 +15,17 @@ class ExerciseType {
this.exerciseTypeId = json['exerciseTypeId'];
this.name = json['name'];
this.description = json['description'];
+ this.unit = json['unit'];
+ this.unitQuantity = json['unitQuantity'];
+ this.unitQuantityUnit = json['unitQuantityUnit'];
}
Map toJson() =>
{
"name": name,
"description": description,
+ "unit": unit,
+ "unitQuantity": unitQuantity,
+ "unitQuantityUnit": unitQuantityUnit
};
}
\ No newline at end of file
diff --git a/lib/model/workout_tree.dart b/lib/model/workout_tree.dart
new file mode 100644
index 0000000..2d3ece7
--- /dev/null
+++ b/lib/model/workout_tree.dart
@@ -0,0 +1,16 @@
+import 'dart:collection';
+import 'dart:ui';
+
+class WorkoutTree {
+ int id;
+ int parent;
+ String name; // is also the key
+ String imageName;
+ Color color;
+ double fontSize;
+ bool child;
+ int exercise_type_id;
+
+ WorkoutTree(this.id, this.parent, this.name, this.imageName, this.color, this.fontSize, this.child, this.exercise_type_id);
+
+}
diff --git a/lib/service/api.dart b/lib/service/api.dart
index bacdaec..1779944 100644
--- a/lib/service/api.dart
+++ b/lib/service/api.dart
@@ -7,11 +7,18 @@ class APIClient extends ChangeNotifier {
Future get(String endPoint, String param) async {
final url = Auth.getBaseUrl() + endPoint + param;
- Auth auth = Auth();
+ String authToken = Auth().getAuthToken();
+ if ( authToken.length == 0 ) {
+ var responseJson = await APIClient.authenticateUser(
+ Auth.username,
+ Auth.password
+ );
+ authToken = responseJson['token'];
+ }
final response = await http.get(url,
headers: {
'Content-Type': 'application/json',
- 'Authorization' : "Bearer " + auth.getAuthToken() }
+ 'Authorization' : "Bearer " + authToken }
);
notifyListeners();
if(response.statusCode == 200) {
@@ -24,11 +31,18 @@ class APIClient extends ChangeNotifier {
Future post(String endPoint, String body) async {
final url = Auth.getBaseUrl() + endPoint;
print(" ------------ http/post endpoint $endPoint body $body - url: $url ");
- Auth auth = Auth();
+ String authToken = Auth().getAuthToken();
+ if ( authToken.length == 0 ) {
+ var responseJson = await APIClient.authenticateUser(
+ Auth.username,
+ Auth.password
+ );
+ authToken = responseJson['token'];
+ }
final response = await http.post(url,
headers: {
'Content-Type': 'application/json',
- 'Authorization' : "Bearer " + auth.getAuthToken()
+ 'Authorization' : "Bearer " + authToken
},
body: body
);
@@ -59,7 +73,8 @@ class APIClient extends ChangeNotifier {
return responseJson;
} catch (exception) {
- return { "error" : "Network error, try again later"};
+ return { "error" : "Network error, try again later "
+ + exception.toString()};
}
}
diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart
index 2de8f5e..a2fa5b6 100644
--- a/lib/service/customer_service.dart
+++ b/lib/service/customer_service.dart
@@ -37,9 +37,8 @@ class CustomerApi {
final String responseBody = await _client.post(
"registration",
body);
- Auth auth = Auth();
Customer customer = Customer.fromJson(jsonDecode(responseBody));
- auth.afterRegistration(customer);
+ Auth().afterRegistration(customer);
}
@@ -49,9 +48,8 @@ class CustomerApi {
final String responseBody = await _client.post(
"login",
body);
- Auth auth = Auth();
Customer customer = Customer.fromJson(jsonDecode(responseBody));
- auth.afterRegistration(customer);
+ Auth().afterRegistration(customer);
}
Future getCustomer(int customerId) async {
@@ -60,8 +58,7 @@ class CustomerApi {
final String responseBody = await _client.get(
"customers/"+customerId.toString(),
body);
- Auth auth = Auth();
Customer customer = Customer.fromJson(jsonDecode(responseBody));
- auth.afterRegistration(customer);
+ Auth().afterRegistration(customer);
}
}
\ No newline at end of file
diff --git a/lib/service/exercise_service.dart b/lib/service/exercise_service.dart
index d89ddd5..f08831e 100644
--- a/lib/service/exercise_service.dart
+++ b/lib/service/exercise_service.dart
@@ -30,4 +30,12 @@ class ExerciseApi {
body);
}
+ Future> getExercisesByCustomer(int customerId ) async {
+ final body = await _client.get("exercises/customer/", customerId.toString() );
+ final Iterable json = jsonDecode(body);
+ final List exercises = json.map( (exercise) => Exercise.fromJson(exercise) ).toList();
+
+ return exercises;
+ }
+
}
\ No newline at end of file
diff --git a/lib/service/exercisetype_service.dart b/lib/service/exercisetype_service.dart
index 80ea54c..d7178e4 100644
--- a/lib/service/exercisetype_service.dart
+++ b/lib/service/exercisetype_service.dart
@@ -1,4 +1,5 @@
import 'dart:convert';
+import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/service/api.dart';
@@ -10,7 +11,7 @@ class ExerciseTypeApi {
final body = await _client.get("exercise_type", param);
final Iterable json = jsonDecode(body);
final List exerciseTypes = json.map( (exerciseType) => ExerciseType.fromJson(exerciseType) ).toList();
-
+ Auth().setExerciseTypes(exerciseTypes);
return exerciseTypes;
}
diff --git a/lib/util/common.dart b/lib/util/common.dart
index a430238..23c4c0c 100644
--- a/lib/util/common.dart
+++ b/lib/util/common.dart
@@ -1,5 +1,7 @@
-
-import 'dart:collection';
+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:intl/intl.dart';
class Common {
@@ -13,4 +15,29 @@ class Common {
return rc;
}
+ static ExerciseType getExerciseType( int exerciseTypeId ) {
+ ExerciseType returnElement = null;
+ List listExerciseType = Auth().getExerciseTypes();
+ if ( listExerciseType != null ) {
+ for ( var element in listExerciseType ) {
+ if (exerciseTypeId == element.exerciseTypeId) {
+ returnElement = element;
+ break;
+ }
+ };
+ }
+ return returnElement;
+ }
+
+ static String getDateLocale( DateTime datetime, bool timeDisplay ) {
+ AppLanguage appLanguage = AppLanguage();
+ var date = datetime;
+
+ String dateName = DateFormat(DateFormat.YEAR_MONTH_DAY, appLanguage.appLocal.toString()).format(date.toUtc());
+ if ( timeDisplay ) {
+ dateName += " " +DateFormat(DateFormat.HOUR_MINUTE, appLanguage.appLocal.toString()).format(date.toUtc());
+ }
+
+ return dateName;
+ }
}
\ No newline at end of file
diff --git a/lib/util/loading_screen.dart b/lib/util/loading_screen.dart
new file mode 100644
index 0000000..407c7f0
--- /dev/null
+++ b/lib/util/loading_screen.dart
@@ -0,0 +1,61 @@
+import 'package:aitrainer_app/util/loading_screen_state.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+
+
+/// Loading Screen Widget that updates the screen once all inistializer methods
+/// are called
+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
new file mode 100644
index 0000000..bfcbaa9
--- /dev/null
+++ b/lib/util/loading_screen_state.dart
@@ -0,0 +1,131 @@
+import 'dart:core';
+import 'dart:async';
+import 'file:///D:/projects/aitrainer/src/aitrainer_app/lib/util/loading_screen.dart';
+import 'package:aitrainer_app/localization/app_language.dart';
+import 'package:aitrainer_app/localization/app_localization.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
new file mode 100644
index 0000000..5eef1c2
--- /dev/null
+++ b/lib/util/menu_tests.dart
@@ -0,0 +1,93 @@
+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, 48, false,0);
+ this.tree['Aerobic']= WorkoutTree(2, 1, AppLocalizations.of(context).translate("Aerobic"),
+ 'asset/menu/1.1.aerob.png',
+ Colors.white, 48, false,0);
+ this.tree['Cooper']= WorkoutTree(21, 2, AppLocalizations.of(context).translate("Cooper"),
+ 'asset/menu/1.1.1.cooper.png',
+ Colors.white, 48, true,30);
+ this.tree['Anaerobic']= WorkoutTree(3, 1, AppLocalizations.of(context).translate("Anaerobic"),
+ 'asset/menu/1.2.anaerob.png',
+ Colors.white, 48, false,0);
+ this.tree['300m']= WorkoutTree(22, 3, "300m",
+ 'asset/menu/1.2.1.300m.png',
+ Colors.white, 48, true,31);
+ this.tree['400m']= WorkoutTree(24, 3, "400m",
+ 'asset/menu/1.2.2.400m.png',
+ Colors.white, 48, true,32);
+
+ this.tree['Strength']= WorkoutTree(4, 0, AppLocalizations.of(context).translate("Strength"),
+ 'asset/menu/2.strength.png',
+ Colors.white, 48, 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, 48, true,38);
+ this.tree['Pushups']= WorkoutTree(7, 5, AppLocalizations.of(context).translate("Pushups"),
+ 'asset/menu/2.1.2.pushup.png',
+ Colors.white, 48, true,33);
+ this.tree['Situps']= WorkoutTree(10, 5, AppLocalizations.of(context).translate("Sit-ups"),
+ 'asset/menu/2.1.3.sit-ups.png',
+ Colors.white, 48, true,36);
+ this.tree['Squats']= WorkoutTree(11, 5, AppLocalizations.of(context).translate("Squats"),
+ 'asset/menu/2.1.4.squats.png',
+ Colors.white, 48, 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, 48, true,45);
+
+ this.tree['1RM']= WorkoutTree(8, 4, AppLocalizations.of(context).translate("1RM"),
+ 'asset/menu/2.2.1.1RM.png',
+ Colors.white, 48, false,0);
+ this.tree['Chestpress']= WorkoutTree(13, 8, AppLocalizations.of(context).translate("Chest Press"),
+ 'asset/menu/2.2.1.1.chestpress.png',
+ Colors.white, 48, true,37);
+ this.tree['PullUps1rm']= WorkoutTree(14, 8, AppLocalizations.of(context).translate("Pull Ups"),
+ 'asset/menu/2.2.1.2.pullups.png',
+ Colors.white, 48, true, 38);
+ this.tree['Biceps']= WorkoutTree(15, 8, AppLocalizations.of(context).translate("Biceps"),
+ 'asset/menu/2.2.1.3.biceps.png',
+ Colors.white, 48, true, 39);
+ this.tree['Triceps']= WorkoutTree(16, 8, AppLocalizations.of(context).translate("Triceps"),
+ 'asset/menu/2.2.1.4.triceps.png',
+ Colors.white, 48, true, 40);
+ this.tree['Shoulders']= WorkoutTree(17, 8, AppLocalizations.of(context).translate("Shoulders"),
+ 'asset/menu/2.2.1.5.shoulders.png',
+ Colors.white, 48, true, 41);
+
+ this.tree['BodyCompositions']= WorkoutTree(9, 0, AppLocalizations.of(context).translate("Body Compositions"),
+ 'asset/menu/3.bcs1.png',
+ Colors.white, 40, 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,0);
+ this.tree['Sizes']= WorkoutTree(20, 9, AppLocalizations.of(context).translate("Sizes"),
+ 'asset/menu/3.3.sizes.png',
+ Colors.black, 48, true,0);
+
+ }
+
+ 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
new file mode 100644
index 0000000..2f83923
--- /dev/null
+++ b/lib/util/message_state.dart
@@ -0,0 +1,32 @@
+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
new file mode 100644
index 0000000..391ab8f
--- /dev/null
+++ b/lib/util/session.dart
@@ -0,0 +1,74 @@
+import 'package:aitrainer_app/localization/app_language.dart';
+import 'package:aitrainer_app/service/api.dart';
+import 'package:aitrainer_app/service/customer_service.dart';
+import 'package:aitrainer_app/service/exercisetype_service.dart';
+import 'package:aitrainer_app/viewmodel/exercise_type_changing_view_model.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:aitrainer_app/model/auth.dart';
+
+class Session {
+
+ Future _prefs = SharedPreferences.getInstance();
+ Auth _auth = Auth();
+ SharedPreferences _sharedPreferences;
+ final AppLanguage appLanguage = AppLanguage();
+
+ fetchSessionAndNavigate(Function callback ) async {
+ _sharedPreferences = await _prefs;
+
+
+ if ( _auth.firstLoad ) {
+ _fetchToken(_sharedPreferences, callback);
+ appLanguage.fetchLocale();
+ }
+
+ }
+
+ /*
+ Auth flow of the user, see auth.dart
+ */
+ _fetchToken(SharedPreferences prefs, Function callback) async {
+
+ var responseJson = await APIClient.authenticateUser(
+ Auth.username,
+ Auth.password
+ );
+ print("--- Lang: " + appLanguage.appLocal.toString());
+ if(responseJson['error'] != null) {
+ print("************** Here big error - no authentication");
+ } else if (responseJson['token'] != null) {
+ prefs.setString(Auth.authTokenKey, responseJson['token']);
+ Auth auth = Auth();
+ auth.authToken = responseJson['token'];
+ if (prefs.get(Auth.customerIdKey) == null) {
+ print("************** Registration");
+ // registration
+ //Navigator.of(context).pushNamed('registration');
+ prefs.setBool(Auth.isRegisteredKey, true);
+ } else {
+ DateTime now = DateTime.now();
+ DateTime lastStoreDate = DateTime.parse(
+ prefs.get(Auth.lastStoreDateKey));
+ DateTime minStoreDate = now.add(Duration(days: -10));
+
+ if (lastStoreDate == null ||
+ lastStoreDate.difference(minStoreDate) > Duration(days: 10) ||
+ prefs.get(Auth.isLoggedInKey) == null ||
+ prefs.get(Auth.isLoggedInKey) == false) {
+ print("************* Login");
+ //Navigator.of(context).pushNamed('login');
+
+ } else {
+ print("************** Store SharedPreferences");
+ // get API customer
+ await CustomerApi().getCustomer(prefs.getInt(Auth.customerIdKey));
+ }
+
+ await ExerciseTypeApi().getExerciseTypes("");
+ print("--- Session finished, call callback ");
+ callback();
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/view/account.dart b/lib/view/account.dart
new file mode 100644
index 0000000..f4cc075
--- /dev/null
+++ b/lib/view/account.dart
@@ -0,0 +1,248 @@
+
+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/util/common.dart';
+import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
+import 'package:aitrainer_app/viewmodel/exercise_view_model.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:aitrainer_app/viewmodel/user_view_model.dart';
+import 'package:aitrainer_app/widgets/bottom_nav.dart';
+import 'package: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;
+ }
+}
+
+class _AccountPagePageState extends State {
+ final UserViewModel user = UserViewModel();
+ final AppLanguage appLanguage = AppLanguage();
+ final Future _prefs = SharedPreferences.getInstance();
+ final BottomNavigator bottomNav = BottomNavigator();
+ bool _loggedIn = Auth().userLoggedIn != null && Auth().userLoggedIn.email.length > 0;
+ Future> _exercises;
+ ExerciseChangingViewModel model;
+
+
+
+ @override
+ void initState() {
+ super.initState();
+ model = Provider.of(context, listen: false);
+ if ( Auth().userLoggedIn != null ) {
+ _exercises = model.getExercisesByCustomer(Auth().userLoggedIn.customerId);
+ }
+ }
+
+ @override
+ Widget build(BuildContext 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'),
+ //fit: BoxFit.scaleDown,
+ scale: 1.2,
+ 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(_loggedIn ? Auth().userLoggedIn.email + " " +
+ Auth().userLoggedIn.name + " " +
+ Auth().userLoggedIn.firstName : "",
+ style: TextStyle(color: Colors.blue)),
+ Icon(Icons.arrow_forward_ios),
+ ]),
+ textColor: Colors.grey,
+ color: Colors.white,
+ onPressed: () {
+ if (_loggedIn) {
+ 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(),
+ exercises( model ),
+ ]
+ )
+ ),
+ bottomNavigationBar: bottomNav.buildBottomNavigator(context, widget._state)
+ );
+ }
+
+ ListTile loginOut() {
+ ListTile element = ListTile();
+
+ String text = "Logout";
+ Color buttonColor = Colors.orange;
+
+ if ( ! _loggedIn ) {
+ text = "Login";
+ buttonColor = Colors.blue;
+ }
+
+ element = ListTile(
+ enabled: true,
+ leading: Icon(Icons.input),
+ title: FlatButton(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(AppLocalizations.of(context).translate(text),
+ style: TextStyle(
+ color: buttonColor
+ )),
+ Icon(Icons.arrow_forward_ios),
+ ]),
+ textColor: buttonColor,
+ color: Colors.white,
+ onPressed: () {
+ setState(() {
+ if ( ! _loggedIn) {
+ print("Login");
+ Navigator.of(context).pushNamed("login");
+ } else {
+ print("Logout");
+ _loggedIn = false;
+ Auth().logout();
+
+ }
+
+ });
+ },
+ ),
+ );
+
+ return element;
+ }
+
+ ListTile exercises( ExerciseChangingViewModel model ) {
+ ListTile element = ListTile();
+ if ( Auth().userLoggedIn == null ) {
+ return element;
+ }
+
+ element = ListTile(
+ title: Text(AppLocalizations.of(context).translate("Exercises")),
+ subtitle: Column(
+ children: [
+ FutureBuilder>(
+ future: _exercises,
+ builder: (context, snapshot) {
+ if (snapshot.hasData) {
+ return getExercises( model );//CustomerListWidget(customers: _exerciseViewModel.exerciseList);
+ } else if (snapshot.hasError) {
+ return Text("${snapshot.error}");
+ }
+
+ // By default, show a loading spinner.
+ return CircularProgressIndicator();
+ }
+ ),]
+ ));
+
+ return element;
+ }
+
+ Widget getExercises( ExerciseChangingViewModel model ) {
+ List exercises = model.exerciseList;
+
+ Column element = Column();
+ if (exercises.length > 0) {
+ List rows = List();
+
+ exercises.forEach((exercise) {
+ String exerciseName = AppLocalizations.of(context).translate(
+ Common.getExerciseType(exercise.getExercise().exerciseTypeId).name);
+
+ String quantity = exercise.getExercise().quantity.toString() + " " +
+ AppLocalizations.of(context).translate(exercise.getExercise().unit);
+
+ String unitQuantity = "";
+ String unitQuantityUnit = "";
+ String date = Common.getDateLocale(exercise.getExercise().dateAdd, false);
+ if (exercise.getExercise().unitQuantity != null) {
+ unitQuantity = exercise.getExercise().unitQuantity.toString();
+ unitQuantityUnit = AppLocalizations.of(context).translate(
+ Common.getExerciseType(exercise.getExercise().exerciseTypeId).unitQuantityUnit);
+ }
+
+ TableRow row = TableRow(
+ children: [
+ Text(date),
+ Text(exerciseName),
+ Text(quantity),
+
+ Text(unitQuantity + " " + unitQuantityUnit),
+ ]
+ );
+
+ Table table = Table(
+ defaultColumnWidth: FractionColumnWidth(0.28),
+ children: [row],
+ );
+
+ Column col = Column(
+ children: [
+ table,
+ Row(
+ children: [
+ Text(" "),
+ ]
+ )
+ ],
+ );
+ rows.add(col);
+ });
+
+
+ element = Column(
+ children: rows,
+ );
+ }
+
+ return element;
+
+ }
+}
\ No newline at end of file
diff --git a/lib/view/customer_bodytype_page.dart b/lib/view/customer_bodytype_page.dart
new file mode 100644
index 0000000..fc32759
--- /dev/null
+++ b/lib/view/customer_bodytype_page.dart
@@ -0,0 +1,81 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
+import 'package:flutter/material.dart';
+
+// ignore: must_be_immutable
+class CustomerBodyTypePage extends StatefulWidget{
+ _CustomerBodyTypePageState _state;
+
+ _CustomerBodyTypePageState createState() {
+ _state = _CustomerBodyTypePageState();
+ return _state;
+ }
+}
+
+class GenderItem {
+ GenderItem(this.dbValue,this.name);
+ final String dbValue;
+ String name;
+}
+
+class _CustomerBodyTypePageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final CustomerChangingViewModel changingViewModel = 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,
+ height: 65.0,
+ ),
+ ],
+ ),
+ backgroundColor: Colors.transparent,
+ ),
+ body: Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('asset/image/WT_light_background.png'),
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ ),
+ child: Center(
+ child: Column(
+ children: [
+ Divider(),
+ InkWell(
+ child: Text("Your Body Type",
+ style: TextStyle(color: Colors.orange,
+ fontSize: 50, fontFamily: 'Arial',
+ fontWeight: FontWeight.w900 ),),
+ highlightColor: Colors.white,
+ ),
+
+
+ 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)
+ },
+ )
+ ],
+ ),
+ )
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/view/customer_fitness_page.dart b/lib/view/customer_fitness_page.dart
new file mode 100644
index 0000000..bdc1722
--- /dev/null
+++ b/lib/view/customer_fitness_page.dart
@@ -0,0 +1,80 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
+import 'package:flutter/material.dart';
+
+// ignore: must_be_immutable
+class CustomerFitnessPage extends StatefulWidget{
+ _CustomerFitnessPageState _state;
+
+ _CustomerFitnessPageState createState() {
+ _state = _CustomerFitnessPageState();
+ return _state;
+ }
+}
+
+class GenderItem {
+ GenderItem(this.dbValue,this.name);
+ final String dbValue;
+ String name;
+}
+
+class _CustomerFitnessPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final CustomerChangingViewModel changingViewModel = 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,
+ height: 65.0,
+ ),
+ ],
+ ),
+ backgroundColor: Colors.transparent,
+ ),
+ body: Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('asset/image/WT_light_background.png'),
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ ),
+ child: Center(
+ child: Column(
+ children: [
+ Divider(),
+ InkWell(
+ child: Text("Your Fitness State",
+ style: TextStyle(color: Colors.orange,
+ fontSize: 50, fontFamily: 'Arial',
+ fontWeight: FontWeight.w900 ),),
+ highlightColor: Colors.white,
+ ),
+
+ 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)
+ },
+ )
+ ],
+ ),
+ )
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/view/customer_goal_page.dart b/lib/view/customer_goal_page.dart
new file mode 100644
index 0000000..80ea9af
--- /dev/null
+++ b/lib/view/customer_goal_page.dart
@@ -0,0 +1,124 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
+import 'package:flutter/material.dart';
+
+// ignore: must_be_immutable
+class CustomerGoalPage extends StatefulWidget{
+ _CustomerGoalPageState _state;
+
+ _CustomerGoalPageState createState() {
+ _state = _CustomerGoalPageState();
+ return _state;
+ }
+}
+
+class GenderItem {
+ GenderItem(this.dbValue,this.name);
+ final String dbValue;
+ String name;
+}
+
+class _CustomerGoalPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final CustomerChangingViewModel changingViewModel = 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,
+ height: 65.0,
+ ),
+ ],
+ ),
+ backgroundColor: Colors.transparent,
+ ),
+ body: Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('asset/image/WT_light_background.png'),
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ ),
+ child: Center(
+ child: Column(
+ children: [
+ Divider(),
+ InkWell(
+ child: Text("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: 250,),
+ padding: EdgeInsets.all(0.0),
+ onPressed:() =>
+ {
+ print("gain muscle"),
+ changingViewModel.customer.setGoal("gain_muscle"),
+ }
+ ),
+ InkWell(
+ child: Text("Gain Muscle",
+ style: TextStyle(color: Colors.white,
+ fontSize: 36, fontFamily: 'Arial',
+ fontWeight: FontWeight.w900 ),),
+ highlightColor: Colors.white,
+ )
+ ]
+ ),
+ Stack(
+ alignment: Alignment.bottomLeft,
+ overflow: Overflow.visible,
+ children: [
+ FlatButton(
+ child: Image.asset("asset/image/WT_weight_loss.png", height: 220,),
+ padding: EdgeInsets.all(0.0),
+ onPressed:() =>
+ {
+ print("weight_loss"),
+ changingViewModel.customer.setGoal("weight_loss"),
+ }
+ ),
+ InkWell(
+ child: Text("Loose Weight",
+ style: TextStyle(color: Colors.white,
+ fontSize: 36, fontFamily: 'Arial',
+ fontWeight: FontWeight.w900 ),),
+ highlightColor: Colors.white,
+ )
+ ]
+ ),
+ 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)
+ },
+ )
+ ],
+ ),
+ )
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart
index 59820eb..48bf05d 100644
--- a/lib/view/customer_modify_page.dart
+++ b/lib/view/customer_modify_page.dart
@@ -1,29 +1,277 @@
+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/customer_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
-import 'package:aitrainer_app/widgets/nav_drawer.dart';
+import 'package:flutter/services.dart';
class CustomerModifyPage extends StatefulWidget{
- _CustomerModifyPageState createState() => _CustomerModifyPageState();
+ _CustomerModifyPageState _state;
+
+ _CustomerModifyPageState createState() {
+ _state = _CustomerModifyPageState();
+ return _state;
+ }
}
-class _CustomerModifyPageState extends State {
- //final _formKey = GlobalKey();
+class GenderItem {
+ GenderItem(this.dbValue,this.name);
+ final String dbValue;
+ String name;
+}
+
+class _CustomerModifyPageState extends State {
+ 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);
+ customerChangeModel.customer.customer.sex = selectedGender.dbValue;
+
+ // we cannot initialize the translations in the initState
+ genders.forEach((GenderItem element) {
+ if ( element.dbValue == "m") {
+ element.name = AppLocalizations.of(context).translate("Man");
+ }
+ if ( element.dbValue == "w") {
+ element.name = AppLocalizations.of(context).translate("Woman");
+ }
+ });
+
return Scaffold(
- drawer: NavDrawer(),
+ resizeToAvoidBottomInset: true,
appBar: AppBar(
- title: Text('Modify customer'),
+ 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: Center(
- child: Text('Modify customer'),
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: () => {},
- child: Icon(Icons.save,),
- mini: true,
+ 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: 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: customerChangeModel.customer.customer.email,
+ onFieldSubmitted: (input) => customerChangeModel.customer.setEmail(input)
+ )
+ )
+ ],
+ ),
+ Divider(),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+
+ 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)' ),
+ ),
+ initialValue: customerChangeModel.customer.customer.password,
+ onFieldSubmitted: (input) => customerChangeModel.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'),
+ ),
+ initialValue: customerChangeModel.customer.customer.name,
+ onFieldSubmitted: (input) => customerChangeModel.customer.setName(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('First Name'),
+ ),
+ keyboardType: TextInputType.emailAddress,
+ initialValue: customerChangeModel.customer.customer.firstName,
+ onFieldSubmitted: (input) => customerChangeModel.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
+ ],
+ initialValue: customerChangeModel.customer.customer.birthYear.toString(),
+ onFieldSubmitted: (input) => customerChangeModel.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'),
+ ),
+ inputFormatters: [
+ WhitelistingTextInputFormatter.digitsOnly
+ ],
+ initialValue: customerChangeModel.customer.customer.weight.toString(),
+ keyboardType: TextInputType.number,
+ onFieldSubmitted: (input) => customerChangeModel.customer.setWeight(int.parse(input)),
+ )
+ )
+ ],
+ ),
+ Divider(),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+
+ Expanded(
+ child: DropdownButtonHideUnderline(
+ child: DropdownButton(
+ 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){
+ return DropdownMenuItem(
+ value: gender,
+ child: Text(gender.name)
+ );
+ }).toList(),
+ onChanged:(GenderItem gender) => {
+ setState(() {
+ selectedGender = gender;
+ customerChangeModel.customer.setSex(gender.dbValue);
+
+ 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: () => {
+ customerChangeModel.saveCustomer(),
+ Navigator.of(context).pushNamed("customerGoalPage", arguments: customerChangeModel)
+ },
+ )
+ )
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
)
+ )
);
}
}
\ No newline at end of file
diff --git a/lib/view/customer_new_page.dart b/lib/view/customer_new_page.dart
index 43f6b79..43415df 100644
--- a/lib/view/customer_new_page.dart
+++ b/lib/view/customer_new_page.dart
@@ -68,7 +68,7 @@ class _CustomerNewPageState extends State {
validator: (input) => (int.parse(input) < 99 && int.parse(input) > 0) ?
null :
"Please type the right age 0-99",
- onChanged: (input) => customer.setAge(int.parse(input)),
+ onChanged: (input) => customer.setBirthYear(int.parse(input)),
),
RadioListTile(
title: const Text('Man'),
diff --git a/lib/view/customer_welcome_page.dart b/lib/view/customer_welcome_page.dart
new file mode 100644
index 0000000..d00312b
--- /dev/null
+++ b/lib/view/customer_welcome_page.dart
@@ -0,0 +1,75 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:aitrainer_app/viewmodel/customer_changing_view_model.dart';
+import 'package:flutter/material.dart';
+
+// ignore: must_be_immutable
+class CustomerWelcomePage extends StatefulWidget{
+ _CustomerWelcomePageState _state;
+
+ _CustomerWelcomePageState createState() {
+ _state = _CustomerWelcomePageState();
+ return _state;
+ }
+}
+
+class GenderItem {
+ GenderItem(this.dbValue,this.name);
+ final String dbValue;
+ String name;
+}
+
+class _CustomerWelcomePageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final CustomerChangingViewModel changingViewModel = 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,
+ height: 65.0,
+ ),
+ ],
+ ),
+ backgroundColor: Colors.transparent,
+ ),
+ body: Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('asset/image/WT_welcome.png'),
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ ),
+ child: Center(
+ child: Column(
+ children: [
+ Divider(),
+
+
+
+ RaisedButton(
+
+ color: Colors.orange,
+ textColor: Colors.white,
+ child: InkWell(
+ child: Text(AppLocalizations.of(context).translate("Next"))),
+ onPressed: () => {
+
+ Navigator.of(context).pop(),
+ Navigator.of(context).pushNamed("home", arguments: changingViewModel)
+ },
+ )
+ ],
+ ),
+ )
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart
index 17c629f..207d1d8 100644
--- a/lib/view/exercise_new_page.dart
+++ b/lib/view/exercise_new_page.dart
@@ -1,108 +1,226 @@
+import 'package:aitrainer_app/localization/app_language.dart';
+import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/exercise_changing_view_model.dart';
-import 'package:aitrainer_app/widgets/nav_drawer.dart';
import 'package:intl/intl.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:datetime_picker_formfield/datetime_picker_formfield.dart';
class ExerciseNewPage extends StatefulWidget{
_ExerciseNewPageState createState() => _ExerciseNewPageState();
}
class _ExerciseNewPageState extends State {
+ final List excluded = [43,44];
final _formKey = GlobalKey();
- final format = DateFormat("yyyy-MM-dd HH:mm");
@override
Widget build(BuildContext context) {
- ExerciseChangingViewModel model = Provider.of(context, listen: false);
- model.createNewModel();
- 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: 'Name',
- ),
- readOnly: true,
- initialValue: model != null && model.customer != null ? model.customer.name + " " + model.customer.firstName : "Please select a customer",
- ),
- TextFormField(
- decoration: InputDecoration(
- border: OutlineInputBorder(),
- labelText: 'Exercise',
- ),
- readOnly: true,
- initialValue: model != null && model.exerciseType != null ? model.exerciseType.name : "Please select an exercise",
- ),
- TextFormField(
- decoration: InputDecoration(
- border: OutlineInputBorder(),
- labelText: 'Quantity',
- ),
- validator: (input) => (int.parse(input) < 1000 && int.parse(input) > 0) ?
- null :
- "Please type the right quantity 0-1000",
- onChanged: (input) => model.exerciseViewModel.setQuantity(int.parse(input)),
- ),
- Text('Exercise date and time'),
- DateTimeField(
- format: format,
- initialValue: DateTime.now(),
- onShowPicker: (context, currentValue) async {
- final date = await showDatePicker(
- context: context,
- firstDate: DateTime(1900),
- initialDate: DateTime.now(),
- lastDate: DateTime(2100),
- builder: (context, child) => Localizations.override(
- context: context,
- locale: Locale('hu'),
- child: child,
- ),
- );
- if (date != null) {
- final time = await showTimePicker(
- context: context,
- initialTime:
- TimeOfDay.fromDateTime(currentValue ?? DateTime.now()),
- builder: (context, child) => Localizations.override(
- context: context,
- locale: Locale('hu'),
- child: child,
- ),
- );
- return DateTimeField.combine(date, time);
- } else {
- return currentValue;
- }
- },
- onChanged: (input) => model.exerciseViewModel.setDatetimeExercise(input),
- ),
- ]),
- )
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: () => {
- if (_formKey.currentState.validate()) {
- //model = ExerciseChangingViewModel(model.exerciseViewModel),
- model.addExercise(),
- Navigator.pop(context),
+ return Consumer(
+ builder: (context, model, child ) {
+ String exerciseName = "";
+ String customerName = "";
+ if ( model != null ) {
+ if ( model.exerciseViewModel == null ) {
+ model.createNewModel();
}
- },
- child: Icon(Icons.save,),
- mini: true,
- )
- );
+ model.exerciseViewModel.createNew();
+ customerName = model != null && model.customer != null
+ ? model.customer.name + " " +
+ model.customer.firstName
+ : "Please select a customer";
+
+ exerciseName = model != null &&
+ model.exerciseType != null
+ ? model.exerciseType.name
+ : "Please select an exercise";
+ }
+
+ 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,
+ autovalidate: true,
+ child: Scaffold(
+ resizeToAvoidBottomInset: false,
+ appBar: AppBar(
+ leading: IconButton(
+ icon: Icon(Icons.arrow_back, color: Colors.deepOrange),
+ 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(
+ image: DecorationImage(
+ image: AssetImage('asset/image/WT_login.png'),
+ fit: BoxFit.cover,
+ //height: double.infinity,
+ //width: double.infinity,
+ alignment: Alignment.center,
+ ),
+ ),
+ child: Container(
+ padding: const EdgeInsets.only (top: 65, left:25, right: 100),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ columnQuantityUnit(model),
+ columnQuantity(model),
+
+ 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(
+
+ padding: EdgeInsets.only(bottom: 0),
+ color: Colors.transparent,
+ splashColor: Colors.black26,
+ child: Row(
+ children: [
+
+ Icon(Icons.arrow_forward_ios, color: Colors.orange,)
+ ]),
+ onPressed:() { print("date change");},
+
+ )),
+ ],
+ ),
+
+ new InkWell(
+ child: new Text(AppLocalizations.of(context).translate('Exercise date and time'),
+ style: TextStyle( fontSize: 16)),
+ ),
+
+ ]),
+ RaisedButton(
+ 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),
+ }
+ },
+ child: Text("Save", style: TextStyle(fontSize: 16),)
+ ),
+
+ ]),
+ )
+ ),
+ ),
+ );
+ });
+ }
+
+ Column columnQuantityUnit( ExerciseChangingViewModel model) {
+ Column column = Column();
+ if ( model.exerciseType != null && model.exerciseType.unitQuantity == "1") {
+ column = Column(
+ children: [
+ TextFormField(
+ autovalidate: true,
+ textAlign: TextAlign.center,
+ initialValue: "0",
+ style: TextStyle(fontSize: 30,
+ color: Colors.lightBlue,
+ fontWeight: FontWeight.bold),
+ validator: (input) {
+ return validateNumberInput(input);
+ },
+ onFieldSubmitted: (input) => {
+ print ("UnitQuantity value $input"),
+ model.exerciseViewModel.setUnitQuantity(
+ double.parse(input))
+ },
+
+ ),
+ new InkWell(
+ child: new Text(AppLocalizations.of(context).translate(
+ model.exerciseType.unitQuantityUnit),
+ style: TextStyle(fontSize: 16)),
+ ),
+
+ ]);
+ };
+ return column;
+ }
+
+ Column columnQuantity( ExerciseChangingViewModel model) {
+ Column column = Column();
+
+ column = Column(
+ children: [
+ TextFormField(
+ autovalidate: true,
+ textAlign: TextAlign.center,
+ initialValue: "0",
+ style: TextStyle(fontSize: 60,
+ color: Colors.deepOrange,
+ fontWeight: FontWeight.bold),
+ validator: (input) {
+ return validateNumberInput(input);
+ },
+ onFieldSubmitted: (input) =>
+ {
+ print ("Quantity value $input"),
+ model.exerciseViewModel.setQuantity(
+ double.parse(input)),
+ model.exerciseViewModel.setUnit(model.exerciseType.unit)
+
+ }
+ ),
+ new InkWell(
+ child: new Text(AppLocalizations.of(context).translate(model.exerciseType.unit),
+ style: TextStyle(fontSize: 16)),
+ ),
+
+ ]);
+
+ return column;
+ }
+
+ String validateNumberInput( input ) {
+ String error = AppLocalizations.of(context).translate("Please type the right quantity 0-10000");
+ dynamic rc = ( input != null && input.length > 0);
+ if ( ! rc ) {
+ return null;
+ }
+
+ rc = double.tryParse(input);
+ if ( rc == null ) {
+ return error;
+ }
+
+ if ( ! ( double.parse(input) < 10000 && double.parse(input) > 0) ) {
+ return error;
+ }
+
+ return null;
}
}
diff --git a/lib/view/gdpr.dart b/lib/view/gdpr.dart
new file mode 100644
index 0000000..c63ce40
--- /dev/null
+++ b/lib/view/gdpr.dart
@@ -0,0 +1,39 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class Gdpr extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Container(
+ padding: const EdgeInsets.only (left:15, right: 15),
+ child: ListView(
+ children: [
+ new InkWell(
+ child: new Text(
+ AppLocalizations.of(context).translate('gdpr_text'),
+
+ ),
+
+ customBorder: Border.all(color:Colors.teal, width:1),
+ ),
+ Spacer(flex:2),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ FlatButton(
+ textColor: Colors.black,
+
+ onPressed: () { },
+
+ )
+ ],
+ )
+ ],
+ )
+ )
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/view/login.dart b/lib/view/login.dart
index 8d71dbd..fd7383f 100644
--- a/lib/view/login.dart
+++ b/lib/view/login.dart
@@ -1,9 +1,12 @@
-
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:aitrainer_app/model/auth.dart';
+import 'package:aitrainer_app/viewmodel/exercise_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:flutter/material.dart';
import 'package:flutter/cupertino.dart';
-import 'package:aitrainer_app/widgets/nav_drawer.dart';
+import 'package:provider/provider.dart';
+
class LoginPage extends StatefulWidget{
_LoginPageState createState() => _LoginPageState();
@@ -11,71 +14,126 @@ class LoginPage extends StatefulWidget{
class _LoginPageState extends State {
final UserViewModel user = UserViewModel();
- bool _obscureText = true;
-
+ final bool _obscureText = true;
final _formKey = GlobalKey();
@override
Widget build(BuildContext context) {
UserChangingViewModel model = UserChangingViewModel(user);
+ ExerciseChangingViewModel exerciseModel = Provider.of(context, listen: false);
user.createNew();
- return Scaffold(
- drawer: NavDrawer(),
- appBar: AppBar(
- title: Text('Login'),
- ),
- body: Center(
- child: Form(
- key: _formKey,
- child: Column(
- children: [
- TextFormField(
- decoration: InputDecoration(
- border: OutlineInputBorder(),
- labelText: 'Email',
- icon: const Padding(
- padding:const EdgeInsets.only(left: 20.0, top: 50.0),
- child: const Icon(Icons.people)
- )
- ),
- validator: (String input) {
- RegExp exp = new RegExp(r"[\w._]+\@[\w._]+.[a-z]+",
- caseSensitive: false,
- multiLine: false,);
- String ret = exp.hasMatch(input) == true ?
- null:
- "Please type an email address";
- return ret;
- },
- onChanged: (input) => user.setEmail(input),
- ),
- new TextFormField(
- decoration: const InputDecoration(
- labelText: 'Password',
- icon: const Padding(
- padding: const EdgeInsets.only(left: 20.0, top: 15.0),
- child: const Icon(Icons.lock))),
- validator: (val) => val.length < 6 ? 'Password too short.' : null,
- obscureText: _obscureText,
- onChanged: (input) => user.setPassword(input),
- ),
- new InkWell(
- child: new Text('SignUp'),
- onTap: () => Navigator.of(context).pushNamed('registration'),
- ),
- new FloatingActionButton(
- child: Icon(Icons.cloud_done,),
- onPressed:() => {
- if (_formKey.currentState.validate()) {
- model = UserChangingViewModel(user),
- model.getUser(),
- Navigator.pop(context),
- }
- })
- ])
- ),
- ),
+ return Scaffold(
+ 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(
+ '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(),
+ exerciseModel.setCustomer(
+ Auth().userLoggedIn),
+ Navigator.pop(context),
+ }
+ }),
+ ]),
+ 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),
+ ])
+ ),
+ ),
+ )
);
}
}
\ No newline at end of file
diff --git a/lib/view/menu_page.dart b/lib/view/menu_page.dart
new file mode 100644
index 0000000..e6232b0
--- /dev/null
+++ b/lib/view/menu_page.dart
@@ -0,0 +1,154 @@
+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:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'dart:collection';
+import 'package:provider/provider.dart';
+
+// ignore: must_be_immutable
+class MenuPage extends StatefulWidget {
+ _MenuPageState _state;
+ static const routeName = '/menu_page';
+ int parent;
+
+ 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);
+ return Scaffold(
+ 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.black),
+ onPressed: () => {
+ this.setState(() {
+ widget.parent = 0;
+ },
+ )},
+ ),
+ ),
+
+ body: Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('asset/image/WT_menu_dark.png'),
+ fit: BoxFit.fill,
+ alignment: Alignment.center,
+ ),
+ ),
+
+ child: CustomScrollView(
+ scrollDirection: Axis.vertical,
+ slivers: [
+ buildMenuColumn(widget.parent, context, menu)
+ ]
+ )
+ ),
+ );
+ }
+
+ 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.exercise_type_id),
+ model.setExerciseType(exerciseType),
+ model.setCustomer(Auth().userLoggedIn),
+ if ( Auth().userLoggedIn == null ) {
+ Scaffold.of(context)
+ . showSnackBar(SnackBar(content: Text('Please log in')))
+ } 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;
+ }
+
+}
diff --git a/lib/view/registration.dart b/lib/view/registration.dart
index 881874b..a5b40e0 100644
--- a/lib/view/registration.dart
+++ b/lib/view/registration.dart
@@ -1,4 +1,5 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/viewmodel/user_changing_view_model.dart';
import 'package:aitrainer_app/viewmodel/user_view_model.dart';
import 'package:flutter/material.dart';
@@ -19,66 +20,102 @@ class _RegistrationPageState extends State {
Widget build(BuildContext context) {
UserChangingViewModel model = UserChangingViewModel(user);
user.createNew();
+
return Scaffold(
- drawer: NavDrawer(),
- appBar: AppBar(
- title: Text('Registration'),
+
+ 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,
+ ),
),
- body: Center(
- child: Form(
- key: _formKey,
- child: Column(
+ 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)),
+ ),
+ ],
+ ),
+
TextFormField(
decoration: InputDecoration(
- border: OutlineInputBorder(),
+ fillColor: Colors.white,
+ filled:true,
labelText: 'Email',
- icon: const Padding(
- padding:const EdgeInsets.only(left: 20.0, top: 50.0),
- child: const Icon(Icons.people)
- )
),
- /* validator: (String input) {
- RegExp exp = new RegExp(r"[\w._]+\@[\w._]+.[a-z]+",
- caseSensitive: false,
- multiLine: false,);
- String ret = exp.hasMatch(input) == true ?
- null:
- "Please type an email address";
- return ret;
- },*/
+ 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(
- labelText: 'Password',
- icon: const Padding(
- padding: const EdgeInsets.only(left: 20.0, top: 15.0),
- child: const Icon(Icons.lock))),
- /* validator: (String input) {
- String rc = input.length < 4 ? 'Password too short.' : null;
- return rc;
- }, */
- onChanged: (input) => user.setPassword(input),
+ 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),
),
- new InkWell(
- child: new Text('I have an account'),
- onTap: () => Navigator.of(context).pushNamed('login'),
- ),
- new FloatingActionButton(
- child: Icon(Icons.cloud_done,),
- onPressed:() => {
- if (_formKey.currentState.validate()) {
- model = UserChangingViewModel(user),
- model.addUser(),
- Navigator.pop(context),
- }
- }
- )
- ])
- ),
+ 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.addUser(),
+ Navigator.of(context).pushNamed("customerModifyPage",)
+ }
+ }),
+ ]),
+ 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),
+ ])
+ ),
),
+ ),
);
}
diff --git a/lib/view/settings.dart b/lib/view/settings.dart
new file mode 100644
index 0000000..27f4adf
--- /dev/null
+++ b/lib/view/settings.dart
@@ -0,0 +1,103 @@
+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:flutter/material.dart';
+import 'package:flutter/cupertino.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();
+
+ @override
+ Widget build(BuildContext context) {
+ BottomNavigator bottomNav = BottomNavigator();
+ _locale = appLanguage.appLocal;
+ return Scaffold(
+ appBar: AppBar(
+ title: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(AppLocalizations.of(context).translate('Settings')),
+ 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,
+ ),
+ ),
+ child: Form(
+ key: _formKey,
+ child:
+ ListView(
+ padding: EdgeInsets.only(top: 150),
+ children: [
+ ListTile(
+ leading: Icon(Icons.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) {
+ return DropdownMenuItem(
+ value: value,
+ child: Text(value),
+ );
+ }).toList(),
+ onChanged:(String lang) => _changeLanguage(lang),
+ )
+
+ ),
+ ]
+
+ ),
+ ),
+ ),
+ bottomNavigationBar: bottomNav.buildBottomNavigator(context, widget._state)
+
+ );
+ }
+
+ _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
index d4cc0ae..b0d7e0c 100644
--- a/lib/viewmodel/customer_changing_view_model.dart
+++ b/lib/viewmodel/customer_changing_view_model.dart
@@ -19,7 +19,7 @@ class CustomerChangingViewModel extends ChangeNotifier {
}
Future saveCustomer() async {
- this.customer = customer;
+ //this.customer = customer;
final Customer modelCustomer = customer.getCustomer();
await CustomerApi().saveCustomer(modelCustomer);
}
diff --git a/lib/viewmodel/customer_view_model.dart b/lib/viewmodel/customer_view_model.dart
index 36a2ce8..5d55df1 100644
--- a/lib/viewmodel/customer_view_model.dart
+++ b/lib/viewmodel/customer_view_model.dart
@@ -18,8 +18,8 @@ class CustomerViewModel {
return this.customer.sex == "m" ? "Man" : "Woman";
}
- int get age {
- return this.customer.age;
+ int get birthYear {
+ return this.birthYear;
}
setName(String name) {
@@ -29,18 +29,39 @@ class CustomerViewModel {
this.customer.firstName = firstName;
}
+ setPassword( String password ) {
+ this.customer.password = password;
+ }
+
setEmail(String email) {
this.customer.email = email;
}
- setAge(int age) {
- this.customer.age = age;
- }
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();
}
diff --git a/lib/viewmodel/exercise_changing_view_model.dart b/lib/viewmodel/exercise_changing_view_model.dart
index 60cff9a..0b3df88 100644
--- a/lib/viewmodel/exercise_changing_view_model.dart
+++ b/lib/viewmodel/exercise_changing_view_model.dart
@@ -9,6 +9,7 @@ import 'exercise_view_model.dart';
class ExerciseChangingViewModel with ChangeNotifier {
Customer customer;
ExerciseType exerciseType;
+ List exerciseList = List();
ExerciseViewModel exerciseViewModel = ExerciseViewModel();
@@ -20,10 +21,12 @@ class ExerciseChangingViewModel with ChangeNotifier {
setCustomer(Customer customer) {
this.customer = customer;
+ notifyListeners();
}
setExerciseType( ExerciseType exerciseType) {
this.exerciseType = exerciseType;
+ notifyListeners();
}
setQuantity(int quantity) {
@@ -31,7 +34,7 @@ class ExerciseChangingViewModel with ChangeNotifier {
}
addExercise() async {
- this.exerciseViewModel = exerciseViewModel;
+// this.exerciseViewModel = exerciseViewModel;
final Exercise modelExercise = exerciseViewModel.getExercise();
modelExercise.customerId = this.customer.customerId;
modelExercise.exerciseTypeId = this.exerciseType.exerciseTypeId;
@@ -43,4 +46,11 @@ class ExerciseChangingViewModel with ChangeNotifier {
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_view_model.dart b/lib/viewmodel/exercise_view_model.dart
index af0065e..35fb91d 100644
--- a/lib/viewmodel/exercise_view_model.dart
+++ b/lib/viewmodel/exercise_view_model.dart
@@ -6,15 +6,23 @@ class ExerciseViewModel {
createNew() {
this.exercise = Exercise();
- exercise.datetimeExercise = DateTime.now();
+ exercise.dateAdd = DateTime.now();
}
- setQuantity(int quantity) {
+ 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.datetimeExercise = datetimeExercise;
+ this.exercise.dateAdd = datetimeExercise;
}
Exercise getExercise() {
diff --git a/lib/widgets/bottom_nav.dart b/lib/widgets/bottom_nav.dart
new file mode 100644
index 0000000..538b872
--- /dev/null
+++ b/lib/widgets/bottom_nav.dart
@@ -0,0 +1,58 @@
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:flutter/material.dart';
+
+class BottomNavigator {
+ BottomNavigationBar buildBottomNavigator(BuildContext context, State state) {
+ return BottomNavigationBar(
+ currentIndex: 0, // this will be set when a new tab is tapped
+ backgroundColor: Colors.black12,
+ selectedItemColor: Colors.yellow,
+ unselectedItemColor: Colors.lightGreen,
+ type: BottomNavigationBarType.shifting,
+ showSelectedLabels: true,
+ items: [
+ BottomNavigationBarItem(
+ backgroundColor: Colors.black12,
+ icon: new Icon(Icons.home, color: Colors.lightGreen),
+ activeIcon: new Icon(Icons.home, color: Colors.yellow,),
+ title: new Text(AppLocalizations.of(context).translate("Home")),
+ ),
+ BottomNavigationBarItem(
+ icon: new Icon(Icons.event, color: Colors.lightGreen),
+ activeIcon: new Icon(Icons.event, color: Colors.yellow,),
+ title: new Text(AppLocalizations.of(context).translate("Events")),
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.person, color: Colors.lightGreen,),
+ activeIcon: new Icon(Icons.person, color: Colors.yellow,),
+ title: Text(AppLocalizations.of(context).translate("Account"))
+ ),
+ BottomNavigationBarItem(
+ 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
+ switch (index) {
+ case 0:
+ Navigator.of(context).pop();
+ Navigator.of(context).pushNamed('home');
+
+ break;
+ case 2:
+ Navigator.of(context).pop();
+ Navigator.of(context).pushNamed('account');
+
+ break;
+ case 3:
+ Navigator.of(context).pop();
+ Navigator.of(context).pushNamed('settings');
+
+ break;
+ }
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/widgets/customer_list_widget.dart b/lib/widgets/customer_list_widget.dart
index 54a1cf0..332783b 100644
--- a/lib/widgets/customer_list_widget.dart
+++ b/lib/widgets/customer_list_widget.dart
@@ -36,7 +36,7 @@ class _CustomerListWidget extends State {
visible: customer.visibleDetails,
child: Row(
children: [
- Text(customer.age.toString() + " years, " + customer.sex),
+ Text(customer.birthYear.toString() + " years, " + customer.sex),
new RaisedButton(
child: new Text('Modify'),
color: Color.fromRGBO(244, 122, 22, 0.9),
diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart
new file mode 100644
index 0000000..35fe2ed
--- /dev/null
+++ b/lib/widgets/home.dart
@@ -0,0 +1,57 @@
+import 'package:aitrainer_app/localization/app_language.dart';
+import 'package:aitrainer_app/localization/app_localization.dart';
+import 'package:aitrainer_app/view/menu_page.dart';
+import 'package:aitrainer_app/widgets/bottom_nav.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'bottom_nav.dart';
+import 'nav_drawer.dart';
+
+class AitrainerHome extends StatefulWidget {
+ _HomePageState _state;
+ @override
+ State createState() {
+ _state = new _HomePageState();
+ return _state;
+ }
+
+ void callback() {
+ _state.setLangNoContext();
+ }
+}
+
+class _HomePageState extends State {
+ GlobalKey _scaffoldKey = new GlobalKey();
+ final AppLanguage appLanguage = AppLanguage();
+
+ @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");
+ });
+ }
+
+}
diff --git a/lib/widgets/loading.dart b/lib/widgets/loading.dart
new file mode 100644
index 0000000..bc94f06
--- /dev/null
+++ b/lib/widgets/loading.dart
@@ -0,0 +1,55 @@
+import 'package:aitrainer_app/util/message_state.dart';
+import 'package:aitrainer_app/util/session.dart';
+import 'package:aitrainer_app/view/menu_page.dart';
+import 'package:aitrainer_app/widgets/bottom_nav.dart';
+import 'package:aitrainer_app/widgets/home.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'file:///D:/projects/aitrainer/src/aitrainer_app/lib/util/loading_screen.dart';
+
+class LoadingScreenMain extends StatefulWidget {
+ @override
+ LoadingScreenMainState createState() => LoadingScreenMainState();
+}
+
+class LoadingScreenMainState extends State {
+
+ @override
+ Widget build(BuildContext context) {
+ Image _backgroundImage = Image.asset('asset/WT01_loading_layers.png',
+ fit: BoxFit.cover,
+ height: double.infinity,
+ 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),
+ )
+ );
+ }
+
+
+}
+
+class TimeMessages {
+ static Future timer(MessageState state, Function callback) async {
+ //while (true) {
+ await Future.delayed(Duration(seconds: 4), () {
+ state.setMessage = DateTime.now().toIso8601String();
+ print("---- TimeMessages initializer");
+ Session session = Session();
+ session.fetchSessionAndNavigate(callback);
+ });
+ //}
+ }
+}
diff --git a/lib/widgets/nav_drawer.dart b/lib/widgets/nav_drawer.dart
index 43f1698..5dd8c79 100644
--- a/lib/widgets/nav_drawer.dart
+++ b/lib/widgets/nav_drawer.dart
@@ -1,8 +1,25 @@
+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 StatelessWidget {
+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(
@@ -11,49 +28,64 @@ class NavDrawer extends StatelessWidget {
children: [
DrawerHeader(
child: Text(
- 'Customers And Exercises',
+ AppLocalizations.of(context).translate('Customers And Exercises'),
style: TextStyle(color: Colors.blue, fontSize: 25),
),
),
ListTile(
leading: Icon(Icons.home),
- title: Text('Home'),
+ title: Text( AppLocalizations.of(context).translate('Home')),
onTap: () => Navigator.of(context).pushNamed('home'),
),
ListTile(
leading: Icon(Icons.people),
- title: Text('Customers'),
- //onTap: () => navigateToPage(context, 'customersPage'),
+ title: Text( AppLocalizations.of(context).translate('Customers')),
onTap: () => Navigator.of(context).pushNamed('customersPage'),
),
ListTile(
leading: Icon(Icons.directions_run),
- title: Text('Exercises'),
- onTap: () => Navigator.of(context).pushNamed('exerciseTypeListPage'),
+ title: Text(AppLocalizations.of(context).translate('Exercises')),
+ onTap: () =>
+ Navigator.of(context).pushNamed('exerciseTypeListPage'),
),
ListTile(
leading: Icon(Icons.arrow_upward),
- title: Text("TRAINING!"),
+ title: Text(AppLocalizations.of(context).translate("TRAINING!")),
onTap: () => Navigator.of(context).pushNamed('exerciseNewPage'),
),
ListTile(
leading: Icon(Icons.perm_identity),
- title: Text('Login'),
+ title: Text(AppLocalizations.of(context).translate('Login')),
onTap: () => Navigator.of(context).pushNamed('login'),
),
ListTile(
- leading: Icon(Icons.cancel),
- title: Text("Logout"),
- onTap: () => {
- auth.logout(),
- Navigator.of(context).pushNamed('home'),
- }
+ 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/pubspec.yaml b/pubspec.yaml
index fbccdba..398a0cc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -56,8 +56,44 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- - asset/WT01_loading_layers.png
- # - images/a_dot_ham.jpeg
+ - asset/image/WT_menu_welcome.png
+ - asset/image/WT_menu_backround.png
+ - asset/image/WT_menu.png
+ - asset/image/WT_login.png
+ - asset/image/WT_OK.png
+ - asset/image/dots.gif
+ - asset/image/WT_long_logo.png
+ - asset/image/WT_light_background.png
+ - asset/image/WT_menu_dark.png
+ - asset/image/WT_gain_muscle.png
+ - asset/image/WT_weight_loss.png
+ - asset/image/WT_welcome.png
+ - asset/menu/1.cardio.png
+ - asset/menu/1.1.aerob.png
+ - asset/menu/1.2.anaerob.png
+ - asset/menu/1.1.1.cooper.png
+ - asset/menu/1.2.1.300m.png
+ - asset/menu/1.2.2.400m.png
+ - asset/menu/2.strength.png
+ - asset/menu/2.1.endurance.png
+ - asset/menu/2.1.1.pull-ups.png
+ - asset/menu/2.1.2.pushup.png
+ - asset/menu/2.1.3.sit-ups.png
+ - asset/menu/2.1.4.squats.png
+ - asset/menu/2.1.5.timedpushup.png
+ - asset/menu/2.1.6.core.png
+ - asset/menu/2.2.1.1RM.png
+ - asset/menu/2.2.1.1.chestpress.png
+ - asset/menu/2.2.1.2.pullups.png
+ - asset/menu/2.2.1.3.biceps.png
+ - asset/menu/2.2.1.4.triceps.png
+ - asset/menu/2.2.1.5.shoulders.png
+ - asset/menu/3.bcs1.png
+ - asset/menu/3.1.BMI.png
+ - asset/menu/3.2.BMR.png
+ - asset/menu/3.3.sizes.png
+ - i18n/en.json
+ - i18n/hu.json
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.