ft/adds persistent app state up to mail verification

This commit is contained in:
itsscb 2023-11-12 23:55:20 +01:00
parent d0b93581b2
commit df74e8e12f
9 changed files with 1004 additions and 710 deletions

View File

@ -1,9 +1,6 @@
import 'package:app/model/services/auth_service.dart'; import 'package:app/model/services/auth_service.dart';
import 'package:app/model/services/storage_service.dart'; import 'package:app/model/services/storage_service.dart';
import 'package:app/pages/notifications_page.dart';
import 'package:app/pages/start_page.dart'; import 'package:app/pages/start_page.dart';
import 'package:app/pages/verify_email_page.dart';
import 'package:app/pages/registration_page.dart';
import 'package:app/util/colors.dart'; import 'package:app/util/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart' import 'package:flutter_localizations/flutter_localizations.dart'
@ -77,53 +74,39 @@ class _DigitalerFriedenState extends State<DigitalerFrieden> {
void _init() async { void _init() async {
accountLevel = await _storageService.accountLevel; accountLevel = await _storageService.accountLevel;
print(accountLevel!);
if (accountLevel! > 0) { if (accountLevel! > 0) {
authenticated = await AuthService.authenticateWithBiometrics(); authenticated = await AuthService.authenticateWithBiometrics();
} }
_loading = false; setState(() {
setState(() {}); _loading = false;
});
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_loading) { if (_loading) {
return Center( return SafeArea(
child: Column( child: Center(
children: [ child: Column(
Image.asset( children: [
'assets/JPEG.jpg', const SizedBox(
height: 180, height: 150,
), ),
CircularProgressIndicator( Hero(
color: CustomColors.primary, tag: 'logo',
), child: Image.asset(
], 'assets/JPEG.jpg',
height: 180,
),
),
CircularProgressIndicator(
color: CustomColors.primary,
),
],
),
), ),
); );
} }
if (accountLevel == null || accountLevel == 0) { return const StartPage();
return const StartPage();
}
// else if (authenticated == null) {
// AuthService.authenticateWithBiometrics().then((value) {
// setState(() {
// authenticated = value;
// });
// });
// }
switch (accountLevel) {
case null:
return const StartPage();
case < 1:
return NotificationsPage();
case 1:
return const RegistrationPage();
case 2:
return VerifyEmailPage();
default:
return const StartPage();
}
} }
} }

View File

