ft/adds toasts to api calls

This commit is contained in:
itsscb 2023-11-07 00:22:11 +01:00
parent 604c2bdd27
commit c4da7bea27
15 changed files with 818 additions and 418 deletions

View File

@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'package:app/pb/session.pbjson.dart';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:app/pb/google/protobuf/timestamp.pb.dart'; import 'package:app/pb/google/protobuf/timestamp.pb.dart';
@ -167,7 +166,7 @@ class Session {
final db = await database; final db = await database;
final List<Map<String, Object?>> maps = await db.query('sessions'); final List<Map<String, Object?>> maps = await db.query('sessions');
print(maps); // print(maps);
final List<Session> sessions = List.generate( final List<Session> sessions = List.generate(
maps.length, maps.length,
(i) { (i) {

View File

@ -41,7 +41,9 @@ void main() async {
backgroundColor: Colors.black, backgroundColor: Colors.black,
foregroundColor: Colors.white, foregroundColor: Colors.white,
)), )),
home: const HomePage(), home: HomePage(
loggedOut: false,
),
), ),
); );
} }

View File

@ -5,7 +5,7 @@ class ApiResponse<T> {
ApiResponse.initial(this.message) : status = Status.INITIAL; ApiResponse.initial(this.message) : status = Status.INITIAL;
ApiResponse.loading(this.message) : status = Status.LOADING; ApiResponse.loading(this.message) : status = Status.LOADING;
ApiResponse.completed(this.data, this.message) : status = Status.COMPLETED; ApiResponse.completed(this.data) : status = Status.COMPLETED;
ApiResponse.error(this.message) : status = Status.ERROR; ApiResponse.error(this.message) : status = Status.ERROR;
@override @override

View File

@ -3,9 +3,11 @@ import 'dart:io';
import 'package:app/model/apis/app_exception.dart'; import 'package:app/model/apis/app_exception.dart';
import 'package:app/pb/account.pb.dart'; import 'package:app/pb/account.pb.dart';
import 'package:app/pb/account_info.pb.dart'; import 'package:app/pb/account_info.pb.dart';
import 'package:app/pb/google/protobuf/timestamp.pb.dart';
import 'package:app/pb/person.pb.dart'; import 'package:app/pb/person.pb.dart';
import 'package:app/data/database.dart'; import 'package:app/data/database.dart';
import 'package:app/pb/rpc_create_account.pb.dart'; import 'package:app/pb/rpc_create_account.pb.dart';
import 'package:app/pb/rpc_create_person.pb.dart';
import 'package:app/pb/rpc_get_account.pb.dart'; import 'package:app/pb/rpc_get_account.pb.dart';
import 'package:app/pb/rpc_get_account_info.pb.dart'; import 'package:app/pb/rpc_get_account_info.pb.dart';
import 'package:app/pb/rpc_get_person.pb.dart'; import 'package:app/pb/rpc_get_person.pb.dart';
@ -15,7 +17,6 @@ import 'package:app/pb/rpc_refresh_token.pb.dart';
import 'package:app/pb/service_df.pbgrpc.dart'; import 'package:app/pb/service_df.pbgrpc.dart';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:sqflite/sqflite.dart';
class BackendService { class BackendService {
BackendService() { BackendService() {
@ -138,17 +139,16 @@ class BackendService {
} }
try { try {
final GetAccountResponse response = await _client.getAccount( final GetAccountResponse response = await _client.getAccount(
GetAccountRequest(id: session!.accountId), GetAccountRequest(id: session.accountId),
options: CallOptions( options: CallOptions(
metadata: {'Authorization': 'Bearer ${session.accessToken}'})); metadata: {'Authorization': 'Bearer ${session.accessToken}'}));
return response.account; return response.account;
} on SocketException { } on SocketException {
throw FetchDataException('Keine Internet Verbindung'); throw FetchDataException('Keine Internet Verbindung');
} on GrpcError catch (err) { } on GrpcError catch (err) {
// if (err.code == 16) { if (err.code == 16) {
// await refreshToken(session); throw UnauthorizedException(err.message);
// return getAccount(); }
// }
throw FetchDataException(err.message); throw FetchDataException(err.message);
} catch (err) { } catch (err) {
throw InternalException(err.toString()); throw InternalException(err.toString());
@ -207,6 +207,47 @@ class BackendService {
} }
} }
Future<Person> createPerson(
{required String firstname,
required String lastname,
required String street,
required String zip,
required String city,
required String country,
required DateTime birthday}) async {
Session session = await Session.session;
if (session.accessTokenExpiresAt == null) {
throw UnauthorizedException('Keine Siztung gefunden');
}
if (session.accessTokenExpiresAt!.toDateTime().isBefore(DateTime.now())) {
session = await refreshToken(session);
if (session.accessTokenExpiresAt == null) {
throw UnauthorizedException('Sitzung ist abgelaufen');
}
}
try {
final CreatePersonResponse response = await _client.createPerson(
CreatePersonRequest(
accountId: session.accountId,
lastname: lastname,
firstname: firstname,
street: street,
zip: zip,
country: country,
birthday: Timestamp.fromDateTime(birthday),
),
options: CallOptions(
metadata: {'Authorization': 'Bearer ${session.accessToken}'}));
return response.person;
} on SocketException {
throw FetchDataException('Keine Internet Verbindung');
} on GrpcError catch (err) {
throw FetchDataException(err.message);
} catch (err) {
throw InternalException(err.toString());
}
}
Future<List<Person>> listPersons() async { Future<List<Person>> listPersons() async {
Session session = await Session.session; Session session = await Session.session;
if (session.accessTokenExpiresAt == null) { if (session.accessTokenExpiresAt == null) {

View File

@ -1,9 +1,9 @@
import 'package:app/model/apis/api_response.dart'; import 'package:app/model/apis/api_response.dart';
import 'package:app/model/services/backend_service.dart'; import 'package:app/model/services/backend_service.dart';
import 'package:app/model/view_model/base_vm.dart';
import 'package:app/pb/account.pb.dart'; import 'package:app/pb/account.pb.dart';
import 'package:flutter/material.dart';
class AccountViewModel with ChangeNotifier { class AccountViewModel extends BaseViewModel {
AccountViewModel() { AccountViewModel() {
_init(); _init();
} }
@ -21,36 +21,12 @@ class AccountViewModel with ChangeNotifier {
} }
void _init() async { void _init() async {
_apiResponse = ApiResponse.loading('Bereite alles vor'); super.init();
try { // try {
_apiResponse = ApiResponse.completed(await _service.getAccount(), 'done'); // _apiResponse = ApiResponse.completed(await _service.getAccount());
} catch (e) { // } catch (e) {
_apiResponse = ApiResponse.error(e.toString()); // _apiResponse = ApiResponse.error(e.toString());
} // }
notifyListeners(); // notifyListeners();
}
Future<void> fetchAccount() async {
_apiResponse = ApiResponse.loading('Hole Account');
notifyListeners();
try {
_apiResponse = ApiResponse.completed(await _service.getAccount(), 'done');
} catch (e) {
_apiResponse = ApiResponse.error(e.toString());
}
notifyListeners();
}
Future<void> logout() async {
_apiResponse = ApiResponse.loading('Logge aus');
notifyListeners();
try {
await BackendService.logout();
_apiResponse = ApiResponse.completed(null, 'Erfolgreich ausgeloggt');
} catch (e) {
_apiResponse = ApiResponse.error(e.toString());
}
print(_apiResponse.message);
notifyListeners();
} }
} }

View File

@ -0,0 +1,254 @@
import 'package:app/model/apis/api_response.dart';
import 'package:app/model/services/backend_service.dart';
import 'package:app/pages/home_page.dart';
import 'package:app/util/colors.dart';
import 'package:flutter/material.dart';
class BaseViewModel with ChangeNotifier {
BaseViewModel() {
init();
}
ApiResponse _apiResponse = ApiResponse.initial('Keine Daten');
final BackendService _service = BackendService();
ApiResponse get response {
return _apiResponse;
}
void init() async {
// if (await BackendService.isLoggedIn) {
try {
_apiResponse = ApiResponse.completed(await _service.getAccount());
} catch (e) {
_apiResponse = ApiResponse.error(e.toString());
}
notifyListeners();
// }
}
Future<bool> isLoggedIn(BuildContext context) async {
final messenger = ScaffoldMessenger.of(context);
final navigator = Navigator.of(context);
bool loggedIn = false;
try {
loggedIn = await BackendService.isLoggedIn;
} catch (err) {
if (err.toString().contains('session is blocked')) {
_apiResponse = ApiResponse.error('Sitzung ist abgelaufen');
navigator.pushAndRemoveUntil(
MaterialPageRoute(
builder: (builder) => HomePage(
loggedOut: true,
)),
(route) => false);
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.error,
content: const Text(
'Sitzung ist abgelaufen',
style: TextStyle(color: Colors.white),
),
// action: SnackBarAction(
// label: 'Details',
// onPressed: () {
// if (context.mounted) {
// showDialog(
// context: context,
// builder: (context) => AlertDialog(
// backgroundColor: Colors.black,
// icon: Icon(
// Icons.error,
// color: CustomColors.error,
// ),
// content: Text(
// err.toString(),
// textAlign: TextAlign.center,
// ),
// ));
// }
// },
// ),
));
}
}
return loggedIn;
}
Future<void> getAccount(BuildContext context) async {
_apiResponse = ApiResponse.loading('Lade Daten');
notifyListeners();
final messenger = ScaffoldMessenger.of(context);
try {
_apiResponse = ApiResponse.completed(await _service.getAccount());
} catch (e) {
if (e.toString().contains('session is blocked')) {
_apiResponse = ApiResponse.error('Sitzung ist abgelaufen');
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.error,
content: const Text(
'Sitzung ist abgelaufen',
style: TextStyle(color: Colors.white),
),
action: SnackBarAction(
label: 'Details',
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.black,
icon: Icon(
Icons.error,
color: CustomColors.error,
),
content: Text(
e.toString(),
textAlign: TextAlign.center,
),
));
},
),
));
}
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.error,
content: const Text(
'Sitzung ist abgelaufen',
style: TextStyle(color: Colors.white),
),
action: SnackBarAction(
label: 'Details',
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.black,
icon: Icon(
Icons.error,
color: CustomColors.error,
),
content: Text(
e.toString(),
textAlign: TextAlign.center,
),
));
},
),
));
_apiResponse = ApiResponse.error(e.toString());
}
notifyListeners();
}
Future<void> logout() async {
_apiResponse = ApiResponse.loading('Logge aus');
notifyListeners();
try {
await BackendService.logout();
_apiResponse = ApiResponse.completed(true);
} catch (e) {
_apiResponse = ApiResponse.error(e.toString());
}
print(_apiResponse.message);
notifyListeners();
}
Future<bool> login(BuildContext context,
{required String email, required String password}) async {
bool resp = false;
_apiResponse = ApiResponse.loading('Logge ein');
notifyListeners();
final messenger = ScaffoldMessenger.of(context);
try {
resp = await BackendService.login(email: email, password: password);
_apiResponse = ApiResponse.completed(resp);
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.success,
content: const Text(
'Erfolgreich eingeloggt',
style: TextStyle(color: Colors.white),
),
));
} catch (e) {
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.error,
content: const Text(
'Login fehlgeschlagen',
style: TextStyle(color: Colors.white),
),
action: SnackBarAction(
label: 'Details',
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.black,
icon: Icon(
Icons.error,
color: CustomColors.error,
),
content: Text(
e.toString(),
textAlign: TextAlign.center,
),
));
},
),
));
_apiResponse = ApiResponse.error(e.toString());
}
print(_apiResponse.message);
notifyListeners();
return resp;
}
Future<bool> createAccount(BuildContext context,
{required String email, required String password}) async {
bool resp = false;
final messenger = ScaffoldMessenger.of(context);
_apiResponse = ApiResponse.loading('Logge ein');
notifyListeners();
try {
resp =
await BackendService.createAccount(email: email, password: password);
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.success,
content: const Text(
'Account angelegt',
style: TextStyle(color: Colors.white),
),
));
_apiResponse = ApiResponse.completed(resp);
} catch (e) {
messenger.showSnackBar(SnackBar(
backgroundColor: CustomColors.error,
content: const Text(
'Account anlegen fehlgeschlagen',
style: TextStyle(color: Colors.white),
),
action: SnackBarAction(
label: 'Details',
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.black,
icon: Icon(
Icons.error,
color: CustomColors.error,
),
content: Text(
e.toString(),
textAlign: TextAlign.center,
),
));
},
),
));
_apiResponse = ApiResponse.error(e.toString());
}
print(_apiResponse.message);
notifyListeners();
return resp;
}
}

