Fliin
On this page

Screens / account

Account settings

Groups the profile, security and notifications of an account, and lets the user delete it after a confirmation, with its loading, error and offline states.

16 of 16 variants pass
Account settings, extended state
Platform
Brightness
Text scale
Direction

Import

Account settings 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 account-settings

Also installs: core, app-bar, avatar, confirm-delete, dialog, empty-state, list-row, page, profile-card, settings-section, skeleton, switch, chip, spinner

Examples

Extended

Your own sections among the screen's, the account's photo, and a Sign out row: sections takes any widgets, usually settings sections of list rows, and showDelete: false removes the deletion when accounts are deleted elsewhere.

Account settings, extended state
import 'package:your_app/ui/fliin/components/list_row/fl_list_row.dart';
import 'package:your_app/ui/fliin/components/settings_section/fl_settings_section.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_strings.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    final strings = AccountSettingsStrings.of(context);
    final account = AccountSettingsData(
      name: sampleAccountSettings.name,
      email: sampleAccountSettings.email,
      initials: sampleAccountSettings.initials,
      plan: sampleAccountSettings.plan,
      preferences: sampleAccountSettings.preferences,
      photo: const AssetImage('images/people/1.jpg'),
    );
    return AccountSettingsScreen.standalone(
      service: SampleAccountSettingsService(data: account, latency: const Duration(milliseconds: 600)),
      initial: AccountSettingsLoaded(account),
      onBack: () {},
      onEditProfile: () {},
      onChangePassword: () {},
      onSignOut: () {},
      onDeleted: () {},
      sections: [
        FlSettingsSection(
          title: strings.sampleBilling,
          children: [
            FlListRow(title: strings.sampleSubscription, trailing: const FlListRowChevron(), onPressed: () {}),
            FlListRow(title: strings.sampleInvoices, trailing: const FlListRowChevron(), onPressed: () {}),
          ],
        ),
      ],
    );
  }
}

Loaded

The account loaded: profile, security and notifications. standalone holds its own controller over the service you pass; give it yours to reach your backend.

Account settings, loaded state
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AccountSettingsScreen.standalone(
      service: const SampleAccountSettingsService(
        latency: Duration(milliseconds: 600),
      ),
      initial: const AccountSettingsLoaded(sampleAccountSettings),
      onBack: () {},
      onEditProfile: () {},
      onChangePassword: () {},
      onDeleted: () {},
    );
  }
}

Loading

While the service answers, placeholders keep the layout of each section.

Account settings, loading state
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AccountSettingsScreen.standalone(
      service: const SampleAccountSettingsService(
        latency: Duration(milliseconds: 600),
      ),
      initial: const AccountSettingsLoading(),
      onBack: () {},
      onEditProfile: () {},
      onChangePassword: () {},
      onDeleted: () {},
    );
  }
}

Error

When loading fails, the screen says so and offers to try again, which calls the service once more.

Account settings, error state
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AccountSettingsScreen.standalone(
      service: const SampleAccountSettingsService(
        latency: Duration(milliseconds: 600),
      ),
      initial: const AccountSettingsFailed(),
      onBack: () {},
      onEditProfile: () {},
      onChangePassword: () {},
      onDeleted: () {},
    );
  }
}

Offline

Without a connection, the screen asks to reconnect rather than showing an error.

Account settings, offline state
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AccountSettingsScreen.standalone(
      service: const SampleAccountSettingsService(
        latency: Duration(milliseconds: 600),
      ),
      initial: const AccountSettingsOffline(),
      onBack: () {},
      onEditProfile: () {},
      onChangePassword: () {},
      onDeleted: () {},
    );
  }
}

Routing

fliin add account-settings 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/account_settings_route.dart, next to account_settings_page.dart.
import 'package:go_router/go_router.dart';

import 'account_settings_page.dart';

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

navigator

// lib/account_settings_route.dart, next to account_settings_page.dart.
import 'package:flutter/material.dart';

import 'account_settings_page.dart';

/// Opens “Account settings”: `Navigator.of(context).push(accountSettingsRoute())`.
Route<void> accountSettingsRoute() => MaterialPageRoute<void>(
  builder: (context) => AccountSettingsPage(
    onBack: () => Navigator.of(context).maybePop(),
  ),
);

State

The controller is a ChangeNotifier from widgets.dart, with no package. fliin add account-settings 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/account_settings_page.dart
// Your backend: implement AccountSettingsService (account_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  final VoidCallback onBack;

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

provider

// lib/account_settings_page.dart
// Your backend: implement AccountSettingsService (account_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

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

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context) => ChangeNotifierProvider(
    create: (_) => AccountSettingsController(service: const SampleAccountSettingsService())..load(),
    child: Builder(
      builder: (context) => AccountSettingsScreen(
        controller: context.read<AccountSettingsController>(),
        onBack: onBack,
      ),
    ),
  );
}

riverpod

