Authsome

Flutter UI

Pre-built Material Design 3 authentication screens and widgets for Flutter.

The authsome_flutter_ui package provides a complete set of Material Design 3 authentication screens, headless builder widgets, and user management components for Flutter. It integrates with AuthProvider from authsome_flutter and supports auto-configuration from your Authsome backend.

Installation

Add both packages to your pubspec.yaml:

dependencies:
  authsome_flutter:
    # your version or path
  authsome_flutter_ui:
    # your version or path

authsome_flutter_ui re-exports authsome_flutter for convenience, so a single import gives you everything:

import 'package:authsome_flutter_ui/authsome_flutter_ui.dart';

Quick start

Wrap your app with AuthProvider, then use any pre-built screen:

import 'package:flutter/material.dart';
import 'package:authsome_flutter_ui/authsome_flutter_ui.dart';

void main() {
  runApp(
    AuthProvider(
      config: AuthConfig(baseURL: 'https://api.example.com'),
      child: const MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
      home: const AuthGuard(
        child: HomePage(),
        fallback: SignInPage(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SignInForm(
          onSuccess: () => Navigator.pushReplacementNamed(context, '/home'),
          onSignUpTap: () => Navigator.pushNamed(context, '/sign-up'),
          onForgotPasswordTap: () => Navigator.pushNamed(context, '/forgot'),
        ),
      ),
    );
  }
}

Styled screens

All screens are Material Design 3 styled, use Theme.of(context) for colors and text styles, and accept string-override props for localization.

SignInForm

Multi-step email-then-password flow with optional social login buttons. Auto-discovers social providers from clientConfig.

SignInForm(
  onSuccess: () => Navigator.pushReplacementNamed(context, '/home'),
  onSignUpTap: () => Navigator.pushNamed(context, '/sign-up'),
  onForgotPasswordTap: () => Navigator.pushNamed(context, '/forgot'),
  onMagicLinkTap: () => Navigator.pushNamed(context, '/magic-link'),
  logo: Image.asset('assets/logo.png', height: 48),
  // Override auto-detected social providers:
  socialProviders: [
    SocialProvider(id: 'google', name: 'Google'),
    SocialProvider(id: 'github', name: 'GitHub'),
  ],
  socialLayout: SocialButtonLayout.grid,
)
PropTypeDescription
onSuccessVoidCallback?Called after successful sign-in
onSignUpTapVoidCallback?Navigate to sign-up
onForgotPasswordTapVoidCallback?Navigate to forgot password
onMagicLinkTapVoidCallback?Navigate to magic link
onSocialLoginFunction(String, String)?Social login callback (providerId, url)
socialProvidersList<SocialProvider>?Override auto-detected providers
socialLayoutSocialButtonLayout?grid, iconRow, or vertical
logoWidget?Logo above the title
titleTextStringDefault: "Sign in"
emailLabelStringDefault: "Email"
passwordLabelStringDefault: "Password"

SignUpForm

Multi-step email-then-name-and-password registration flow.

SignUpForm(
  onSuccess: () => Navigator.pushReplacementNamed(context, '/home'),
  onSignInTap: () => Navigator.pop(context),
)

ForgotPasswordForm

Email input with success confirmation view showing a mail icon.

ForgotPasswordForm(
  onSuccess: () => print('Reset email sent'),
  onSignInTap: () => Navigator.pop(context),
)

ResetPasswordForm

New password and confirm password fields with strength indicator. Requires a token from a deep link.

ResetPasswordForm(
  token: resetToken,
  onSuccess: () => Navigator.pushReplacementNamed(context, '/sign-in'),
)

MfaChallengeForm

Tabbed multi-method MFA: TOTP (authenticator), SMS (with 60s resend cooldown), and recovery codes. Auto-discovers available methods from clientConfig.mfa.methods.

MfaChallengeForm(
  enrollmentId: enrollmentId,
  onSuccess: () => Navigator.pushReplacementNamed(context, '/home'),
  // Override auto-detected methods:
  methods: [MfaMethod.totp, MfaMethod.sms, MfaMethod.recovery],
  defaultMethod: MfaMethod.totp,
)

MagicLinkForm

Email input that sends a passwordless sign-in link.

MagicLinkForm(
  onSuccess: () => print('Magic link sent'),
  onSignInTap: () => Navigator.pop(context),
)

ChangePasswordForm

Current password, new password, and confirm password fields.

ChangePasswordForm(
  onSuccess: () => ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('Password changed')),
  ),
)

EmailVerificationForm

6-digit OTP input with a resend button and 60-second cooldown timer.

EmailVerificationForm(
  email: 'user@example.com',
  onSuccess: () => Navigator.pushReplacementNamed(context, '/home'),
  cooldownSeconds: 60,
)

User management widgets

UserAvatar

CircleAvatar with image URL or initials fallback. Reads user data from context.auth.user.

// Automatic from auth state
UserAvatar(size: AvatarSize.md)

// Manual override
UserAvatar(
  imageUrl: 'https://example.com/avatar.jpg',
  name: 'John Doe',
  size: AvatarSize.lg,
)

UserButton

Avatar with a popup menu containing profile, settings, and sign-out actions.

UserButton(
  onProfileTap: () => Navigator.pushNamed(context, '/profile'),
  onSettingsTap: () => Navigator.pushNamed(context, '/settings'),
  onSignOut: () => context.auth.signOut(),
  menuItems: [
    PopupMenuItem(value: 'help', child: Text('Help')),
  ],
)

UserProfileCard

Card displaying avatar, name, email, and other user info with optional inline editing.

UserProfileCard(
  editable: true,
  onUpdate: () => print('Profile updated'),
)

OrgSwitcher

