Fliin
On this page

Screens / account

Edit profile

Lets people change the photo, name, username checked while typing and bio of their profile, and asks before discarding unsaved changes.

16 of 16 variants pass
Edit profile, simple state
Platform
Brightness
Text scale
Direction

Import

Edit profile 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 edit-profile

Also installs: alert, app-bar, avatar, button, confirm-delete, core, empty-state, link-button, page, sheet, skeleton, text-field, icons

Examples

Simple

A profile with a photo and a name only: showUsername and showBio remove the fields your app does not have, and the draft keeps their values unchanged.

Edit profile, simple state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    final profile = ProfileData(
      name: sampleProfile.name,
      username: sampleProfile.username,
      bio: sampleProfile.bio,
      photo: const AssetImage('images/people/1.jpg'),
    );
    return EditProfileScreen.standalone(
      service: SampleEditProfileService(profile: profile, latency: const Duration(milliseconds: 600)),
      initial: EditProfileEditing(saved: profile, draft: profile),
      showUsername: false,
      showBio: false,
      onPickPhoto: (source) async => const AssetImage('images/people/3.jpg'),
      onBack: () {},
    );
  }
}

Ready

The profile as saved: photo, name, username and bio. standalone holds its own controller over the service you pass.

Edit profile, ready state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: const EditProfileEditing(saved: sampleProfile, draft: sampleProfile),
      onBack: () {},
    );
  }
}

Modified

Changes not saved yet: the save action lights up, and an available username is confirmed under its field.

Edit profile, modified state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: EditProfileEditing(
        saved: sampleProfile,
        draft: sampleProfile.copyWith(name: 'Camille Dunn', username: 'camille.dunn'),
        username: UsernameCheck.available,
      ),
      onBack: () {},
    );
  }
}

Saving

While the changes are saved, the save action shows its spinner.

Edit profile, saving state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: EditProfileEditing(
        saved: sampleProfile,
        draft: sampleProfile.copyWith(name: 'Camille Dunn', username: 'camille.dunn'),
        username: UsernameCheck.available,
        save: EditProfileSave.saving,
      ),
      onBack: () {},
    );
  }
}

Save error

Saving failed: an alert says so and the changes stay in the form.

Edit profile, save-error state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: EditProfileEditing(
        saved: sampleProfile,
        draft: sampleProfile.copyWith(name: 'Camille Dunn', username: 'camille.dunn'),
        username: UsernameCheck.available,
        save: EditProfileSave.failed,
      ),
      onBack: () {},
    );
  }
}

Loading

While the profile loads, placeholders hold the place of each field.

Edit profile, loading state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: const EditProfileLoading(),
      onBack: () {},
    );
  }
}

Error

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

Edit profile, error state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: const EditProfileFailed(),
      onBack: () {},
    );
  }
}

Offline

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

Edit profile, offline state
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  @override
  Widget build(BuildContext context) {
    return EditProfileScreen.standalone(
      service: const SampleEditProfileService(latency: Duration(milliseconds: 600)),
      initial: const EditProfileOffline(),
      onBack: () {},
    );
  }
}

Routing

fliin add edit-profile 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/edit_profile_route.dart, next to edit_profile_page.dart.
import 'package:go_router/go_router.dart';

import 'edit_profile_page.dart';

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

navigator

// lib/edit_profile_route.dart, next to edit_profile_page.dart.
import 'package:flutter/material.dart';

import 'edit_profile_page.dart';

/// Opens “Edit profile”: `Navigator.of(context).push(editProfileRoute())`.
Route<void> editProfileRoute() => MaterialPageRoute<void>(
  builder: (context) => EditProfilePage(
    onBack: () => Navigator.of(context).maybePop(),
    onSaved: (_) => Navigator.of(context).maybePop(),
  ),
);

State

The controller is a ChangeNotifier from widgets.dart, with no package. fliin add edit-profile 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/edit_profile_page.dart
// Your backend: implement EditProfileService (edit_profile_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';

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

  final VoidCallback onBack;
  final ValueChanged<ProfileData> onSaved;

  @override
  Widget build(BuildContext context) => EditProfileScreen.standalone(
    service: const SampleEditProfileService(),
    onBack: onBack,
    onSaved: onSaved,
  );
}