View File

@ -1,6 +1,5 @@
import 'package:app/model/apis/api_response.dart'; import 'package:app/model/apis/api_response.dart';
import 'package:app/model/services/backend_service.dart'; import 'package:app/model/services/backend_service.dart';
import 'package:app/pb/account.pb.dart';
import 'package:app/pb/person.pb.dart'; import 'package:app/pb/person.pb.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -16,14 +15,43 @@ class PersonsViewModel with ChangeNotifier {
return _apiResponse; return _apiResponse;
} }
void listPersons() async { Future<List<Person>> listPersons() async {
_apiResponse = ApiResponse.loading('Bereite alles vor'); List<Person> persons = [];
_apiResponse = ApiResponse.loading('Lade Daten');
try { try {
_apiResponse = persons = await _service.listPersons();
ApiResponse.completed(await _service.listPersons(), 'done'); _apiResponse = ApiResponse.completed(persons);
} catch (e) { } catch (e) {
_apiResponse = ApiResponse.error(e.toString()); _apiResponse = ApiResponse.error(e.toString());
} }
notifyListeners(); notifyListeners();
return persons;
}
Future<Person> createPerson(BuildContext context,
{required String firstname,
required String lastname,
required String street,
required String zip,
required String city,
required String country,
required DateTime birthday}) async {
Person person = Person();
_apiResponse = ApiResponse.loading('Erstelle Person');
try {
person = await _service.createPerson(
firstname: firstname,
lastname: lastname,
street: street,
zip: zip,
city: city,
country: country,
birthday: birthday);
_apiResponse = ApiResponse.completed(person);
} catch (err) {
_apiResponse = ApiResponse.error(err.toString());
}
notifyListeners();
return person;
} }
} }