@ -57,9 +57,9 @@ class StorageService {
Future<int> get accountLevel async { Future<int> get accountLevel async {
int? level; int? level;
final l = await readData('account_level'); final lev = await readData('account_level');
if (l != null) { if (lev != null) {
level = int.tryParse(l); level = int.tryParse(lev);
} }
return level ?? 0; return level ?? 0;
} }

View File

@ -3,119 +3,189 @@ import 'package:app/pages/registration_page.dart';
import 'package:app/util/colors.dart'; import 'package:app/util/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class NotificationsPage extends StatelessWidget { class NotificationsPage extends StatefulWidget {
NotificationsPage({super.key}); const NotificationsPage({super.key});
@override
State<NotificationsPage> createState() => _NotificationsPageState();
}
class _NotificationsPageState extends State<NotificationsPage> {
final StorageService _storageService = StorageService(); final StorageService _storageService = StorageService();
bool _loading = true;
void _setNotificationSetting(bool enabled) { Future<void> _setNotificationSetting(bool enabled) async {
_storageService.addAccountLevel(); await _storageService.setNotificationSetting(enabled);
_storageService.setNotificationSetting(enabled).then( }
(x) => _storageService.notificationSetting.then(
(value) => print('notifications: $value'), @override
), void initState() {
); _init();
super.initState();
}
void _init() async {
final accountLevel = await _storageService.accountLevel;
if (accountLevel > 2 && mounted) {
await Navigator.push(context,
MaterialPageRoute(builder: (builder) => const RegistrationPage()));
setState(() {
_loading = false;
});
} else {
setState(() {
_loading = false;
});
}
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
child: Scaffold( child: _loading
appBar: AppBar( ? Center(
iconTheme: IconThemeData( child: Column(
color: CustomColors.primary, children: [
), const SizedBox(
), height: 150,
body: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 80,
),
Image.asset('assets/chat_bubbles.png'),
const SizedBox(
height: 60,
),
const Text(
'Erhalte Mitteilungen',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
),
const SizedBox(
height: 20,
),
const Text(
'Du erhältst z. B. eine Mitteilung sobald wir eine Digitale Spur gefunden haben.',
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
const Text(
'Du kannst die Mitteilungen jederzeit wieder deaktivieren.',
textAlign: TextAlign.center,
),
const Spacer(
flex: 2,
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
), ),
onPressed: () { Hero(
_setNotificationSetting(true); tag: 'logo',
Navigator.push( child: Image.asset(
context, 'assets/JPEG.jpg',
MaterialPageRoute( height: 180,
builder: (builder) => const RegistrationPage(),
// builder: (builder) => SecurityPage(),
),
);
},
child: const SizedBox(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Mitteilungen erhalten',
style: TextStyle(
fontSize: 20,
),
),
],
), ),
), ),
CircularProgressIndicator(
color: CustomColors.primary,
),
],
),
)
: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: BackButton(
color: CustomColors.primary,
onPressed: () async {
await _storageService.setAccountLevel(1);
if (mounted) {
Navigator.pop(context);
}
},
),
iconTheme: IconThemeData(
color: CustomColors.primary,
), ),
), ),
const SizedBox( body: Padding(
height: 10, padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
), child: Column(
TextButton( // mainAxisAlignment: MainAxisAlignment.center,
onPressed: () { children: [
_setNotificationSetting(false); const SizedBox(
}, height: 80,
child: Text( ),
'Später', Image.asset('assets/chat_bubbles.png'),
style: TextStyle(color: CustomColors.primary), const SizedBox(
height: 60,
),
const Text(
'Erhalte Mitteilungen',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
),
const SizedBox(
height: 20,
),
const Text(
'Du erhältst z. B. eine Mitteilung sobald wir eine Digitale Spur gefunden haben.',
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
const Text(
'Du kannst die Mitteilungen jederzeit wieder deaktivieren.',
textAlign: TextAlign.center,
),
const Spacer(
flex: 2,
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
),
onPressed: () async {
await _setNotificationSetting(true);
await _storageService.setAccountLevel(3);
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => const RegistrationPage(),
// builder: (builder) => SecurityPage(),
),
);
}
},
child: const SizedBox(
height: 60,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: Text(
'Mitteilungen erhalten',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
),
),
),
],
),
),
),
),
const SizedBox(
height: 10,
),
TextButton(
onPressed: () async {
await _setNotificationSetting(false);
await _storageService.setAccountLevel(3);
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => const RegistrationPage(),
// builder: (builder) => SecurityPage(),
),
);
}
},
child: Text(
'Später',
style: TextStyle(color: CustomColors.primary),
),
),
const Spacer(
flex: 1,
),
],
), ),
), ),
const Spacer( ),
flex: 1,
),
],
),
),
),
); );
} }
} }

View File

