19 KiB
Auth-Screens: Layout-Skizzen und Texte (Spec #1, Task U1)
Grundlage: docs/specs/1-auth-registrierung-anmeldung.md (§ 4.4 Meldungen, § 5.4 Screens, § 6 Plattform).
Komponenten: app/lib/core/widgets/ (siehe docs/design-system.md). Alle Texte unten sind
Vorschläge und gehen über l10n (ARB); die ARB-Dateien pflegt der flutter-dev (A1/A2).
Schlüssel sind lowerCamelCase mit Präfix auth.
1. Gemeinsames Layout
- Alle sechs Screens nutzen
AppFormScaffold. Kompakt (< 600 dp): Vollbild, scrollbar, Seitenrand 16 dp. Ab 600 dp: zentrierte Karte, max. 480 dp breit (AppBreakpoints.authCardMaxWidth), Innenabstand 24 dp. - Inhalt in einer Spalte, Abstand zwischen Feldern 16 dp, zwischen Gruppen 24 dp.
- Reihenfolge = Lesereihenfolge = Tab-Reihenfolge: Titel, Banner (falls vorhanden), Felder, Checkboxen, Primärknopf, Sekundärlinks.
- Titel:
headlineSmall, darunter optional ein Einleitungssatz inbodyMedium. Titel als Semantics-Überschrift (Semantics(header: true)). - Banner (
AppBanner) stehen über den Feldern, direkt unter dem Titel. Fehler am Feld stehen unter dem Feld. - Genau ein Primärknopf (
AppPrimaryButton, volle Breite). Sekundäraktionen sindTextButtons (48 dp). - Eingaben bleiben bei jedem Fehler erhalten. Während
submittingsind Knopf und Felder gesperrt, der Knopf zeigt den Spinner und den gesprochenen TextauthSubmitting. - Nach einem Fehler setzt der Screen den Fokus auf das erste fehlerhafte Feld; bei Banner-Fehlern bleibt der Fokus am Knopf, das Banner wird als Live-Region angesagt.
- Enter im letzten Feld sendet ab (
TextInputAction.done), sonstnext.
Legende der Skizzen: [ ] Feld, [x] Checkbox, ( ) Primärknopf, <…> Link/TextButton, ! Banner.
2. Skizzen
RegisterScreen (/register)
Konto erstellen (Titel)
! Fehler-Banner (nur bei registrationRejected / network / rateLimited / unexpected)
E-Mail
[ name@example.com ] Fehler: authEmailInvalid
Passwort
[ •••••••••• (Auge) ] Hinweis: authPasswordRule / Fehler je Regel
Anzeigename
[ Alex 4/30 ] Fehler: authDisplayNameEmpty | TooLong | Invalid
[x] Ich bin mindestens 16 Jahre alt Fehler: authAgeNotConfirmed
[x] Ich habe die <Datenschutzerklärung> gelesen und stimme den <Nutzungsbedingungen> zu
Fehler: authTermsNotAccepted
( Konto erstellen )
<Schon ein Konto? Anmelden>
CheckEmailScreen (/register/check-email, Modus Bestätigung bzw. Reset)
Prüfe dein Postfach (Titel)
Wir haben dir eine E-Mail geschickt, falls ... (Text, bodyLarge)
Der Link ist 1 Stunde gültig. Die E-Mail kann einige Minuten dauern. Schau auch im Spam-Ordner nach.
! Info-Banner "E-Mail wurde erneut gesendet" (nach Resend, auch bei emailSendLimited)
! Fehler-Banner bei rateLimited / network
(nur wenn E-Mail fehlt, z. B. nach Reload:) E-Mail [ ]
( E-Mail erneut senden ) -> nur Modus Bestätigung, gesperrt solange Anfrage läuft
<Zurück zur Anmeldung> (Modus Reset: statt des Resend-Knopfs <Andere E-Mail-Adresse eingeben> -> /forgot-password)
Der Modus steckt im Pfad, nicht im extra: /register/check-email (Bestätigung, E-Mail als extra) und
/forgot-password/check-email (Reset, ohne extra). So überlebt der Modus einen Reload im Web. Im Modus Reset gibt es
keinen Resend (Spec § 5.4), Text authCheckEmailBodyReset, Gültigkeit und Zusatz „kann einige Minuten dauern“ wie im
Modus Bestätigung; Keys check_email_different_address (statt check_email_resend).
LoginScreen (/login, umgesetzt in 01b)
Anmelden
! Fehler-Banner: authErrorInvalidCredentials | authErrorRateLimited | authErrorNetwork (+ "Erneut versuchen")
! Info-Banner authErrorEmailNotConfirmed + Aktion "Bestätigungs-E-Mail erneut senden"
! Info-Banner "Wir haben die E-Mail erneut gesendet ..." | Fehler-Banner (authErrorRateLimited / authErrorNetwork) nach dem Resend
E-Mail [ ] Fehler (nur lokal): authErrorEmailInvalid
Passwort [ ••••• (Auge) ] Fehler (nur lokal): authPasswordRequired
( Anmelden )
<Passwort vergessen?> -> /forgot-password
<Konto erstellen> -> /register
Umsetzungsentscheidungen:
- Falsche Zugangsdaten stehen nie am Feld, sondern nur im Banner; Text für falsche E-Mail und falsches Passwort ist identisch (Krit. 7, Test vergleicht alle sichtbaren Texte). Felder zeigen nur lokale Fehler (leer / E-Mail-Syntax).
- Bei
emailNotConfirmedmerkt sich der Controller die E-Mail des Versuchs; "erneut senden" nutzt diese Adresse (ResendController,ResendOrigin.login), nicht den aktuellen Feldinhalt. Dort wirdemailSendLimitedals Drosselungshinweis gezeigt (F12). Während des Sendens ist die Aktion deaktiviert, aber sichtbar (AppBanner.actionEnabled: false): kein Doppelversand, und der Fokus eines Screenreaders verschwindet nicht mit dem Knopf (Semantics: Knopf, nicht aktiviert). - Ein neuer Anmeldeversuch setzt das Resend-Ergebnis zurück.
fromwird an „Konto erstellen“ und „Passwort vergessen?“ weitergegeben (authLocation, geprüft mitsafeFromPath); Register- und ForgotPassword-Screen geben es mit „Anmelden“ bzw. „Zurück zur Anmeldung“ an/loginzurück.- Erfolg:
context.go(<from oder />).fromwird mitsafeFromPathgeprüft (nur interne Pfade, keine Auth-Seiten, kein//,/\oder Schema); sonst Startseite. Zusätzlich leitet der Router bei StatussignedInvon/loginum. - Autofill:
AutofillGroup, E-Mailusername+email, Passwortpassword;TextInput.finishAutofillContext()bei Erfolg. - Neue l10n-Schlüssel:
authLoginTitle,authLoginSubmit,authLoginForgotPassword,authLoginCreateAccount. - Widget-Keys:
login_screen,login_email,login_password,login_submit,login_banner,login_not_confirmed_banner,login_resent_banner,login_resend_error_banner,login_forgot_link,login_register_link.
ForgotPasswordScreen (/forgot-password)
Passwort zurücksetzen
Gib die E-Mail-Adresse deines Kontos ein. Wir senden dir einen Link zum Zurücksetzen.
E-Mail [ ] Fehler: authEmailInvalid
( Link senden )
<Zurück zur Anmeldung> -> /login (mit from)
Umsetzung: Nach jedem 200 und bei emailSendLimited (F12) geht es ohne Hinweis zu /forgot-password/check-email
(Antwort für bekannte und unbekannte Adresse sowie für die Mail-Sperre ist identisch, getestet per exaktem
Textvergleich). Sichtbare Fehler nur für lokale Syntax, invalidEmail (am Feld), rateLimited/network/unexpected
(Banner, bei den beiden letzten mit „Erneut versuchen“, während des Sendens deaktiviert). Die Adresse wird nicht an
die Folgeseite übergeben. Keys: forgot_password_screen, forgot_password_email, forgot_password_submit,
forgot_password_banner, forgot_password_body, forgot_password_back.
EmailLinkScreen (/auth/confirm)
Zustände: idle (Knopf), submitting, success (nur Bestätigung), error (Link ungültig).
E-Mail-Adresse bestätigen | Neues Passwort festlegen (Titel je Typ)
Ein Klick genügt, um fortzufahren.
( E-Mail-Adresse bestätigen ) | ( Weiter zum neuen Passwort )
Erfolg: ! Info-Banner "E-Mail bestätigt" (+ Mobile-Web: "Du kannst dich jetzt auch in der App anmelden.")
Fehler: ! Fehler-Banner "Der Link ist abgelaufen ..." ( Neuen Link anfordern )
Typ Recovery (01c): Titel, Text und Knopf aus authRecovery*; das Einlösen passiert erst per Klick. Bei Erfolg zeigt die
Seite nichts weiter an, der Statuswechsel passwordRecovery leitet nach /reset-password. Ungültiger oder abgelaufener
Link (otp_expired) → Fehlerzustand mit „Neuen Link anfordern“ → /forgot-password (Bestätigung: /login).
ResetPasswordScreen (/reset-password)
Neues Passwort festlegen
Neues Passwort
[ ••••••••• (Auge) ] Live-Regeln: authPasswordRule, Fehler je Regel / samePassword
( Passwort speichern )
<Abbrechen> -> signOut -> /login
Erfolg (Ansicht ersetzt das Formular, kein Abbrechen mehr):
Passwort geändert. (Hinweis authPasswordChanged)
( Weiter ) -> /
Umsetzungsentscheidungen (01c):
- Der Redirect nach
userUpdatedwäre ohne Eingriff ein Fehler: GoTrue sendetuserUpdated, bevorupdateUserzurückkehrt; die Regel „angemeldet auf/reset-password→/“ hätte die Seite entfernt, bevor die Erfolgsmeldung (Krit. 9) sichtbar ist. Lösung:userUpdatedwährend der Recovery beendet den Guard nicht, sondern setztAuthStatus.passwordRecovery(passwordChanged: true). Erst „Weiter“ (AuthRepository.finishPasswordRecovery()) beendet den Guard; der Router leitet dann nach/. Die Erfolgsansicht leitet sich aus dem Status ab (passwordRecoveryCompleted), überlebt also einen Neuaufbau der Seite. Bewusst kein zeitgesteuerter Sprung (Screenreader, Lesezeit). Spec § 5.5 ist entsprechend ergänzt. - „Abbrechen“ gibt es nur im Formular, nicht in der Erfolgsansicht (es würde eine gültige Sitzung abmelden).
- Passwortregel wie bei der Registrierung (10 Zeichen, Liste häufiger Passwörter; die Liste wird vor dem Absenden
geladen, Fallback nur Länge).
weak_passwordundsame_passwordstehen am Feld, alles andere im Banner. - Hinweistext ehrlich zu F1: andere Geräte können bis zu etwa einer Stunde angemeldet bleiben; nichts von „sofort“.
- Keys:
reset_password_screen,reset_password_password,reset_password_submit,reset_password_cancel,reset_password_banner,reset_password_success_banner,reset_password_continue.
3. Textvorschläge
Ton: du-Form, kurz, sachlich-freundlich, keine Schuldzuweisung. Platzhalter in ICU-Syntax ({n}).
Neutrale Texte (Krit. 4, F12) verraten nie, ob eine Adresse registriert ist.
3.1 Meldungen aus § 4.4 (Fehlerabbildung)
| Schlüssel | de | en |
|---|---|---|
authErrorInvalidCredentials |
E-Mail-Adresse oder Passwort ist nicht korrekt. | Email address or password is incorrect. |
authErrorEmailNotConfirmed |
Bestätige zuerst deine E-Mail-Adresse. Wir haben dir dazu eine E-Mail geschickt. | Please confirm your email address first. We sent you an email for this. |
authActionResendConfirmation |
Bestätigungs-E-Mail erneut senden | Resend confirmation email |
authErrorRateLimited |
Zu viele Versuche. Bitte warte einige Minuten und versuche es erneut. | Too many attempts. Please wait a few minutes and try again. |
authErrorWeakPasswordLength |
Das Passwort ist zu kurz. | The password is too short. |
authErrorWeakPasswordCharacters |
Das Passwort ist zu einfach. Wähle ein anderes. | The password is too simple. Choose a different one. |
authErrorWeakPasswordPwned |
Dieses Passwort ist sehr verbreitet und leicht zu erraten. Wähle ein anderes. | This password is very common and easy to guess. Choose a different one. |
authErrorSamePassword |
Wähle ein anderes als dein bisheriges Passwort. | Choose a password different from your current one. |
authErrorLinkInvalidOrExpired |
Der Link ist abgelaufen oder wurde schon benutzt. | The link has expired or has already been used. |
authActionRequestNewLink |
Neuen Link anfordern | Request a new link |
authErrorEmailInvalid |
Gib eine gültige E-Mail-Adresse ein. | Enter a valid email address. |
authErrorRegistrationRejected |
Registrierung nicht möglich. Bitte prüfe deine Angaben. | Registration is not possible. Please check your details. |
authErrorNetwork |
Keine Verbindung. Für diesen Schritt brauchst du Internet. | No connection. You need internet access for this step. |
authErrorUnexpected |
Etwas ist schiefgelaufen. Bitte versuche es erneut. | Something went wrong. Please try again. |
authActionRetry |
Erneut versuchen | Try again |
emailSendLimited hat bewusst keinen eigenen Text: Register/Forgot/CheckEmail zeigen den neutralen Erfolgstext
(§ 3.3), nur der Resend im LoginScreen zeigt authErrorRateLimited.
3.2 Meldungen aus § 5.4 (Felder, Banner, Hinweise)
| Schlüssel | de | en |
|---|---|---|
authPasswordTooShort |
Mindestens 10 Zeichen – {missing, plural, =1{es fehlt noch 1 Zeichen} other{es fehlen noch {missing} Zeichen}}. | At least 10 characters – {missing, plural, =1{1 more character needed} other{{missing} more characters needed}}. |
authPasswordCommon |
Dieses Passwort ist sehr verbreitet und leicht zu erraten. Wähle ein anderes. | This password is very common and easy to guess. Choose a different one. |
authPasswordRule |
Mindestens 10 Zeichen. Kein sehr verbreitetes Passwort. | At least 10 characters. Not a very common password. |
authPasswordRequired |
Gib ein Passwort ein. | Enter a password. |
authDisplayNameEmpty |
Gib einen Anzeigenamen ein. | Enter a display name. |
authDisplayNameTooLong |
Der Anzeigename darf höchstens {max} Zeichen lang sein. | The display name can be at most {max} characters long. |
authDisplayNameInvalid |
Dieser Anzeigename ist nicht erlaubt. Verwende sichtbare Zeichen. | This display name is not allowed. Use visible characters. |
authAgeNotConfirmed |
Shuffle ist erst ab 16 Jahren nutzbar. | Shuffle can only be used from age 16. |
authTermsNotAccepted |
Bitte stimme Datenschutzerklärung und Nutzungsbedingungen zu. | Please agree to the privacy policy and the terms of use. |
authFieldEmailLabel |
E-Mail-Adresse | Email address |
authFieldPasswordLabel |
Passwort | Password |
authFieldNewPasswordLabel |
Neues Passwort | New password |
authFieldDisplayNameLabel |
Anzeigename | Display name |
authPasswordShow |
Passwort anzeigen | Show password |
authPasswordHide |
Passwort verbergen | Hide password |
authCheckboxAgeLabel |
Ich bin mindestens 16 Jahre alt | I am at least 16 years old |
authCheckboxTermsPrefix |
Ich habe die | I have read the |
authCheckboxTermsPrivacyLink |
Datenschutzerklärung | privacy policy |
authCheckboxTermsMiddle |
gelesen und stimme den | and agree to the |
authCheckboxTermsTermsLink |
Nutzungsbedingungen | terms of use |
authCheckboxTermsSuffix |
zu | (leer) |
authBannerTypeError |
Fehler | Error |
authBannerTypeInfo |
Hinweis | Note |
authSubmitting |
Bitte warten … | Please wait … |
Hinweis zur Checkbox „Nutzungsbedingungen“: Satzbau ist je Sprache verschieden. Der Satz wird deshalb aus den
Segmenten Prefix, PrivacyLink, Middle, TermsLink, Suffix zusammengesetzt (AppCheckboxField.rich);
im Englischen bleibt Suffix leer. Alternativ ein ICU-Satz mit Platzhaltern, den der Screen an den Linkstellen
teilt – Entscheidung beim flutter-dev (A2).
3.3 Screen-Texte
| Schlüssel | de | en |
|---|---|---|
authRegisterTitle |
Konto erstellen | Create account |
authRegisterSubmit |
Konto erstellen | Create account |
authRegisterHaveAccount |
Schon ein Konto? Anmelden | Already have an account? Sign in |
authCheckEmailTitle |
Prüfe dein Postfach | Check your inbox |
authCheckEmailBody |
Wir haben dir eine E-Mail geschickt, falls die Adresse noch nicht registriert ist. Folge dem Link darin, um fortzufahren. | We sent you an email if the address is not registered yet. Follow the link in it to continue. |
authCheckEmailBodyReset |
Wir haben dir eine E-Mail mit einem Link zum Zurücksetzen geschickt, falls zu dieser Adresse ein Konto besteht. | We sent you an email with a reset link if an account exists for this address. |
authCheckEmailDelayHint |
Die E-Mail kann einige Minuten dauern. Schau auch im Spam-Ordner nach. | The email can take a few minutes. Also check your spam folder. |
authCheckEmailValidity |
Der Link ist {duration} gültig. | The link is valid for {duration}. |
authCheckEmailValidityOneHour |
1 Stunde | 1 hour |
authCheckEmailResend |
E-Mail erneut senden | Resend email |
authCheckEmailResent |
Wir haben die E-Mail erneut gesendet, falls es zu dieser Adresse etwas zu senden gibt. | We resent the email, if there is something to send for this address. |
authCheckEmailDifferentAddress |
Andere E-Mail-Adresse eingeben | Enter a different email address |
authBackToLogin |
Zurück zur Anmeldung | Back to sign in |
authLoginTitle |
Anmelden | Sign in |
authLoginSubmit |
Anmelden | Sign in |
authLoginForgotPassword |
Passwort vergessen? | Forgot password? |
authLoginCreateAccount |
Konto erstellen | Create account |
authForgotTitle |
Passwort zurücksetzen | Reset password |
authForgotBody |
Gib die E-Mail-Adresse deines Kontos ein. Wir senden dir einen Link zum Zurücksetzen. | Enter the email address of your account. We will send you a reset link. |
authForgotSubmit |
Link senden | Send link |
authConfirmTitle |
E-Mail-Adresse bestätigen | Confirm email address |
authConfirmBody |
Ein Klick genügt, um deine E-Mail-Adresse zu bestätigen. | One click is enough to confirm your email address. |
authConfirmSubmit |
E-Mail-Adresse bestätigen | Confirm email address |
authConfirmSuccess |
E-Mail bestätigt. | Email confirmed. |
authConfirmSuccessMobileWeb |
Du kannst dich jetzt auch in der App anmelden. | You can now also sign in in the app. |
authRecoveryTitle |
Neues Passwort festlegen | Set a new password |
authRecoveryBody |
Ein Klick genügt, um ein neues Passwort festzulegen. | One click is enough to set a new password. |
authRecoverySubmit |
Weiter zum neuen Passwort | Continue to new password |
authResetTitle |
Neues Passwort festlegen | Set a new password |
authResetSubmit |
Passwort speichern | Save password |
authResetCancel |
Abbrechen | Cancel |
authPasswordChanged |
Passwort geändert. Andere Geräte werden abgemeldet; das kann bis zu einer Stunde dauern. | Password changed. Other devices will be signed out; this can take up to an hour. |
4. Barrierefreiheit pro Screen (Checkliste für A2–A4 und T2)
- Jedes Feld hat ein sichtbares Label (kein Platzhalter-Label); Fehler per
errorText(Icon + Text + Live-Region). - Banner und Fehler werden beim Erscheinen vorgelesen (Live-Region); keine reinen Farb-Signale.
- Passwortfelder:
AppPasswordFieldmitisNewPassword: truebei Register/Reset, sonstfalse; E-Mail mitAutofillHints.email(Login:AutofillHints.username/email),TextInputType.emailAddress. - Links in Checkbox-Sätzen (
AppCheckboxField.rich) sind eigene Semantics-Knoten (link: true, Label = Linktext, Tap), keine Custom Actions (Flutter-Web setzt diese nicht in ARIA um). Der Checkbox-Knoten trägt den ganzen Satz. - Meldungen, die sich bei jedem Tastendruck ändern („es fehlen noch n“), sind während des Tippens keine Live-Region
(
liveError: false) und werden beim Verlassen des Feldes angesagt; der sichtbare Text bleibt. - Alle interaktiven Elemente >= 48 dp, Tab-Reihenfolge wie Lesereihenfolge, Fokus sichtbar.
- Test je Screen mit
textScaler2.0 (Android 320 x 640),androidTapTargetGuideline,labeledTapTargetGuideline,textContrastGuideline, Hell und Dunkel (T2).