View File

@ -1,4 +1,3 @@
import 'package:app/model/apis/api_response.dart';
import 'package:app/model/services/backend_service.dart'; import 'package:app/model/services/backend_service.dart';
import 'package:app/model/view_model/account_vm.dart'; import 'package:app/model/view_model/account_vm.dart';
import 'package:app/pages/login_overlay.dart'; import 'package:app/pages/login_overlay.dart';
@ -11,8 +10,11 @@ import 'package:app/widgets/side_drawer_item.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// ignore: must_be_immutable
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const HomePage({super.key}); HomePage({super.key, required this.loggedOut});
bool loggedOut;
@override @override
State<HomePage> createState() => _HomePageState(); State<HomePage> createState() => _HomePageState();
@ -25,182 +27,192 @@ class _HomePageState extends State<HomePage> {
_init(); _init();
} }
AccountViewModel vm = AccountViewModel();
void _init() async { void _init() async {
_setLoading(true); // _setLoading(true);
_loggedin = await BackendService.isLoggedIn; // _setLoading(widget.loggedOut);
// _loading = widget.loggedOut;
// _loggedin = await BackendService.isLoggedIn;
// if (!_loggedin) { // if (!_loggedin) {
// await BackendService.logout(); // await BackendService.logout();
// Navigator.of(context).pushAndRemoveUntil( // final navigator = Navigator.of(context);
// MaterialPageRoute(builder: (builder) => HomePage()), // navigator.pushAndRemoveUntil(
// MaterialPageRoute(
// builder: (builder) => HomePage(
// loggedOut: true,
// )),
// (route) => false); // (route) => false);
// } // }
_setLoading(false); _setLoading(false);
} }
_isLoggedIn(BuildContext context) async {
bool logged = await vm.isLoggedIn(context);
_loggedin = logged;
}
void _setLoading(bool loading) { void _setLoading(bool loading) {
setState(() { setState(() {
_loading = loading; _loading = loading;
}); });
} }
void _checkResponse(ApiResponse response) async {
print('${response.message}');
if (response.status == Status.ERROR &&
response.message!.contains('unauthorized')) {
await BackendService.logout();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (builder) => const HomePage()),
(route) => false);
}
}
bool _loading = true; bool _loading = true;
bool _loggedin = false; bool _loggedin = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Background( return Background(
child: ChangeNotifierProvider<AccountViewModel>( child: Scaffold(
create: (context) => AccountViewModel(), appBar: AppBar(
child: Consumer<AccountViewModel>( automaticallyImplyLeading: false,
builder: (context, value, child) { // flexibleSpace: Image.asset(
_checkResponse(value.response); // 'lib/assets/logo_300x200.png',
return Scaffold( // // height: 400,
appBar: AppBar( // ),
automaticallyImplyLeading: false, ),
// flexibleSpace: Image.asset( drawer: SideDrawer(
// 'lib/assets/logo_300x200.png', children: [
// // height: 400, const Spacer(
// ), flex: 3,
), ),
drawer: SideDrawer( SideDrawerItem(
children: [ onPressed: () {},
const Spacer( icon: Icons.question_answer,
flex: 3, color: Colors.white,
), label: 'About',
SideDrawerItem( ),
onPressed: () {}, SideDrawerItem(
icon: Icons.question_answer, onPressed: () {},
color: Colors.white, icon: Icons.privacy_tip,
label: 'About', color: Colors.white,
), label: 'Datenschutz',
SideDrawerItem( ),
onPressed: () {}, SideDrawerItem(
icon: Icons.privacy_tip, onPressed: () {},
color: Colors.white, icon: Icons.apartment,
label: 'Datenschutz', color: Colors.white,
), label: 'Impressum',
SideDrawerItem( ),
onPressed: () {}, const Spacer(
icon: Icons.apartment, flex: 1,
color: Colors.white, ),
label: 'Impressum', if (_loggedin)
), SideDrawerItem(
const Spacer( onPressed: () async {
flex: 1, setState(() {
), _loading = true;
if (_loggedin && value.response.data != null) });
SideDrawerItem( await BackendService.logout();
onPressed: () async { // ignore: use_build_context_synchronously
setState(() { Navigator.of(context).pushAndRemoveUntil(
_loading = true; MaterialPageRoute(
}); builder: (builder) => HomePage(
await BackendService.logout(); loggedOut: true,
// ignore: use_build_context_synchronously )),
Navigator.of(context).pushAndRemoveUntil( (route) => false);
MaterialPageRoute( setState(() {
builder: (builder) => const HomePage()), _loggedin = false;
(route) => false); _loading = false;
setState(() { });
_loggedin = false; },
_loading = false; icon: Icons.logout,
}); color: Colors.white,
}, label: 'Logout',
icon: Icons.logout, ),
color: Colors.white, ],
label: 'Logout', ),
), bottomNavigationBar: BottomNavigation(
], children: [
), if (!_loggedin) ...[
bottomNavigationBar: BottomNavigation( BottomNavigationItem(
children: [ onPressed: () async {
if (!_loggedin) ...[ final bool res = await showLogin(context, registration: true);
BottomNavigationItem( setState(() {
onPressed: () async { _loggedin = res;
final bool res = });
await showLogin(context, registration: true); },
setState(() { icon: Icons.person_add_alt,
_loggedin = res; color: Colors.white,
}); label: 'Registrieren',
}, ),
icon: Icons.person_add_alt, BottomNavigationItem(
color: Colors.white, onPressed: () async {
label: 'Registrieren', await showLogin(context);
), setState(() {
BottomNavigationItem( vm.isLoggedIn(context);
onPressed: () async { });
final bool res = await showLogin(context); },
setState(() { icon: Icons.login,
_loggedin = res; color: Colors.white,
}); label: 'Login',
}, ),
icon: Icons.login, ] else
color: Colors.white, BottomNavigationItem(
label: 'Login', onPressed: () async {
), final navigator = Navigator.of(context);
] else if (await vm.isLoggedIn(context)) {
BottomNavigationItem( navigator.push(MaterialPageRoute(
onPressed: () { builder: (builder) => const PersonsPage()));
Navigator.of(context).push(MaterialPageRoute( } else {
builder: (builder) => const PersonsPage())); navigator.pushAndRemoveUntil(
}, MaterialPageRoute(
icon: Icons.person_search, builder: (builder) => const PersonsPage()),
color: Colors.white, (route) => false);
label: 'Personen', }
), },
BottomNavigationItem( icon: Icons.person_search,
onPressed: () {}, color: Colors.white,
icon: Icons.dashboard, label: 'Personen',
color: Colors.white, ),
label: 'Dashboard', BottomNavigationItem(
), onPressed: () {},
...[] icon: Icons.dashboard,
], color: Colors.white,
), label: 'Dashboard',
body: Padding( ),
padding: const EdgeInsets.fromLTRB(16, 40, 16, 16), ...[]
child: Center( ],
child: _loading ),
? const CircularProgressIndicator( body: Padding(
color: Colors.grey, padding: const EdgeInsets.fromLTRB(16, 45, 16, 16),
) child: Center(
: Column( child: ChangeNotifierProvider<AccountViewModel>(
children: [ create: (context) => AccountViewModel(),
Image.asset( child:
'lib/assets/logo_300x200.png', Consumer<AccountViewModel>(builder: (context, value, child) {
// _checkResponse(value.response);
if (!widget.loggedOut) {
_isLoggedIn(context);
}
return _loading
? const CircularProgressIndicator(
color: Colors.grey,
)
: Column(
children: [
Image.asset(
'lib/assets/logo_300x200.png',
),
const SizedBox(
height: 40,
),
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 SizedBox( ),
height: 40, ],
), );
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,
),
),
],
),
),
),
);
},
), ),
), ),
); ));
} }
} }