Popup menu for switching between organizations and creating new ones.

OrgSwitcher(
  activeOrgId: currentOrgId,
  onOrgChange: (orgId) => setState(() => currentOrgId = orgId),
  onCreateOrg: () => print('New org created'),
)

Session and device management

SessionList

Fetches and displays active sessions with device icons, IP addresses, relative timestamps, and revoke actions. The current session is badged and protected from accidental revocation.

SessionList(
  currentSessionToken: context.auth.session?.sessionToken,
  onRevoke: (sessionId) => print('Revoked $sessionId'),
)

DeviceList

Fetches and displays registered devices with trust/untrust badges and delete actions.

DeviceList(
  onTrust: (deviceId) => print('Trusted $deviceId'),
  onDelete: (deviceId) => print('Deleted $deviceId'),
)

Headless builders

Builder-pattern widgets that expose form state without rendering any UI. Use these when you need full control over the layout.

AuthGuard

Shows different widgets based on authentication state.

AuthGuard(
  child: const HomePage(),
  fallback: const SignInPage(),
  loading: const Center(child: CircularProgressIndicator()),
)

SignInFormBuilder

SignInFormBuilder(
  builder: (state) => Column(
    children: [
      TextField(
        onChanged: state.setEmail,
        decoration: InputDecoration(hintText: 'Email'),
      ),
      TextField(
        onChanged: state.setPassword,
        obscureText: true,
        decoration: InputDecoration(hintText: 'Password'),
      ),
      if (state.error != null) Text(state.error!, style: TextStyle(color: Colors.red)),
      ElevatedButton(
        onPressed: state.isLoading ? null : state.submit,
        child: state.isLoading
            ? CircularProgressIndicator()
            : Text('Sign In'),
      ),
    ],
  ),
)

SignUpFormBuilder

Same pattern as SignInFormBuilder, adds name/setName to the state object.

MfaChallengeFormBuilder

MfaChallengeFormBuilder(
  enrollmentId: enrollmentId,
  builder: (state) => Column(
    children: [
      TextField(onChanged: state.setCode),
      ElevatedButton(
        onPressed: state.isLoading ? null : state.submit,
        child: Text('Verify'),
      ),
    ],
  ),
)

Shared widgets

Reusable building blocks used by the styled screens. You can use them in your own custom layouts.

WidgetDescription
AuthCardCentered card with title, description, logo, footer, and content area
ErrorDisplayInline error banner using colorScheme.errorContainer
PasswordInputTextField with visibility toggle (eye icon)
OtpInput6-digit PIN input with auto-submit on completion
OrDividerRow with "or" text between two dividers
SocialButtonsSocial provider buttons in grid, iconRow, or vertical layout
LoadingIndicatorSized CircularProgressIndicator (sm, md, lg)

Customization

Theme integration

All widgets use Theme.of(context).colorScheme and Theme.of(context).textTheme. Customize the look by adjusting your ThemeData:

MaterialApp(
  theme: ThemeData(
    useMaterial3: true,
    colorSchemeSeed: Colors.indigo,
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
    ),
  ),
  // ...
)

AuthTheme

For finer control over spacing and border radius, wrap your tree with AuthTheme:

AuthTheme(
  data: AuthThemeData(
    cardMaxWidth: 420,
    cardPadding: 32,
    fieldSpacing: 16,
    sectionSpacing: 24,
    borderRadius: 16,
  ),
  child: SignInForm(/* ... */),
)

String overrides (localization)

Every user-facing string is an optional named parameter with an English default:

SignInForm(
  titleText: 'Connexion',
  emailLabel: 'Adresse e-mail',
  passwordLabel: 'Mot de passe',
  continueLabel: 'Continuer',
  signUpLabel: 'Cr\u00e9er un compte',
  forgotPasswordLabel: 'Mot de passe oubli\u00e9 ?',
)

Social login

Social login uses a callback pattern to keep the package dependency-free. Your app is responsible for launching the OAuth URL:

SignInForm(
  onSocialLogin: (providerId, url) async {
    // Use url_launcher or custom WebView to open the OAuth URL
    await launchUrl(Uri.parse(url));
  },
)

Auto-configuration

When AuthProvider fetches the client config from your backend (/v1/auth/client-config), the UI widgets automatically adapt:

  • Social providers: SignInForm and SignUpForm show buttons for providers enabled in your dashboard
  • MFA methods: MfaChallengeForm shows tabs only for enabled methods (TOTP, SMS, recovery)
  • Magic link: Sign-in forms can show a magic link option when enabled
  • Passkeys: Future support for passkey-based authentication

You can override any auto-detected value with explicit props.

React to Flutter mapping

React ComponentFlutter Widget
<SignIn />SignInForm
<SignUp />SignUpForm
<ForgotPassword />ForgotPasswordForm
<ResetPassword />ResetPasswordForm
<MFAChallenge />MfaChallengeForm
<MagicLink />MagicLinkForm
<ChangePassword />ChangePasswordForm
<EmailVerification />EmailVerificationForm
<UserAvatar />UserAvatar
<UserButton />UserButton
<UserProfile />UserProfileCard
<OrgSwitcher />OrgSwitcher
<SessionList />SessionList
<DeviceList />DeviceList
useSignIn()SignInFormBuilder
useSignUp()SignUpFormBuilder
useMFA()MfaChallengeFormBuilder
<AuthGuard>AuthGuard

Platform support

PlatformStatus
iOSSupported
AndroidSupported
WebSupported
macOSSupported
WindowsSupported
LinuxSupported

All platforms use flutter_secure_storage for credential persistence (Keychain on Apple, EncryptedSharedPreferences on Android, libsecret on Linux, Windows Credential Manager on Windows).

On this page