provider

// lib/edit_profile_page.dart
// Your backend: implement EditProfileService (edit_profile_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

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

  final VoidCallback onBack;
  final ValueChanged<ProfileData> onSaved;

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

riverpod

// lib/edit_profile_page.dart
// Your backend: implement EditProfileService (edit_profile_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final editProfileProvider = Provider.autoDispose<EditProfileController>((ref) {
  final controller = EditProfileController(service: const SampleEditProfileService())..load();
  ref.onDispose(controller.dispose);
  return controller;
});

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

  final VoidCallback onBack;
  final ValueChanged<ProfileData> onSaved;

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

bloc

// lib/edit_profile_page.dart
// Your backend: implement EditProfileService (edit_profile_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_controller.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_data.dart';
import 'package:your_app/ui/fliin/screens/edit_profile/edit_profile_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 EditProfileCubit extends Cubit<({EditProfileState state, int version})> {
  EditProfileCubit(this.controller) : super((state: controller.state, version: 0)) {
    controller.addListener(_changed);
  }

  final EditProfileController controller;

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

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

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

  final VoidCallback onBack;
  final ValueChanged<ProfileData> onSaved;

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => EditProfileCubit(EditProfileController(service: const SampleEditProfileService())..load()),
    child: Builder(
      builder: (context) => EditProfileScreen(
        controller: context.read<EditProfileCubit>().controller,
        onBack: onBack,
        onSaved: onSaved,
      ),
    ),
  );
}

API

