Fliin
On this page

Screens / auth

Forgot password

Lets people get an email link to choose a new password, then check their inbox, resend the link or go back to sign in, offline included.

16 of 16 variants pass
Forgot password, ready state
Platform
Brightness
Text scale
Direction

Import

Forgot password is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:

fliin login
fliin add forgot-password

Also installs: core, alert, app-bar, auth-form, auth-header, button, icons, page, text-field

Examples

Ready

The email that receives the reset link, filled in from the sign-in form when there is one.

Forgot password, ready state
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';

class ForgotPasswordReadyExample extends StatelessWidget {
  const ForgotPasswordReadyExample({super.key});

  @override
  Widget build(BuildContext context) {
    return ForgotPasswordScreen.standalone(
      service: const SampleForgotPasswordService(latency: Duration(milliseconds: 600)),
      initial: const ForgotPasswordReady(),
      email: '[email protected]',
      onBack: () {},
    );
  }
}

Sending

While the link is sent, the button shows its spinner.

Forgot password, sending state
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';

class ForgotPasswordSendingExample extends StatelessWidget {
  const ForgotPasswordSendingExample({super.key});

  @override
  Widget build(BuildContext context) {
    return ForgotPasswordScreen.standalone(
      service: const SampleForgotPasswordService(latency: Duration(milliseconds: 600)),
      initial: const ForgotPasswordSending(),
      email: '[email protected]',
      onBack: () {},
    );
  }
}

Offline

Without a connection, an alert asks to reconnect and keeps the email.

Forgot password, offline state
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';

class ForgotPasswordOfflineExample extends StatelessWidget {
  const ForgotPasswordOfflineExample({super.key});

  @override
  Widget build(BuildContext context) {
    return ForgotPasswordScreen.standalone(
      service: const SampleForgotPasswordService(latency: Duration(milliseconds: 600)),
      initial: const ForgotPasswordReady(problem: ForgotPasswordProblem.offline),
      email: '[email protected]',
      onBack: () {},
    );
  }
}

Sent

The link is on its way: the screen names the address, without saying whether it has an account.

Forgot password, sent state
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';

class ForgotPasswordSentExample extends StatelessWidget {
  const ForgotPasswordSentExample({super.key});

  @override
  Widget build(BuildContext context) {
    return ForgotPasswordScreen.standalone(
      service: const SampleForgotPasswordService(latency: Duration(milliseconds: 600)),
      initial: const ForgotPasswordSent('[email protected]'),
      email: '[email protected]',
      onBack: () {},
    );
  }
}

Routing

fliin add forgot-password prints the route for the router it finds in your pubspec, a file next to the page that wires its navigation. Each of them:

go_router

// lib/forgot_password_route.dart, next to forgot_password_page.dart.
import 'package:go_router/go_router.dart';

import 'forgot_password_page.dart';

/// Add forgotPasswordRoute to the routes of your GoRouter.
final forgotPasswordRoute = GoRoute(
  path: '/forgot-password',
  builder: (context, state) => ForgotPasswordPage(
    // Alone in the stack (deep link, go): your app's home.
    onBack: () => context.canPop() ? context.pop() : context.go('/'),
  ),
);

navigator

// lib/forgot_password_route.dart, next to forgot_password_page.dart.
import 'package:flutter/material.dart';

import 'forgot_password_page.dart';

/// Opens “Forgot password”: `Navigator.of(context).push(forgotPasswordRoute())`.
Route<void> forgotPasswordRoute() => MaterialPageRoute<void>(
  builder: (context) => ForgotPasswordPage(
    onBack: () => Navigator.of(context).maybePop(),
  ),
);

State

The controller is a ChangeNotifier from widgets.dart, with no package. fliin add forgot-password prints the page that holds it with the state management it finds, its imports written for your package; below they read your_app. Your backend implements the Service interface of the screen's _data.dart file:

none

// lib/forgot_password_page.dart
// Your backend: implement ForgotPasswordService (forgot_password_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';

