Fliin
On this page

Screens / settings

App settings

Groups the settings of the app: appearance, text size, language, haptics, a cache to clear after a confirmation, version, licenses and legal notices, with a link to the account.

16 of 16 variants pass
App settings, custom state
Platform
Brightness
Text scale
Direction

Import

App 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 app-settings

Also installs: core, alert, app-bar, avatar, confirm-delete, empty-state, list-row, page, profile-card, radio, settings-section, sheet, skeleton, switch, button, segmented

Examples

Custom

The settings of your app: a section of your own before About, and only the rows that apply (showHaptics and showStorage remove the haptics and the cache).

App settings, custom 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/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_strings.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    final strings = AppSettingsStrings.of(context);
    return AppSettingsScreen.standalone(
      service: const SampleAppSettingsService(latency: Duration(milliseconds: 600)),
      initial: const AppSettingsReady(sampleAppSettings),
      showHaptics: false,
      showStorage: false,
      onBack: () {},
      onAccount: () {},
      onTextSize: () {},
      onLicenses: () {},
      sections: [
        FlSettingsSection(
          title: strings.sampleHelp,
          children: [
            FlListRow(title: strings.sampleHelpCenter, trailing: const FlListRowChevron(), onPressed: () {}),
            FlListRow(title: strings.sampleContact, trailing: const FlListRowChevron(), onPressed: () {}),
          ],
        ),
      ],
    );
  }
}

Ready

The app settings: appearance, notifications, storage and the about section. standalone holds its own controller over the service you pass.

App settings, ready state
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AppSettingsScreen.standalone(
      service: const SampleAppSettingsService(latency: Duration(milliseconds: 600)),
      initial: const AppSettingsReady(sampleAppSettings),
      onBack: () {},
      onAccount: () {},
      onTextSize: () {},
      onLicenses: () {},
      onLegal: () {},
    );
  }
}

Loading

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

App settings, loading state
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AppSettingsScreen.standalone(
      service: const SampleAppSettingsService(latency: Duration(milliseconds: 600)),
      initial: const AppSettingsLoading(),
      onBack: () {},
      onAccount: () {},
      onTextSize: () {},
      onLicenses: () {},
      onLegal: () {},
    );
  }
}

Clearing

While the cache is cleared, its row shows a spinner and the rest stays usable.

App settings, clearing state
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AppSettingsScreen.standalone(
      service: const SampleAppSettingsService(latency: Duration(milliseconds: 600)),
      initial: const AppSettingsReady(sampleAppSettings, clearing: true),
      onBack: () {},
      onAccount: () {},
      onTextSize: () {},
      onLicenses: () {},
      onLegal: () {},
    );
  }
}

Error

When loading fails, the screen says so and offers to try again.

App settings, error state
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return AppSettingsScreen.standalone(
      service: const SampleAppSettingsService(latency: Duration(milliseconds: 600)),
      initial: const AppSettingsFailed(),
      onBack: () {},
      onAccount: () {},
      onTextSize: () {},
      onLicenses: () {},
      onLegal: () {},
    );
  }
}

Routing

fliin add app-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/app_settings_route.dart, next to app_settings_page.dart.
import 'package:go_router/go_router.dart';

import 'app_settings_page.dart';

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

navigator

// lib/app_settings_route.dart, next to app_settings_page.dart.
import 'package:flutter/material.dart';

import 'app_settings_page.dart';

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

State

The controller is a ChangeNotifier from widgets.dart, with no package. fliin add app-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/app_settings_page.dart
// Your backend: implement AppSettingsService (app_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';

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

  final VoidCallback onBack;

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

provider

// lib/app_settings_page.dart
// Your backend: implement AppSettingsService (app_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

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

  final VoidCallback onBack;

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

riverpod

// lib/app_settings_page.dart
// Your backend: implement AppSettingsService (app_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final appSettingsProvider = Provider.autoDispose<AppSettingsController>((ref) {
  final controller = AppSettingsController(service: const SampleAppSettingsService())..load();
  ref.onDispose(controller.dispose);
  return controller;
});

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

  final VoidCallback onBack;

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

bloc

// lib/app_settings_page.dart
// Your backend: implement AppSettingsService (app_settings_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_controller.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_settings_data.dart';
import 'package:your_app/ui/fliin/screens/app_settings/app_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 AppSettingsCubit extends Cubit<({AppSettingsState state, int version})> {
  AppSettingsCubit(this.controller) : super((state: controller.state, version: 0)) {
    controller.addListener(_changed);
  }

  final AppSettingsController controller;

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

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

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

  final VoidCallback onBack;

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => AppSettingsCubit(AppSettingsController(service: const SampleAppSettingsService())..load()),
    child: Builder(
      builder: (context) => AppSettingsScreen(
        controller: context.read<AppSettingsCubit>().controller,
        onBack: onBack,
      ),
    ),
  );
}

API

