sign in with pocketbase working

This commit is contained in:
davmar
2026-09-09 19:59:38 +02:00
parent ae464ff304
commit c6d1fdb19e
27 changed files with 1267 additions and 312 deletions
+156
View File
@@ -0,0 +1,156 @@
/// Email/password sign-in against PocketBase.
library;
import 'package:flutter/material.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
import '../utils/constants.dart';
import '../widgets/app_text_field.dart';
import '../widgets/responsive_body.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _busy = false;
bool _obscurePassword = true;
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _submit() async {
if (_busy) return;
if (!(_formKey.currentState?.validate() ?? false)) return;
setState(() => _busy = true);
final auth = context.read<AuthController>();
try {
await auth.login(
email: _emailController.text,
password: _passwordController.text,
);
if (!mounted) return;
Navigator.of(context).pop(true);
} on ClientException catch (error) {
if (!mounted) return;
final message = error.response['message'] as String? ??
'Sign-in failed. Check email and password.';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
} catch (error) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Could not reach $kPocketBaseUrl')),
);
} finally {
if (mounted) setState(() => _busy = false);
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: const Text('Sign in')),
body: ResponsiveBody(
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 24, 16, 32),
children: [
Text(
'Sync account',
style: theme.textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
'Sign in to link this device with PocketBase. Your labels, '
'notes, and bank cache stay on this phone until sync is added.',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 24),
Form(
key: _formKey,
child: Column(
children: [
AppTextField(
controller: _emailController,
label: 'Email',
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
autofocus: true,
validator: (value) {
final text = value?.trim() ?? '';
if (text.isEmpty) return 'Enter your email';
if (!text.contains('@')) return 'Enter a valid email';
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
textInputAction: TextInputAction.done,
onFieldSubmitted: (_) => _submit(),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Enter your password';
}
return null;
},
decoration: InputDecoration(
labelText: 'Password',
suffixIcon: IconButton(
tooltip: _obscurePassword
? 'Show password'
: 'Hide password',
onPressed: () => setState(
() => _obscurePassword = !_obscurePassword,
),
icon: Icon(
_obscurePassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
),
),
),
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: _busy ? null : _submit,
icon: _busy
? SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: theme.colorScheme.onPrimary,
),
)
: const Icon(Icons.login),
label: Text(_busy ? 'Signing in…' : 'Sign in'),
),
],
),
),
],
),
),
);
}
}