layou_auth 1.0.1 copy "layou_auth: ^1.0.1" to clipboard
layou_auth: ^1.0.1 copied to clipboard

Firebase Auth package with Google, Apple, and Email sign-in/linking support. Provides ready-to-use widgets and Riverpod providers.

πŸ” LayouAuth #

Firebase Auth in 5 minutes. Sign-in, account linking, and beautiful UI included.

pub package likes popularity License: MIT

Features β€’ Quick Start β€’ Screenshots β€’ Examples β€’ Docs


πŸ€” Why LayouAuth? #

Stop writing Firebase Auth boilerplate. Get a production-ready auth system with beautiful UI in minutes.

❌ Without LayouAuth #

// 500+ lines of boilerplate
class AuthService {
  Future<void> signInWithGoogle() async {
    try {
      final googleUser = await GoogleSignIn().signIn();
      if (googleUser == null) return;
      final googleAuth = await googleUser.authentication;
      final credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      await FirebaseAuth.instance.signInWithCredential(credential);
    } on FirebaseAuthException catch (e) {
      // Handle errors...
    }
  }
  // + 400 more lines for Apple, Email, linking...
}

// Then build UI from scratch
// Handle states, errors, loading...
// Wire up Riverpod...

βœ… With LayouAuth #

// Initialize once
void main() async {
  LayouAuth.initialize(
    config: LayouAuthConfig(
      providers: [
        GoogleProviderConfig(iosClientId: '...'),
        const AppleProviderConfig(),
        const EmailProviderConfig(),
      ],
    ),
  );
  runApp(const ProviderScope(child: MyApp()));
}

// Use anywhere
LayouAuthSheet.show(context, mode: LayouAuthMode.signIn);

That's it. Sign-in, linking, errors, loading states, UIβ€”all handled.


✨ Features #

πŸš€ Ready-to-use UI #

Beautiful bottom sheets, inline sections, and individual buttons. Dark mode included.

πŸ”— Account Linking #

Convert anonymous users to permanent accounts seamlessly.

🧩 Riverpod Integration #

Providers for auth state, user info, and actions. No setup needed.

🎨 Fully Customizable #

Override strings, theme, or build your own UI with the builder API.

πŸ›‘οΈ Type-safe Errors #

No more catching FirebaseAuthException and parsing codes.

🌍 i18n Ready #

All strings customizable. Use your own translation system.

🎯 Supported Auth Methods #

Provider Sign In Link Account Status
πŸ”— Anonymous βœ… β€” Built-in
🌐 Google βœ… βœ… Fully supported
🍎 Apple βœ… βœ… iOS/macOS
πŸ“§ Email/Password βœ… βœ… Fully supported

πŸ†• Latest Enhancements #

  • πŸ—‘οΈ Account Deletion with automatic reauthentication
  • πŸ”„ Smart Credential Handling - If a provider is already linked elsewhere, offer to sign in
  • πŸ§ͺ Debug Mode - Long-press email field for test credentials
  • 🎣 Lifecycle Callbacks - Run code before/after sign-out or deletion

πŸ“Έ Screenshots #

πŸ”— Account Linking Flow #

Profile with Badge Link Options Sheet Email Form Settings Integration
[screenshots/profile_linked.png] [screenshots/link_options.jpg] [screenshots/email_form.jpg] [screenshots/settings_mobile.png]
User profile showing "Linked account" badge and "Link my account" button Clean bottom sheet with Google, Apple, and Email options Expandable email/password form with validation Settings page with "Link Account" entry and "Recommended" badge

🎨 What You Get #

  • βœ… Beautiful bottom sheets with handle bar and rounded corners
  • βœ… Smart error handling with inline error messages
  • βœ… Loading states with spinners on buttons
  • βœ… Expandable forms for email/password
  • βœ… Native platform look (Apple button black on iOS)
  • βœ… Dark mode support automatically
  • βœ… Consistent spacing and typography
  • βœ… Accessible with proper labels

⚑ Quick Start #

1️⃣ Install #

flutter pub add layou_auth

2️⃣ Initialize #

import 'package:layou_auth/layou_auth.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

  LayouAuth.initialize(
    config: LayouAuthConfig(
      providers: [
        GoogleProviderConfig(iosClientId: DefaultFirebaseOptions.ios.iosClientId),
        const AppleProviderConfig(),
        const EmailProviderConfig(),
      ],
    ),
  );

  runApp(const ProviderScope(child: MyApp()));
}