AppSettingsScreen: class extends StatefulWidget
AppSettingsScreen.new: const new({super.key, required AppSettingsController controller, VoidCallback? onBack, VoidCallback? onAccount, VoidCallback? onTextSize, VoidCallback? onLicenses, VoidCallback? onLegal, List<Widget> sections, bool showHaptics, bool showStorage})
AppSettingsScreen.standalone: const new({super.key, required AppSettingsService service, AppSettingsState? initial, VoidCallback? onBack, VoidCallback? onAccount, VoidCallback? onTextSize, VoidCallback? onLicenses, VoidCallback? onLegal, List<Widget> sections, bool showHaptics, bool showStorage})
AppSettingsScreen.controller: get AppSettingsController?
AppSettingsScreen.service: get AppSettingsService?
AppSettingsScreen.initial: get AppSettingsState?
AppSettingsScreen.onBack: get VoidCallback?
AppSettingsScreen.onAccount: get VoidCallback?
AppSettingsScreen.onTextSize: get VoidCallback?
AppSettingsScreen.onLicenses: get VoidCallback?
AppSettingsScreen.onLegal: get VoidCallback?
AppSettingsScreen.sections: get List<Widget>
AppSettingsScreen.showHaptics: get bool
AppSettingsScreen.showStorage: get bool
AppSettingsScreen.createState: State<AppSettingsScreen>()
AppSettingsState: sealed class permits {AppSettingsFailed, AppSettingsLoading, AppSettingsReady}
AppSettingsState.new: const new()
AppSettingsLoading: final class extends AppSettingsState
AppSettingsLoading.new: const new()
AppSettingsNotice: enum {saveFailed, clearFailed}
AppSettingsNotice.saveFailed: static final AppSettingsNotice
AppSettingsNotice.clearFailed: static final AppSettingsNotice
AppSettingsReady: final class extends AppSettingsState
AppSettingsReady.new: const new(AppSettingsData data, {bool clearing, AppSettingsNotice? notice})
AppSettingsReady.data: get AppSettingsData
AppSettingsReady.clearing: get bool
AppSettingsReady.notice: get AppSettingsNotice?
AppSettingsFailed: final class extends AppSettingsState
AppSettingsFailed.new: const new()
AppSettingsController: class extends ChangeNotifier
AppSettingsController.new: new({required AppSettingsService service, AppSettingsState initial})
AppSettingsController.service: get AppSettingsService
AppSettingsController.state: get AppSettingsState
AppSettingsController.load: Future<void>()
AppSettingsController.setAppearance: Future<bool>(AppAppearance value)
AppSettingsController.setLanguage: Future<bool>(String code)
AppSettingsController.setHaptics: Future<bool>(bool value)
AppSettingsController.clearCache: Future<bool>()
AppSettingsController.dispose: void()
AppAppearance: enum {system, light, dark}
AppAppearance.system: static final AppAppearance
AppAppearance.light: static final AppAppearance
AppAppearance.dark: static final AppAppearance
AppLanguage: final class
AppLanguage.new: const new(String code, String name)
AppLanguage.code: get String
AppLanguage.name: get String
AppSettingsAccount: final class
AppSettingsAccount.new: const new({required String name, required String email, required String initials, ImageProvider? photo})
AppSettingsAccount.name: get String
AppSettingsAccount.email: get String
AppSettingsAccount.initials: get String
AppSettingsAccount.photo: get ImageProvider?
AppSettingsData: final class
AppSettingsData.new: const new({required AppAppearance appearance, required String language, required List<AppLanguage> languages, required bool haptics, required int cacheBytes, required String version, required AppSettingsAccount? account})
AppSettingsData.appearance: get AppAppearance
AppSettingsData.language: get String
AppSettingsData.languages: get List<AppLanguage>
AppSettingsData.haptics: get bool
AppSettingsData.cacheBytes: get int
AppSettingsData.version: get String
AppSettingsData.account: get AppSettingsAccount?
AppSettingsData.languageName: get String
AppSettingsData.copyWith: AppSettingsData({AppAppearance? appearance, String? language, bool? haptics, int? cacheBytes})
AppSettingsService: abstract interface class
AppSettingsService.new: new()
AppSettingsService.load: abstract Future<AppSettingsData>()
AppSettingsService.save: abstract Future<void>(AppSettingsData settings)
AppSettingsService.clearCache: abstract Future<void>()
sampleAppLanguages: const dynamic
sampleAppSettings: const dynamic
SampleAppSettingsService: final class implements AppSettingsService
SampleAppSettingsService.new: const new({AppSettingsData data, Duration latency})
SampleAppSettingsService.data: get AppSettingsData
SampleAppSettingsService.latency: get Duration
SampleAppSettingsService.load: Future<AppSettingsData>()
SampleAppSettingsService.save: Future<void>(AppSettingsData settings)
SampleAppSettingsService.clearCache: Future<void>()
appSettingsL10n: const Map<String, Map<String, String>>
AppSettingsStrings: final class
AppSettingsStrings.new: const new(String language)
AppSettingsStrings.of: factory new(BuildContext context)
AppSettingsStrings.language: get String
AppSettingsStrings.title: get String
AppSettingsStrings.back: get String
AppSettingsStrings.loading: get String
AppSettingsStrings.display: get String
AppSettingsStrings.appearance: get String
AppSettingsStrings.appearanceSystem: get String
AppSettingsStrings.appearanceLight: get String
AppSettingsStrings.appearanceDark: get String
AppSettingsStrings.textSize: get String
AppSettingsStrings.percent: String(String value)
AppSettingsStrings.general: get String
AppSettingsStrings.languageLabel: get String
AppSettingsStrings.haptics: get String
AppSettingsStrings.storage: get String
AppSettingsStrings.clearCache: get String
AppSettingsStrings.cacheEmpty: get String
AppSettingsStrings.clearTitle: get String
AppSettingsStrings.clearMessage: get String
AppSettingsStrings.clearConfirm: get String
AppSettingsStrings.cancel: get String
AppSettingsStrings.about: get String
AppSettingsStrings.version: get String
AppSettingsStrings.licenses: get String
AppSettingsStrings.legal: get String
AppSettingsStrings.close: get String
AppSettingsStrings.errorTitle: get String
AppSettingsStrings.errorMessage: get String
AppSettingsStrings.retry: get String
AppSettingsStrings.appearanceName: String(AppAppearance value)
AppSettingsStrings.cacheSize: String(int bytes)
AppSettingsStrings.saveFailed: get String
AppSettingsStrings.clearFailed: get String
AppSettingsStrings.textSizeDefault: get String
AppSettingsStrings.clear: get String
AppSettingsStrings.cacheUsed: String(int bytes)
AppSettingsStrings.sampleHelp: get String
AppSettingsStrings.sampleHelpCenter: get String
AppSettingsStrings.sampleContact: 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
appSettingsTitleSettingsRéglagesالإعدادات
appSettingsBackBackRetourرجوع
appSettingsLoadingLoading settingsChargement des réglagesجارٍ تحميل الإعدادات
appSettingsDisplayDisplayAffichageالعرض
appSettingsAppearanceAppearanceApparenceالمظهر
appSettingsAppearanceSystemSystemSystèmeالنظام
appSettingsAppearanceLightLightClairفاتح
appSettingsAppearanceDarkDarkSombreداكن
appSettingsTextSizeText sizeTaille du texteحجم النص
appSettingsPercent{value}%{value} %{value}٪
appSettingsTextSizeDefaultDefaultPar défautافتراضي
appSettingsGeneralGeneralGénéralعام
appSettingsLanguageLanguageLangueاللغة
appSettingsHapticsHaptic feedbackRetour haptiqueالاهتزاز عند اللمس
appSettingsStorageStorageStockageالتخزين
appSettingsClearCacheClear cacheVider le cacheمسح الذاكرة المؤقتة
appSettingsClearClearViderمسح
appSettingsCacheUsedCache: {size}Cache : {size}الذاكرة المؤقتة: {size}
appSettingsCacheEmptyEmptyVideفارغة
appSettingsKilobytes{size} KB{size} Ko{size} ك.ب
appSettingsMegabytes{size} MB{size} Mo{size} م.ب
appSettingsClearTitleClear the cache?Vider le cache ?مسح الذاكرة المؤقتة؟
appSettingsClearMessageDownloaded images and files will be erased and fetched again when needed. Your settings and account stay.Les images et fichiers téléchargés seront effacés, puis rechargés au besoin. Vos réglages et votre compte restent.سيتم مسح الصور والملفات التي تم تنزيلها وإعادة تحميلها عند الحاجة. تبقى إعداداتك وحسابك.
appSettingsClearConfirmClearViderمسح
appSettingsCancelCancelAnnulerإلغاء
appSettingsAboutAboutÀ proposحول التطبيق
appSettingsVersionVersionVersionالإصدار
appSettingsLicensesLicensesLicencesالتراخيص
appSettingsLegalLegal noticesMentions légalesالإشعارات القانونية
appSettingsCloseCloseFermerإغلاق
appSettingsErrorTitleSettings could not loadImpossible de lire les réglagesتعذّر تحميل الإعدادات
appSettingsErrorMessageSomething went wrong on this device.Un problème est survenu sur l'appareil.حدثت مشكلة على هذا الجهاز.
appSettingsRetryTry againRéessayerأعد المحاولة
appSettingsSaveFailedYour change couldn't be saved. The setting is back to its saved value.Votre modification n'a pas pu être enregistrée. Le réglage a repris sa valeur enregistrée.تعذّر حفظ التغيير. عاد الإعداد إلى قيمته المحفوظة.
appSettingsClearFailedThe cache couldn't be cleared. Try again.Le cache n'a pas pu être vidé. Réessayez.تعذّر مسح الذاكرة المؤقتة. أعد المحاولة.
appSettingsSampleHelpHelpAideالمساعدة
appSettingsSampleHelpCenterHelp centerCentre d’aideمركز المساعدة
appSettingsSampleContactContact usNous contacterاتصل بنا

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 app-settings compares your copy to them.

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

Use it to

  • choose the light, dark or system theme
  • change the interface language
  • delete cached files after a confirmation
  • show the version number and open-source licenses
  • turn vibrations on or off
Not for
  • the signed-in account's profile and security: use the account screen (account-settings)
  • a single switch in a page: use a list row (list-row)
States
  • Ready
  • Loading
  • Clearing
  • Error

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