ForgotPasswordScreen with neutral reaction (F12), CheckEmail reset mode on its own path, recovery link handling and ResetPasswordScreen with the recovery guard held until the user confirms the success message. Carries the from parameter between login, register and forgot password. Removes the route placeholders. Refs #1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
602 lines
18 KiB
Dart
602 lines
18 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/widgets.dart';
|
||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||
import 'package:intl/intl.dart' as intl;
|
||
|
||
import 'app_localizations_de.dart';
|
||
import 'app_localizations_en.dart';
|
||
|
||
// ignore_for_file: type=lint
|
||
|
||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
||
/// returned by `AppLocalizations.of(context)`.
|
||
///
|
||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
||
/// `localizationDelegates` list, and the locales they support in the app's
|
||
/// `supportedLocales` list. For example:
|
||
///
|
||
/// ```dart
|
||
/// import 'l10n/app_localizations.dart';
|
||
///
|
||
/// return MaterialApp(
|
||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||
/// supportedLocales: AppLocalizations.supportedLocales,
|
||
/// home: MyApplicationHome(),
|
||
/// );
|
||
/// ```
|
||
///
|
||
/// ## Update pubspec.yaml
|
||
///
|
||
/// Please make sure to update your pubspec.yaml to include the following
|
||
/// packages:
|
||
///
|
||
/// ```yaml
|
||
/// dependencies:
|
||
/// # Internationalization support.
|
||
/// flutter_localizations:
|
||
/// sdk: flutter
|
||
/// intl: any # Use the pinned version from flutter_localizations
|
||
///
|
||
/// # Rest of dependencies
|
||
/// ```
|
||
///
|
||
/// ## iOS Applications
|
||
///
|
||
/// iOS applications define key application metadata, including supported
|
||
/// locales, in an Info.plist file that is built into the application bundle.
|
||
/// To configure the locales supported by your app, you’ll need to edit this
|
||
/// file.
|
||
///
|
||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||
/// project’s Runner folder.
|
||
///
|
||
/// Next, select the Information Property List item, select Add Item from the
|
||
/// Editor menu, then select Localizations from the pop-up menu.
|
||
///
|
||
/// Select and expand the newly-created Localizations item then, for each
|
||
/// locale your application supports, add a new item and select the locale
|
||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
|
||
/// property.
|
||
abstract class AppLocalizations {
|
||
AppLocalizations(String locale)
|
||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||
|
||
final String localeName;
|
||
|
||
static AppLocalizations of(BuildContext context) {
|
||
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
|
||
}
|
||
|
||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||
_AppLocalizationsDelegate();
|
||
|
||
/// A list of this localizations delegate along with the default localizations
|
||
/// delegates.
|
||
///
|
||
/// Returns a list of localizations delegates containing this delegate along with
|
||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
||
/// and GlobalWidgetsLocalizations.delegate.
|
||
///
|
||
/// Additional delegates can be added by appending to this list in
|
||
/// MaterialApp. This list does not have to be used at all if a custom list
|
||
/// of delegates is preferred or required.
|
||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||
<LocalizationsDelegate<dynamic>>[
|
||
delegate,
|
||
GlobalMaterialLocalizations.delegate,
|
||
GlobalCupertinoLocalizations.delegate,
|
||
GlobalWidgetsLocalizations.delegate,
|
||
];
|
||
|
||
/// A list of this localizations delegate's supported locales.
|
||
static const List<Locale> supportedLocales = <Locale>[
|
||
Locale('de'),
|
||
Locale('en'),
|
||
];
|
||
|
||
/// Name of the app
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Shuffle'**
|
||
String get appTitle;
|
||
|
||
/// Placeholder text on the start page until real content exists
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Welcome to Shuffle. The board game catalog is coming soon.'**
|
||
String get homePlaceholderMessage;
|
||
|
||
/// Title of the 404 page
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Page not found'**
|
||
String get notFoundTitle;
|
||
|
||
/// Explanation on the 404 page
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This page does not exist or has been moved.'**
|
||
String get notFoundMessage;
|
||
|
||
/// Button on the 404 page that navigates to the start page
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Back to start'**
|
||
String get notFoundBackHome;
|
||
|
||
/// No description provided for @authErrorInvalidCredentials.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Email address or password is incorrect.'**
|
||
String get authErrorInvalidCredentials;
|
||
|
||
/// No description provided for @authErrorEmailNotConfirmed.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Please confirm your email address first. We sent you an email for this.'**
|
||
String get authErrorEmailNotConfirmed;
|
||
|
||
/// No description provided for @authActionResendConfirmation.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Resend confirmation email'**
|
||
String get authActionResendConfirmation;
|
||
|
||
/// No description provided for @authErrorRateLimited.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Too many attempts. Please wait a few minutes and try again.'**
|
||
String get authErrorRateLimited;
|
||
|
||
/// No description provided for @authErrorWeakPasswordLength.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'The password is too short.'**
|
||
String get authErrorWeakPasswordLength;
|
||
|
||
/// No description provided for @authErrorWeakPasswordCharacters.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'The password is too simple. Choose a different one.'**
|
||
String get authErrorWeakPasswordCharacters;
|
||
|
||
/// No description provided for @authErrorWeakPasswordPwned.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This password is very common and easy to guess. Choose a different one.'**
|
||
String get authErrorWeakPasswordPwned;
|
||
|
||
/// No description provided for @authErrorSamePassword.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Choose a password different from your current one.'**
|
||
String get authErrorSamePassword;
|
||
|
||
/// No description provided for @authErrorLinkInvalidOrExpired.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'The link has expired or has already been used.'**
|
||
String get authErrorLinkInvalidOrExpired;
|
||
|
||
/// No description provided for @authActionRequestNewLink.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Request a new link'**
|
||
String get authActionRequestNewLink;
|
||
|
||
/// No description provided for @authErrorEmailInvalid.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Enter a valid email address.'**
|
||
String get authErrorEmailInvalid;
|
||
|
||
/// No description provided for @authErrorRegistrationRejected.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Registration is not possible. Please check your details.'**
|
||
String get authErrorRegistrationRejected;
|
||
|
||
/// No description provided for @authErrorNetwork.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'No connection. You need internet access for this step.'**
|
||
String get authErrorNetwork;
|
||
|
||
/// No description provided for @authErrorUnexpected.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Something went wrong. Please try again.'**
|
||
String get authErrorUnexpected;
|
||
|
||
/// No description provided for @authActionRetry.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Try again'**
|
||
String get authActionRetry;
|
||
|
||
/// No description provided for @authPasswordTooShort.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'At least 10 characters – {missing, plural, =1{1 more character needed} other{{missing} more characters needed}}.'**
|
||
String authPasswordTooShort(int missing);
|
||
|
||
/// No description provided for @authPasswordCommon.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This password is very common and easy to guess. Choose a different one.'**
|
||
String get authPasswordCommon;
|
||
|
||
/// No description provided for @authPasswordRule.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'At least 10 characters. Not a very common password.'**
|
||
String get authPasswordRule;
|
||
|
||
/// No description provided for @authPasswordRequired.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Enter a password.'**
|
||
String get authPasswordRequired;
|
||
|
||
/// No description provided for @authDisplayNameEmpty.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Enter a display name.'**
|
||
String get authDisplayNameEmpty;
|
||
|
||
/// No description provided for @authDisplayNameTooLong.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'The display name can be at most {max} characters long.'**
|
||
String authDisplayNameTooLong(int max);
|
||
|
||
/// No description provided for @authDisplayNameInvalid.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This display name is not allowed. Use visible characters.'**
|
||
String get authDisplayNameInvalid;
|
||
|
||
/// No description provided for @authDisplayNameCounter.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'{count}/{max}'**
|
||
String authDisplayNameCounter(int count, int max);
|
||
|
||
/// No description provided for @authAgeNotConfirmed.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Shuffle can only be used from age 16.'**
|
||
String get authAgeNotConfirmed;
|
||
|
||
/// No description provided for @authTermsNotAccepted.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Please agree to the privacy policy and the terms of use.'**
|
||
String get authTermsNotAccepted;
|
||
|
||
/// No description provided for @authFieldEmailLabel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Email address'**
|
||
String get authFieldEmailLabel;
|
||
|
||
/// No description provided for @authFieldPasswordLabel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Password'**
|
||
String get authFieldPasswordLabel;
|
||
|
||
/// No description provided for @authFieldDisplayNameLabel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Display name'**
|
||
String get authFieldDisplayNameLabel;
|
||
|
||
/// No description provided for @authPasswordShow.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Show password'**
|
||
String get authPasswordShow;
|
||
|
||
/// No description provided for @authPasswordHide.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Hide password'**
|
||
String get authPasswordHide;
|
||
|
||
/// No description provided for @authCheckboxAgeLabel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'I am at least 16 years old'**
|
||
String get authCheckboxAgeLabel;
|
||
|
||
/// No description provided for @authCheckboxTermsPrefix.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'I have read the'**
|
||
String get authCheckboxTermsPrefix;
|
||
|
||
/// No description provided for @authCheckboxTermsPrivacyLink.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'privacy policy'**
|
||
String get authCheckboxTermsPrivacyLink;
|
||
|
||
/// No description provided for @authCheckboxTermsMiddle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'and agree to the'**
|
||
String get authCheckboxTermsMiddle;
|
||
|
||
/// No description provided for @authCheckboxTermsTermsLink.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'terms of use'**
|
||
String get authCheckboxTermsTermsLink;
|
||
|
||
/// No description provided for @authCheckboxTermsSuffix.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **''**
|
||
String get authCheckboxTermsSuffix;
|
||
|
||
/// No description provided for @authBannerTypeError.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Error'**
|
||
String get authBannerTypeError;
|
||
|
||
/// No description provided for @authBannerTypeInfo.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Note'**
|
||
String get authBannerTypeInfo;
|
||
|
||
/// No description provided for @authSubmitting.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Please wait …'**
|
||
String get authSubmitting;
|
||
|
||
/// No description provided for @authRegisterTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Create account'**
|
||
String get authRegisterTitle;
|
||
|
||
/// No description provided for @authRegisterSubmit.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Create account'**
|
||
String get authRegisterSubmit;
|
||
|
||
/// No description provided for @authRegisterHaveAccount.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Already have an account? Sign in'**
|
||
String get authRegisterHaveAccount;
|
||
|
||
/// No description provided for @authLoginTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Sign in'**
|
||
String get authLoginTitle;
|
||
|
||
/// No description provided for @authLoginSubmit.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Sign in'**
|
||
String get authLoginSubmit;
|
||
|
||
/// No description provided for @authLoginForgotPassword.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Forgot password?'**
|
||
String get authLoginForgotPassword;
|
||
|
||
/// No description provided for @authLoginCreateAccount.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Create account'**
|
||
String get authLoginCreateAccount;
|
||
|
||
/// No description provided for @authCheckEmailTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Check your inbox'**
|
||
String get authCheckEmailTitle;
|
||
|
||
/// No description provided for @authCheckEmailBody.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'We sent you an email if the address is not registered yet. Follow the link in it to continue.'**
|
||
String get authCheckEmailBody;
|
||
|
||
/// No description provided for @authCheckEmailDelayHint.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'The email can take a few minutes. Also check your spam folder.'**
|
||
String get authCheckEmailDelayHint;
|
||
|
||
/// No description provided for @authCheckEmailValidity.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'The link is valid for {duration}.'**
|
||
String authCheckEmailValidity(String duration);
|
||
|
||
/// No description provided for @authCheckEmailValidityOneHour.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'1 hour'**
|
||
String get authCheckEmailValidityOneHour;
|
||
|
||
/// No description provided for @authCheckEmailResend.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Resend email'**
|
||
String get authCheckEmailResend;
|
||
|
||
/// No description provided for @authCheckEmailResent.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'We resent the email, if there is something to send for this address.'**
|
||
String get authCheckEmailResent;
|
||
|
||
/// No description provided for @authBackToLogin.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Back to sign in'**
|
||
String get authBackToLogin;
|
||
|
||
/// No description provided for @authConfirmTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Confirm email address'**
|
||
String get authConfirmTitle;
|
||
|
||
/// No description provided for @authConfirmBody.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'One click is enough to confirm your email address.'**
|
||
String get authConfirmBody;
|
||
|
||
/// No description provided for @authConfirmSubmit.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Confirm email address'**
|
||
String get authConfirmSubmit;
|
||
|
||
/// No description provided for @authConfirmSuccess.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Email confirmed.'**
|
||
String get authConfirmSuccess;
|
||
|
||
/// No description provided for @authConfirmSuccessMobileWeb.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'You can now also sign in in the app.'**
|
||
String get authConfirmSuccessMobileWeb;
|
||
|
||
/// No description provided for @authConfirmContinue.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Continue'**
|
||
String get authConfirmContinue;
|
||
|
||
/// No description provided for @authRecoveryTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Set a new password'**
|
||
String get authRecoveryTitle;
|
||
|
||
/// No description provided for @authRecoveryBody.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'One click is enough to set a new password.'**
|
||
String get authRecoveryBody;
|
||
|
||
/// No description provided for @authRecoverySubmit.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Continue to new password'**
|
||
String get authRecoverySubmit;
|
||
|
||
/// No description provided for @authForgotTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Reset password'**
|
||
String get authForgotTitle;
|
||
|
||
/// No description provided for @authForgotBody.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Enter the email address of your account. We will send you a reset link.'**
|
||
String get authForgotBody;
|
||
|
||
/// No description provided for @authForgotSubmit.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Send link'**
|
||
String get authForgotSubmit;
|
||
|
||
/// No description provided for @authCheckEmailBodyReset.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'We sent you an email with a reset link if an account exists for this address.'**
|
||
String get authCheckEmailBodyReset;
|
||
|
||
/// No description provided for @authCheckEmailDifferentAddress.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Enter a different email address'**
|
||
String get authCheckEmailDifferentAddress;
|
||
|
||
/// No description provided for @authFieldNewPasswordLabel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'New password'**
|
||
String get authFieldNewPasswordLabel;
|
||
|
||
/// No description provided for @authResetTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Set a new password'**
|
||
String get authResetTitle;
|
||
|
||
/// No description provided for @authResetSubmit.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Save password'**
|
||
String get authResetSubmit;
|
||
|
||
/// No description provided for @authResetCancel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Cancel'**
|
||
String get authResetCancel;
|
||
|
||
/// No description provided for @authPasswordChanged.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Password changed. On other devices you will be signed out after about an hour at the latest.'**
|
||
String get authPasswordChanged;
|
||
}
|
||
|
||
class _AppLocalizationsDelegate
|
||
extends LocalizationsDelegate<AppLocalizations> {
|
||
const _AppLocalizationsDelegate();
|
||
|
||
@override
|
||
Future<AppLocalizations> load(Locale locale) {
|
||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
||
}
|
||
|
||
@override
|
||
bool isSupported(Locale locale) =>
|
||
<String>['de', 'en'].contains(locale.languageCode);
|
||
|
||
@override
|
||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||
}
|
||
|
||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
||
// Lookup logic when only language code is specified.
|
||
switch (locale.languageCode) {
|
||
case 'de':
|
||
return AppLocalizationsDe();
|
||
case 'en':
|
||
return AppLocalizationsEn();
|
||
}
|
||
|
||
throw FlutterError(
|
||
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
|
||
'an issue with the localizations generation tool. Please file an issue '
|
||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
||
'that was used.',
|
||
);
|
||
}
|