ft/adds registration and secure_storage [alpha]
This commit is contained in:
parent
dc8d75e221
commit
d0b93581b2
@ -45,7 +45,7 @@ android {
|
|||||||
applicationId "com.example.app"
|
applicationId "com.example.app"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 18
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
|
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/start_page.dart';
|
import 'package:app/pages/start_page.dart';
|
||||||
import 'package:app/pages_draft/home_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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart'
|
import 'package:flutter_localizations/flutter_localizations.dart'
|
||||||
show GlobalMaterialLocalizations;
|
show GlobalMaterialLocalizations;
|
||||||
@ -47,7 +52,78 @@ void main() async {
|
|||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
)),
|
)),
|
||||||
home: StartPage(),
|
home: const DigitalerFrieden(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DigitalerFrieden extends StatefulWidget {
|
||||||
|
const DigitalerFrieden({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DigitalerFrieden> createState() => _DigitalerFriedenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DigitalerFriedenState extends State<DigitalerFrieden> {
|
||||||
|
final StorageService _storageService = StorageService();
|
||||||
|
int? accountLevel;
|
||||||
|
bool? authenticated;
|
||||||
|
bool _loading = true;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_init();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _init() async {
|
||||||
|
accountLevel = await _storageService.accountLevel;
|
||||||
|
print(accountLevel!);
|
||||||
|
if (accountLevel! > 0) {
|
||||||
|
authenticated = await AuthService.authenticateWithBiometrics();
|
||||||
|
}
|
||||||
|
_loading = false;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (_loading) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/JPEG.jpg',
|
||||||
|
height: 180,
|
||||||
|
),
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color: CustomColors.primary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (accountLevel == null || accountLevel == 0) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -131,7 +131,7 @@ class BackendService {
|
|||||||
} on SocketException {
|
} on SocketException {
|
||||||
throw FetchDataException('Keine Internet Verbindung');
|
throw FetchDataException('Keine Internet Verbindung');
|
||||||
} on GrpcError catch (err) {
|
} on GrpcError catch (err) {
|
||||||
throw FetchDataException(err.message);
|
throw FetchDataException('${err.message}');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw InternalException(err.toString());
|
throw InternalException(err.toString());
|
||||||
}
|
}
|
||||||
|
84
frontend/app/lib/model/services/storage_service.dart
Normal file
84
frontend/app/lib/model/services/storage_service.dart
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
|
class StorageItem {
|
||||||
|
StorageItem(this.key, this.value);
|
||||||
|
|
||||||
|
final String key;
|
||||||
|
final String value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StorageService {
|
||||||
|
final _secureStorage = const FlutterSecureStorage(
|
||||||
|
aOptions: AndroidOptions(
|
||||||
|
encryptedSharedPreferences: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> writeData(StorageItem item) async {
|
||||||
|
await _secureStorage.write(
|
||||||
|
key: item.key,
|
||||||
|
value: item.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> readData(String key) async {
|
||||||
|
return await _secureStorage.read(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<StorageItem>> readAllSecureData() async {
|
||||||
|
var allData = await _secureStorage.readAll();
|
||||||
|
List<StorageItem> list =
|
||||||
|
allData.entries.map((e) => StorageItem(e.key, e.value)).toList();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> containsData(String key) async {
|
||||||
|
return await _secureStorage.containsKey(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteSecureData(StorageItem item) async {
|
||||||
|
await _secureStorage.delete(
|
||||||
|
key: item.key,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteAllSecureData() async {
|
||||||
|
await _secureStorage.deleteAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setNotificationSetting(bool enabled) async {
|
||||||
|
return await writeData(StorageItem('notifications', enabled ? '1' : '0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> get notificationSetting async {
|
||||||
|
final enabled = await readData('notifications') == '1' ? true : false;
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> get accountLevel async {
|
||||||
|
int? level;
|
||||||
|
final l = await readData('account_level');
|
||||||
|
if (l != null) {
|
||||||
|
level = int.tryParse(l);
|
||||||
|
}
|
||||||
|
return level ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setAccountLevel(int level) async {
|
||||||
|
return await writeData(StorageItem('account_level', '$level'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initAccountLevel() async {
|
||||||
|
return await writeData(StorageItem('account_level', '0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addAccountLevel() async {
|
||||||
|
int? level;
|
||||||
|
final l = await readData('account_level');
|
||||||
|
if (l != null) {
|
||||||
|
level = int.tryParse(l);
|
||||||
|
}
|
||||||
|
return await writeData(
|
||||||
|
StorageItem('account_level', '${level != null ? level + 1 : 1}'));
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,21 @@
|
|||||||
|
import 'package:app/model/services/storage_service.dart';
|
||||||
import 'package:app/pages/registration_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';
|
||||||
|
|
||||||
class NotificationsPage extends StatelessWidget {
|
class NotificationsPage extends StatelessWidget {
|
||||||
const NotificationsPage({super.key});
|
NotificationsPage({super.key});
|
||||||
|
|
||||||
|
final StorageService _storageService = StorageService();
|
||||||
|
|
||||||
|
void _setNotificationSetting(bool enabled) {
|
||||||
|
_storageService.addAccountLevel();
|
||||||
|
_storageService.setNotificationSetting(enabled).then(
|
||||||
|
(x) => _storageService.notificationSetting.then(
|
||||||
|
(value) => print('notifications: $value'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -60,6 +72,7 @@ class NotificationsPage extends StatelessWidget {
|
|||||||
backgroundColor: CustomColors.primary,
|
backgroundColor: CustomColors.primary,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
_setNotificationSetting(true);
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@ -88,7 +101,9 @@ class NotificationsPage extends StatelessWidget {
|
|||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
_setNotificationSetting(false);
|
||||||
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Später',
|
'Später',
|
||||||
style: TextStyle(color: CustomColors.primary),
|
style: TextStyle(color: CustomColors.primary),
|
||||||
|
219
frontend/app/lib/pages/password_page.dart
Normal file
219
frontend/app/lib/pages/password_page.dart
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import 'package:app/model/services/storage_service.dart';
|
||||||
|
import 'package:app/model/view_model/base_vm.dart';
|
||||||
|
import 'package:app/pages/verify_email_page.dart';
|
||||||
|
import 'package:app/util/colors.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:app/util/validation.dart';
|
||||||
|
|
||||||
|
class PasswordPage extends StatefulWidget {
|
||||||
|
PasswordPage({super.key, required this.email, required this.register});
|
||||||
|
|
||||||
|
final String email;
|
||||||
|
final bool register;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PasswordPage> createState() => _PasswordPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PasswordPageState extends State<PasswordPage> {
|
||||||
|
final BaseViewModel _vm = BaseViewModel();
|
||||||
|
final StorageService _storageService = StorageService();
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final _passwordController1 = TextEditingController();
|
||||||
|
|
||||||
|
final _passwordController2 = TextEditingController();
|
||||||
|
|
||||||
|
bool _validPassword = false;
|
||||||
|
|
||||||
|
bool _passwordsFilled = false;
|
||||||
|
|
||||||
|
bool _showPassword1 = false;
|
||||||
|
bool _showPassword2 = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_passwordController1.dispose();
|
||||||
|
_passwordController2.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: CustomColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Sichere dein Konto mit einem Passwort',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'sans-serif',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 2.0,
|
||||||
|
fontSize: 25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
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) {
|
||||||
|
if (value == null || !value.isValidPassword) {
|
||||||
|
_validPassword = false;
|
||||||
|
return 'Mindestens 12 Zeichen, Zahlen, Sonderzeichen (-_?!=.,*+), Groß- & Kleinbuchstaben';
|
||||||
|
} else {
|
||||||
|
_validPassword = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChanged: (value) {
|
||||||
|
_formKey.currentState?.validate();
|
||||||
|
if (!value.isValidPassword) {
|
||||||
|
setState(() {
|
||||||
|
_validPassword = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_validPassword = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: !_validPassword
|
||||||
|
? null
|
||||||
|
: 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: !_passwordsFilled ||
|
||||||
|
_passwordController1.text != _passwordController2.text
|
||||||
|
? null
|
||||||
|
: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
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,
|
||||||
|
child: const SizedBox(
|
||||||
|
height: 50,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Registrierung abschließen',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,25 @@
|
|||||||
import 'package:app/pages_old/start_page.dart';
|
import 'package:app/pages/password_page.dart';
|
||||||
import 'package:app/util/colors.dart';
|
import 'package:app/util/colors.dart';
|
||||||
|
import 'package:app/util/validation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class RegistrationPage extends StatelessWidget {
|
class RegistrationPage extends StatefulWidget {
|
||||||
const RegistrationPage({super.key});
|
const RegistrationPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RegistrationPage> createState() => _RegistrationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RegistrationPageState extends State<RegistrationPage> {
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
final mailController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
mailController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
@ -42,56 +57,99 @@ class RegistrationPage extends StatelessWidget {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
const Text(
|
Form(
|
||||||
'Du kannst die Mitteilungen jederzeit wieder deaktivieren.',
|
key: formKey,
|
||||||
textAlign: TextAlign.center,
|
child: Column(
|
||||||
),
|
children: [
|
||||||
const Spacer(
|
TextFormField(
|
||||||
flex: 2,
|
autocorrect: false,
|
||||||
),
|
autofocus: true,
|
||||||
Hero(
|
controller: mailController,
|
||||||
tag: 'flow-button',
|
keyboardType: TextInputType.emailAddress,
|
||||||
child: ElevatedButton(
|
decoration: const InputDecoration(
|
||||||
style: ElevatedButton.styleFrom(
|
helperText: 'test',
|
||||||
backgroundColor: CustomColors.primary,
|
label: Text('E-Mail Adresse'),
|
||||||
),
|
filled: true,
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (builder) => StartPage(),
|
|
||||||
// builder: (builder) => SecurityPage(),
|
|
||||||
),
|
),
|
||||||
);
|
validator: (value) {
|
||||||
},
|
if (value == null || !value.isValidEmail) {
|
||||||
child: const SizedBox(
|
return 'Bitte eine valide E-Mail Adresse angeben';
|
||||||
height: 60,
|
} else {
|
||||||
child: Row(
|
return null;
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
}
|
||||||
children: [
|
},
|
||||||
Text(
|
|
||||||
'Mitteilungen erhalten',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 20,
|
||||||
),
|
),
|
||||||
TextButton(
|
Row(
|
||||||
onPressed: () {},
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Text(
|
children: [
|
||||||
'Später',
|
TextButton(
|
||||||
style: TextStyle(color: CustomColors.primary),
|
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(
|
const Spacer(
|
||||||
flex: 1,
|
flex: 2,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
110
frontend/app/lib/pages/verify_email_page.dart
Normal file
110
frontend/app/lib/pages/verify_email_page.dart
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import 'package:app/util/colors.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class VerifyEmailPage extends StatelessWidget {
|
||||||
|
VerifyEmailPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: CustomColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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: () {},
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class CustomColors {
|
class CustomColors {
|
||||||
static Color get error {
|
static Color get error {
|
||||||
return const Color.fromARGB(200, 255, 90, 90);
|
return const Color.fromARGB(255, 255, 82, 82);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Color get success {
|
static Color get success {
|
||||||
|
0
frontend/app/lib/util/enums.dart
Normal file
0
frontend/app/lib/util/enums.dart
Normal file
@ -2,7 +2,7 @@ final emailRegExp = RegExp(r"^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+");
|
|||||||
final nameRegExp =
|
final nameRegExp =
|
||||||
RegExp(r"^\s*([A-Za-z]{1,}([\.,] |[-']| ))+[A-Za-z]+\.?\s*$");
|
RegExp(r"^\s*([A-Za-z]{1,}([\.,] |[-']| ))+[A-Za-z]+\.?\s*$");
|
||||||
final phoneRegExp = RegExp(r"^\+?0[0-9]{10}$");
|
final phoneRegExp = RegExp(r"^\+?0[0-9]{10}$");
|
||||||
final passwordRegExp = RegExp(r'^.+$');
|
final passwordRegExp = RegExp(r'^[0-9a-zA-Z\-\_\.\,\*\+\=?!]{12,64}$');
|
||||||
|
|
||||||
extension valString on String {
|
extension valString on String {
|
||||||
bool get isValidEmail {
|
bool get isValidEmail {
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
flutter_secure_storage_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
@ -89,6 +89,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -123,11 +131,64 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.17"
|
version: "2.0.17"
|
||||||
|
flutter_secure_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage
|
||||||
|
sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.0"
|
||||||
|
flutter_secure_storage_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_linux
|
||||||
|
sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
flutter_secure_storage_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_macos
|
||||||
|
sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
|
flutter_secure_storage_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_platform_interface
|
||||||
|
sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
flutter_secure_storage_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_web
|
||||||
|
sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
flutter_secure_storage_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_windows
|
||||||
|
sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
googleapis_auth:
|
googleapis_auth:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -272,6 +333,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.3"
|
version: "1.8.3"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -405,6 +522,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4-beta"
|
version: "0.1.4-beta"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.9"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.4 <4.0.0"
|
dart: ">=3.1.4 <4.0.0"
|
||||||
flutter: ">=3.10.0"
|
flutter: ">=3.10.0"
|
||||||
|
@ -47,6 +47,7 @@ dependencies:
|
|||||||
fixnum: ^1.1.0
|
fixnum: ^1.1.0
|
||||||
provider: ^6.0.5
|
provider: ^6.0.5
|
||||||
intl: ^0.18.1
|
intl: ^0.18.1
|
||||||
|
flutter_secure_storage: ^9.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
lints: ^2.0.0
|
lints: ^2.0.0
|
||||||
|
@ -6,9 +6,12 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <local_auth_windows/local_auth_plugin.h>
|
#include <local_auth_windows/local_auth_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
LocalAuthPluginRegisterWithRegistrar(
|
LocalAuthPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
flutter_secure_storage_windows
|
||||||
local_auth_windows
|
local_auth_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user