class ForgotPasswordPage extends StatelessWidget {
  const ForgotPasswordPage({
    super.key,
    required this.onBack,
  });

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context) => ForgotPasswordScreen.standalone(
    service: const SampleForgotPasswordService(),
    onBack: onBack,
  );
}

provider

// lib/forgot_password_page.dart
// Your backend: implement ForgotPasswordService (forgot_password_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

class ForgotPasswordPage extends StatelessWidget {
  const ForgotPasswordPage({
    super.key,
    required this.onBack,
  });

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context) => ChangeNotifierProvider(
    create: (_) => ForgotPasswordController(service: const SampleForgotPasswordService()),
    child: Builder(
      builder: (context) => ForgotPasswordScreen(
        controller: context.read<ForgotPasswordController>(),
        onBack: onBack,
      ),
    ),
  );
}

riverpod

// lib/forgot_password_page.dart
// Your backend: implement ForgotPasswordService (forgot_password_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final forgotPasswordControllerProvider = Provider.autoDispose<ForgotPasswordController>((ref) {
  final controller = ForgotPasswordController(service: const SampleForgotPasswordService());
  ref.onDispose(controller.dispose);
  return controller;
});

class ForgotPasswordPage extends ConsumerWidget {
  const ForgotPasswordPage({
    super.key,
    required this.onBack,
  });

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context, WidgetRef ref) => ForgotPasswordScreen(
    controller: ref.watch(forgotPasswordControllerProvider),
    onBack: onBack,
  );
}

bloc

// lib/forgot_password_page.dart
// Your backend: implement ForgotPasswordService (forgot_password_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_controller.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_data.dart';
import 'package:your_app/ui/fliin/screens/forgot_password/forgot_password_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

/// The screen's controller as a bloc, for your BlocListener and BlocBuilder:
/// one emission per controller notification. `version` tells them apart
/// when `state` does not change (mode, filters, text…, read them on
/// `controller`). The screen listens to the controller itself.
class ForgotPasswordCubit extends Cubit<({ForgotPasswordState state, int version})> {
  ForgotPasswordCubit(this.controller) : super((state: controller.state, version: 0)) {
    controller.addListener(_changed);
  }

  final ForgotPasswordController controller;

  void _changed() => emit((state: controller.state, version: state.version + 1));

  @override
  Future<void> close() {
    controller
      ..removeListener(_changed)
      ..dispose();
    return super.close();
  }
}

class ForgotPasswordPage extends StatelessWidget {
  const ForgotPasswordPage({
    super.key,
    required this.onBack,
  });

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => ForgotPasswordCubit(ForgotPasswordController(service: const SampleForgotPasswordService())),
    child: Builder(
      builder: (context) => ForgotPasswordScreen(
        controller: context.read<ForgotPasswordCubit>().controller,
        onBack: onBack,
      ),
    ),
  );
}

API