@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
import 'package:app/util/validation.dart'; import 'package:app/util/validation.dart';
class PasswordPage extends StatefulWidget { class PasswordPage extends StatefulWidget {
PasswordPage({super.key, required this.email, required this.register}); const PasswordPage({super.key, required this.email, required this.register});
final String email; final String email;
final bool register; final bool register;
@ -47,170 +47,195 @@ class _PasswordPageState extends State<PasswordPage> {
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: Form( child: SingleChildScrollView(
key: _formKey, child: Form(
child: Column( key: _formKey,
mainAxisAlignment: MainAxisAlignment.center, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
const Text( children: [
'Sichere dein Konto mit einem Passwort', Text(
textAlign: TextAlign.center, widget.register
style: TextStyle( ? 'Sichere dein Konto mit einem Passwort'
fontFamily: 'sans-serif', : 'Login',
fontWeight: FontWeight.bold, textAlign: TextAlign.center,
letterSpacing: 2.0, style: const TextStyle(
fontSize: 25, fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
), ),
), const SizedBox(
const SizedBox( height: 60,
height: 60,
),
TextFormField(
controller: _passwordController1,
autocorrect: false,
autofocus: true,
keyboardType: TextInputType.visiblePassword,
obscureText: !_showPassword1,
autovalidateMode: AutovalidateMode.always,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () {
setState(() {
_showPassword1 = !_showPassword1;
});
},
icon: Icon(_showPassword1
? Icons.remove_red_eye
: Icons.remove_red_eye_outlined)),
label: const Text('Passwort'),
filled: true,
), ),
validator: (value) { TextFormField(
if (value == null || !value.isValidPassword) { controller: _passwordController1,
_validPassword = false; autocorrect: false,
return 'Mindestens 12 Zeichen, Zahlen, Sonderzeichen (-_?!=.,*+), Groß- & Kleinbuchstaben'; autofocus: true,
} else { keyboardType: TextInputType.visiblePassword,
_validPassword = true; obscureText: !_showPassword1,
return null; autovalidateMode: AutovalidateMode.always,
} decoration: InputDecoration(
}, suffixIcon: IconButton(
onChanged: (value) { onPressed: () {
_formKey.currentState?.validate(); setState(() {
if (!value.isValidPassword) { _showPassword1 = !_showPassword1;
setState(() { });
},
icon: Icon(_showPassword1
? Icons.remove_red_eye
: Icons.remove_red_eye_outlined)),
label: const Text('Passwort'),
filled: true,
),
validator: (value) {
if (value == null || !value.isValidPassword) {
_validPassword = false; _validPassword = false;
}); return 'Mindestens 12 Zeichen, Zahlen, Sonderzeichen (-_?!=.,*+), Groß- & Kleinbuchstaben';
} else { } else {
setState(() { if (!widget.register) {
_passwordsFilled = true;
}
_validPassword = true; _validPassword = true;
}); return null;
} }
}, },
), onChanged: (value) {
const SizedBox( _formKey.currentState?.validate();
height: 20, if (!value.isValidPassword) {
), setState(() {
AnimatedSwitcher( _validPassword = false;
duration: const Duration(milliseconds: 300), });
child: !_validPassword } else {
? null setState(() {
: TextFormField( if (!widget.register) {
controller: _passwordController2, _passwordsFilled = true;
keyboardType: TextInputType.visiblePassword, }
obscureText: !_showPassword2, _validPassword = true;
decoration: InputDecoration( });
suffixIcon: IconButton( }
onPressed: () { },
setState(() { ),
_showPassword2 = !_showPassword2; const SizedBox(
}); height: 20,
}, ),
icon: Icon(_showPassword2 AnimatedSwitcher(
? Icons.remove_red_eye duration: const Duration(milliseconds: 300),
: Icons.remove_red_eye_outlined)), child: !widget.register || !_validPassword
label: const Text('Passwort bestätigen'), ? null
filled: true, : TextFormField(
controller: _passwordController2,
keyboardType: TextInputType.visiblePassword,
obscureText: !_showPassword2,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () {
setState(() {
_showPassword2 = !_showPassword2;
});
},
icon: Icon(_showPassword2
? Icons.remove_red_eye
: Icons.remove_red_eye_outlined)),
label: const Text('Passwort bestätigen'),
filled: true,
),
validator: (value) {
if (_passwordController1.text !=
_passwordController2.text) {
setState(() {
_passwordsFilled = false;
});
return 'Passwörter stimmen nicht überein';
} else {
setState(() {
_passwordsFilled = true;
});
return null;
}
},
onTap: () => _formKey.currentState?.validate(),
onChanged: (value) {
_formKey.currentState?.validate();
if (_passwordController1.text !=
_passwordController2.text) {
setState(() {
_passwordsFilled = false;
});
} else {
setState(() {
_passwordsFilled = true;
});
}
},
), ),
validator: (value) { ),
if (_passwordController1.text != const SizedBox(
_passwordController2.text) { height: 20,
setState(() { ),
_passwordsFilled = false; AnimatedSwitcher(
}); duration: const Duration(milliseconds: 300),
return 'Passwörter stimmen nicht überein'; child: !_passwordsFilled ||
} else { (widget.register &&
setState(() { _passwordController1.text !=
_passwordsFilled = true; _passwordController2.text)
}); ? null
return null; : ElevatedButton(
} style: ElevatedButton.styleFrom(
}, backgroundColor: CustomColors.primary,
onTap: () => _formKey.currentState?.validate(), ),
onChanged: (value) { onPressed: _validPassword && _passwordsFilled
_formKey.currentState?.validate(); ? () async {
if (_passwordController1.text != if (_formKey.currentState!.validate()) {
_passwordController2.text) { FocusScope.of(context).unfocus();
setState(() { final navigator = Navigator.of(context);
_passwordsFilled = false; bool loggedin = false;
}); if (widget.register) {
} else { loggedin = await _vm.createAccount(
setState(() { context,
_passwordsFilled = true; email: widget.email,
}); password: _passwordController1.text,
} );
}, } else {
), loggedin = await _vm.login(
), context,
const SizedBox( email: widget.email,
height: 20, password: _passwordController1.text,
), );
AnimatedSwitcher( }
duration: const Duration(milliseconds: 300), if (loggedin && mounted) {
child: !_passwordsFilled || await _storageService.setAccountLevel(4);
_passwordController1.text != _passwordController2.text navigator.push(
? null MaterialPageRoute(
: ElevatedButton( builder: (builder) =>
style: ElevatedButton.styleFrom( const VerifyEmailPage(),
backgroundColor: CustomColors.primary, ),
), );
onPressed: _validPassword && _passwordsFilled }
? () async {
if (_formKey.currentState!.validate()) {
final navigator = Navigator.of(context);
final loggedin = await _vm.createAccount(
context,
email: widget.email,
password: _passwordController1.text,
);
if (loggedin && mounted) {
_storageService.addAccountLevel();
navigator.push(
MaterialPageRoute(
builder: (builder) => VerifyEmailPage(),
),
);
} }
} }
} : null,
: null, child: SizedBox(
child: const SizedBox( height: 50,
height: 50, child: Row(
child: Row( mainAxisSize: MainAxisSize.max,
mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, children: [
children: [ Text(
Text( widget.register
'Registrierung abschließen', ? 'Registrierung abschließen'
style: TextStyle( : 'Einloggen',
fontSize: 20, style: const TextStyle(
fontSize: 20,
),
), ),
), ],
], ),
), ),
), ),
), ),
), ],
], ),
), ),
), ),
), ),

