From 5bd03eab7ca210d09def4adf5248af53ee9e3c25 Mon Sep 17 00:00:00 2001 From: itsscb Date: Sat, 28 Oct 2023 23:27:51 +0200 Subject: [PATCH] ft/adds drawer and refactors navigation --- frontend/app/lib/gapi/client.dart | 8 +- frontend/app/lib/main.dart | 6 +- frontend/app/lib/pages/dashboard_page.dart | 126 +++---- frontend/app/lib/pages/home_page.dart | 14 +- frontend/app/lib/pages/login_page.dart | 377 ++++++++++++++++----- frontend/app/lib/pages/pages.dart | 2 +- frontend/app/lib/pages/start_page.dart | 289 ++++++++++++---- 7 files changed, 577 insertions(+), 245 deletions(-) diff --git a/frontend/app/lib/gapi/client.dart b/frontend/app/lib/gapi/client.dart index a27f966..e4e225e 100644 --- a/frontend/app/lib/gapi/client.dart +++ b/frontend/app/lib/gapi/client.dart @@ -5,7 +5,7 @@ import 'package:app/pb/rpc_login.pb.dart'; import 'package:app/pb/service_df.pbgrpc.dart'; import 'package:grpc/grpc.dart'; -class Client { +class GClient { String baseUrl = 'localhost'; int port = 9090; @@ -39,7 +39,7 @@ class Client { Future login( {required String email, required String password, - required Function onError, + required Function({GrpcError? error}) onError, required Function onSuccess}) async { LoginResponse response = LoginResponse(); try { @@ -56,9 +56,9 @@ class Client { } on GrpcError catch (e) { print('caught error: ${e.message}'); metadata['Authorization'] = ''; - onError(); + onError(error: e); } catch (e) { - print('caught error: ${e}'); + print('caught error: $e'); metadata['Authorization'] = ''; onError(); } diff --git a/frontend/app/lib/main.dart b/frontend/app/lib/main.dart index 2ef1cba..4a983e1 100644 --- a/frontend/app/lib/main.dart +++ b/frontend/app/lib/main.dart @@ -1,5 +1,5 @@ import 'package:app/pages/home_page.dart'; -import 'package:app/pages/login_page.dart'; +import 'package:app/pages/start_page.dart'; import 'package:app/widgets/background.dart'; import 'package:flutter/material.dart'; @@ -43,8 +43,8 @@ void main() async { backgroundColor: Colors.black, foregroundColor: Colors.white, )), - home: const Background( - child: HomePage(), + home: Background( + child: StartPage(), ), ), ); diff --git a/frontend/app/lib/pages/dashboard_page.dart b/frontend/app/lib/pages/dashboard_page.dart index 84bd1d4..012d74a 100644 --- a/frontend/app/lib/pages/dashboard_page.dart +++ b/frontend/app/lib/pages/dashboard_page.dart @@ -5,10 +5,31 @@ import 'package:app/widgets/background.dart'; import 'package:app/widgets/loading_widget.dart'; import 'package:flutter/material.dart'; +GlobalKey scaffolKey = GlobalKey(); + +List bottomBarButtons = const [ + BottomNavigationBarItem( + label: 'Zurueck', + backgroundColor: Colors.white, + icon: Icon( + Icons.arrow_back, + color: Colors.white, + ), + ), + BottomNavigationBarItem( + backgroundColor: Colors.white, + label: 'AccountInf', + icon: Icon( + Icons.person, + color: Colors.white, + ), + ), +]; + class DashboardPage extends StatefulWidget { DashboardPage({super.key, required this.client}); - final Client client; + final GClient client; @override State createState() => _DashboardPageState(); @@ -50,83 +71,40 @@ class _DashboardPageState extends State { @override Widget build(BuildContext context) { - print(widget.client.accessToken); - return Scaffold( - bottomNavigationBar: BottomNavigationBar( - items: const [ - BottomNavigationBarItem( - label: '', - backgroundColor: Colors.white, - icon: Icon( - Icons.arrow_back, - color: Colors.white, - ), + return Background( + child: Scaffold( + key: scaffolKey, + appBar: AppBar( + automaticallyImplyLeading: false, + flexibleSpace: Image.asset( + 'lib/assets/logo_300x200.png', + height: 80, ), - BottomNavigationBarItem( - backgroundColor: Colors.white, - label: '', - icon: Icon( - Icons.person, - color: Colors.white, - ), - ), - BottomNavigationBarItem( - backgroundColor: Colors.white, - label: '', - icon: Icon( - Icons.group, - color: Colors.white, - ), - ), - BottomNavigationBarItem( - backgroundColor: Colors.white, - label: '', - icon: Icon( - Icons.file_copy, - color: Colors.white, - ), - ), - ], - backgroundColor: Colors.transparent, - ), - appBar: AppBar( - automaticallyImplyLeading: false, - flexibleSpace: Image.asset( - 'lib/assets/logo_300x200.png', - height: 80, ), - // actions: [ - // IconButton( - // onPressed: () {}, - // icon: const Icon(Icons.menu), - // tooltip: 'Menu', - // ), - // IconButton( - // onPressed: () {}, - // icon: const Icon(Icons.login_sharp), - // tooltip: 'Login', - // ), - // ], - ), - body: !_loading - ? Background( - child: Center( - child: Column( - children: [ - const SizedBox( - height: 48, - ), - Text( - 'Willkommen ${accountInfo.firstname} ${accountInfo.lastname}!', - style: const TextStyle( - fontSize: 24, + bottomNavigationBar: BottomNavigationBar( + items: bottomBarButtons, + backgroundColor: Colors.black, + ), + body: !_loading + ? Background( + child: Center( + child: Column( + children: [ + const SizedBox( + height: 48, ), - ), - ], + Text( + 'Willkommen ${accountInfo.firstname} ${accountInfo.lastname}!', + style: const TextStyle( + fontSize: 24, + ), + ), + ], + ), ), - ), - ) - : const LoadingWidget(), + ) + : const LoadingWidget(), + ), ); } } diff --git a/frontend/app/lib/pages/home_page.dart b/frontend/app/lib/pages/home_page.dart index 1e23107..6b544e8 100644 --- a/frontend/app/lib/pages/home_page.dart +++ b/frontend/app/lib/pages/home_page.dart @@ -94,9 +94,13 @@ class _HomePageState extends State { Widget _selectPage(Pages page) { switch (page) { case Pages.login: - return const LoginPage(); + return LoginPage( + // onChangePage: changePage, + ); default: - return StartPage(onChangePage: changePage); + return StartPage( + // onChangePage: changePage, + ); } } @@ -120,12 +124,12 @@ class _HomePageState extends State { }); } - final Client client = Client(); + final GClient client = GClient(); @override Widget build(BuildContext context) { return Scaffold( - appBar: _selectedPage == 'start' + appBar: _selectedPage == Pages.start ? AppBar( automaticallyImplyLeading: false, ) @@ -138,7 +142,7 @@ class _HomePageState extends State { ), bottomNavigationBar: BottomNavigationBar( items: bottomBarButtons[_selectedPage]!.toList(), - backgroundColor: Colors.transparent, + backgroundColor: Colors.black, ), body: Container( padding: const EdgeInsets.fromLTRB(16, 32, 16, 32), diff --git a/frontend/app/lib/pages/login_page.dart b/frontend/app/lib/pages/login_page.dart index b9239c8..9daad16 100644 --- a/frontend/app/lib/pages/login_page.dart +++ b/frontend/app/lib/pages/login_page.dart @@ -1,10 +1,41 @@ import 'package:app/gapi/client.dart'; -import 'package:app/pages/dashboard_page.dart'; +import 'package:app/pages/start_page.dart'; +import 'package:app/widgets/background.dart'; import 'package:app/widgets/loading_widget.dart'; import 'package:flutter/material.dart'; +import 'package:grpc/grpc.dart'; + +GlobalKey scaffoldKey = GlobalKey(); + +List bottomBarButtons = [ + const BottomNavigationBarItem( + label: 'back', + backgroundColor: Colors.white, + icon: Icon( + Icons.arrow_back, + color: Colors.white, + ), + ), + BottomNavigationBarItem( + backgroundColor: Colors.white, + label: 'Menu', + icon: IconButton( + onPressed: () => scaffoldKey.currentState!.openDrawer(), + icon: const Icon( + Icons.menu, + color: Colors.white, + ), + ), + ), +]; class LoginPage extends StatefulWidget { - const LoginPage({super.key}); + LoginPage({ + super.key, + // required this.onChangePage, + }); + + // void Function(Pages page) onChangePage; @override State createState() => _LoginPageState(); @@ -13,13 +44,22 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { bool _loading = false; + List _selectedBottomBarButtons = bottomBarButtons; + + void _bottomBarAction(int index) { + switch (_selectedBottomBarButtons[index].label?.toLowerCase()) { + case 'back': + Navigator.of(context).pop(); + } + } + void _setLoading(bool loading) { setState(() { _loading = loading; }); } - final Client client = Client(); + final GClient client = GClient(); final _formKey = GlobalKey(); final mailController = TextEditingController(); @@ -27,101 +67,258 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { - return !_loading - ? Form( - key: _formKey, + return Background( + child: Scaffold( + key: scaffoldKey, + appBar: AppBar( + automaticallyImplyLeading: false, + // flexibleSpace: Image.asset( + // 'lib/assets/logo_300x200.png', + // height: 80, + // ), + ), + bottomNavigationBar: BottomNavigationBar( + items: bottomBarButtons, + backgroundColor: Colors.black, + fixedColor: Colors.black, + onTap: (value) => _bottomBarAction(value), + ), + drawer: Drawer( + backgroundColor: Colors.black, + child: Padding( + padding: const EdgeInsets.all(40.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ - TextFormField( - // style: TextStyle( - // color: Theme.of(context).colorScheme.primary, - // ), - controller: mailController, - decoration: const InputDecoration( - fillColor: Color.fromARGB(30, 255, 255, 255), - filled: true, - hintStyle: TextStyle( - color: Colors.white38, - ), - hintText: 'E-Mail Adresse', - ), - keyboardType: TextInputType.emailAddress, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Bitte eine gültige E-Mail Adresse eingeben'; - } - return null; + const Spacer(), + TextButton( + onPressed: () { + scaffoldKey.currentState!.closeDrawer(); }, + child: const Row( + children: [ + Text( + 'About', + style: TextStyle(fontSize: 20), + ), + Spacer(), + Icon( + Icons.question_answer, + color: Colors.white, + ), + ], + ), ), - TextFormField( - style: const TextStyle( - color: Colors.white, - ), - controller: passwordController, - decoration: const InputDecoration( - fillColor: Color.fromARGB(30, 255, 255, 255), - filled: true, - hintStyle: TextStyle( - color: Colors.white38, - ), - hintText: 'Passwort', - ), - keyboardType: TextInputType.visiblePassword, - obscureText: true, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Bitte geben Sie Ihr Passwort ein'; - } - return null; + TextButton( + onPressed: () { + scaffoldKey.currentState!.closeDrawer(); }, + child: const Row( + children: [ + Text( + 'Datenschutz', + style: TextStyle(fontSize: 20), + ), + Spacer(), + Icon( + Icons.privacy_tip, + color: Colors.white, + ), + ], + ), ), - ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - // final navigator = Navigator.of(context); - _setLoading(true); - client - .login( - email: mailController.text, - password: passwordController.text, - onError: () { - _setLoading(false); - ScaffoldMessenger.of(context) - .showSnackBar(const SnackBar( - content: Text('Login fehlgeschlagen'), - )); - }, - onSuccess: () { - // _setLoading(false); - ScaffoldMessenger.of(context) - .showSnackBar(const SnackBar( - content: Text('Login erfolgreich'), - )); - }, - ) - .then( - (r) { - if (r.accessToken != '') { - Navigator.push( - context, - MaterialPageRoute( - builder: (ctx) => DashboardPage( - client: client, - ), - ), - ); - } - // _setLoading(false); - }, - ); - } - }, - child: const Icon(Icons.arrow_forward)) + TextButton( + onPressed: () { + scaffoldKey.currentState!.closeDrawer(); + }, + child: const Row( + children: [ + Text( + 'Impressum', + style: TextStyle(fontSize: 20), + ), + Spacer(), + Icon( + Icons.apartment, + color: Colors.white, + ), + ], + ), + ), + const SizedBox( + height: 250, + ) ], ), - ) - : const LoadingWidget(); + ), + ), + body: !_loading + ? Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 100, 16, 16), + child: SingleChildScrollView( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Image( + width: 180, + image: AssetImage( + 'lib/assets/logo_300x200.png', + ), + ), + const SizedBox( + height: 30, + ), + const Text( + 'Login', + style: TextStyle( + fontFamily: 'sans-serif', + fontSize: 24, + height: 1.6, + fontWeight: FontWeight.normal, + letterSpacing: 6, + ), + ), + const SizedBox( + height: 20, + ), + TextFormField( + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + controller: mailController, + decoration: const InputDecoration( + fillColor: Color.fromARGB(30, 255, 255, 255), + filled: true, + hintStyle: TextStyle( + color: Colors.white38, + ), + hintText: 'E-Mail Adresse', + ), + keyboardType: TextInputType.emailAddress, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Bitte eine gültige E-Mail Adresse eingeben'; + } + return null; + }, + ), + TextFormField( + style: const TextStyle( + color: Colors.white, + ), + controller: passwordController, + decoration: const InputDecoration( + fillColor: Color.fromARGB(30, 255, 255, 255), + filled: true, + hintStyle: TextStyle( + color: Colors.white38, + ), + hintText: 'Passwort', + ), + keyboardType: TextInputType.visiblePassword, + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Bitte geben Sie Ihr Passwort ein'; + } + return null; + }, + ), + SizedBox( + height: 15, + ), + ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + // final navigator = Navigator.of(context); + _setLoading(true); + client + .login( + email: mailController.text, + password: passwordController.text, + onError: ({GrpcError? error}) { + _setLoading(false); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar( + content: const Text( + 'Login fehlgeschlagen', + ), + action: SnackBarAction( + textColor: Colors.grey, + label: 'Details', + onPressed: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + content: error != null + ? Text( + 'Fehler: ${error.message}', + textAlign: + TextAlign.center, + style: + const TextStyle( + color: Colors + .black), + ) + : const Text( + 'Interner Fehler', + textAlign: + TextAlign.center, + style: TextStyle( + color: + Colors.black), + ), + icon: const Icon( + Icons.error, + color: Colors.black, + ), + ); + }, + ); + }), + )); + }, + onSuccess: () { + // _setLoading(false); + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar( + content: Text('Login erfolgreich'), + )); + }, + ) + .then( + (r) { + if (r.accessToken != '') { + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute( + builder: (ctx) => StartPage( + client: client, + ), + ), + (ctx) => false, + ); + // widget.onChangePage( + // Pages.dashboard, + // ); + } + // _setLoading(false); + }, + ); + } + }, + child: const Icon(Icons.login)) + ], + ), + ), + ), + ) + : const LoadingWidget(), + ), + ); } } diff --git a/frontend/app/lib/pages/pages.dart b/frontend/app/lib/pages/pages.dart index b8f802a..6db3efe 100644 --- a/frontend/app/lib/pages/pages.dart +++ b/frontend/app/lib/pages/pages.dart @@ -1 +1 @@ -enum Pages { start, login, about, persons } +enum Pages { start, login, about, persons, dashboard } diff --git a/frontend/app/lib/pages/start_page.dart b/frontend/app/lib/pages/start_page.dart index 0896168..c1a6d59 100644 --- a/frontend/app/lib/pages/start_page.dart +++ b/frontend/app/lib/pages/start_page.dart @@ -1,87 +1,240 @@ -import 'package:app/pages/pages.dart'; +import 'package:app/gapi/client.dart'; +import 'package:app/pages/login_page.dart'; +import 'package:app/widgets/background.dart'; import 'package:flutter/material.dart'; import 'dart:core'; -class StartPage extends StatelessWidget { +GlobalKey scaffoldKey = GlobalKey(); + +List bottomBarButtons = [ + const BottomNavigationBarItem( + label: 'register', + backgroundColor: Colors.white, + icon: Column( + children: [ + Icon( + Icons.person_add, + color: Colors.white, + ), + Text( + 'Registrieren', + style: TextStyle( + color: Colors.white, + fontSize: 16, + ), + ) + ], + ), + ), + const BottomNavigationBarItem( + label: 'login', + backgroundColor: Colors.white, + icon: Column( + children: [ + Icon( + Icons.login, + color: Colors.white, + ), + Text( + 'Login', + style: TextStyle( + color: Colors.white, + fontSize: 16, + ), + ) + ], + ), + ), + BottomNavigationBarItem( + backgroundColor: Colors.white, + label: 'Menu', + icon: IconButton( + onPressed: () => scaffoldKey.currentState!.openDrawer(), + icon: const Icon( + Icons.menu, + color: Colors.white, + ), + ), + ), +]; + +class StartPage extends StatefulWidget { StartPage({ super.key, - required this.onChangePage, + this.client, }); - void Function(Pages page) onChangePage; + final GClient? client; + + @override + State createState() => _StartPageState(); +} + +class _StartPageState extends State { + List _selectedBottomBarButtons = bottomBarButtons; + + void _bottomBarAction(int index) { + switch (_selectedBottomBarButtons[index].label?.toLowerCase()) { + case 'login': + Navigator.of(context) + .push(MaterialPageRoute(builder: (context) => LoginPage())); + } + } @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Image( - image: AssetImage( - 'lib/assets/logo_300x200.png', + return Background( + child: Scaffold( + key: scaffoldKey, + appBar: AppBar( + automaticallyImplyLeading: false, + // flexibleSpace: Image.asset( + // 'lib/assets/logo_300x200.png', + // height: 80, + // ), + ), + drawer: Drawer( + backgroundColor: Colors.black, + child: Padding( + padding: const EdgeInsets.all(40.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer(), + TextButton( + onPressed: () { + scaffoldKey.currentState!.closeDrawer(); + }, + child: const Row( + children: [ + Text( + 'About', + style: TextStyle(fontSize: 20), + ), + Spacer(), + Icon( + Icons.question_answer, + color: Colors.white, + ), + ], + ), + ), + TextButton( + onPressed: () { + scaffoldKey.currentState!.closeDrawer(); + }, + child: const Row( + children: [ + Text( + 'Datenschutz', + style: TextStyle(fontSize: 20), + ), + Spacer(), + Icon( + Icons.privacy_tip, + color: Colors.white, + ), + ], + ), + ), + TextButton( + onPressed: () { + scaffoldKey.currentState!.closeDrawer(); + }, + child: const Row( + children: [ + Text( + 'Impressum', + style: TextStyle(fontSize: 20), + ), + Spacer(), + Icon( + Icons.apartment, + color: Colors.white, + ), + ], + ), + ), + const SizedBox( + height: 250, + ) + ], + ), ), ), - // Text( - // 'Peace of Mind \nin the\nAfterlife', - // textAlign: TextAlign.center, - // style: TextStyle( - // fontFamily: 'JosefinSans', - // height: 1.7, - // letterSpacing: 8, - // wordSpacing: 2, - // fontWeight: FontWeight.bold, - // fontSize: 32, - // color: Colors.white, - // decoration: TextDecoration.none, - // decorationColor: Colors.white, - // ), - // ), - const SizedBox( - height: 40, + bottomNavigationBar: BottomNavigationBar( + onTap: (value) => _bottomBarAction(value), + items: bottomBarButtons, + backgroundColor: Colors.black, + fixedColor: Colors.black, ), - Text( - 'Digitale Spuren auf Knopfdruck entfernen'.toUpperCase(), - textAlign: TextAlign.center, - style: const TextStyle( - fontFamily: 'sans-serif', - fontSize: 24, - height: 1.6, - fontWeight: FontWeight.normal, - letterSpacing: 6, - ), - ), - const Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ElevatedButton.icon( - label: const Text('Login'), - onPressed: () { - onChangePage(Pages.login); - }, - icon: const Icon( - Icons.login, - semanticLabel: 'Login', - size: 16, + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Image( + image: AssetImage( + 'lib/assets/logo_300x200.png', + ), ), - ), - ElevatedButton.icon( - label: const Text('Registrieren'), - onPressed: () {}, - icon: const Icon( - Icons.person_add, - semanticLabel: 'Register', - size: 16, + const SizedBox( + height: 40, ), - ), - ], - ), - // const SizedBox( - // height: 32, - // ), + Text( + 'Digitale Spuren auf Knopfdruck entfernen'.toUpperCase(), + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'sans-serif', + fontSize: 24, + height: 1.6, + fontWeight: FontWeight.normal, + letterSpacing: 6, + ), + ), + // const Spacer(), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: widget.client?.accessToken == null + // ? [ + // ElevatedButton.icon( + // label: const Text('Login'), + // onPressed: () { + // // onChangePage(Pages.login); + // Navigator.of(context).push( + // MaterialPageRoute( + // builder: ((context) => LoginPage()), + // ), + // ); + // }, + // icon: const Icon( + // Icons.login, + // semanticLabel: 'Login', + // size: 16, + // ), + // ), + // ElevatedButton.icon( + // label: const Text('Registrieren'), + // onPressed: () {}, + // icon: const Icon( + // Icons.person_add, + // semanticLabel: 'Register', + // size: 16, + // ), + // ), + // ] + // : [], + // ), + // const SizedBox( + // height: 38, + // ), - // const Text('data'), - ], + // const Text('data'), + ], + ), + ), + ), ); } }