3️⃣ Use #

// Show auth bottom sheet
LayouAuthSheet.show(context, mode: LayouAuthMode.signIn);

That's it! πŸŽ‰ You have a complete auth system.


πŸ’‘ Examples #

Sign In #

LayouAuthSheet.show(
  context,
  mode: LayouAuthMode.signIn,
  onSuccess: (user, method) {
    print('Signed in with ${method.name}: ${user.email}');
  },
);
// Perfect for converting free users to permanent accounts
LayouAuthSheet.show(
  context,
  mode: LayouAuthMode.link,
  onSuccess: (user, method) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Account linked with ${method.name}!')),
    );
  },
);

Settings Page Integration #

// In your settings page
Card(
  child: ListTile(
    leading: const Icon(Icons.link),
    title: const Text('Link Account'),
    subtitle: const Text('Secure your notes by linking with Google, Apple, or Email'),
    trailing: const Chip(label: Text('Recommended')),
    onTap: () => LayouAuthSheet.show(context, mode: LayouAuthMode.link),
  ),
)

Delete Account with Confirmation #

// New! With automatic reauthentication
ElevatedButton(
  onPressed: () async {
    final confirmed = await LayouDeleteAccountSheet.show(
      context,
      title: 'Delete Account?',
      message: 'This will permanently delete your data.',
      onBeforeDelete: () async {
        // Delete user documents
        await firestore.collection('users').doc(userId).delete();
      },
    );

    if (confirmed == true) {
      // User deleted and automatically logged out
      context.go(Routes.welcome);
    }
  },
  style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
  child: const Text('Delete My Account'),
);

Sign Out with Callbacks #

// Clean up before/after logout
final service = ref.read(layouAuthServiceProvider);

await service.signOut(
  onBeforeLogout: () async {
    // Cancel subscriptions, clear cache, etc.
    await subscriptionService.cancelAll();
  },
  onAfterLogout: () async {
    // Navigate to home, track analytics, etc.
    analytics.logLogout();
    context.go(Routes.home);
  },
);

🎯 Advanced Usage #

Custom Strings (i18n) #

LayouAuthSheet.show(
  context,
  mode: LayouAuthMode.link,
  strings: LayouAuthStrings(
    linkAccountTitle: t.auth.linkTitle,
    linkAccountSubtitle: t.auth.linkSubtitle,
    googleButton: t.auth.continueWithGoogle,
    appleButton: t.auth.continueWithApple,
    emailButton: t.auth.continueWithEmail,
    closeButton: t.common.close,
  ),
);

Custom Theme #

LayouAuthSheet.show(
  context,
  theme: LayouAuthTheme(
    buttonBorderRadius: 16.0,
    buttonSpacing: 16.0,
    googleButtonStyle: OutlinedButton.styleFrom(...),
    appleButtonStyle: ElevatedButton.styleFrom(...),
  ),
);

Full Custom UI (Builder) #

LayouAuthSheet.show(
  context,
  builder: (context, state, actions) {
    return YourCustomWidget(
      isLoading: state.isLoading,
      error: state.error,
      onGoogleTap: actions.linkWithGoogle,
      onAppleTap: actions.linkWithApple,
    );
  },
);

Individual Buttons #

LayouGoogleButton(
  label: 'Continue with Google',
  isLoading: _isLoading,
  onPressed: () async {
    final result = await ref.read(layouAuthActionsProvider.notifier).signInWithGoogle();
    result.when(
      success: (user) => print('Success: ${user.email}'),
      error: (error) => print('Error: ${error.message}'),
    );
  },
)

Inline Section (Settings) #

LayouAuthSection(
  title: 'Link your account',
  subtitle: 'Keep your data safe',
  showGoogle: true,
  showApple: true,
  showEmail: true,
  onSuccess: (user, method) {
    showSuccessSnackbar('Account linked!');
  },
)

πŸ§ͺ Debug Mode Features #

Auto-fill Test Credentials #

In debug mode, long-press the email field to generate test credentials:

LayouEmailForm(
  onSubmit: _handleSubmit,
  debugCredentialsMessage: 'Test credentials generated!', // Optional snackbar
)

Result:

  • Email: test_XXX@yopmail.com (random number)
  • Password: azerty123

Perfect for rapid testing without typing!