View File

@ -1,4 +1,6 @@
import 'package:app/model/services/storage_service.dart';
import 'package:app/pages/password_page.dart'; import 'package:app/pages/password_page.dart';
import 'package:app/pages/verify_email_page.dart';
import 'package:app/util/colors.dart'; import 'package:app/util/colors.dart';
import 'package:app/util/validation.dart'; import 'package:app/util/validation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -13,6 +15,30 @@ class RegistrationPage extends StatefulWidget {
class _RegistrationPageState extends State<RegistrationPage> { class _RegistrationPageState extends State<RegistrationPage> {
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
final mailController = TextEditingController(); final mailController = TextEditingController();
bool _loading = true;
final StorageService _storageService = StorageService();
@override
void initState() {
_init();
super.initState();
}
void _init() async {
final accountLevel = await _storageService.accountLevel;
if (accountLevel > 3 && mounted) {
await Navigator.push(context,
MaterialPageRoute(builder: (builder) => const VerifyEmailPage()));
setState(() {
_loading = false;
});
} else {
setState(() {
_loading = false;
});
}
}
@override @override
void dispose() { void dispose() {
@ -23,138 +49,167 @@ class _RegistrationPageState extends State<RegistrationPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
child: Scaffold( child: _loading
appBar: AppBar( ? Center(
iconTheme: IconThemeData( child: Column(
color: CustomColors.primary, children: [
), const SizedBox(
), height: 150,
body: Padding( ),
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), Hero(
child: Column( tag: 'logo',
// mainAxisAlignment: MainAxisAlignment.center, child: Image.asset(
children: [ 'assets/JPEG.jpg',
const SizedBox( height: 180,
height: 20, ),
),
CircularProgressIndicator(
color: CustomColors.primary,
),
],
), ),
const Text( )
'Jetzt Registrieren', : Scaffold(
textAlign: TextAlign.center, appBar: AppBar(
style: TextStyle( leading: BackButton(
fontFamily: 'sans-serif', color: CustomColors.primary,
fontWeight: FontWeight.bold, onPressed: () async {
letterSpacing: 2.0, await _storageService.setAccountLevel(2);
fontSize: 25, if (mounted) {
Navigator.pop(context);
}
},
),
iconTheme: IconThemeData(
color: CustomColors.primary,
), ),
), ),
const SizedBox( body: Padding(
height: 20, padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
),
const Text(
'Gib deine E-Mail Adresse ein.',
// textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
Form(
key: formKey,
child: Column( child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
TextFormField( const SizedBox(
autocorrect: false, height: 20,
autofocus: true, ),
controller: mailController, const Text(
keyboardType: TextInputType.emailAddress, 'Jetzt Registrieren',
decoration: const InputDecoration( textAlign: TextAlign.center,
helperText: 'test', style: TextStyle(
label: Text('E-Mail Adresse'), fontFamily: 'sans-serif',
filled: true, fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
), ),
validator: (value) { ),
if (value == null || !value.isValidEmail) { const SizedBox(
return 'Bitte eine valide E-Mail Adresse angeben'; height: 20,
} else { ),
return null; const Text(
} 'Gib deine E-Mail Adresse ein.',
}, // textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
Form(
key: formKey,
child: Column(
children: [
TextFormField(
autocorrect: false,
autofocus: true,
controller: mailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
helperText: 'test',
label: Text('E-Mail Adresse'),
filled: true,
),
validator: (value) {
if (value == null || !value.isValidEmail) {
return 'Bitte eine valide E-Mail Adresse angeben';
} else {
return null;
}
},
),
],
),
),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
if (formKey.currentState!.validate()) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => PasswordPage(
email: mailController.text,
register: false,
),
),
);
}
},
child: Text(
'Stattdessen anmelden',
// textAlign: TextAlign.center,
style: TextStyle(
color: CustomColors.primary,
),
),
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
),
onPressed: () {
if (formKey.currentState!.validate()) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => PasswordPage(
email: mailController.text,
register: true,
),
),
);
}
},
child: const SizedBox(
height: 50,
width: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Weiter',
style: TextStyle(
fontSize: 20,
),
),
],
),
),
),
),
],
),
const Spacer(
flex: 2,
), ),
], ],
), ),
), ),
const SizedBox( ),
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
if (formKey.currentState!.validate()) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => PasswordPage(
email: mailController.text,
register: false,
),
),
);
}
},
child: Text(
'Stattdessen anmelden',
// textAlign: TextAlign.center,
style: TextStyle(
color: CustomColors.primary,
),
),
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
),
onPressed: () {
if (formKey.currentState!.validate()) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => PasswordPage(
email: mailController.text,
register: true,
),
),
);
}
},
child: const SizedBox(
height: 50,
width: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Weiter',
style: TextStyle(
fontSize: 20,
),
),
],
),
),
),
),
],
),
const Spacer(
flex: 2,
),
],
),
),
),
); );
} }
} }

