Files
Handwerks_app/screens/auth/auth_screen.dart
JUSN 9ddce354c0 Feature
ein paar feature aber datenbank macht probleme wenn man aufträge speichern möchge
2026-04-05 12:47:57 +02:00

313 lines
9.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:appwrite/appwrite.dart';
import 'package:flutter/material.dart';
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, required this.onLoggedIn});
final VoidCallback onLoggedIn;
@override
State<AuthScreen> createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen>
with SingleTickerProviderStateMixin {
late final TabController _tabController;
final _account = Account(appwriteClient);
final _loginEmail = TextEditingController();
final _loginPassword = TextEditingController();
final _regName = TextEditingController();
final _regEmail = TextEditingController();
final _regPassword = TextEditingController();
bool _loading = false;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
_loginEmail.dispose();
_loginPassword.dispose();
_regName.dispose();
_regEmail.dispose();
_regPassword.dispose();
super.dispose();
}
void _snack(String text) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(text)));
}
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 {
final email = _loginEmail.text.trim();
final password = _loginPassword.text;
if (email.isEmpty || password.isEmpty) {
_snack('E-Mail und Passwort eingeben.');
return;
}
setState(() => _loading = true);
try {
await _account.createEmailPasswordSession(
email: email,
password: password,
);
if (mounted) widget.onLoggedIn();
} on AppwriteException catch (e) {
_snack(_authMessage(e));
} catch (e) {
_snack('Unerwarteter Fehler: $e');
} finally {
if (mounted) setState(() => _loading = false);
}
}
Future<void> _register() async {
final email = _regEmail.text.trim();
final password = _regPassword.text;
final name = _regName.text.trim();
if (email.isEmpty || password.isEmpty) {
_snack('E-Mail und Passwort eingeben.');
return;
}
if (password.length < 8) {
_snack('Passwort mindestens 8 Zeichen (Appwrite-Standard).');
return;
}
setState(() => _loading = true);
try {
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) {
try {
await _account.updateName(name: name);
} catch (_) {}
}
if (mounted) widget.onLoggedIn();
} on AppwriteException catch (e) {
_snack(_authMessage(e));
} catch (e) {
_snack('Unerwarteter Fehler: $e');
} finally {
if (mounted) setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
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.handyman_rounded, size: 48, color: scheme.primary),
const SizedBox(height: 16),
Text(
'HandwerkPro',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
'Aufträge, Fotos, Unterschrift, PDF alles in einer App. '
'Anmelden oder registrieren (Appwrite).',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: const Color(0xFFB0B0B0),
),
),
const SizedBox(height: 32),
TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Anmelden'),
Tab(text: 'Registrieren'),
],
),
const SizedBox(height: 16),
SizedBox(
height: 320,
child: TabBarView(
controller: _tabController,
children: [
_LoginForm(
email: _loginEmail,
password: _loginPassword,
loading: _loading,
onSubmit: _login,
),
_RegisterForm(
name: _regName,
email: _regEmail,
password: _regPassword,
loading: _loading,
onSubmit: _register,
),
],
),
),
],
),
),
);
}
}
class _LoginForm extends StatelessWidget {
const _LoginForm({
required this.email,
required this.password,
required this.loading,
required this.onSubmit,
});
final TextEditingController email;
final TextEditingController password;
final bool loading;
final VoidCallback onSubmit;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: email,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
autofillHints: const [AutofillHints.email],
decoration: const InputDecoration(labelText: 'E-Mail'),
),
const SizedBox(height: 12),
TextField(
controller: password,
obscureText: true,
autofillHints: const [AutofillHints.password],
decoration: const InputDecoration(labelText: 'Passwort'),
onSubmitted: (_) => onSubmit(),
),
const Spacer(),
FilledButton(
onPressed: loading ? null : onSubmit,
child: loading
? const SizedBox(
height: 22,
width: 22,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Anmelden'),
),
],
);
}
}
class _RegisterForm extends StatelessWidget {
const _RegisterForm({
required this.name,
required this.email,
required this.password,
required this.loading,
required this.onSubmit,
});
final TextEditingController name;
final TextEditingController email;
final TextEditingController password;
final bool loading;
final VoidCallback onSubmit;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: name,
textCapitalization: TextCapitalization.words,
decoration: const InputDecoration(
labelText: 'Firmen- oder Anzeigename',
),
),
const SizedBox(height: 12),
TextField(
controller: email,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
autofillHints: const [AutofillHints.email],
decoration: const InputDecoration(labelText: 'E-Mail'),
),
const SizedBox(height: 12),
TextField(
controller: password,
obscureText: true,
autofillHints: const [AutofillHints.newPassword],
decoration: const InputDecoration(
labelText: 'Passwort (min. 8 Zeichen)',
),
),
const Spacer(),
FilledButton(
onPressed: loading ? null : onSubmit,
child: loading
? const SizedBox(
height: 22,
width: 22,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Konto erstellen'),
),
],
);
}
}