ForgotPasswordScreen: class extends StatefulWidget
ForgotPasswordScreen.new: const new({super.key, required ForgotPasswordController controller, String? email, Widget? mark, String? title, String? subtitle, VoidCallback? onBack})
ForgotPasswordScreen.standalone: const new({super.key, required ForgotPasswordService service, ForgotPasswordState? initial, String? email, Widget? mark, String? title, String? subtitle, VoidCallback? onBack})
ForgotPasswordScreen.controller: get ForgotPasswordController?
ForgotPasswordScreen.service: get ForgotPasswordService?
ForgotPasswordScreen.initial: get ForgotPasswordState?
ForgotPasswordScreen.email: get String?
ForgotPasswordScreen.mark: get Widget?
ForgotPasswordScreen.title: get String?
ForgotPasswordScreen.subtitle: get String?
ForgotPasswordScreen.onBack: get VoidCallback?
ForgotPasswordScreen.createState: State<ForgotPasswordScreen>()
ForgotPasswordProblem: enum {offline, failed}
ForgotPasswordProblem.offline: static final ForgotPasswordProblem
ForgotPasswordProblem.failed: static final ForgotPasswordProblem
ForgotPasswordState: sealed class permits {ForgotPasswordReady, ForgotPasswordSending, ForgotPasswordSent}
ForgotPasswordState.new: const new()
ForgotPasswordReady: final class extends ForgotPasswordState
ForgotPasswordReady.new: const new({ForgotPasswordProblem? problem})
ForgotPasswordReady.problem: get ForgotPasswordProblem?
ForgotPasswordSending: final class extends ForgotPasswordState
ForgotPasswordSending.new: const new()
ForgotPasswordSent: final class extends ForgotPasswordState
ForgotPasswordSent.new: const new(String email, {bool resending, bool resent, ForgotPasswordProblem? problem})
ForgotPasswordSent.email: get String
ForgotPasswordSent.resending: get bool
ForgotPasswordSent.resent: get bool
ForgotPasswordSent.problem: get ForgotPasswordProblem?
ForgotPasswordController: class extends ChangeNotifier
ForgotPasswordController.new: new({required ForgotPasswordService service, ForgotPasswordState initial})
ForgotPasswordController.service: get ForgotPasswordService
ForgotPasswordController.state: get ForgotPasswordState
ForgotPasswordController.sendLink: Future<bool>(String email)
ForgotPasswordController.resend: Future<bool>()
ForgotPasswordController.changeEmail: void()
ForgotPasswordController.dispose: void()
ForgotPasswordEmailIssue: enum {missing, malformed}
ForgotPasswordEmailIssue.missing: static final ForgotPasswordEmailIssue
ForgotPasswordEmailIssue.malformed: static final ForgotPasswordEmailIssue
forgotPasswordEmailIssue: ForgotPasswordEmailIssue?(String email)
ForgotPasswordNoNetwork: final class implements Exception
ForgotPasswordNoNetwork.new: const new()
ForgotPasswordService: abstract interface class
ForgotPasswordService.new: new()
ForgotPasswordService.sendLink: abstract Future<void>(String email)
SampleForgotPasswordService: final class implements ForgotPasswordService
SampleForgotPasswordService.new: const new({Duration latency})
SampleForgotPasswordService.latency: get Duration
SampleForgotPasswordService.sendLink: Future<void>(String email)
forgotPasswordL10n: const Map<String, Map<String, String>>
ForgotPasswordStrings: final class
ForgotPasswordStrings.new: const new(String language)
ForgotPasswordStrings.of: factory new(BuildContext context)
ForgotPasswordStrings.language: get String
ForgotPasswordStrings.title: get String
ForgotPasswordStrings.subtitle: get String
ForgotPasswordStrings.email: get String
ForgotPasswordStrings.emailPlaceholder: get String
ForgotPasswordStrings.submit: get String
ForgotPasswordStrings.remember: get String
ForgotPasswordStrings.back: get String
ForgotPasswordStrings.previous: get String
ForgotPasswordStrings.emailMissing: get String
ForgotPasswordStrings.emailMalformed: get String
ForgotPasswordStrings.sentTitle: get String
ForgotPasswordStrings.sentMessage: String(String email)
ForgotPasswordStrings.sentAnnouncement: String(String email)
ForgotPasswordStrings.resend: get String
ForgotPasswordStrings.otherEmail: get String
ForgotPasswordStrings.resentTitle: get String
ForgotPasswordStrings.resentMessage: get String
ForgotPasswordStrings.offlineTitle: get String
ForgotPasswordStrings.offlineMessage: get String
ForgotPasswordStrings.failedTitle: get String
ForgotPasswordStrings.failedMessage: get String

Strings

Every label in English, French and Arabic. The screen follows the closest Localizations; the same table is in the manifest, ready for your ARB files.