// lib/account_settings_page.dart
// Your backend: implement AccountSettingsService (account_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final accountSettingsProvider = Provider.autoDispose<AccountSettingsController>((ref) {
  final controller = AccountSettingsController(service: const SampleAccountSettingsService())..load();
  ref.onDispose(controller.dispose);
  return controller;
});

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

  final VoidCallback onBack;

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

bloc

// lib/account_settings_page.dart
// Your backend: implement AccountSettingsService (account_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_data.dart';
import 'package:your_app/ui/fliin/screens/account_settings/account_settings_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 AccountSettingsCubit extends Cubit<({AccountSettingsState state, int version})> {
  AccountSettingsCubit(this.controller) : super((state: controller.state, version: 0)) {
    controller.addListener(_changed);
  }

  final AccountSettingsController controller;

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

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

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

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => AccountSettingsCubit(AccountSettingsController(service: const SampleAccountSettingsService())..load()),
    child: Builder(
      builder: (context) => AccountSettingsScreen(
        controller: context.read<AccountSettingsCubit>().controller,
        onBack: onBack,
      ),
    ),
  );
}

API

AccountSettingsScreen: class extends StatefulWidget
AccountSettingsScreen.new: const new({super.key, required AccountSettingsController controller, VoidCallback? onBack, VoidCallback? onEditProfile, VoidCallback? onChangePassword, VoidCallback? onDeleted, VoidCallback? onSignOut, List<Widget> sections, bool showDelete})
AccountSettingsScreen.standalone: const new({super.key, required AccountSettingsService service, AccountSettingsState? initial, VoidCallback? onBack, VoidCallback? onEditProfile, VoidCallback? onChangePassword, VoidCallback? onDeleted, VoidCallback? onSignOut, List<Widget> sections, bool showDelete})
AccountSettingsScreen.controller: get AccountSettingsController?
AccountSettingsScreen.service: get AccountSettingsService?
AccountSettingsScreen.initial: get AccountSettingsState?
AccountSettingsScreen.onBack: get VoidCallback?
AccountSettingsScreen.onEditProfile: get VoidCallback?
AccountSettingsScreen.onChangePassword: get VoidCallback?
AccountSettingsScreen.onDeleted: get VoidCallback?
AccountSettingsScreen.onSignOut: get VoidCallback?
AccountSettingsScreen.sections: get List<Widget>
AccountSettingsScreen.showDelete: get bool
AccountSettingsScreen.createState: State<AccountSettingsScreen>()
AccountSettingsDeleteDialog: class extends StatelessWidget
AccountSettingsDeleteDialog.new: const new({super.key})
AccountSettingsDeleteDialog.build: Widget(BuildContext context)
AccountSettingsState: sealed class permits {AccountSettingsFailed, AccountSettingsLoaded, AccountSettingsLoading, AccountSettingsOffline}
AccountSettingsState.new: const new()
AccountSettingsLoading: final class extends AccountSettingsState
AccountSettingsLoading.new: const new()
AccountSettingsLoaded: final class extends AccountSettingsState
AccountSettingsLoaded.new: const new(AccountSettingsData data, {bool deleting})
AccountSettingsLoaded.data: get AccountSettingsData
AccountSettingsLoaded.deleting: get bool
AccountSettingsFailed: final class extends AccountSettingsState
AccountSettingsFailed.new: const new()
AccountSettingsOffline: final class extends AccountSettingsState
AccountSettingsOffline.new: const new()
AccountSettingsController: class extends ChangeNotifier
AccountSettingsController.new: new({required AccountSettingsService service, AccountSettingsState initial})
AccountSettingsController.service: get AccountSettingsService
AccountSettingsController.state: get AccountSettingsState
AccountSettingsController.load: Future<void>()
AccountSettingsController.refresh: Future<void>()
AccountSettingsController.setPreference: Future<bool>(AccountPreference preference, bool value)
AccountSettingsController.deleteAccount: Future<bool>()
AccountSettingsController.dispose: void()
AccountPreference: enum {twoFactor, pushNotifications, emailDigest}
AccountPreference.twoFactor: static final AccountPreference
AccountPreference.pushNotifications: static final AccountPreference
AccountPreference.emailDigest: static final AccountPreference
AccountSettingsData: final class
AccountSettingsData.new: const new({required String name, required String email, required String initials, required String? plan, required Map<AccountPreference, bool> preferences, ImageProvider? photo})
AccountSettingsData.name: get String
AccountSettingsData.email: get String
AccountSettingsData.initials: get String
AccountSettingsData.photo: get ImageProvider?
AccountSettingsData.plan: get String?
AccountSettingsData.preferences: get Map<AccountPreference, bool>
AccountSettingsData.isOn: bool(AccountPreference preference)
AccountSettingsData.withPreference: AccountSettingsData(AccountPreference preference, bool value)
AccountSettingsNoNetwork: final class implements Exception
AccountSettingsNoNetwork.new: const new()
AccountSettingsService: abstract interface class
AccountSettingsService.new: new()
AccountSettingsService.load: abstract Future<AccountSettingsData>()
AccountSettingsService.setPreference: abstract Future<void>(AccountPreference preference, bool value)
AccountSettingsService.deleteAccount: abstract Future<void>()
sampleAccountSettings: const dynamic
SampleAccountSettingsService: final class implements AccountSettingsService
SampleAccountSettingsService.new: const new({AccountSettingsData data, Duration latency})
SampleAccountSettingsService.data: get AccountSettingsData
SampleAccountSettingsService.latency: get Duration
SampleAccountSettingsService.load: Future<AccountSettingsData>()
SampleAccountSettingsService.setPreference: Future<void>(AccountPreference preference, bool value)
SampleAccountSettingsService.deleteAccount: Future<void>()
accountSettingsL10n: const Map<String, Map<String, String>>
AccountSettingsStrings: final class
AccountSettingsStrings.new: const new(String language)
AccountSettingsStrings.of: factory new(BuildContext context)
AccountSettingsStrings.language: get String
AccountSettingsStrings.title: get String
AccountSettingsStrings.back: get String
AccountSettingsStrings.refresh: get String
AccountSettingsStrings.loading: get String
AccountSettingsStrings.plan: String(String plan)
AccountSettingsStrings.security: get String
AccountSettingsStrings.password: get String
AccountSettingsStrings.twoFactor: get String
AccountSettingsStrings.notifications: get String
AccountSettingsStrings.push: get String
AccountSettingsStrings.digest: get String
AccountSettingsStrings.delete: get String
AccountSettingsStrings.deleteTitle: get String
AccountSettingsStrings.deleteMessage: get String
AccountSettingsStrings.deleteConfirm: get String
AccountSettingsStrings.cancel: get String
AccountSettingsStrings.errorTitle: get String
AccountSettingsStrings.errorMessage: get String
AccountSettingsStrings.offlineTitle: get String
AccountSettingsStrings.offlineMessage: get String
AccountSettingsStrings.retry: get String
AccountSettingsStrings.deleting: get String
AccountSettingsStrings.signOut: get String
AccountSettingsStrings.sampleBilling: get String
AccountSettingsStrings.sampleSubscription: get String
AccountSettingsStrings.sampleInvoices: 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
accountSettingsTitleAccountCompteالحساب
accountSettingsBackBackRetourرجوع
accountSettingsRefreshRefreshActualiserتحديث
accountSettingsLoadingLoading your accountChargement du compteجارٍ تحميل حسابك
accountSettingsPlan{plan} planFormule {plan}باقة {plan}
accountSettingsSecuritySecuritySécuritéالأمان
accountSettingsPasswordPasswordMot de passeكلمة المرور
accountSettingsTwoFactorTwo-factor authenticationDouble authentificationالمصادقة الثنائية
accountSettingsNotificationsNotificationsNotificationsالإشعارات
accountSettingsPushPush notificationsNotifications pushالإشعارات الفورية
accountSettingsDigestWeekly email summaryRésumé hebdomadaire par e-mailملخص أسبوعي بالبريد
accountSettingsDeleteDelete accountSupprimer le compteحذف الحساب
accountSettingsDeletingDeleting the accountSuppression du compteجارٍ حذف الحساب
accountSettingsDeleteTitleDelete your account?Supprimer votre compte ?حذف حسابك؟
accountSettingsDeleteMessageYour profile, settings and history will be erased. This cannot be undone.Votre profil, vos réglages et votre historique seront effacés. Cette action est définitive.سيتم مسح ملفك الشخصي وإعداداتك وسجلك. لا يمكن التراجع عن ذلك.
accountSettingsDeleteConfirmDeleteSupprimerحذف
accountSettingsCancelCancelAnnulerإلغاء
accountSettingsErrorTitleYour account could not loadImpossible de charger le compteتعذّر تحميل حسابك
accountSettingsErrorMessageSomething went wrong on our side.Un problème est survenu de notre côté.حدثت مشكلة من جهتنا.
accountSettingsOfflineTitleYou are offlineVous êtes hors ligneأنت غير متصل
accountSettingsOfflineMessageCheck your connection, then try again.Vérifiez votre connexion, puis réessayez.تحقق من اتصالك ثم أعد المحاولة.
accountSettingsRetryTry againRéessayerأعد المحاولة
accountSettingsSignOutSign outSe déconnecterتسجيل الخروج
accountSettingsSampleBillingBillingFacturationالفوترة
accountSettingsSampleSubscriptionManage subscriptionGérer l’abonnementإدارة الاشتراك
accountSettingsSampleInvoicesInvoicesFacturesالفواتير

Reference renders

The 16 images published with version 0.2.4, 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 account-settings compares your copy to them.

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

Use it to

  • edit the user profile from account settings
  • change the account password
  • delete the account after a confirmation
  • toggle account notifications
  • adjust sign-in security for the account
Not for
  • a single settings row in an existing page: use a list row (list-row)
  • a message when the list is empty: use an empty state (empty-state)
  • a bank statement, line by line: use a transaction row (transaction-row)
States
  • Loaded
  • Loading
  • Error
  • Offline

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