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.