Keyenfrar
forgotPasswordTitleReset your passwordRéinitialiser le mot de passeإعادة تعيين كلمة المرور
forgotPasswordSubtitleEnter your account email. We'll send you a link to choose a new password.Saisissez l’adresse de votre compte. Nous vous enverrons un lien pour choisir un nouveau mot de passe.أدخل البريد الإلكتروني لحسابك وسنرسل إليك رابطًا لاختيار كلمة مرور جديدة.
forgotPasswordEmailEmailAdresse e-mailالبريد الإلكتروني
forgotPasswordEmailPlaceholder[email protected][email protected][email protected]
forgotPasswordSubmitSend linkEnvoyer le lienإرسال الرابط
forgotPasswordRememberRemember your password?Mot de passe retrouvé ?تذكرت كلمة المرور؟
forgotPasswordBackBack to sign inRetour à la connexionالعودة إلى تسجيل الدخول
forgotPasswordPreviousBackRetourرجوع
forgotPasswordEmailMissingEnter your email.Saisissez votre adresse e-mail.أدخل بريدك الإلكتروني.
forgotPasswordEmailMalformedEnter an email such as [email protected].Saisissez une adresse comme [email protected].أدخل بريدًا مثل [email protected].
forgotPasswordSentTitleCheck your inboxVérifiez votre boîte de réceptionتحقق من بريدك الوارد
forgotPasswordSentAnnouncement{title}. {message}{title}. {message}{title}. {message}
forgotPasswordSentMessageIf {email} has an account, a link to reset your password is on its way.Si {email} a un compte, un lien pour réinitialiser votre mot de passe est en route.إذا كان للعنوان {email} حساب، فرابط إعادة تعيين كلمة المرور في طريقه إليك.
forgotPasswordResendResend linkRenvoyer le lienإعادة إرسال الرابط
forgotPasswordOtherEmailUse another emailUtiliser une autre adresseاستخدام بريد آخر
forgotPasswordResentTitleLink sent againLien renvoyéأُعيد إرسال الرابط
forgotPasswordResentMessageIf it does not arrive, check your spam folder.S’il n’arrive pas, vérifiez vos courriers indésirables.إن لم يصل، فتحقق من مجلد الرسائل غير المرغوب فيها.
forgotPasswordOfflineTitleYou are offlineVous êtes hors ligneأنت غير متصل
forgotPasswordOfflineMessageCheck your connection, then try again.Vérifiez votre connexion, puis réessayez.تحقق من اتصالك ثم أعد المحاولة.
forgotPasswordFailedTitleThe link could not be sentImpossible d’envoyer le lienتعذّر إرسال الرابط
forgotPasswordFailedMessageSomething went wrong on our side. Try again in a moment.Un problème est survenu de notre côté. Réessayez dans un instant.حدثت مشكلة من جهتنا. أعد المحاولة بعد قليل.

Reference renders

The 16 images published with version 0.2.5, rendered by the shared harness on macOS with Flutter 3.47.7. Each shows every state side by side, at the size of an iPhone (390 × 844) or of an Android phone (412 × 915). fliin verify forgot-password compares your copy to them.

iOS · Light
Forgot password, iOS, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Forgot password, iOS, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Forgot password, iOS, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Forgot password, iOS, Light, text 2.0×, Right to left
Text 2.0× · Right to left
iOS · Dark
Forgot password, iOS, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Forgot password, iOS, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Forgot password, iOS, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Forgot password, iOS, Dark, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Light
Forgot password, Android, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Forgot password, Android, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Forgot password, Android, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Forgot password, Android, Light, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Dark
Forgot password, Android, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Forgot password, Android, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Forgot password, Android, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Forgot password, Android, Dark, text 2.0×, Right to left
Text 2.0× · Right to left

Use it to

  • send a link to reset a forgotten password
  • ask for the email of the account whose password was lost
  • confirm a reset link was sent without telling whether the account exists
  • send the reset link again from the confirmation
  • offer a way back to sign in after a password reset
Not for
  • signing in with a password: use the sign-in screen (sign-in)
  • confirming an address with a code: use the verification code screen (verify-code)
States
  • Ready
  • Sending
  • Offline
  • Sent

Arrows to move, Enter to open, Escape to close.