View File

@ -1,126 +1,187 @@
import 'package:app/model/services/auth_service.dart'; import 'package:app/model/services/auth_service.dart';
import 'package:app/model/services/storage_service.dart';
import 'package:app/pages/notifications_page.dart'; import 'package:app/pages/notifications_page.dart';
import 'package:app/util/colors.dart'; import 'package:app/util/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SecurityPage extends StatelessWidget { class SecurityPage extends StatefulWidget {
const SecurityPage({super.key}); const SecurityPage({super.key});
@override
State<SecurityPage> createState() => _SecurityPageState();
}
class _SecurityPageState extends State<SecurityPage> {
final StorageService _storageService = StorageService();
bool _loading = true;
@override
void initState() {
_init();
super.initState();
}
void _init() async {
final accountLevel = await _storageService.accountLevel;
if (accountLevel > 1 && mounted) {
await Navigator.push(context,
MaterialPageRoute(builder: (builder) => const NotificationsPage()));
setState(() {
_loading = false;
});
} else {
setState(() {
_loading = false;
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
child: Scaffold( child: _loading
appBar: AppBar( ? Center(
iconTheme: IconThemeData(color: CustomColors.primary), child: Column(
), children: [
body: Padding( const SizedBox(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), height: 150,
child: Center(
child: Column(
children: [
const Spacer(),
const Hero(
tag: 'flow-icon',
child: Icon(
Icons.fingerprint,
color: Colors.white,
size: 200,
), ),
), Hero(
const Spacer(), tag: 'logo',
const Text( child: Image.asset(
'Deine Sicherheit kommt an erster Stelle', 'assets/JPEG.jpg',
textAlign: TextAlign.center, height: 180,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 25,
fontWeight: FontWeight.bold),
),
const SizedBox(
height: 30,
),
const Text(
'Schütze dein Konto mit der biometrischen Erkennung deines Geräts oder lege einen Code fest.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 18,
fontWeight: FontWeight.bold),
),
const Spacer(),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
), ),
onPressed: () async { ),
bool isAuthenticated = CircularProgressIndicator(
await AuthService.authenticateWithBiometrics(); color: CustomColors.primary,
if (isAuthenticated) { ),
// ignore: use_build_context_synchronously ],
Navigator.push( ),
context, )
MaterialPageRoute( : Scaffold(
builder: (context) => NotificationsPage()), appBar: AppBar(
); leading: BackButton(
} color: CustomColors.primary,
}, onPressed: () async {
child: const SizedBox( await _storageService.setAccountLevel(0);
height: 60, if (mounted) {
child: Row( Navigator.pop(context);
mainAxisAlignment: MainAxisAlignment.center, }
children: [ },
Text( ),
'App absichern', iconTheme: IconThemeData(color: CustomColors.primary),
style: TextStyle( ),
fontSize: 20, body: Padding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16),
child: Center(
child: Column(
children: [
const Spacer(),
const Hero(
tag: 'flow-icon',
child: Icon(
Icons.fingerprint,
color: Colors.white,
size: 200,
),
),
const Spacer(),
const Text(
'Deine Sicherheit kommt an erster Stelle',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 25,
fontWeight: FontWeight.bold),
),
const SizedBox(
height: 30,
),
const Text(
'Schütze dein Konto mit der biometrischen Erkennung deines Geräts oder lege einen Code fest.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 18,
fontWeight: FontWeight.bold),
),
const Spacer(),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
),
onPressed: () async {
bool isAuthenticated =
await AuthService.authenticateWithBiometrics();
if (isAuthenticated) {
await _storageService.setAccountLevel(2);
// ignore: use_build_context_synchronously
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const NotificationsPage()),
);
}
},
child: const SizedBox(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'App absichern',
style: TextStyle(
fontSize: 20,
),
),
],
), ),
), ),
], ),
), ),
), // const SizedBox(
// height: 10,
// ),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: CustomColors.secondary,
// ),
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (builder) => SecurityPage(),
// ),
// );
// },
// child: const SizedBox(
// height: 60,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// 'Eigenen Code festlegen',
// style: TextStyle(
// color: Colors.white,
// fontSize: 22,
// ),
// ),
// ],
// ),
// ),
// ),
const Spacer(
flex: 2,
),
],
), ),
), ),
// const SizedBox( ),
// height: 10,
// ),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: CustomColors.secondary,
// ),
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (builder) => SecurityPage(),
// ),
// );
// },
// child: const SizedBox(
// height: 60,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// 'Eigenen Code festlegen',
// style: TextStyle(
// color: Colors.white,
// fontSize: 22,
// ),
// ),
// ],
// ),
// ),
// ),
const Spacer(
flex: 2,
),
],
), ),
),
),
),
); );
} }
} }

