diff --git a/asset/image/predictions.jpg b/asset/image/predictions.jpg new file mode 100644 index 0000000..6009b11 Binary files /dev/null and b/asset/image/predictions.jpg differ diff --git a/i18n/en.json b/i18n/en.json index aac6282..b628f19 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -9,7 +9,7 @@ "Logout": "Logout", "Tests": "Tests", "Change Language": "Change Language", - "Password too short": "Password too short", + "Password too short": "Password too short (at least 6 characters)", "Please type an email address": "Please type an email address", "SignUp": "SignUp", "Privacy": "Privacy", @@ -24,7 +24,7 @@ "gdpr_text": "Kik vagyunk\nA weboldalunk a https://andio.biz (blogbejegyzések) és https://natur-haztartas.hu webáruház.\n\nCélunk a természetes és natúr életmód népszerűsítése, hogy az vásárlóinkat és érdeklődőinket segítsünk a vegyszermentes és natúr életmód felé vezető úton.\n\nMelyek azok a személyes adatok amiket gyűjtünk, és milyen céllal gyűjtjük azokat\nVásárlás\nMegrendelés esetén elkérjük a következő adatokat:\n\na nevet: hogy azonosítsunk csomagszállítás esetén, illetve a számlára is rákerül a vásárló neve\nemail címet: megrendelés visszaigazoláshoz, a csomag állapotának a közléséhez. Emellett, ha engedélyt kapunk, hírleveleket is küldünk natúr életmód tippekről, ajánlatokról. Ezekről a levelekről bármikor, egy kattintással le tudsz iratkozni, a levél alján.\ncímet: A csomag szállításához fontos a pontos cím. A számlára is ez a cím kerül, de lehetőséged van itt egy másik, csak számlázási címet megadni.\ntelefonszámot: SMS értesítőt küldünk a csomag kiszállítása előtt egy munkanappal. Ha valamilyen más probléma merül fel a csomag összeállításakor, akkor lehetséges, hogy telefonon megkeresünk és tisztázzuk a problémákat (pl. áruhiány, rosszul megadott adatok kijavítása, stb).\nHozzászólások\nHozzászólás beküldésekor a hozzászólási űrlapban megadottakon kívül begyűjtésre kerül a hozzászóló IP címe és a böngészőazonosító karakterlánc a kéretlen tartalmak kiszűrése céljából.\n\nEgy személytelenített, az e-mail címből előállított karakterlánc (hashnek szokás nevezni) kerül továbbításra a Gravatar szolgáltatás felé, ha ez az oldalon használatban van. A Gravatar szolgáltatás feltételei az alábbi címen tekinthetőek meg: https://automattic.com/privacy/. A hozzászólás elfogadása után, a hozzászólásunk tartalma és a profil képünk is megjelenik nyilvánosan.\n\nKapcsolatfelvételi űrlapok\nAmennyiben szeretnél, feliratkozhatsz hírlevelünkre weboldalunkon az ott található regisztrációs űrlap kitöltésével. (pl: https://natur-haztartas.hu/login?back=my-account)\n\nHa kitöltöd a regisztrációs űrlapot és rendelkezésünkre bocsátod személyes adataidat vagy a vevőként történő regisztrációjakor hozzájárultál ahhoz, hogy hírlevelet küldjünk az számodra, e-mailt fogsz kapni tőlünk a megadott e-mail címre.\n\nA regisztrációs űrlapon megadott személyes adatok kezelésére kizárólag a hírlevélnek az e-mail címre történő küldése céljából kerül.\n\nA hírleveleinkben található linkek nyomon követési információkat tartalmaznak, melyek segítségével megállapíthatjuk, hogy különösen mely linkek érdekeltek téged, amelyekre rákattintottál.\n\nA nyomon követési link segítségével a következő adatok tárolása történik a hírlevélkezelő rendszerünkbe: e-mail cím, hírlevél, link, dátum és idő. Az személyes adataidat csak addig tároljuk, amíg a „leiratkozás” linkre kattintva le nem iratkozol a hírlevélről. E link megtalálható minden egyes tőlünk kapott hírlevél alján. Amennyiben leiratkozol személyes adataid késedelem nélkül törlésre kerülnek. Felhívjuk a figyelmed arra, hogy személyes adatok törlését követően nem fogsz tőlünk több hírlevelet vagy kupont kapni.\n\nHa üzenetet küldesz az Andió Facebook messengerére, akkor jogosultak vagyunk az Andió által viszontüzenetet küldeni a Te messengeredre. Ezekről az üzeneteket bármikor leállíthatod, ha a messengerbe beírod a STOP szót és utána megnyomod a „leiratkozok” gombot.\n\nSütik és nyomon követés\nHozzászólás írása után a honlap a megadott nevet, e-mail és web címet sütiben eltárolja. A tárolás csak kényelmi célokat szolgál, hogy a következő hozzászóláskor ne kelljen automatikusan kitölteni. A sütik lejárati ideje 1 év.\n\nHa rendelkezel felhasználói fiókkal és be is vagy jelentkezve erre a honlapra, akkor átmeneti sütiket állítunk be, annak érdekében, hogy megállapítsuk, hogy a böngésző elfogadja-e a sütiket. Ezek a sütik nem tartalmaznak személyes információt, és törlődnek, ahogy bezárjuk a böngészőt.\n\nA honlapra történő bejelentkezéskor több sütit hozunk létre, amely elmenti a bejelentkezési információt és a szerkesztőfelület megjelenítési opcióit. A bejelentkezési sütik két napig érvényesek, a szerkesztőfelület megjelenítési opcióit tároló süti egy évig. Amennyiben az “Emlékezz rám” opciót bejelöljük, a bejelentkezés két hétig folytatódik. Kijelentkezéskor a bejelentkezési sütik eltávolításra kerülnek.\n\nHa a webáruházban jársz, akkor a kosarad tartalmát és a bejelentkezési állapotodat is a süti tartalmazza. Így ha kikapcsolod a sütiket a böngésződben, akkor nem tudsz a honlapon rendelni, így azt ímélen vagy telefonon tudod megtenni.\n\nA Google Inc. („Google”) által biztosított Google Analytics internetes elemző szolgáltatást is igénybe vesszük. A Google Analytics szintén sütiket használ. A sütik által a weboldalhasználattal kapcsolatban létrehozott információk általában egy USA-beli Google-szerverre kerülnek, és ott tárolják őket. Ezt megelőzően a Google lerövidíti az IP-címedet az Európai Unió tagállamain és az Európai Gazdasági Térségről szóló megállapodás részes államain belül. Teljes IP-címet csak kivételes esetekben továbbítanak lerövidítés céljából a Google USA-ban található szervereire. A Google a weboldal üzemeltetője megbízásából ezen információk alapján értékeli az weboldalhasználatod, és weboldalaktivitással kapcsolatos jelentéseket készít, valamint további weboldal- és internethasználattal kapcsolatos szolgáltatásokat nyújt a weboldal üzemeltetője számára. Az böngésződ által a Google Analytics szolgáltatása révén továbbított IP-címet nem kapcsolják össze a Google más adataival. A böngésződ megfelelő beállításainak kiválasztásával elutasíthatod a sütik használatát. Megtilthatod azt is, hogy a Google gyűjtse és feldolgozza a sütik által létrehozott és a weboldalhasználatodra vonatkozó adatokat (beleértve az IP-címet). Ehhez töltsd le és telepítse a következő linken található beépülő böngészőmodult: https://tools.google.com/dlpage/gaoptout?hl=en\n\nA Facebook által biztosított adatelemzést és követést is használjuk. Így ha a honlapon jársz, később találkozhatsz egy hirdetéssel, amely a meglátogatott honlap oldalról szól, mert feltételezzük, hogy érdekel Téged a téma, és még többet szeretnél megtudni róla.\n\nVásárlás után véleményt kérünk a vásárolt termékről a Yotpo.com szolgáltatásán keresztül. Itt megírhatod a véleményed, amely kikerül a termék weboldalára esetleg az Andió facebook oldalára is. A vásárlás után kb. 2 héttel egy emailt kapsz, hogy írd meg a véleményed. Erről a véleménykérő emailről is leiratkozhatsz a levél alján látható leiratkozási linkkel.\n\nNéhány weboldalunkat elemezzük, hogy minél jobban követhető, olvasható és használható legyen. Ez az elemzés a Hotjar.com szolgáltatásával személyes adatok gyűjtése és felhasználása nélkül rögzíti a tevékenységedet az oldalon és a sok látogató össztevékenységét összevetve tudjuk az oldal minőségét javítani.\n\nMás honlapokról származó beágyazott tartalmak\nA honlapon elérhető bejegyzések külső forrásból származó beágyazott tartalmakat (pl. videók, képek, cikkek stb.) használhatnak. A külső forrásból származó beágyazott tartalmak pontosan úgy viselkednek, mintha meglátogattunk volna egy másik honlapot.\n\nEzek a webhelyek lehetséges, hogy adatot gyűjtenek a látogatókról, sütiket vagy harmadik féltől származó követőkódot használnak, figyelik a beágyazott tartalommal kapcsolatos felhasználói viselkedést, ha rendelkezünk felhasználói fiókkal és be vagyunk jelentkezve az oldalra.\n\nKivel osztjuk meg és hová továbbítjuk a felhasználói adatokat\nAz elkért és tárolt adatokat a webtárhelyen adatbázisban őrizzük. A webtárhely tulajdonosa a Magyar Hosting Kft (Székhely: 1132 Budapest, Victor Hugo u. 18, Postacím: ua. Telefon: (1) 700 2323 Web: https://www.mhosting.hu/ )\n\nA nevet és a címet csomagszállítás esetén továbbítjuk a futárszolgálat felé, amely kiszállítja a címedre a csomagot.\n\nBelföldi szállítás esetén: Complexpress Logisztika Kft.,1033 Budapest, Szentendrei út 89-95. Telefon: 06-1-203-4681\n\nKülföldi szállítás esetén: GLS General Logistics Systems Hungary Csomag-Logisztikai Kft. 2351 Alsónémedi GLS Európa u. 2. info@gls-hungary.com.\n\nA vásárláshoz minden esetben számlát adunk ki, amelyet bármikor letölthetsz a webáruház felhasználó fiókodból: https://natur-haztartas.hu/ugyfelfiok. A számlán levő adatokat (név és cím) elküldjük a könyvelőirodának, amelyet törvényi kötelezettségünk 8 évig megőrizni.\n\nA könyvelőiroda: Adókalkulátor Kft. Cím: Budapest, Szigligeti u. 44, 1205 Telefon: (20) 292 1072\n\nMennyi ideig őrizzük a személyes adatot\nHa hozzászólsz egy blogbejegyzéshez, hozzászólás és annak metaadatai nem meghatározható ideig a rendszerben maradnak. Ennek célja, hogy az összes ezt követő bármely hozzászólás általunk megismertté és jóváhagyottá váljon, azaz ne kerüljön fel a moderálandó hozzászólások listájára.\n\nA honlapon regisztrált felhasználók személyes adatai a felhasználói profiljukban is tárolásra kerülnek. (https://natur-haztartas.hu/ugyfelfiok)\n\nA vásárláskor megadott adatokat a törvény szerint 8 év őrizzük.\n\nA hírlevél feliratkozáskor ill. kapcsolati űrlapon megadott adatokat nem töröljük, csak kérésre. Ezt egy kattintással megteheted minden hírlevél alján, ill. az ügyfelszolgálati email címen is kérheted a törlést.\n\nMilyen jogokkal rendelkezik a felhasználó a saját adatai kapcsán\nA weboldalon regisztrált fiók vagy hozzászólás írása esetén kérhető a személyes adatok export fájlban történő megküldése, amely bármilyen adatot tartalmaz, amit korábban a felhasználó rendelkezésünkre bocsátott.\n\nKérhető továbbá, hogy bármilyen korábban megadott személyes adatot töröljük. Ezt megteheted minden hírlevél alján levő leiratkozás gombbal, vagy ügyfélszolgálati elérhetőségeink egyikén. A törlés nem vonatkozik azokra az adatokra, amelyeket adminisztrációs, jogi vagy biztonsági okokból kötelező megőriznünk.\n\nAdathelyesbítés: ha pontatlanok az adataid (név, email cím, szállítási cím), megteheted az ugyfelszolgalat@andio.biz email címen keresztül\n\nKapcsolati adatok\nÜgyfélszolgálat:\n\nemail: ugyfelszolgalat@andio.biz\n\ntelefon munkaidőben: +36 30 580 0499", "Please log in": "Please log in", - "Exception: Customer does not exist or the password is wrong": "Customer does not exist or the password is wrong", + "Exception: Customer does not exist or the password is wrong": "The email does not exist or the password is wrong", "Customer exists": "The email address has been registered already", "There is an error: during registration:": "There is an error: during registration:", "Please select an exercise": "Please select an exercise", @@ -204,5 +204,7 @@ "Are you sure to logout?": "Are you sure to logout?", "hu_with": " ", - "Are you sure to delete this exercise?": "Are you sure to delete this exercise?" + "Are you sure to delete this exercise?": "Are you sure to delete this exercise?", + + "I forgot the password":"I forgot the password" } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index e6ba203..a139378 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -207,5 +207,6 @@ "3rd Control": "3. kontrollgyakorlat", "Summary of your test":"A teszt összefoglalása:", - "Are you sure to delete this exercise?": "Biztos, hogy törlöd a gyakorlatot?" + "Are you sure to delete this exercise?": "Biztos, hogy törlöd a gyakorlatot?", + "I forgot the password":"Elfelejtett jelszó" } \ No newline at end of file diff --git a/lib/bloc/exercise_add_by_plan_bloc.dart b/lib/bloc/exercise_add_by_plan_bloc.dart deleted file mode 100644 index e1bcfad..0000000 --- a/lib/bloc/exercise_add_by_plan_bloc.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:aitrainer_app/model/cache.dart'; -import 'package:aitrainer_app/model/customer.dart'; -import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/repository/exercise_plan_repository.dart'; -import 'package:aitrainer_app/repository/exercise_repository.dart'; -import 'package:flutter_form_bloc/flutter_form_bloc.dart'; - -class ExerciseAddByPlanFormBloc extends FormBloc { - final ExerciseRepository exerciseRepository; - final ExercisePlanRepository exercisePlanRepository; - final WorkoutMenuTree workoutTree; - final customerId; - Customer customer; - int step = 1; - int countSteps = 1; - - final quantity1Field = TextFieldBloc( - ); - - final unitQuantity1Field = TextFieldBloc( - ); - - ExerciseAddByPlanFormBloc({this.exerciseRepository, this.exercisePlanRepository, this.customerId, this.workoutTree}) { - addFieldBlocs(fieldBlocs: [ - quantity1Field, - unitQuantity1Field, - ]); - - quantity1Field.onValueChanges(onData: (previous, current) async* { - exerciseRepository.setQuantity(current.valueToDouble); - }); - - unitQuantity1Field.onValueChanges(onData: (previous, current) async* { - exerciseRepository.setUnitQuantity(unitQuantity1Field.valueToDouble); - }); - - exerciseRepository.exerciseType = workoutTree.exerciseType; - if ( Cache().userLoggedIn.customerId == customerId) { - customer = Cache().userLoggedIn; - } else if ( Cache().getTrainee().customerId == customerId) { - customer = Cache().getTrainee(); - } - exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType); - exerciseRepository.customer = customer; - countSteps = exercisePlanRepository.getActualPlanDetail().serie; - - unitQuantity1Field.updateInitialValue(exercisePlanRepository.getActualPlanDetail().weightEquation); - quantity1Field.updateInitialValue(exercisePlanRepository.getActualPlanDetail().repeats.toString()); - - } - - @override - void onSubmitting() async { - - try { - emitLoading(progress: 30); - step++; - - exerciseRepository.setUnitQuantity(unitQuantity1Field.valueToDouble); - exerciseRepository.setQuantity(quantity1Field.valueToDouble); - exerciseRepository.exercise.exercisePlanDetailId = - exercisePlanRepository.getActualPlanDetail().exercisePlanDetailId; - exerciseRepository.exercise.unit = workoutTree.exerciseType.unit; - workoutTree.executed = true; - print("On Submitting Add Exercise By Plan " + exerciseRepository.exercise.toJson().toString()); - await exerciseRepository.addExercise(); - - emitSuccess(canSubmitAgain: true); - } on Exception catch (ex) { - emitFailure(failureResponse: ex.toString()); - } - } - - //@override - Future close() { - quantity1Field.close(); - unitQuantity1Field.close(); - - return super.close(); - } -} diff --git a/lib/bloc/reset_password_bloc.dart b/lib/bloc/reset_password_bloc.dart new file mode 100644 index 0000000..5a803f0 --- /dev/null +++ b/lib/bloc/reset_password_bloc.dart @@ -0,0 +1,49 @@ + +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +class ResetPasswordFormBloc extends FormBloc with Common { + final UserRepository userRepository; + + final emailField = TextFieldBloc( + validators: [ + FieldBlocValidators.required, + ], + ); + + ResetPasswordFormBloc({this.userRepository}) { + addFieldBlocs(fieldBlocs: [ + emailField + ]); + + emailField.onValueChanges(onData: (previous, current) async* { + userRepository.setEmail(current.value); + }); + } + + @override + void onSubmitting() async { + try { + emitLoading(progress: 30); + if ( ! validateEmail(userRepository)) { + emailField.addFieldError(EMAIL_ERROR, isPermanent: true); + + emitFailure(failureResponse: EMAIL_ERROR); + } else { + // Emit either Loaded or Error + await userRepository.resetPassword(); + emitSuccess(canSubmitAgain: false); + } + } on Exception catch (ex) { + emitFailure(failureResponse: ex.toString()); + + } + } + + Future close() { + emailField.close(); + return super.close(); + } + +} \ No newline at end of file diff --git a/lib/library/numberpicker.dart b/lib/library/numberpicker.dart index 3dae677..17575d0 100644 --- a/lib/library/numberpicker.dart +++ b/lib/library/numberpicker.dart @@ -305,15 +305,50 @@ class NumberPicker extends StatelessWidget { ? selectedStyle : defaultStyle; + double top = defaultStyle != null && defaultStyle.fontSize != null + ? listViewHeight / 2 - defaultStyle.fontSize / 2 - 15 + : listViewHeight / 2 - 22; + double left = defaultStyle != null && defaultStyle.fontSize != null + ? listViewWidth / 6 - defaultStyle.fontSize / 2 - 10 + : listViewHeight / 2 - 27; + bool isExtra = index == 0 || index == listItemCount - 1; return isExtra ? Container() //empty first and last element : Center( - child: Text( - getDisplayedValue(value), - style: itemStyle, - ), + child: value != selectedIntValue ? + Container( + padding: EdgeInsets.only(top: 15, left: 10, right: 5, bottom: 10), + child: Stack( + children: [ + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: value < selectedIntValue ? Alignment.centerRight : Alignment.centerLeft, + end: value < selectedIntValue ? Alignment.centerLeft : Alignment.centerRight, + colors: [Colors.white12, Colors.black12]), + borderRadius: BorderRadius.circular(8.0), + ), + ), + Positioned( + top: top, + left: left, + child: + Text( + getDisplayedValue(value), + style: itemStyle, + ), + + ), + ], + ) + ) : + + Text( + getDisplayedValue(value), + style: itemStyle, + ), ); }, ), diff --git a/lib/library/tree_view.dart b/lib/library/tree_view.dart index 7cde1a1..a9a832e 100644 --- a/lib/library/tree_view.dart +++ b/lib/library/tree_view.dart @@ -5,7 +5,6 @@ import 'package:aitrainer_app/util/common.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'dart:math' as math; class TreeView extends InheritedWidget { final List children; @@ -99,7 +98,6 @@ class TreeViewChildState extends State with Common, SingleTicker Color _color; double _opacity = 0; -/* List listWidgets; */ AnimationController _controller; Animation sizeAnimation; @@ -107,16 +105,6 @@ class TreeViewChildState extends State with Common, SingleTicker void initState() { super.initState(); isExpanded = widget.startExpanded; - _color = Colors.transparent; - _opacity = 0; - /* listWidgets = List.from(widget.children); */ - - /* _controller = AnimationController( - duration: const Duration(seconds: 1), - vsync: this, - ); - - sizeAnimation = Tween(begin: 0.0, end: 1.0).animate(_controller);*/ } @override @@ -136,105 +124,27 @@ class TreeViewChildState extends State with Common, SingleTicker ), Flexible( child: Container( - /*animation: _controller, - builder: (BuildContext context, Widget child) { - return Transform.scale( - scale: _controller.value, - child: child, - ); - },*/ - /* color: _color, - duration: Duration(seconds: 2), - curve: Curves.easeInOut,*/ - //height: double.infinity, - /*child: isExpanded //animateChildren() - ? Column( + child: + AnimatedSwitcher( + duration: Duration(milliseconds:900), + reverseDuration: Duration(milliseconds:500), + switchInCurve: Curves.easeIn, + child: isExpanded ? Column( mainAxisSize: MainAxisSize.min, children: widget.children, - ) - : Offstage(),*/ - child: - /*AnimatedCrossFade( - firstChild: Column( - mainAxisSize: MainAxisSize.min, - children: widget.children, + ) : Offstage(), ), - secondChild: Offstage(), - duration: Duration(milliseconds:800), - crossFadeState: isExpanded ? CrossFadeState.showFirst : CrossFadeState.showSecond, - )*/ - AnimatedSwitcher( - duration: Duration(milliseconds:900), - reverseDuration: Duration(milliseconds:500), - switchInCurve: Curves.easeIn, - child: isExpanded ? Column( - mainAxisSize: MainAxisSize.min, - children: widget.children, - ) : Offstage(), - ), ), ) ], ); } - /*AnimatedList animateChildren() { - - return AnimatedList( - shrinkWrap: true, - key: listKey, - initialItemCount: listWidgets.length, - itemBuilder: (context, index, animation) { - return slideIt(animation, listWidgets[index]); - }, - ); - } - - Widget slideIt(Animation animation, Widget element) { - - return SizeTransition( - sizeFactor: animation, - axis: Axis.vertical, - child: element, - ); - }*/ - void toggleExpanded() { setState(() { this.isExpanded = !this.isExpanded; _color = isExpanded ? Colors.black12 : Colors.transparent; _opacity = isExpanded ? 1 : 0; - - /* if ( isExpanded == false ) { - _removeAllItems(); - } else { - //_addAllItems(); - listWidgets = List.from(widget.children); - }*/ }); } - - /* void _removeAllItems() { - final int itemCount = widget.children.length; - - for (var i = 0; i < itemCount; i++) { - Widget itemToRemove = listWidgets[0]; - listKey.currentState.removeItem(0, - (BuildContext context, Animation animation) => slideIt(animation, itemToRemove), - duration: const Duration(milliseconds: 250), - ); - - listWidgets.removeAt(0); - } - } - - void _addAllItems() { - final int itemCount = widget.children.length; - - for (var i = 0; i < itemCount; i++) { - listKey.currentState.insertItem(0); - listWidgets.insert(0, widget.children[i]); - } - }*/ - } diff --git a/lib/main.dart b/lib/main.dart index 7782e57..d5d9677 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,16 +25,18 @@ import 'package:aitrainer_app/view/mydevelopment_muscle_page.dart'; import 'package:aitrainer_app/view/mydevelopment_page.dart'; import 'package:aitrainer_app/view/myexcercise_plan_page.dart'; import 'package:aitrainer_app/view/registration.dart'; +import 'package:aitrainer_app/view/reset_password.dart'; import 'package:aitrainer_app/view/settings.dart'; import 'package:aitrainer_app/widgets/home.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_analytics/observer.dart'; +//import 'package:firebase_analytics/firebase_analytics.dart'; +//import 'package:firebase_analytics/observer.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:sentry/sentry.dart'; import 'bloc/account/account_bloc.dart'; import 'bloc/body_development/body_development_bloc.dart'; @@ -157,7 +159,7 @@ class AitrainerApp extends StatelessWidget { @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - final FirebaseAnalytics analytics = FirebaseAnalytics(); + //final FirebaseAnalytics analytics = FirebaseAnalytics(); return MaterialApp( localizationsDelegates: [ // ... app-specific localization delegate[s] here @@ -197,6 +199,7 @@ class AitrainerApp extends StatelessWidget { 'exerciseCustomPage': (context) => CustomExercisePage(), 'exerciseControlPage': (context) => ExerciseControlPage(), 'login': (context) => LoginPage(), + 'resetPassword': (context) => ResetPasswordPage(), 'registration': (context) => RegistrationPage(), 'gdpr': (context) => Gdpr(), 'menu_page': (context) => MenuPage(), @@ -218,13 +221,13 @@ class AitrainerApp extends StatelessWidget { theme: ThemeData( brightness: Brightness.light, //primarySwatch: Colors.transparent, - fontFamily: 'Arial', + //fontFamily: 'Arial', textTheme: TextTheme( - bodyText1: TextStyle(fontSize: 14.0), + bodyText1: GoogleFonts.openSans(textStyle: TextStyle(fontSize: 14.0)), ) ), navigatorObservers: [ - FirebaseAnalyticsObserver(analytics: analytics), + //FirebaseAnalyticsObserver(analytics: analytics), ], home: AitrainerHome(), diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 3436c0e..5fc1f82 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -40,6 +40,7 @@ class Cache { // Keys to store and fetch data from SharedPreferences static final String authTokenKey = 'auth_token'; static final String customerIdKey = 'customer_id'; + static final String firebaseUidKey = 'firebase_uid'; static final String lastStoreDateKey = 'last_date'; static final String isRegisteredKey = 'is_registered'; static final String isLoggedInKey = 'is_logged_in'; @@ -52,6 +53,8 @@ class Cache { String authToken = ""; Customer userLoggedIn; + String firebaseUid; + bool firstLoad = true; List _exerciseTypes; @@ -108,18 +111,24 @@ class Cache { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; - setPreferences(prefs, SharePrefsChange.registration, customer.customerId); + setPreferences(prefs, SharePrefsChange.registration, customer.customerId, Cache().firebaseUid); } afterLogin(Customer customer) async { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; - await setPreferences(prefs, SharePrefsChange.login, customer.customerId); + await setPreferences(prefs, SharePrefsChange.login, customer.customerId, Cache().firebaseUid); + } + + afterFirebaseLogin() async { + Future prefs = SharedPreferences.getInstance(); + await setPreferences(prefs, SharePrefsChange.login, userLoggedIn.customerId, Cache().firebaseUid); } logout() async { userLoggedIn = null; + firebaseUid = null; authToken = ""; _trainee = null; _percentExercises = -1; @@ -129,12 +138,13 @@ class Cache { _myExercisesPlanDetails = LinkedHashMap(); print("Trainees is null? " + (_trainee == null).toString() ); Future prefs = SharedPreferences.getInstance(); - await setPreferences(prefs, SharePrefsChange.logout, 0); + await setPreferences(prefs, SharePrefsChange.logout, 0, ""); } setPreferences(Future prefs, SharePrefsChange type, - int customerId) async { + int customerId, + String firebaseUid) async { SharedPreferences sharedPreferences; sharedPreferences = await prefs; @@ -146,19 +156,22 @@ class Cache { sharedPreferences.setInt(Cache.customerIdKey, customerId); sharedPreferences.setBool(Cache.isRegisteredKey, true); sharedPreferences.setBool(Cache.isLoggedInKey, true); + sharedPreferences.setString(Cache.firebaseUidKey, firebaseUid); await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); - exerciseRepository.getExercisesByCustomer(customerId); + await exerciseRepository.getExercisesByCustomer(customerId); } else if ( type == SharePrefsChange.login ) { Cache().startPage = "home"; sharedPreferences.setInt(Cache.customerIdKey, customerId); + sharedPreferences.setString(Cache.firebaseUidKey, firebaseUid); sharedPreferences.setBool(Cache.isLoggedInKey, true); await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); - exerciseRepository.getExercisesByCustomer(customerId); + await exerciseRepository.getExercisesByCustomer(customerId); } else if ( type == SharePrefsChange.logout ) { sharedPreferences.setBool(Cache.isLoggedInKey, false); sharedPreferences.setInt(Cache.customerIdKey, 0); + sharedPreferences.setString(Cache.firebaseUidKey, null); sharedPreferences.setString(authTokenKey, ""); } } diff --git a/lib/model/customer.dart b/lib/model/customer.dart index 23444a6..5e55dd8 100644 --- a/lib/model/customer.dart +++ b/lib/model/customer.dart @@ -15,6 +15,7 @@ class Customer { int admin; int trainer; int dataPolicyAllowed; + String firebaseUid; Customer({this.customerId, @@ -32,7 +33,8 @@ class Customer { this.weight, this.admin, this.trainer, - this.dataPolicyAllowed + this.dataPolicyAllowed, + this.firebaseUid, }); Customer.fromJson(Map json) { @@ -50,6 +52,7 @@ class Customer { this.weight = json['weight']; this.admin = json['admin']; this.trainer = json['trainer']; + this.firebaseUid = json['firebaseUid']; } Map toJson() => diff --git a/lib/model/user.dart b/lib/model/user.dart index 14cba3c..495dfc6 100644 --- a/lib/model/user.dart +++ b/lib/model/user.dart @@ -2,6 +2,7 @@ String email; String password; int customerId; + String firebaseUid; User({this.customerId, this.email, this.password}); @@ -11,5 +12,6 @@ { "username": email, "password": password, + "firebaseUid": firebaseUid, }; } diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart index 52dd49f..63cf07c 100644 --- a/lib/repository/exercise_repository.dart +++ b/lib/repository/exercise_repository.dart @@ -87,10 +87,14 @@ class ExerciseRepository { Future> getExercisesByCustomer( int customerId ) async { final results = await ExerciseApi().getExercisesByCustomer(customerId); this.exerciseList = results; - if ( customerId == Cache().userLoggedIn.customerId) { - Cache().setExercises(exerciseList); - } else if ( Cache().getTrainee() != null && customerId == Cache().getTrainee().customerId ) { - Cache().setExercisesTrainee(exerciseList); + if ( Cache().userLoggedIn != null ) { + if (customerId == Cache().userLoggedIn.customerId) { + Cache().setExercises(exerciseList); + } else if (Cache().getTrainee() != null && customerId == Cache() + .getTrainee() + .customerId) { + Cache().setExercisesTrainee(exerciseList); + } } return this.exerciseList; } diff --git a/lib/repository/user_repository.dart b/lib/repository/user_repository.dart index 4a7a94a..ab1a0c8 100644 --- a/lib/repository/user_repository.dart +++ b/lib/repository/user_repository.dart @@ -1,5 +1,7 @@ +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/user.dart'; import 'package:aitrainer_app/service/customer_service.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; class UserRepository { User user; @@ -22,11 +24,27 @@ class UserRepository { Future addUser() async { final User modelUser = this.user; - await CustomerApi().addUser(modelUser); + String rc = await FirebaseApi().registerEmail(modelUser.email, modelUser.password); + if ( rc == FirebaseApi.SIGN_IN_OK ) { + modelUser.firebaseUid = Cache().firebaseUid; + await CustomerApi().addUser(modelUser); + } } Future getUser() async { final User modelUser = this.user; - await CustomerApi().getUser(modelUser); + String rc = await FirebaseApi().signInEmail(modelUser.email, modelUser.password); + if ( rc == FirebaseApi.SIGN_IN_NOT_FOUND ) { + rc = await FirebaseApi().registerEmail(modelUser.email, modelUser.password); + } + if ( rc == FirebaseApi.SIGN_IN_OK ) { + await CustomerApi().getUserByEmail(modelUser.email); + Cache().afterFirebaseLogin(); + } + } + + Future resetPassword() async { + final User modelUser = this.user; + await FirebaseApi().resetPassword(modelUser.email); } } diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index 5e2eaa0..03ba511 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -25,6 +25,14 @@ class CustomerApi { body); } + Future updateFirebaseUid(int customerId, String uid) async { + print(" ===== update Firebase uid : " + customerId.toString() + ": " + + uid); + await _client.post( + "customers/update_firebase_uid/" + customerId.toString(), + uid); + } + Future addCustomer(Customer customer) async { String body = JsonEncoder().convert(customer.toJson()); print(" ===== add new customer: " + body); @@ -35,7 +43,7 @@ class CustomerApi { Future addUser(User user) async { String body = JsonEncoder().convert(user.toJson()); - print(" ===== register new user: " + body); + print(" ===== add new user: " + body); final String responseBody = await _client.post( "registration", body); @@ -68,6 +76,23 @@ class CustomerApi { } } + Future getUserByEmail(String email) async { + print(" ===== User getByEmail : " + email); + final String responseBody = await _client.get( + "customers/find_by_email/" + email, + ""); + Customer customer; + try { + customer = Customer.fromJson(jsonDecode(responseBody)); + if ( customer.firebaseUid == null ) { + await this.updateFirebaseUid(customer.customerId, Cache().firebaseUid); + } + Cache().userLoggedIn = customer; + } on FormatException { + throw new Exception(responseBody); + } + } + Future getCustomer(int customerId) async { String body = ""; diff --git a/lib/service/firebase_api.dart b/lib/service/firebase_api.dart new file mode 100644 index 0000000..35333b9 --- /dev/null +++ b/lib/service/firebase_api.dart @@ -0,0 +1,105 @@ +import 'package:aitrainer_app/model/cache.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; + +class FirebaseApi { + static FirebaseApi _instance; + + static final FirebaseAuth auth = FirebaseAuth.instance; + + static const String SIGN_IN_OK = "OK"; + static const String SIGN_IN_NOT_FOUND = "user-not-found"; + static const String SIGN_IN_WRONG_PWD = "wrong-password"; + static const String REGISTER_WEAK_PWD = "weak-password"; + static const String REGISTER_EMAIL_IN_USE = "email-already-in-use"; + + UserCredential userCredential; + + factory FirebaseApi() => _instance ?? FirebaseApi._internal(); + + FirebaseApi._internal() { + _instance = this; + } + + + + // Define an async function to initialize FlutterFire + Future initializeFlutterFire() async { + try { + // Wait for Firebase to initialize and set `_initialized` state to true + await Firebase.initializeApp(); + } catch (e) { + // Set `_error` state to true if Firebase initialization fails + print("Error initializing Firebase"); + } + } + + Future signInEmail(String email, String password) async { + String rc = SIGN_IN_OK; + try { + userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword( + email: email, + password: password + ); + Cache().firebaseUid = userCredential.user.uid; + } on FirebaseAuthException catch (e) { + if (e.code == 'user-not-found') { + print('No user found for that email.'); + rc = SIGN_IN_NOT_FOUND; + } else if (e.code == 'wrong-password') { + print('Wrong password provided for that user.'); + rc = SIGN_IN_WRONG_PWD; + throw Exception("Customer does not exist or the password is wrong"); + } + return e.code; + } + return rc; + } + + Future registerEmail(String email, String password) async { + String rc = SIGN_IN_OK; + try { + userCredential = await FirebaseAuth.instance.createUserWithEmailAndPassword( + email: email, + password: password + ); + Cache().firebaseUid = userCredential.user.uid; + } on FirebaseAuthException catch (e) { + if (e.code == 'weak-password') { + print('The password provided is too weak.'); + rc = REGISTER_WEAK_PWD; + throw Exception("Password too short"); + } else if (e.code == 'email-already-in-use') { + print('The account already exists for that email.'); + throw Exception("Customer exists"); + rc = REGISTER_EMAIL_IN_USE; + } + } catch (e) { + print(e); + throw Exception(e.toString()); + } + + return rc; + } + + Future signInWithFacebook() async { + // Trigger the sign-in flow + final LoginResult result = await FacebookAuth.instance.login(); + + // Create a credential from the access token + final FacebookAuthCredential facebookAuthCredential = + FacebookAuthProvider.credential(result.accessToken.token); + + // Once signed in, return the UserCredential + return await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential); + } + + Future signOut() async { + await FirebaseAuth.instance.signOut(); + } + + Future resetPassword(String email) async { + await FirebaseAuth.instance.sendPasswordResetEmail(email: email); + } +} \ No newline at end of file diff --git a/lib/util/session.dart b/lib/util/session.dart index 60df05d..27b0511 100644 --- a/lib/util/session.dart +++ b/lib/util/session.dart @@ -5,6 +5,7 @@ import 'package:aitrainer_app/service/api.dart'; import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/service/exercise_tree_service.dart'; import 'package:aitrainer_app/service/exercisetype_service.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -31,7 +32,8 @@ class Session { await AppLocalizations.delegate.load(AppLanguage().appLocal); print (" -- Session: fetch token.."); await _fetchToken(_sharedPreferences); - await _initializeFlutterFire(); + print (" -- FireBase init.."); + await FirebaseApi().initializeFlutterFire(); //initDeviceLocale(); // Create the initialization Future outside of `build`: @@ -42,17 +44,6 @@ class Session { } - // Define an async function to initialize FlutterFire - void _initializeFlutterFire() async { - try { - // Wait for Firebase to initialize and set `_initialized` state to true - await Firebase.initializeApp(); - } catch (e) { - // Set `_error` state to true if Firebase initialization fails - print("Error initializing Firebase"); - } - } - Future initDeviceLocale() async { List languages; String currentLocale; @@ -90,10 +81,10 @@ class Session { } else if (responseJson['token'] != null) { prefs.setString(Cache.authTokenKey, responseJson['token']); Cache().authToken = responseJson['token']; + Cache().firebaseUid = prefs.get(Cache.firebaseUidKey); if (prefs.get(Cache.customerIdKey) == null) { print("************** Registration"); // registration - //Navigator.of(context).pushNamed('registration'); prefs.setBool(Cache.isRegisteredKey, true); Cache().startPage = "registration"; } else { @@ -107,13 +98,19 @@ class Session { prefs.get(Cache.isLoggedInKey) == null || prefs.get(Cache.isLoggedInKey) == false) { print("************* Login"); - //Navigator.of(context).pushNamed('login'); Cache().startPage = "login"; } else { - // get API customer - customerId = prefs.getInt(Cache.customerIdKey); - await CustomerApi().getCustomer(customerId); - Cache().startPage = "home"; + + // only + if ( Cache().firebaseUid == null) { + print("************* firebaseUid is null, Login"); + Cache().startPage = "login"; + } else { + // get API customer + customerId = prefs.getInt(Cache.customerIdKey); + await CustomerApi().getCustomer(customerId); + Cache().startPage = "home"; + } } await ExerciseTypeApi().getExerciseTypes(); diff --git a/lib/view/exercise_execute_plan_add_page.dart b/lib/view/exercise_execute_plan_add_page.dart index dc99299..2782cfa 100644 --- a/lib/view/exercise_execute_plan_add_page.dart +++ b/lib/view/exercise_execute_plan_add_page.dart @@ -6,6 +6,7 @@ import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:aitrainer_app/library/numberpicker.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -71,24 +72,7 @@ class _ExerciseExecuteAddPage extends State with Tra autovalidate: true, child: Scaffold( resizeToAvoidBottomInset: true, - appBar: AppBar( - backgroundColor: Colors.black, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(t("Save Exercise"), style: TextStyle(fontSize: 18),), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => Navigator.of(context).pop(), - ), - ), + appBar: AppBarNav(depth: 1), body: Container( width: MediaQuery .of(context) @@ -115,6 +99,7 @@ class _ExerciseExecuteAddPage extends State with Tra child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Text(t("Save Exercise")), Text(exerciseName, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, diff --git a/lib/view/exercise_plan_custom_detail_add_page.dart b/lib/view/exercise_plan_custom_detail_add_page.dart index 27ad4ca..0a76a64 100644 --- a/lib/view/exercise_plan_custom_detail_add_page.dart +++ b/lib/view/exercise_plan_custom_detail_add_page.dart @@ -50,6 +50,8 @@ class _ExercisePlanDetailAddPage extends State with T )); } + + Widget getForm(ExercisePlanCustomAddBloc bloc, WorkoutMenuTree workoutMenuTree) { String exerciseName = ""; if (bloc != null) { @@ -94,25 +96,58 @@ class _ExercisePlanDetailAddPage extends State with T fit: FlexFit.tight, flex: 8, child: - Text(t("Serie")), + Text( + t("Serie"), + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + + ), ), NumberPicker.horizontal( highlightSelectedValue: true, initialValue: bloc.serie.toInt(), - minValue: 0, - maxValue: 200, + itemExtent: 85, + minValue: 1, + maxValue: 20, + //decoration: _decoration, step: 1, onChanged: (value) => { bloc.add(ExercisePlanCustomAddChangeSerie(quantity: value.toDouble())) }, listViewHeight: 80, textStyle: TextStyle(fontSize: 24), - textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold), + textStyleHighlighted: TextStyle(fontSize: 36, color: Colors.orange, fontWeight: FontWeight.bold), //decoration: _decoration, ), ] ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Flexible( + fit: FlexFit.tight, + flex: 8, + child: + Text(t("Repeats"), + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),), + ), + NumberPicker.horizontal( + highlightSelectedValue: true, + initialValue: bloc.quantity.toInt(), + itemExtent: 85, + minValue: 0, + maxValue: 200, + step: 1, + onChanged: (value) => { + bloc.add(ExercisePlanCustomAddChangeQuantity(quantity: value.toDouble())) + }, + listViewHeight: 80, + textStyle: TextStyle(fontSize: 24), + textStyleHighlighted: TextStyle(fontSize: 36, color: Colors.orange, fontWeight: FontWeight.bold), + //decoration: _decoration, + ), + ]), Divider(), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -121,11 +156,14 @@ class _ExercisePlanDetailAddPage extends State with T fit: FlexFit.tight, flex: 8, child: - Text(t("Weight") + " (" + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.unitQuantityUnit + ")"), + Text(t("Weight") + " (" + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.unitQuantityUnit + ")", + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),), ), NumberPicker.horizontal( highlightSelectedValue: true, initialValue: bloc.quantityUnit.toInt(), + //decoration: _decoration, + itemExtent: 85, minValue: 0, maxValue: 650, step: 1, @@ -134,79 +172,19 @@ class _ExercisePlanDetailAddPage extends State with T }, listViewHeight: 80, textStyle: TextStyle(fontSize: 24), - textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold), + textStyleHighlighted: TextStyle(fontSize: 36, color: Colors.orange, fontWeight: FontWeight.bold), //decoration: _decoration, ), ]), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Flexible( - fit: FlexFit.tight, - flex: 8, - child: - Text(t("Repeats")), - ), - NumberPicker.horizontal( - highlightSelectedValue: true, - initialValue: bloc.quantity.toInt(), - minValue: 0, - maxValue: 200, - step: 1, - onChanged: (value) => { - bloc.add(ExercisePlanCustomAddChangeQuantity(quantity: value.toDouble())) - }, - listViewHeight: 80, - textStyle: TextStyle(fontSize: 24), - textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold), - //decoration: _decoration, - ), - ]), + + Divider(), - /*TextFieldBlocBuilder( - textFieldBloc: bloc.serieField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: AppLocalizations.of(context).translate("The number of the serie done with"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate("Serie"), - ), - ),*/ - /*TextFieldBlocBuilder( - textFieldBloc: bloc.quantityField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: - AppLocalizations.of(context).translate("The number of the repeats of one serie"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate("Repeats"), - ), - ),*/ - /*TextFieldBlocBuilder( - textFieldBloc: bloc.weightField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: AppLocalizations.of(context).translate("The weight"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate("Weight"), - ), - ),*/ + Text( + bloc.serie.toStringAsFixed(0) + " x " + bloc.quantity.toStringAsFixed(0) + " x " + bloc.quantityUnit.toStringAsFixed(0) + " kg" , + style: TextStyle(fontSize: 28, fontWeight: FontWeight.normal), + ), + Divider(), + Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -217,11 +195,6 @@ class _ExercisePlanDetailAddPage extends State with T onPressed: () => { bloc.add(ExercisePlanCustomAddRemove()), Navigator.of(context).pop(), - /*print("Remove " + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name), - bloc.exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.delete, - planBloc.add(ExercisePlanRemoveExercise( - exercisePlanDetail: bloc.exercisePlanRepository.getActualPlanDetail() )), - Navigator.of(context).pop(),*/ }, child: Text(t( "Delete")), //Text(AppLocalizations.of(context).translate("Delete"), style: TextStyle(fontSize: 16),) @@ -233,8 +206,6 @@ class _ExercisePlanDetailAddPage extends State with T onPressed: () => { bloc.add(ExercisePlanCustomAddSubmit()), Navigator.of(context).pop(), - /* bloc.submit(), - Navigator.of(context).pop(),*/ }, child: Text( t("Save"), diff --git a/lib/view/login.dart b/lib/view/login.dart index d552e74..abd1b98 100644 --- a/lib/view/login.dart +++ b/lib/view/login.dart @@ -2,12 +2,13 @@ import 'package:aitrainer_app/bloc/login_form_bloc.dart'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_facebook_login/flutter_facebook_login.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import '../library_keys.dart'; @@ -26,13 +27,14 @@ class LoginWidget extends StatefulWidget { State createState() => _LoginWidget(); } -class _LoginWidget extends State with Common { +class _LoginWidget extends State with Common, Trans { final GlobalKey _scaffoldKey = new GlobalKey(); final _formKey = GlobalKey(); @override Widget build(BuildContext context) { final accountBloc = BlocProvider.of(context); + setContext(context); return BlocProvider( create: (context) => LoginFormBloc( userRepository: UserRepository(), @@ -72,7 +74,6 @@ class _LoginWidget extends State with Common { } Widget buildLoginForm(LoginFormBloc formBloc, AccountBloc accountBloc) { - final cWidth = mediaSizeWidth(context); return Form( key: _formKey, @@ -81,17 +82,18 @@ class _LoginWidget extends State with Common { child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 120.0), children: [ - FlatButton( + /* FlatButton( child: new Image.asset( 'asset/image/login_fb.png', width: cWidth * .85, ), onPressed: () => { - _fbLogin(), + //_fbLogin(), + FirebaseApi().signInWithFacebook(), print("Login with FB"), }, ), - Text(AppLocalizations.of(context).translate("OR")), + Text(AppLocalizations.of(context).translate("OR")),*/ Divider(), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -156,10 +158,15 @@ class _LoginWidget extends State with Common { Spacer(flex: 1), new InkWell( child: new Text( - AppLocalizations.of(context).translate('Privacy')), + AppLocalizations.of(context).translate('I forgot the password')), + onTap: () => Navigator.of(context).pushNamed('resetPassword'), + ), + Spacer(flex: 1), + new InkWell( + child: new Text( + AppLocalizations.of(context).translate('Privacy')), onTap: () => Navigator.of(context).pushNamed('gdpr'), ), - Spacer(flex: 2), ]), ])), ); @@ -172,30 +179,4 @@ class _LoginWidget extends State with Common { content: Text(error, style: TextStyle(color: Colors.white)))); } - Future _fbLogin() async { - final FacebookLogin facebookSignIn = new FacebookLogin(); - final FacebookLoginResult result = await facebookSignIn.logIn(['email']); - - switch (result.status) { - case FacebookLoginStatus.loggedIn: - final FacebookAccessToken accessToken = result.accessToken; - showInSnackBar(''' - Logged in! - - Token: ${accessToken.token} - User id: ${accessToken.userId} - Expires: ${accessToken.expires} - Permissions: ${accessToken.permissions} - Declined permissions: ${accessToken.declinedPermissions} - '''); - break; - case FacebookLoginStatus.cancelledByUser: - showInSnackBar('Login cancelled by the user.'); - break; - case FacebookLoginStatus.error: - showInSnackBar('Something went wrong with the login process.\n' - 'Here\'s the error Facebook gave us: ${result.errorMessage}'); - break; - } - } } diff --git a/lib/view/mydevelopment_page.dart b/lib/view/mydevelopment_page.dart index 2736bbb..569011d 100644 --- a/lib/view/mydevelopment_page.dart +++ b/lib/view/mydevelopment_page.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; @@ -22,114 +23,121 @@ class _MyDevelopmentPage extends State with Trans { final CustomerRepository customerRepository = CustomerRepository(); final LinkedHashMap args = LinkedHashMap(); setContext(context); + double mediaWidth = MediaQuery.of(context).size.width; + double imageWidth = (mediaWidth - 45) / 2; + print("Media: " + mediaWidth.toString() + " imageWidth: " + imageWidth.toString()); return Scaffold( - appBar: AppBarNav(depth: 0), - body: Container( - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_menu_dark.png'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), - ), - child: CustomScrollView( - scrollDirection: Axis.vertical, - slivers: - [ - SliverGrid( - delegate: SliverChildListDelegate( - [ + appBar: AppBarNav(depth: 0), + body: Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_menu_dark.png'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + child: CustomScrollView(scrollDirection: Axis.vertical, slivers: [ + SliverGrid( + delegate: SliverChildListDelegate([ ImageButton( + width: imageWidth, textAlignment: Alignment.topCenter, text: t("My Exercise Logs"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4) + ) + ), image: "asset/image/edzesnaplom400400.jpg", - top: 150, left: 10, - onTap:() => this.callBackExerciseLog(exerciseRepository, customerRepository), + onTap: () => this.callBackExerciseLog(exerciseRepository, customerRepository), isLocked: false, ), - ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("My Whole Body Development"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4)), + ), image: "asset/image/testemfejl400x400.jpg", - top: 150, left: 10, - onTap:() => { + onTap: () => { args['customerId'] = Cache().userLoggedIn.customerId, - Navigator.of(context).pushNamed('mydevelopmentBodyPage', - arguments: args) + Navigator.of(context).pushNamed('mydevelopmentBodyPage', arguments: args) }, isLocked: true, ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Development Of Muscles"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/izomcsop400400.jpg", - top: 120, left: 10, - onTap:() => { - Navigator.of(context).pushNamed('mydevelopmentMusclePage', - arguments: args) - }, + onTap: () => {Navigator.of(context).pushNamed('mydevelopmentMusclePage', arguments: args)}, isLocked: true, ), - ImageButton( + width: imageWidth, + left: 10, textAlignment: Alignment.topLeft, text: t("Predictions"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), - image: "asset/menu/2.2.1.1RM.png", - top: 150, - onTap:() => { - - }, + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), + image: "asset/image/predictions.jpg", + onTap: () => {}, isLocked: true, ), - hiddenWidget(customerRepository, exerciseRepository), - ] - ), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 15.0, - crossAxisSpacing: 15.0, - childAspectRatio: 1.0, - ), - ) - ] - ) - ), - bottomNavigationBar: BottomNavigator(bottomNavIndex: 1)); + ]), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 15.0, + crossAxisSpacing: 15.0, + childAspectRatio: 1.0, + ), + ) + ])), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 1)); } Widget hiddenWidget(CustomerRepository customerRepository, ExerciseRepository exerciseRepository) { final LinkedHashMap args = LinkedHashMap(); if (Cache().getTrainee() != null) { return FlatButton( - padding: EdgeInsets.all(20), - textColor: Colors.white, - color: Colors.black12, - focusColor: Colors.blueAccent, - onPressed: () => - { - args['exerciseRepository'] = exerciseRepository, - args['customerRepository'] = customerRepository, - args['customerId'] = Cache().getTrainee().customerId, - Navigator.of(context).pushNamed('exerciseLogPage', - arguments: args) - }, - child: Text(t("My Trainee's Exercise Logs"), - style: TextStyle(fontSize: 18),) - ); + padding: EdgeInsets.all(20), + textColor: Colors.white, + color: Colors.black12, + focusColor: Colors.blueAccent, + onPressed: () => { + args['exerciseRepository'] = exerciseRepository, + args['customerRepository'] = customerRepository, + args['customerId'] = Cache().getTrainee().customerId, + Navigator.of(context).pushNamed('exerciseLogPage', arguments: args) + }, + child: Text( + t("My Trainee's Exercise Logs"), + style: TextStyle(fontSize: 18), + )); } else { return Container(); } @@ -140,10 +148,6 @@ class _MyDevelopmentPage extends State with Trans { args['exerciseRepository'] = exerciseRepository; args['customerRepository'] = customerRepository; args['customerId'] = Cache().userLoggedIn.customerId; - Navigator.of(context).pushNamed('exerciseLogPage', - arguments: args); + Navigator.of(context).pushNamed('exerciseLogPage', arguments: args); } - } - - diff --git a/lib/view/myexcercise_plan_page.dart b/lib/view/myexcercise_plan_page.dart index cc50e9b..24567de 100644 --- a/lib/view/myexcercise_plan_page.dart +++ b/lib/view/myexcercise_plan_page.dart @@ -7,6 +7,7 @@ import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/image_button.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; class MyExercisePlanPage extends StatefulWidget { @override @@ -20,6 +21,10 @@ class _MyExercisePlanPage extends State with Trans { final LinkedHashMap args = LinkedHashMap(); setContext(context); + double mediaWidth = MediaQuery.of(context).size.width; + double imageWidth = (mediaWidth - 45) / 2; + print("Media: " + mediaWidth.toString() + " imageWidth: " + imageWidth.toString()); + return Scaffold( appBar: AppBarNav(depth: 0), body: Container( @@ -39,10 +44,12 @@ class _MyExercisePlanPage extends State with Trans { delegate: SliverChildListDelegate( [ ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Execute My Selected Training Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_execute.jpg", top: 130, left: 10, @@ -55,12 +62,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Edit My Custom Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_custom.jpg", - top: 150, left: 10, onTap:() => { args['exerciseRepository'] = exerciseRepository, @@ -72,12 +80,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Suggested Training Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_suggested.jpg", - top: 130, left: 10, onTap:() => { @@ -86,12 +95,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("My Special Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_special.jpg", - top: 150, left: 10, onTap:() => { @@ -100,12 +110,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("My Arnold's Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_stars.jpg", - top: 120, left: 10, onTap:() => { diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 345ba93..0feda80 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -86,7 +86,7 @@ class _RegistrationWidget extends State with Common { children: [ //Spacer(flex:4), - FlatButton( + /* FlatButton( child: new Image.asset( 'asset/image/login_fb.png', width: cWidth * .85, @@ -96,7 +96,7 @@ class _RegistrationWidget extends State with Common { print("Login with FB"), }, ), - Text(AppLocalizations.of(context).translate("OR")), + Text(AppLocalizations.of(context).translate("OR")),*/ Divider(), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -187,30 +187,4 @@ class _RegistrationWidget extends State with Common { error, style: TextStyle(color: Colors.white)))); } - - /* Future _fbLogin() async { - final FacebookLogin facebookSignIn = new FacebookLogin(); - final FacebookLoginResult result = await facebookSignIn.logIn(['email']); - - switch (result.status) { - case FacebookLoginStatus.loggedIn: - final FacebookAccessToken accessToken = result.accessToken; - showInSnackBar(''' - Logged in! - Token: ${accessToken.token} - User id: ${accessToken.userId} - Expires: ${accessToken.expires} - Permissions: ${accessToken.permissions} - Declined permissions: ${accessToken.declinedPermissions} - '''); - break; - case FacebookLoginStatus.cancelledByUser: - showInSnackBar('Login cancelled by the user.'); - break; - case FacebookLoginStatus.error: - showInSnackBar('Something went wrong with the login process.\n' - 'Here\'s the error Facebook gave us: ${result.errorMessage}'); - break; - } - } */ } diff --git a/lib/view/reset_password.dart b/lib/view/reset_password.dart new file mode 100644 index 0000000..4c452ae --- /dev/null +++ b/lib/view/reset_password.dart @@ -0,0 +1,135 @@ +import 'package:aitrainer_app/bloc/login_form_bloc.dart'; +import 'package:aitrainer_app/bloc/account/account_bloc.dart'; +import 'package:aitrainer_app/bloc/reset_password_bloc.dart'; +import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/splash.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +import '../library_keys.dart'; + +// ignore: must_be_immutable +class ResetPasswordPage extends StatelessWidget with Trans { + final GlobalKey _scaffoldKey = new GlobalKey(); + final _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + setContext(context); + return BlocProvider( + create: (context) => ResetPasswordFormBloc( + userRepository: UserRepository(), + ), + child: Builder(builder: (context) { + final loginBloc = BlocProvider.of(context); + return Scaffold( + key: _scaffoldKey, + body: FormBlocListener( + onSubmitting: (context, state) { + LoadingDialog.show(context); + }, + onSuccess: (context, state) { + LoadingDialog.hide(context); + Navigator.of(context).pop(); + }, + onFailure: (context, state) { + LoadingDialog.hide(context); + showInSnackBar(state.failureResponse); + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_login.png'), + fit: BoxFit.cover, + //height: double.infinity, + //width: double.infinity, + alignment: Alignment.center, + ), + ), + child: buildResetPasswordForm(loginBloc), + ), + ), + ); + })); + } + + Widget buildResetPasswordForm(ResetPasswordFormBloc formBloc) { + + return Form( + key: _formKey, + child: Container( + padding: const EdgeInsets.only(left: 25, right: 100), + child: + ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 120.0), + children: [ + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + new InkWell( + child: new Text( + AppLocalizations.of(context).translate('I forgot the password'), + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 24)), + ), + ], + ), + Divider(), + TextFieldBlocBuilder( + key: LibraryKeys.loginEmailField, + textFieldBloc: formBloc.emailField, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + labelText: 'Email', + ), + ), + Divider( + color: Colors.transparent, + ), + Divider( + color: Colors.transparent, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + new FlatButton( + key: LibraryKeys.loginOKButton, + child: Image.asset('asset/image/WT_OK.png', + width: 100, height: 100), + onPressed: () => { + formBloc.add(SubmitFormBloc()) + }), + ]), + Divider( + color: Colors.transparent, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + new InkWell( + child: new Text( + AppLocalizations.of(context).translate('Login')), + onTap: () => + Navigator.of(context).pushNamed('login'), + ), + Spacer(flex: 1), + ]), + ])), + ); + } + + + void showInSnackBar(String error) { + _scaffoldKey.currentState.showSnackBar(SnackBar( + backgroundColor: Colors.orange, + content: Text(error, style: TextStyle(color: Colors.white)))); + } + +} diff --git a/lib/widgets/image_button.dart b/lib/widgets/image_button.dart index e85d78d..4acb6e3 100644 --- a/lib/widgets/image_button.dart +++ b/lib/widgets/image_button.dart @@ -5,12 +5,12 @@ import 'package:bloc/bloc.dart'; class ImageButton extends StatelessWidget { final String text; - final TextStyle style; + TextStyle style = TextStyle(fontSize: 14); final String image; final double top; final double left; final double height; - final double width; + double width = 180; final bool isShape; final Bloc bloc; final Alignment textAlignment; @@ -30,10 +30,15 @@ class ImageButton extends StatelessWidget { this.textAlignment, this.onTap, @required this.isLocked - }); + }) { + width = width ?? 180; + style = style ?? TextStyle(fontSize: 14, fontFamily: "Roboto Mono"); + } @override Widget build(BuildContext context) { + double top = width - (style.fontSize - 5) * text.length < 0 ? width - 2 * style.fontSize - 10 : width - style.fontSize - 10; + print ("Top: " + top.toStringAsFixed(0) + " length: " + ((style.fontSize - 5) * text.length).toString()); return Stack( //alignment: textAlignment, fit: StackFit.passthrough, @@ -63,11 +68,11 @@ class ImageButton extends StatelessWidget { )] ), Positioned( - top: text.length > 20 ? 140 : 160, + top: top, left: left, child: Container( - height: 200, - width: 180, + height: width - 2 * left, + width: width - 2 * left, child: InkWell( onTap: onTap ?? onTap, child: Text( diff --git a/pubspec.lock b/pubspec.lock index 61507d2..ea93925 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -246,27 +246,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.3.2" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - url: "https://pub.dartlang.org" - source: hosted - version: "6.0.2" - firebase_analytics_platform_interface: - dependency: transitive - description: - name: firebase_analytics_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - firebase_analytics_web: - dependency: transitive - description: - name: firebase_analytics_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.1" firebase_auth: dependency: "direct main" description: @@ -340,13 +319,13 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_facebook_login: + flutter_facebook_auth: dependency: "direct main" description: - name: flutter_facebook_login + name: flutter_facebook_auth url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "0.3.3" flutter_form_bloc: dependency: "direct main" description: @@ -416,6 +395,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" gradient_bottom_navigation_bar: dependency: "direct main" description: @@ -598,6 +584,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.4" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.22" path_provider_linux: dependency: transitive description: @@ -605,6 +598,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+4" path_provider_platform_interface: dependency: transitive description: @@ -632,7 +632,7 @@ packages: name: percent_indicator url: "https://pub.dartlang.org" source: hosted - version: "2.1.7+4" + version: "2.1.8" petitparser: dependency: transitive description: @@ -904,6 +904,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.1+2" + toggle_switch: + dependency: "direct main" + description: + name: toggle_switch + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.8" typed_data: dependency: transitive description: @@ -1004,4 +1011,4 @@ packages: version: "2.2.1" sdks: dart: ">=2.9.0-14.0.dev <3.0.0" - flutter: ">=1.16.0 <2.0.0" + flutter: ">=1.17.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 68e10e2..68a8063 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.0 + google_fonts: ^1.1.1 devicelocale: ^0.3.3 sentry: ^3.0.1 flutter_bloc: ^6.0.6 @@ -33,15 +34,16 @@ dependencies: flutter_form_bloc: ^0.19.0 spider_chart: ^0.1.5 rainbow_color: ^0.1.1 - percent_indicator: ^2.1.7+4 + percent_indicator: ^2.1.8 gradient_bottom_navigation_bar: ^1.0.0+4 fl_chart: ^0.12.0 infinite_listview: ^1.0.1+1 + toggle_switch: ^0.1.8 firebase_core: 0.5.0+1 - firebase_analytics: ^6.0.2 + #firebase_analytics: ^6.0.2 firebase_auth: ^0.18.1+2 - flutter_facebook_login: ^3.0.0 + flutter_facebook_auth: ^0.3.3 mockito: ^4.1.1 @@ -104,6 +106,7 @@ flutter: - asset/image/exercise_plan_execute.jpg - asset/image/exercise_plan_custom.jpg - asset/image/exercise_plan_suggested.jpg + - asset/image/predictions.jpg - asset/menu/1.cardio.png - asset/menu/1.1.aerob.png - asset/menu/1.2.anaerob.png