View File

@ -1,7 +1,12 @@
import 'package:app/model/services/backend_service.dart'; import 'package:app/model/view_model/base_vm.dart';
import 'package:app/widgets/background.dart'; import 'package:app/widgets/background.dart';
import 'package:app/widgets/bottom_navigation.dart';
import 'package:app/widgets/bottom_navigation_item.dart';
import 'package:app/widgets/side_drawer.dart';
import 'package:app/widgets/side_drawer_item.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:app/util/validation.dart'; import 'package:app/util/validation.dart';
import 'package:provider/provider.dart';
Future<bool> showLogin(BuildContext context, Future<bool> showLogin(BuildContext context,
{bool registration = false}) async { {bool registration = false}) async {
@ -9,15 +14,17 @@ Future<bool> showLogin(BuildContext context,
final mailController = TextEditingController(); final mailController = TextEditingController();
final passwordController = TextEditingController(); final passwordController = TextEditingController();
BaseViewModel vm = BaseViewModel();
bool submitted = false; bool submitted = false;
bool loggedin = false; bool loggedin = false;
void login() { void login(BuildContext context) {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
submitted = true; submitted = true;
BackendService.login( FocusScope.of(context).unfocus();
email: mailController.text, vm
password: passwordController.text, .login(context,
).then( email: mailController.text, password: passwordController.text)
.then(
(r) { (r) {
if (r) { if (r) {
loggedin = r; loggedin = r;
@ -25,16 +32,21 @@ Future<bool> showLogin(BuildContext context,
} }
}, },
); );
passwordController.clear();
submitted = false;
} }
} }
void register() { void register(BuildContext context) {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
submitted = true; submitted = true;
BackendService.createAccount( vm
.createAccount(
context,
email: mailController.text, email: mailController.text,
password: passwordController.text, password: passwordController.text,
).then( )
.then(
(r) { (r) {
if (r) { if (r) {
loggedin = r; loggedin = r;
@ -52,120 +64,174 @@ Future<bool> showLogin(BuildContext context,
context: context, context: context,
builder: (builder) { builder: (builder) {
return Background( return Background(
child: Column( child: Scaffold(
mainAxisAlignment: MainAxisAlignment.center, drawer: SideDrawer(
children: [ children: [
const SizedBox( const Spacer(
height: 50, flex: 3,
),
const Image(
width: 180,
image: AssetImage(
'lib/assets/logo_300x200.png',
), ),
), SideDrawerItem(
const SizedBox( onPressed: () {},
height: 30, icon: Icons.question_answer,
), color: Colors.white,
Text( label: 'About',
registration ? 'Registrieren' : 'Login',
style: const TextStyle(
fontFamily: 'sans-serif',
fontSize: 24,
height: 1.6,
fontWeight: FontWeight.normal,
letterSpacing: 6,
), ),
), SideDrawerItem(
Form( onPressed: () {},
key: formKey, icon: Icons.privacy_tip,
child: Column( color: Colors.white,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, label: 'Datenschutz',
crossAxisAlignment: CrossAxisAlignment.center, ),
children: [ SideDrawerItem(
const SizedBox( onPressed: () {},
height: 40, icon: Icons.apartment,
color: Colors.white,
label: 'Impressum',
),
const Spacer(
flex: 1,
),
],
),
bottomNavigationBar: BottomNavigation(
children: [
BottomNavigationItem(
onPressed: () {
Navigator.pop(context, false);
},
icon: Icons.arrow_back,
color: Colors.white,
label: 'Zurück',
),
BottomNavigationItem(
onPressed: () {
Navigator.pop(context, false);
},
icon: Icons.home,
color: Colors.white,
label: 'Home',
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 50,
),
const Image(
width: 180,
image: AssetImage(
'lib/assets/logo_300x200.png',
), ),
TextFormField( ),
autofocus: true, const SizedBox(
// inputFormatters: [ height: 30,
// FilteringTextInputFormatter.allow( ),
// emailRegExp, Text(
// ), registration ? 'Registrieren' : 'Login',
// ], style: const TextStyle(
controller: mailController, fontFamily: 'sans-serif',
decoration: const InputDecoration( fontSize: 24,
fillColor: Color.fromARGB(30, 255, 255, 255), height: 1.6,
filled: true, fontWeight: FontWeight.normal,
hintStyle: TextStyle( letterSpacing: 6,
color: Colors.white38,
),
hintText: 'E-Mail Adresse',
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || !value.isValidEmail) {
return 'Bitte eine gültige E-Mail Adresse eingeben';
}
return null;
},
), ),
TextFormField( ),
style: const TextStyle( ChangeNotifierProvider<BaseViewModel>(
color: Colors.white, create: (context) => BaseViewModel(),
), child: Consumer<BaseViewModel>(
// inputFormatters: [ builder: (context, value, child) => Form(
// FilteringTextInputFormatter.allow( key: formKey,
// passwordRegExp, child: Column(
// ), mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// ], crossAxisAlignment: CrossAxisAlignment.center,
controller: passwordController, children: [
decoration: const InputDecoration( const SizedBox(
fillColor: Color.fromARGB(30, 255, 255, 255), height: 40,
filled: true, ),
hintStyle: TextStyle( TextFormField(
color: Colors.white38, // autofocus: true,
), // inputFormatters: [
hintText: 'Passwort', // FilteringTextInputFormatter.allow(
), // emailRegExp,
keyboardType: TextInputType.visiblePassword, // ),
obscureText: true, // ],
validator: (value) { controller: mailController,
if (value == null || !value.isValidPassword) { decoration: const InputDecoration(
return 'Bitte geben Sie Ihr Passwort ein'; fillColor: Color.fromARGB(30, 255, 255, 255),
} filled: true,
return null; hintStyle: TextStyle(
}, color: Colors.white38,
), ),
const SizedBox( hintText: 'E-Mail Adresse',
height: 15, ),
), keyboardType: TextInputType.emailAddress,
Row( validator: (value) {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, if (value == null || !value.isValidEmail) {
children: [ return 'Bitte eine gültige E-Mail Adresse eingeben';
ElevatedButton(
onPressed: !submitted
? () {
Navigator.pop(context);
} }
: null, return null;
child: const Icon(Icons.arrow_back), },
),
TextFormField(
style: const TextStyle(
color: Colors.white,
),
// inputFormatters: [
// FilteringTextInputFormatter.allow(
// passwordRegExp,
// ),
// ],
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.isValidPassword) {
return 'Bitte geben Sie Ihr Passwort ein';
}
return null;
},
),
const SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: !submitted
? !registration
? () {
login(context);
}
: () {
register(context);
}
: null,
child: const Icon(Icons.login),
),
],
)
],
), ),
ElevatedButton( ),
onPressed: !submitted ),
? !registration ),
? login const Spacer(),
: register ],
: null,
child: const Icon(Icons.login),
),
],
)
],
),
), ),
const Spacer(), ),
],
), ),
); );
}); });