View File

@ -1,112 +1,166 @@
import 'package:app/model/services/storage_service.dart';
import 'package:app/pages/agb_page.dart'; import 'package:app/pages/agb_page.dart';
import 'package:app/pages/security_page.dart'; import 'package:app/pages/security_page.dart';
import 'package:app/util/colors.dart'; import 'package:app/util/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class StartPage extends StatelessWidget { class StartPage extends StatefulWidget {
const StartPage({super.key}); const StartPage({super.key});
@override
State<StartPage> createState() => _StartPageState();
}
class _StartPageState extends State<StartPage> {
final StorageService _storageService = StorageService();
bool _loading = true;
@override
void initState() {
_init();
super.initState();
}
void _init() async {
int accountLevel = await _storageService.accountLevel;
if (accountLevel > 0 && mounted) {
await Navigator.push(context,
MaterialPageRoute(builder: (builder) => const SecurityPage()));
setState(() {
_loading = false;
});
} else {
setState(() {
_loading = false;
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
child: Scaffold( child: _loading
appBar: AppBar( ? Center(
iconTheme: IconThemeData(color: CustomColors.primary), child: Column(
), children: [
body: Padding( const SizedBox(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), height: 150,
child: Column(
children: [
Hero(
tag: 'flow-icon',
child: Image.asset(
'assets/JPEG.jpg',
height: 180,
),
),
const SizedBox(
height: 30,
),
const Text(
'Hallo. Digitale Spuren\nentfernen\nper Knopfdruck.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
),
const SizedBox(
height: 20,
),
const Text(
'Mit uns finden Sie Ihre Digitalen Spuren und können diese entfernen.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 18,
),
),
const Spacer(
flex: 1,
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
), ),
onPressed: () { Hero(
Navigator.push( tag: 'logo',
context, child: Image.asset(
MaterialPageRoute( 'assets/JPEG.jpg',
builder: (builder) => SecurityPage(), height: 180,
// builder: (builder) => SecurityPage(),
),
);
},
child: const SizedBox(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Weiter',
style: TextStyle(
fontSize: 20,
),
),
],
), ),
), ),
CircularProgressIndicator(
color: CustomColors.primary,
),
],
),
)
: Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: CustomColors.primary),
),
body: Padding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16),
child: Column(
children: [
Hero(
tag: 'flow-icon',
child: Image.asset(
'assets/JPEG.jpg',
height: 180,
),
),
const SizedBox(
height: 30,
),
const Text(
'Hallo. Digitale Spuren\nentfernen\nper Knopfdruck.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
),
const SizedBox(
height: 20,
),
const Text(
'Mit uns finden Sie Ihre Digitalen Spuren und können diese entfernen.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 18,
),
),
const Spacer(
flex: 1,
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
),
onPressed: () async {
await _storageService.setAccountLevel(1);
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => const SecurityPage(),
// builder: (builder) => SecurityPage(),
),
);
}
},
child: const SizedBox(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Weiter',
style: TextStyle(
fontSize: 20,
),
),
],
),
),
),
),
const Spacer(
flex: 1,
),
const Text(
'Mit der weiteren Nutzung stimmst du den folgenden Bedingungen zu:',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 16,
),
),
TextButton(
onPressed: () {
showDialog(
context: context,
builder: (builder) => AgbPage());
},
child: Text(
'AGB - Datenschutzerklärung',
textAlign: TextAlign.center,
style: TextStyle(color: CustomColors.primary),
))
],
), ),
), ),
const Spacer( ),
flex: 1,
),
const Text(
'Mit der weiteren Nutzung stimmst du den folgenden Bedingungen zu:',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontSize: 16,
),
),
TextButton(
onPressed: () {
showDialog(
context: context, builder: (builder) => AgbPage());
},
child: Text(
'AGB - Datenschutzerklärung',
textAlign: TextAlign.center,
style: TextStyle(color: CustomColors.primary),
))
],
),
),
),
); );
} }
} }

