pass suggestions

This commit is contained in:
davmar
2026-09-17 15:45:11 +02:00
parent bd3bc5357e
commit 22a535a6fd
2 changed files with 83 additions and 59 deletions
+71 -59
View File
@@ -2,6 +2,7 @@
library; library;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -46,6 +47,8 @@ class _LoginScreenState extends State<LoginScreen> {
email: _emailController.text, email: _emailController.text,
password: _passwordController.text, password: _passwordController.text,
); );
// Lets iOS/Android password managers offer to save / confirm the login.
TextInput.finishAutofillContext();
if (!mounted) return; if (!mounted) return;
if (!widget.asRoot) { if (!widget.asRoot) {
Navigator.of(context).pop(true); Navigator.of(context).pop(true);
@@ -96,68 +99,77 @@ class _LoginScreenState extends State<LoginScreen> {
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
Form( AutofillGroup(
key: _formKey, child: Form(
child: Column( key: _formKey,
children: [ child: Column(
AppTextField( children: [
controller: _emailController, AppTextField(
label: 'Email', controller: _emailController,
keyboardType: TextInputType.emailAddress, label: 'Email',
textInputAction: TextInputAction.next, keyboardType: TextInputType.emailAddress,
autofocus: true, textInputAction: TextInputAction.next,
validator: (value) { autofocus: true,
final text = value?.trim() ?? ''; autofillHints: const [
if (text.isEmpty) return 'Enter your email'; AutofillHints.email,
if (!text.contains('@')) return 'Enter a valid email'; AutofillHints.username,
return null; ],
}, validator: (value) {
), final text = value?.trim() ?? '';
const SizedBox(height: 16), if (text.isEmpty) return 'Enter your email';
TextFormField( if (!text.contains('@')) return 'Enter a valid email';
controller: _passwordController, return null;
obscureText: _obscurePassword, },
textInputAction: TextInputAction.done, ),
onFieldSubmitted: (_) => _submit(), const SizedBox(height: 16),
validator: (value) { TextFormField(
if (value == null || value.isEmpty) { controller: _passwordController,
return 'Enter your password'; obscureText: _obscurePassword,
} textInputAction: TextInputAction.done,
return null; autofillHints: const [AutofillHints.password],
}, enableSuggestions: false,
decoration: InputDecoration( autocorrect: false,
labelText: 'Password', onFieldSubmitted: (_) => _submit(),
suffixIcon: IconButton( validator: (value) {
tooltip: _obscurePassword if (value == null || value.isEmpty) {
? 'Show password' return 'Enter your password';
: 'Hide password', }
onPressed: () => setState( return null;
() => _obscurePassword = !_obscurePassword, },
), decoration: InputDecoration(
icon: Icon( labelText: 'Password',
_obscurePassword suffixIcon: IconButton(
? Icons.visibility_outlined tooltip: _obscurePassword
: Icons.visibility_off_outlined, ? 'Show password'
: 'Hide password',
onPressed: () => setState(
() => _obscurePassword = !_obscurePassword,
),
icon: Icon(
_obscurePassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
),
), ),
), ),
), ),
), const SizedBox(height: 24),
const SizedBox(height: 24), FilledButton.icon(
FilledButton.icon( onPressed: _busy ? null : _submit,
onPressed: _busy ? null : _submit, icon: _busy
icon: _busy ? SizedBox(
? SizedBox( width: 18,
width: 18, height: 18,
height: 18, child: CircularProgressIndicator(
child: CircularProgressIndicator( strokeWidth: 2,
strokeWidth: 2, color: theme.colorScheme.onPrimary,
color: theme.colorScheme.onPrimary, ),
), )
) : const Icon(Icons.login),
: const Icon(Icons.login), label: Text(_busy ? 'Signing in…' : 'Sign in'),
label: Text(_busy ? 'Signing in…' : 'Sign in'), ),
), ],
], ),
), ),
), ),
], ],
+12
View File
@@ -14,6 +14,10 @@ class AppTextField extends StatelessWidget {
this.keyboardType, this.keyboardType,
this.validator, this.validator,
this.autofocus = false, this.autofocus = false,
this.autofillHints,
this.obscureText = false,
this.enableSuggestions,
this.autocorrect,
}); });
final TextEditingController controller; final TextEditingController controller;
@@ -24,6 +28,10 @@ class AppTextField extends StatelessWidget {
final TextInputType? keyboardType; final TextInputType? keyboardType;
final String? Function(String?)? validator; final String? Function(String?)? validator;
final bool autofocus; final bool autofocus;
final Iterable<String>? autofillHints;
final bool obscureText;
final bool? enableSuggestions;
final bool? autocorrect;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -31,8 +39,12 @@ class AppTextField extends StatelessWidget {
controller: controller, controller: controller,
maxLines: maxLines, maxLines: maxLines,
autofocus: autofocus, autofocus: autofocus,
obscureText: obscureText,
textInputAction: textInputAction, textInputAction: textInputAction,
keyboardType: keyboardType, keyboardType: keyboardType,
autofillHints: autofillHints,
enableSuggestions: enableSuggestions ?? !obscureText,
autocorrect: autocorrect ?? !obscureText,
validator: validator, validator: validator,
decoration: InputDecoration( decoration: InputDecoration(
labelText: label, labelText: label,