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.
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-settingsAlso 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.
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.
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.
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.
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.
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 StringStrings
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.
| Key | en | fr | ar |
|---|---|---|---|
accountSettingsTitle | Account | Compte | الحساب |
accountSettingsBack | Back | Retour | رجوع |
accountSettingsRefresh | Refresh | Actualiser | تحديث |
accountSettingsLoading | Loading your account | Chargement du compte | جارٍ تحميل حسابك |
accountSettingsPlan | {plan} plan | Formule {plan} | باقة {plan} |
accountSettingsSecurity | Security | Sécurité | الأمان |
accountSettingsPassword | Password | Mot de passe | كلمة المرور |
accountSettingsTwoFactor | Two-factor authentication | Double authentification | المصادقة الثنائية |
accountSettingsNotifications | Notifications | Notifications | الإشعارات |
accountSettingsPush | Push notifications | Notifications push | الإشعارات الفورية |
accountSettingsDigest | Weekly email summary | Résumé hebdomadaire par e-mail | ملخص أسبوعي بالبريد |
accountSettingsDelete | Delete account | Supprimer le compte | حذف الحساب |
accountSettingsDeleting | Deleting the account | Suppression du compte | جارٍ حذف الحساب |
accountSettingsDeleteTitle | Delete your account? | Supprimer votre compte ? | حذف حسابك؟ |
accountSettingsDeleteMessage | Your 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. | سيتم مسح ملفك الشخصي وإعداداتك وسجلك. لا يمكن التراجع عن ذلك. |
accountSettingsDeleteConfirm | Delete | Supprimer | حذف |
accountSettingsCancel | Cancel | Annuler | إلغاء |
accountSettingsErrorTitle | Your account could not load | Impossible de charger le compte | تعذّر تحميل حسابك |
accountSettingsErrorMessage | Something went wrong on our side. | Un problème est survenu de notre côté. | حدثت مشكلة من جهتنا. |
accountSettingsOfflineTitle | You are offline | Vous êtes hors ligne | أنت غير متصل |
accountSettingsOfflineMessage | Check your connection, then try again. | Vérifiez votre connexion, puis réessayez. | تحقق من اتصالك ثم أعد المحاولة. |
accountSettingsRetry | Try again | Réessayer | أعد المحاولة |
accountSettingsSignOut | Sign out | Se déconnecter | تسجيل الخروج |
accountSettingsSampleBilling | Billing | Facturation | الفوترة |
accountSettingsSampleSubscription | Manage subscription | Gérer l’abonnement | إدارة الاشتراك |
accountSettingsSampleInvoices | Invoices | Factures | الفواتير |
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.
















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