View File

@ -42,13 +42,23 @@ class _PersonsPageState extends State<PersonsPage> {
response.message!.contains('unauthenticated')) { response.message!.contains('unauthenticated')) {
BackendService.logout(); BackendService.logout();
Navigator.of(context).pushAndRemoveUntil( Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (builder) => const HomePage()), MaterialPageRoute(
builder: (builder) => HomePage(
loggedOut: true,
)),
(route) => false); (route) => false);
} }
} }
bool _loading = true; bool _loading = true;
bool _loggedin = false; bool _loggedin = false;
List<Person> persons = [];
PersonsViewModel vm = PersonsViewModel();
void listPersons(BuildContext context) async {
persons = await vm.listPersons();
}
List<Widget> _personsList(List<Person> persons) { List<Widget> _personsList(List<Person> persons) {
final List<Widget> list = []; final List<Widget> list = [];
@ -67,106 +77,106 @@ class _PersonsPageState extends State<PersonsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Background( return Background(
child: ChangeNotifierProvider<PersonsViewModel>( child: Scaffold(
create: (context) => PersonsViewModel(), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
child: Consumer<PersonsViewModel>( floatingActionButton: FloatingActionButton(
builder: (context, value, child) { onPressed: () {},
_checkResponse(value.response); child: const Icon(Icons.add),
return Scaffold( ),
floatingActionButtonLocation: appBar: AppBar(
FloatingActionButtonLocation.centerFloat, automaticallyImplyLeading: false,
floatingActionButton: FloatingActionButton( ),
onPressed: () {}, drawer: SideDrawer(
child: const Icon(Icons.add), children: [
const Spacer(
flex: 3,
),
SideDrawerItem(
onPressed: () {},
icon: Icons.question_answer,
color: Colors.white,
label: 'About',
),
SideDrawerItem(
onPressed: () {},
icon: Icons.privacy_tip,
color: Colors.white,
label: 'Datenschutz',
),
SideDrawerItem(
onPressed: () {},
icon: Icons.apartment,
color: Colors.white,
label: 'Impressum',
),
const Spacer(
flex: 1,
),
if (_loggedin)
SideDrawerItem(
onPressed: () async {
setState(() {
_loading = true;
});
await BackendService.logout();
// ignore: use_build_context_synchronously
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (builder) => HomePage(
loggedOut: true,
)),
(route) => false);
setState(() {
_loggedin = false;
_loading = false;
});
},
icon: Icons.logout,
color: Colors.white,
label: 'Logout',
), ),
appBar: AppBar( ],
automaticallyImplyLeading: false, ),
), bottomNavigationBar: BottomNavigation(
drawer: SideDrawer( children: [
children: [ BottomNavigationItem(
const Spacer( onPressed: () {},
flex: 3, icon: Icons.dashboard,
), color: Colors.white,
SideDrawerItem( label: 'Dashboard',
onPressed: () {}, ),
icon: Icons.question_answer, BottomNavigationItem(
color: Colors.white, onPressed: () {
label: 'About', Navigator.of(context).pop();
), },
SideDrawerItem( icon: Icons.home,
onPressed: () {}, color: Colors.white,
icon: Icons.privacy_tip, label: 'Home',
color: Colors.white, ),
label: 'Datenschutz', ],
), ),
SideDrawerItem( body: Padding(
onPressed: () {}, padding: const EdgeInsets.all(16),
icon: Icons.apartment, child: Center(
color: Colors.white, child: ChangeNotifierProvider<PersonsViewModel>(
label: 'Impressum', create: (context) => PersonsViewModel(),
), child: Consumer<PersonsViewModel>(
const Spacer( builder: (context, value, child) {
flex: 1, _checkResponse(value.response);
), listPersons(context);
if (_loggedin || value.response.data != null) return _loading
SideDrawerItem(
onPressed: () async {
setState(() {
_loading = true;
});
await BackendService.logout();
// ignore: use_build_context_synchronously
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (builder) => const HomePage()),
(route) => false);
setState(() {
_loggedin = false;
_loading = false;
});
},
icon: Icons.logout,
color: Colors.white,
label: 'Logout',
),
],
),
bottomNavigationBar: BottomNavigation(
children: [
BottomNavigationItem(
onPressed: () {},
icon: Icons.dashboard,
color: Colors.white,
label: 'Dashboard',
),
BottomNavigationItem(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icons.home,
color: Colors.white,
label: 'Home',
),
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: _loading
? const CircularProgressIndicator( ? const CircularProgressIndicator(
color: Colors.grey, color: Colors.grey,
) )
: value.response.status == Status.COMPLETED : value.response.status == Status.COMPLETED
? value.response.data.length > 0 ? value.response.data.length > 0
? ListView( ? ListView(children: _personsList(persons))
children: _personsList(
(value.response.data as List<Person>)))
: const Text('Noch keine Personen angelegt') : const Text('Noch keine Personen angelegt')
: const Text('Lade Daten...'), : const Text('Lade Daten...');
), },
), ),
); ),
}, ),
), ),
), ),
); );