πŸ”Œ Riverpod Providers #

Access auth state and actions anywhere in your app:

// Current user
final user = ref.watch(layouCurrentUserProvider);
if (user != null) {
  print('Logged in: ${user.email}');
}

// Auth state stream
ref.listen(layouAuthStateProvider, (previous, next) {
  if (next != null) {
    print('User signed in');
  }
});

// Check auth status
final isAuthenticated = ref.watch(layouIsAuthenticatedProvider);
final isAnonymous = ref.watch(layouIsAnonymousProvider);

// Check linked providers
final hasGoogle = ref.watch(layouHasGoogleProvider);
final hasApple = ref.watch(layouHasAppleProvider);
final hasEmail = ref.watch(layouHasEmailProvider);
final linkedProviders = ref.watch(layouLinkedProvidersProvider);

// Auth actions
final actions = ref.read(layouAuthActionsProvider.notifier);
await actions.signInWithGoogle();
await actions.linkWithApple();
await actions.signOut();
await actions.deleteUser();

πŸ›‘οΈ Error Handling #

All errors are type-safe. No more parsing Firebase error codes!

final result = await actions.signInWithGoogle();

result.when(
  success: (user) => print('Welcome ${user.email}'),
  error: (e) => switch (e) {
    UserCancelledException() => null, // User cancelled, do nothing
    CredentialAlreadyInUseException() => showCredentialInUseDialog(),
    NetworkException() => showError('Check your connection'),
    UserNotFoundException() => showError('No account found'),
    WrongPasswordException() => showError('Incorrect password'),
    WeakPasswordException() => showError('Password too weak'),
    _ => showError('Something went wrong'),
  },
);

Available Exceptions #

Exception When it happens
UserCancelledException User cancelled Google/Apple sign-in
NoUserException No authenticated user found
SignInFailedException Generic sign-in failure
LinkingFailedException Generic linking failure
CredentialAlreadyInUseException Provider already linked elsewhere
EmailAlreadyInUseException Email already registered
WeakPasswordException Password too weak
InvalidEmailException Invalid email format
UserNotFoundException User not found (sign-in)
WrongPasswordException Incorrect password
UserDisabledException Account disabled
NetworkException Network error
RequiresRecentLoginException Sensitive operation needs reauth
UnknownAuthException Unknown error

πŸš€ Smart Credential Handling #

New! When linking a provider that's already used on another account, LayouAuth offers to sign in instead:

// User tries to link Google account
// But that Google account exists on another account
// β†’ LayouAuth shows: "Account Already Exists. Sign in instead?"
// β†’ User clicks "Sign In"
// β†’ Seamlessly switches to the existing account

Customize the messaging:

LayouAuthSheet.show(
  context,
  mode: LayouAuthMode.link,
  credentialInUseTitle: 'Account Exists',
  credentialInUseMessage: 'This {provider} is already registered. Sign in?',
  credentialInUseSignInButton: 'Yes, Sign In',
  credentialInUseCancelButton: 'No, Thanks',
);

🎣 Lifecycle Hooks #

Run custom code at key moments:

Global Hooks (in config) #

LayouAuth.initialize(
  config: LayouAuthConfig(
    providers: [...],
    onSignedIn: (user, method) async {
      // Create user in Firestore
      await firestore.collection('users').doc(user.uid).set({
        'email': user.email,
        'signInMethod': method.name,
        'createdAt': FieldValue.serverTimestamp(),
      });
      // Track analytics
      analytics.logSignIn(method: method.name);
    },
    onAccountLinked: (user, method) async {
      // Award bonus for linking account
      await creditsService.addBonus(5);
      analytics.logAccountLinked(method: method.name);
    },
    onSignedOut: () async {
      // Clear local cache
      await cacheService.clearAll();
    },
    onUserDeleted: (uid) async {
      // Cloud Functions handles Firestore cleanup
      // But you can do client-side cleanup here
      await localDb.clear();
    },
  ),
);

Per-operation Callbacks #

// Sign out
await service.signOut(
  onBeforeLogout: () async {
    // Cancel subscriptions
    await subscriptionService.cancelAll();
  },
  onAfterLogout: () async {
    // Navigate
    context.go(Routes.home);
  },
);

// Delete account
await service.deleteUser(
  onBeforeDelete: () async {
    // Delete user-specific data
    await deleteUserDocuments();
  },
  onAfterDelete: () async {
    // Analytics
    analytics.logAccountDeleted();
  },
);

