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"
|
||||
// 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.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
minSdkVersion 18
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
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_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_localizations/flutter_localizations.dart'
|
||||
show GlobalMaterialLocalizations;
|
||||
@ -47,7 +52,78 @@ void main() async {
|
||||
backgroundColor: Colors.black,
|
||||
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 {
|
||||
throw FetchDataException('Keine Internet Verbindung');
|
||||
} on GrpcError catch (err) {
|
||||
throw FetchDataException(err.message);
|
||||
throw FetchDataException('${err.message}');
|
||||
} catch (err) {
|
||||
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/util/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
@ -60,6 +72,7 @@ class NotificationsPage extends StatelessWidget {
|
||||
backgroundColor: CustomColors.primary,
|
||||
),
|
||||
onPressed: () {
|
||||
_setNotificationSetting(true);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
@ -88,7 +101,9 @@ class NotificationsPage extends StatelessWidget {
|
||||
height: 10,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
_setNotificationSetting(false);
|
||||
},
|
||||
child: Text(
|
||||
'Später',
|
||||
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/validation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RegistrationPage extends StatelessWidget {
|
||||
class RegistrationPage extends StatefulWidget {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
@ -42,56 +57,99 @@ class RegistrationPage extends StatelessWidget {
|
||||
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: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (builder) => StartPage(),
|
||||
// builder: (builder) => SecurityPage(),
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const SizedBox(
|
||||
height: 60,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Mitteilungen erhalten',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidEmail) {
|
||||
return 'Bitte eine valide E-Mail Adresse angeben';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
height: 20,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
'Später',
|
||||
style: TextStyle(color: CustomColors.primary),
|
||||
),
|
||||
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: 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 {
|
||||
static Color get error {
|
||||
return const Color.fromARGB(200, 255, 90, 90);
|
||||
return const Color.fromARGB(255, 255, 82, 82);
|
||||
}
|
||||
|
||||
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 =
|
||||
RegExp(r"^\s*([A-Za-z]{1,}([\.,] |[-']| ))+[A-Za-z]+\.?\s*$");
|
||||
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 {
|
||||
bool get isValidEmail {
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
|
||||
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
|
||||
flutter_secure_storage_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
@ -89,6 +89,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
fixnum:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -123,11 +131,64 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -272,6 +333,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -405,6 +522,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dart: ">=3.1.4 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
@ -47,6 +47,7 @@ dependencies:
|
||||
fixnum: ^1.1.0
|
||||
provider: ^6.0.5
|
||||
intl: ^0.18.1
|
||||
flutter_secure_storage: ^9.0.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
|
@ -6,9 +6,12 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
#include <local_auth_windows/local_auth_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
LocalAuthPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_secure_storage_windows
|
||||
local_auth_windows
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user