ein paar feature aber datenbank macht probleme wenn man aufträge speichern möchge
This commit is contained in:
2026-04-05 12:47:57 +02:00
parent e1d4bb7edf
commit 9ddce354c0
32 changed files with 3931 additions and 612 deletions

View File

@@ -1,9 +1,14 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:appwrite/appwrite.dart';
import 'package:flutter/material.dart';
/// Screen 1: Login & Registrierung (E-Mail / Passwort) über Firebase Auth.
import '../../appwrite_config.dart';
import '../../theme/app_theme.dart';
/// Screen 1: Login & Registrierung (E-Mail / Passwort) über Appwrite.
class AuthScreen extends StatefulWidget {
const AuthScreen({super.key});
const AuthScreen({super.key, required this.onLoggedIn});
final VoidCallback onLoggedIn;
@override
State<AuthScreen> createState() => _AuthScreenState();
@@ -12,6 +17,7 @@ class AuthScreen extends StatefulWidget {
class _AuthScreenState extends State<AuthScreen>
with SingleTickerProviderStateMixin {
late final TabController _tabController;
final _account = Account(appwriteClient);
final _loginEmail = TextEditingController();
final _loginPassword = TextEditingController();
@@ -44,23 +50,27 @@ class _AuthScreenState extends State<AuthScreen>
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(text)));
}
String _authMessage(FirebaseAuthException e) {
switch (e.code) {
case 'user-not-found':
case 'wrong-password':
case 'invalid-credential':
return 'E-Mail oder Passwort ist falsch.';
case 'email-already-in-use':
return 'Diese E-Mail ist bereits registriert.';
case 'weak-password':
return 'Passwort ist zu schwach (mindestens 6 Zeichen).';
case 'invalid-email':
return 'Ungültige E-Mail-Adresse.';
case 'network-request-failed':
return 'Netzwerkfehler. Internet prüfen.';
default:
return e.message ?? 'Fehler: ${e.code}';
String _authMessage(AppwriteException e) {
final t = e.type ?? '';
final m = (e.message ?? '').toLowerCase();
if (t == 'user_invalid_credentials' ||
m.contains('invalid credentials') ||
m.contains('wrong password')) {
return 'E-Mail oder Passwort ist falsch.';
}
if (t == 'user_already_exists' || m.contains('already exists')) {
return 'Diese E-Mail ist bereits registriert.';
}
if (m.contains('password') && m.contains('short')) {
return 'Passwort ist zu schwach (mindestens 8 Zeichen in Appwrite).';
}
if (t == 'general_argument_invalid' && m.contains('email')) {
return 'Ungültige E-Mail-Adresse.';
}
if (e.code == 0 && m.contains('network')) {
return 'Netzwerkfehler. Internet prüfen.';
}
return e.message?.isNotEmpty == true ? e.message! : 'Fehler: $t';
}
Future<void> _login() async {
@@ -72,11 +82,12 @@ class _AuthScreenState extends State<AuthScreen>
}
setState(() => _loading = true);
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
await _account.createEmailPasswordSession(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
if (mounted) widget.onLoggedIn();
} on AppwriteException catch (e) {
_snack(_authMessage(e));
} catch (e) {
_snack('Unerwarteter Fehler: $e');
@@ -93,20 +104,29 @@ class _AuthScreenState extends State<AuthScreen>
_snack('E-Mail und Passwort eingeben.');
return;
}
if (password.length < 6) {
_snack('Passwort mindestens 6 Zeichen (Firebase).');
if (password.length < 8) {
_snack('Passwort mindestens 8 Zeichen (Appwrite-Standard).');
return;
}
setState(() => _loading = true);
try {
final cred = await FirebaseAuth.instance.createUserWithEmailAndPassword(
await _account.create(
userId: ID.unique(),
email: email,
password: password,
name: name.isEmpty ? null : name,
);
await _account.createEmailPasswordSession(
email: email,
password: password,
);
if (name.isNotEmpty && cred.user != null) {
await cred.user!.updateDisplayName(name);
if (name.isNotEmpty) {
try {
await _account.updateName(name: name);
} catch (_) {}
}
} on FirebaseAuthException catch (e) {
if (mounted) widget.onLoggedIn();
} on AppwriteException catch (e) {
_snack(_authMessage(e));
} catch (e) {
_snack('Unerwarteter Fehler: $e');
@@ -120,27 +140,29 @@ class _AuthScreenState extends State<AuthScreen>
final scheme = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: AppTheme.background,
body: SafeArea(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
children: [
const SizedBox(height: 24),
Icon(Icons.construction, size: 48, color: scheme.primary),
Icon(Icons.handyman_rounded, size: 48, color: scheme.primary),
const SizedBox(height: 16),
Text(
'Handwerksapp',
'HandwerkPro',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
'Nach dem Login: Aufträge anlegen, Fotos, Kunden-Unterschrift, PDF teilen.\n'
'Zuerst: Konto anlegen oder anmelden.',
'Aufträge, Fotos, Unterschrift, PDF alles in einer App. '
'Anmelden oder registrieren (Appwrite).',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: scheme.onSurfaceVariant,
color: const Color(0xFFB0B0B0),
),
),
const SizedBox(height: 32),
@@ -269,7 +291,9 @@ class _RegisterForm extends StatelessWidget {
controller: password,
obscureText: true,
autofillHints: const [AutofillHints.newPassword],
decoration: const InputDecoration(labelText: 'Passwort (min. 6 Zeichen)'),
decoration: const InputDecoration(
labelText: 'Passwort (min. 8 Zeichen)',
),
),
const Spacer(),
FilledButton(