View File

@ -1,110 +1,156 @@
import 'package:app/model/services/storage_service.dart';
import 'package:app/util/colors.dart'; import 'package:app/util/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class VerifyEmailPage extends StatelessWidget { class VerifyEmailPage extends StatefulWidget {
VerifyEmailPage({super.key}); const VerifyEmailPage({super.key});
@override
State<VerifyEmailPage> createState() => _VerifyEmailPageState();
}
class _VerifyEmailPageState extends State<VerifyEmailPage> {
final StorageService _storageService = StorageService();
bool _loading = true;
@override
void initState() {
super.initState();
setState(() {
_loading = false;
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
child: Scaffold( child: _loading
appBar: AppBar( ? Center(
iconTheme: IconThemeData( child: Column(
color: CustomColors.primary, children: [
), const SizedBox(
), height: 150,
body: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 80,
),
const Text(
'Verifizieren',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
),
const SizedBox(
height: 50,
),
const Text(
'Wir haben dir eine E-Mail geschickt.',
textAlign: TextAlign.center,
// textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
const Text(
'Bitte verifiziere deine E-Mail Adresse, dann geht es weiter.',
textAlign: TextAlign.center,
// textAlign: TextAlign.center,
),
const SizedBox(
height: 80,
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
), ),
onPressed: () {}, Hero(
child: const SizedBox( tag: 'logo',
height: 50, child: Image.asset(
width: 100, 'assets/JPEG.jpg',
child: Row( height: 180,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Weiter',
style: TextStyle(
fontSize: 20,
),
),
],
), ),
), ),
), CircularProgressIndicator(
),
const SizedBox(
height: 60,
),
const Text(
'Noch keine E-Mail erhalten?',
// textAlign: TextAlign.center,
),
const Text(
'Schon im Spam-Ordner nachgeschaut?',
// textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
TextButton(
onPressed: () {},
child: Text(
'Erneut senden',
// textAlign: TextAlign.center,
style: TextStyle(
color: CustomColors.primary, color: CustomColors.primary,
), ),
],
),
)
: Scaffold(
appBar: AppBar(
leading: BackButton(
color: CustomColors.primary,
onPressed: () async {
await _storageService.setAccountLevel(3);
if (mounted) {
Navigator.pop(context);
}
},
),
iconTheme: IconThemeData(
color: CustomColors.primary,
), ),
), ),
const Spacer( body: Padding(
flex: 2, padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 80,
),
const Text(
'Verifizieren',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'sans-serif',
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
fontSize: 25,
),
),
const SizedBox(
height: 50,
),
const Text(
'Wir haben dir eine E-Mail geschickt.',
textAlign: TextAlign.center,
// textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
const Text(
'Bitte verifiziere deine E-Mail Adresse, dann geht es weiter.',
textAlign: TextAlign.center,
// textAlign: TextAlign.center,
),
const SizedBox(
height: 80,
),
Hero(
tag: 'flow-button',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: CustomColors.primary,
),
onPressed: () {},
child: const SizedBox(
height: 50,
width: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Weiter',
style: TextStyle(
fontSize: 20,
),
),
],
),
),
),
),
const SizedBox(
height: 60,
),
const Text(
'Noch keine E-Mail erhalten?',
// textAlign: TextAlign.center,
),
const Text(
'Schon im Spam-Ordner nachgeschaut?',
// textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
TextButton(
onPressed: () {},
child: Text(
'Erneut senden',
// textAlign: TextAlign.center,
style: TextStyle(
color: CustomColors.primary,
),
),
),
const Spacer(
flex: 2,
),
],
),
), ),
], ),
),
),
),
); );
} }
} }

View File

@ -6,7 +6,7 @@ class CustomColors {
} }
static Color get success { static Color get success {
return const Color.fromARGB(200, 55, 125, 55); return const Color.fromARGB(255, 51, 217, 178);
} }
static Color get primary { static Color get primary {