View File

@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
class CustomColors {
static Color get error {
return const Color.fromARGB(200, 255, 90, 90);
}
static Color get success {
return const Color.fromARGB(200, 55, 125, 55);
}
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// ignore: must_be_immutable
class BottomNavigation extends StatelessWidget { class BottomNavigation extends StatelessWidget {
BottomNavigation({ BottomNavigation({
super.key, super.key,

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// ignore: must_be_immutable
class BottomNavigationItem extends StatelessWidget { class BottomNavigationItem extends StatelessWidget {
BottomNavigationItem({ BottomNavigationItem({
super.key, super.key,

View File

@ -1,13 +1,12 @@
import 'package:app/pages/home_page.dart';
import 'package:app/widgets/side_drawer_item.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// ignore: must_be_immutable
class SideDrawer extends StatelessWidget { class SideDrawer extends StatelessWidget {
SideDrawer({super.key, required this.children, this.backgroundColor}) { SideDrawer({super.key, required this.children, this.backgroundColor}) {
backgroundColor ??= Colors.black; backgroundColor ??= Colors.black;
} }
List<Widget> children; final List<Widget> children;
Color? backgroundColor; Color? backgroundColor;
@override @override

View File

@ -1,6 +1,6 @@
import 'package:app/pages/home_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// ignore: must_be_immutable
class SideDrawerItem extends StatelessWidget { class SideDrawerItem extends StatelessWidget {
SideDrawerItem({ SideDrawerItem({
super.key, super.key,