ProfilePhotoPicker: typedef = Future<ImageProvider?> Function(ProfilePhotoSource source)
EditProfileScreen: class extends StatefulWidget
EditProfileScreen.new: const new({super.key, required EditProfileController controller, VoidCallback? onBack, ValueChanged<ProfileData>? onSaved, ProfilePhotoPicker? onPickPhoto, bool showUsername, bool showBio})
EditProfileScreen.standalone: const new({super.key, required EditProfileService service, EditProfileState? initial, VoidCallback? onBack, ValueChanged<ProfileData>? onSaved, ProfilePhotoPicker? onPickPhoto, bool showUsername, bool showBio})
EditProfileScreen.controller: get EditProfileController?
EditProfileScreen.service: get EditProfileService?
EditProfileScreen.initial: get EditProfileState?
EditProfileScreen.onBack: get VoidCallback?
EditProfileScreen.onSaved: get ValueChanged<ProfileData>?
EditProfileScreen.onPickPhoto: get ProfilePhotoPicker?
EditProfileScreen.showUsername: get bool
EditProfileScreen.showBio: get bool
EditProfileScreen.createState: State<EditProfileScreen>()
EditProfileState: sealed class permits {EditProfileEditing, EditProfileFailed, EditProfileLoading, EditProfileOffline}
EditProfileState.new: const new()
EditProfileLoading: final class extends EditProfileState
EditProfileLoading.new: const new()
EditProfileFailed: final class extends EditProfileState
EditProfileFailed.new: const new()
EditProfileOffline: final class extends EditProfileState
EditProfileOffline.new: const new()
UsernameCheck: enum {unchanged, invalid, checking, available, taken, unknown}
UsernameCheck.unchanged: static final UsernameCheck
UsernameCheck.invalid: static final UsernameCheck
UsernameCheck.checking: static final UsernameCheck
UsernameCheck.available: static final UsernameCheck
UsernameCheck.taken: static final UsernameCheck
UsernameCheck.unknown: static final UsernameCheck
UsernameCheck.allowsSave: get bool
EditProfileSave: enum {idle, saving, failed, offline}
EditProfileSave.idle: static final EditProfileSave
EditProfileSave.saving: static final EditProfileSave
EditProfileSave.failed: static final EditProfileSave
EditProfileSave.offline: static final EditProfileSave
EditProfileEditing: final class extends EditProfileState
EditProfileEditing.new: const new({required ProfileData saved, required ProfileData draft, UsernameCheck username, EditProfileSave save})
EditProfileEditing.saved: get ProfileData
EditProfileEditing.draft: get ProfileData
EditProfileEditing.username: get UsernameCheck
EditProfileEditing.save: get EditProfileSave
EditProfileEditing.dirty: get bool
EditProfileEditing.nameMissing: get bool
EditProfileEditing.bioTooLong: get bool
EditProfileEditing.canSave: get bool
EditProfileController: class extends ChangeNotifier
EditProfileController.new: new({required EditProfileService service, EditProfileState initial, Duration debounce})
EditProfileController.service: get EditProfileService
EditProfileController.debounce: get Duration
EditProfileController.state: get EditProfileState
EditProfileController.load: Future<void>()
EditProfileController.setName: void(String name)
EditProfileController.setBio: void(String bio)
EditProfileController.setPhoto: void(ImageProvider? photo)
EditProfileController.setUsername: void(String username)
EditProfileController.save: Future<bool>()
EditProfileController.dispose: void()
ProfileData: final class
ProfileData.new: const new({required String name, required String username, required String bio, ImageProvider? photo})
ProfileData.name: get String
ProfileData.username: get String
ProfileData.bio: get String
ProfileData.photo: get ImageProvider?
ProfileData.initials: get String
ProfileData.copyWith: ProfileData({String? name, String? username, String? bio, ImageProvider? photo, bool removePhoto})
ProfileData.sameAs: bool(ProfileData other)
maxBioLength: const dynamic
isValidUsername: bool(String username)
ProfilePhotoSource: enum {camera, library}
ProfilePhotoSource.camera: static final ProfilePhotoSource
ProfilePhotoSource.library: static final ProfilePhotoSource
EditProfileNoNetwork: final class implements Exception
EditProfileNoNetwork.new: const new()
EditProfileService: abstract interface class
EditProfileService.new: new()
EditProfileService.load: abstract Future<ProfileData>()
EditProfileService.isUsernameAvailable: abstract Future<bool>(String username)
EditProfileService.save: abstract Future<ProfileData>(ProfileData profile)
sampleProfile: const dynamic
SampleEditProfileService: final class implements EditProfileService
SampleEditProfileService.new: const new({ProfileData profile, Set<String> taken, Duration latency})
SampleEditProfileService.profile: get ProfileData
SampleEditProfileService.taken: get Set<String>
SampleEditProfileService.latency: get Duration
SampleEditProfileService.load: Future<ProfileData>()
SampleEditProfileService.isUsernameAvailable: Future<bool>(String username)
SampleEditProfileService.save: Future<ProfileData>(ProfileData profile)
editProfileL10n: const Map<String, Map<String, String>>
EditProfileStrings: final class
EditProfileStrings.new: const new(String language)
EditProfileStrings.of: factory new(BuildContext context)
EditProfileStrings.language: get String
EditProfileStrings.title: get String
EditProfileStrings.back: get String
EditProfileStrings.save: get String
EditProfileStrings.loading: get String
EditProfileStrings.photo: get String
EditProfileStrings.changePhoto: get String
EditProfileStrings.takePhoto: get String
EditProfileStrings.choosePhoto: get String
EditProfileStrings.removePhoto: get String
EditProfileStrings.close: get String
EditProfileStrings.name: get String
EditProfileStrings.nameRequired: get String
EditProfileStrings.username: get String
EditProfileStrings.usernameRule: get String
EditProfileStrings.usernameChecking: get String
EditProfileStrings.usernameAvailable: String(String username)
EditProfileStrings.usernameTaken: get String
EditProfileStrings.usernameUnknown: get String
EditProfileStrings.bio: get String
EditProfileStrings.bioPlaceholder: get String
EditProfileStrings.bioCount: String(int count)
EditProfileStrings.bioTooLong: get String
EditProfileStrings.saveErrorTitle: get String
EditProfileStrings.saveErrorMessage: get String
EditProfileStrings.saveOfflineMessage: get String
EditProfileStrings.discardTitle: get String
EditProfileStrings.discardMessage: get String
EditProfileStrings.discard: get String
EditProfileStrings.keepEditing: get String
EditProfileStrings.errorTitle: get String
EditProfileStrings.errorMessage: get String
EditProfileStrings.offlineTitle: get String
EditProfileStrings.offlineMessage: get String
EditProfileStrings.retry: get String
EditProfileStrings.usernameNote: 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
editProfileTitleEdit profileModifier le profilتعديل الملف الشخصي
editProfileBackBackRetourرجوع
editProfileSaveSaveEnregistrerحفظ
editProfileLoadingLoading your profileChargement du profilجارٍ تحميل ملفك الشخصي
editProfilePhotoProfile photoPhoto de profilصورة الملف الشخصي
editProfileChangePhotoChange photoChanger la photoتغيير الصورة
editProfileTakePhotoTake a photoPrendre une photoالتقاط صورة
editProfileChoosePhotoChoose from libraryChoisir dans la galerieاختيار من المعرض
editProfileRemovePhotoRemove photoRetirer la photoإزالة الصورة
editProfileCloseCloseFermerإغلاق
editProfileNameDisplay nameNom affichéالاسم المعروض
editProfileNameRequiredEnter your nameSaisissez votre nomأدخل اسمك
editProfileUsernameUsernameNom d’utilisateurاسم المستخدم
editProfileUsernameRule3 to 20 lowercase letters, digits, dots or underscores3 à 20 minuscules, chiffres, points ou tirets basمن 3 إلى 20 حرفًا صغيرًا أو رقمًا أو نقطة أو شرطة سفلية
editProfileUsernameNote3 to 20 lowercase letters, digits, dots or underscores for the username.3 à 20 minuscules, chiffres, points ou tirets bas pour le nom d’utilisateur.من 3 إلى 20 حرفًا صغيرًا أو رقمًا أو نقطة أو شرطة سفلية لاسم المستخدم.
editProfileUsernameCheckingChecking availability…Vérification de la disponibilité…جارٍ التحقق من التوفر…
editProfileUsernameAvailable@{username} is available@{username} est disponible⁦@{username}⁩ متاح
editProfileUsernameTakenThis username is takenCe nom d’utilisateur est déjà prisاسم المستخدم هذا مأخوذ
editProfileUsernameUnknownAvailability could not be checkedDisponibilité non vérifiéeتعذّر التحقق من التوفر
editProfileBioBioBioنبذة
editProfileBioPlaceholderA few words about youQuelques mots sur vousبضع كلمات عنك
editProfileBioCount{count}/{max}{count}/{max}{count}/{max}
editProfileBioTooLong{max} characters at most{max} caractères au plus{max} حرفًا كحد أقصى
editProfileSaveErrorTitleYour changes were not savedVos modifications n’ont pas été enregistréesلم يتم حفظ تغييراتك
editProfileSaveErrorMessageSomething went wrong on our side. Try saving again.Un problème est survenu de notre côté. Enregistrez à nouveau.حدثت مشكلة من جهتنا. أعد الحفظ.
editProfileSaveOfflineMessageYou are offline. Check your connection, then save again.Vous êtes hors ligne. Vérifiez votre connexion, puis enregistrez à nouveau.أنت غير متصل. تحقق من اتصالك ثم احفظ مجددًا.
editProfileDiscardTitleDiscard your changes?Abandonner les modifications ?تجاهل التغييرات؟
editProfileDiscardMessageYour edits to the profile will be lost.Les modifications du profil seront perdues.ستفقد التعديلات على ملفك الشخصي.
editProfileDiscardDiscardAbandonnerتجاهل
editProfileKeepEditingKeep editingContinuerمتابعة التعديل
editProfileErrorTitleYour profile could not loadImpossible de charger le profilتعذّر تحميل ملفك الشخصي
editProfileErrorMessageSomething went wrong on our side.Un problème est survenu de notre côté.حدثت مشكلة من جهتنا.
editProfileOfflineTitleYou are offlineVous êtes hors ligneأنت غير متصل
editProfileOfflineMessageCheck your connection, then try again.Vérifiez votre connexion, puis réessayez.تحقق من اتصالك ثم أعد المحاولة.
editProfileRetryTry againRéessayerأعد المحاولة

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 edit-profile compares your copy to them.

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

Use it to

  • change the profile photo from the camera or the library
  • change the username and check that it is free
  • edit the profile bio with a character counter
  • submit the changes made to the profile
  • confirm before leaving a profile with unsaved changes
Not for
  • a single text field in a page: use a text field (text-field)
  • account settings, security and deletion: use account settings (account-settings)
States
  • Ready
  • Modified
  • Saving
  • Save error
  • Loading
  • Error
  • Offline

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