πŸ”§ Platform Setup #

πŸ“± iOS/macOS (Apple Sign-In)
  1. Enable capability in Xcode:

    • Open ios/Runner.xcworkspace
    • Select your target β†’ Signing & Capabilities
    • Click + Capability β†’ Add "Sign in with Apple"
  2. Configure in Apple Developer Console:

  3. Add to config:

    const AppleProviderConfig()
    
🌐 Google Sign-In
  1. Add configuration files:

    • iOS: Add GoogleService-Info.plist to ios/Runner/
    • Android: Add google-services.json to android/app/
  2. Get iOS client ID:

    GoogleProviderConfig(
      iosClientId: DefaultFirebaseOptions.ios.iosClientId,
      // Or manually: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com'
    )
    
  3. Enable in Firebase Console:

    • Authentication β†’ Sign-in method β†’ Google β†’ Enable
πŸ“§ Email/Password

No platform setup needed! Just enable in Firebase:

  1. Firebase Console β†’ Authentication β†’ Sign-in method β†’ Email/Password β†’ Enable

  2. Optional config:

    const EmailProviderConfig(
      passwordMinLength: 8, // Default: 6
    )
    

πŸ“– Documentation #

Core Concepts #

  • Sign In: Authenticate a user (creates new session)
  • Link Account: Attach a provider to existing anonymous user
  • Anonymous User: Temporary user without credentials
  • Permanent User: User with at least one auth provider linked

Common Patterns #

1. Start Anonymous, Link Later (Freemium)

// App start - create anonymous user
await LayouAuth.auth.signInAnonymously();

// User wants premium features - prompt to link
if (isPremiumFeature && isAnonymous) {
  LayouAuthSheet.show(
    context,
    mode: LayouAuthMode.link,
    onSuccess: (user, method) {
      enablePremiumFeatures();
    },
  );
}

2. Force Link Before Action

// User tries to save data
Future<void> saveNote() async {
  if (ref.read(layouIsAnonymousProvider)) {
    // Must link first
    final result = await LayouAuthSheet.show(
      context,
      mode: LayouAuthMode.link,
    );
    if (result != true) return; // User cancelled
  }

  // Now safe to save
  await notesService.save(note);
}

3. Settings Page with Recommended Badge

// Show "Recommended" badge if anonymous
final isAnonymous = ref.watch(layouIsAnonymousProvider);

ListTile(
  title: const Text('Link Account'),
  subtitle: const Text('Secure your data across devices'),
  trailing: isAnonymous
    ? const Chip(label: Text('Recommended'))
    : null,
  onTap: () => LayouAuthSheet.show(context, mode: LayouAuthMode.link),
)

🀝 Contributing #

Contributions are welcome! Please read CONTRIBUTING.md first.

Development Setup #

# Clone the repo
git clone https://github.com/yourusername/layou_auth.git

# Install dependencies
flutter pub get

# Run tests
flutter test

# Run example app
cd example
flutter run

Reporting Issues #

Found a bug? Open an issue with:

  • Flutter version
  • Device/OS
  • Steps to reproduce
  • Expected vs actual behavior

πŸ“‹ Roadmap #

  • ❌ Phone authentication
  • ❌ Multi-factor authentication
  • ❌ Password reset UI
  • ❌ Email verification UI
  • ❌ Anonymous user migration tools
  • ❌ Biometric authentication
  • ❌ Session management UI

πŸ™ Credits #

Built with ❀️ by the Layou team.

Special thanks to:


πŸ“„ License #

MIT License - see LICENSE file for details.


🌟 Show Your Support #

If LayouAuth saved you time, give it a ⭐️ on GitHub and a πŸ‘ on pub.dev!


⬆ Back to top

Made with ❀️ for the Flutter community

1
likes
0
points
30
downloads

Publisher

unverified uploader

Weekly Downloads

Firebase Auth package with Google, Apple, and Email sign-in/linking support. Provides ready-to-use widgets and Riverpod providers.

Repository (GitHub)
View/report issues

Topics

#firebase #authentication #riverpod #google-sign-in #apple-sign-in

License

unknown (license)

Dependencies

crypto, firebase_auth, flutter, flutter_riverpod, google_sign_in, riverpod_annotation, sign_in_with_apple

More

Packages that depend on layou_auth