Fluxy
E-Commerce Example

Authentication & Onboarding

Secure login and user registration flows.

Authentication & Onboarding

The Authentication module provides beautiful, secure screens for logging in and registering new users.

Auth View (lib/features/auth/auth.view.dart)

import 'package:flutter/material.dart';
import 'package:fluxy/fluxy.dart';
import '../../core/widgets/app_shimmer.dart';
import 'auth.controller.dart';

class AuthView extends StatelessWidget {
  final AuthController controller;
  
  const AuthView({super.key, required this.controller});

  @override
  Widget build(BuildContext context) {
    return Fx(() {
      final isDark = Theme.of(context).brightness == Brightness.dark;
      final isSignUp = controller.isSignUpMode.value;
      final loading = controller.isLoading.value;

      return Fx.scaffold(
        backgroundColor: isDark ? const Color(0xFF121212) : const Color(0xFFF8FAFC),
        body: Fx.safeArea(
          child: Fx.col(
            alignItems: CrossAxisAlignment.start,
            children: [
              // Header
              Fx.col(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Fx.box()
                    .w(60).h(60)
                    .bg(Colors.blueAccent)
                    .rounded(20)
                    .shadowMedium()
                    .center()
                    .child(const Icon(Icons.shopping_bag_rounded, color: Colors.white, size: 30))
                    .mb(32),

                  Fx.text(isSignUp ? 'Create Account' : 'Welcome Back')
                    .font.xl4()
                    .bold()
                    .color(isDark ? Colors.white : const Color(0xFF0F172A)),
                    
                  Fx.gap(8),
                  
                  Fx.text(isSignUp ? 'Sign up to discover premium products.' : 'Sign in to access your premium account.')
                    .font.md()
                    .tw('opacity-70')
                    .color(isDark ? Colors.grey.shade400 : Colors.grey.shade600),
                ]
              ).wFull().px(24).pt(40).pb(20),

              // Form
              Fx.col(
                children: [
                  if (isSignUp) ...[
                    Fx.input(
                      signal: controller.fullName,
                      placeholder: 'Full Name',
                      icon: Icons.person_outline,
                    ).bg(isDark ? const Color(0xFF1E1E1E) : Colors.white)
                     .border(color: isDark ? Colors.white12 : Colors.grey.shade200, width: 1.5)
                     .rounded(20)
                     .mb(20)
                     .py(20),
                  ],

                  Fx.input(
                    signal: controller.email,
                    placeholder: 'Email Address',
                    icon: Icons.email_outlined,
                  ).bg(isDark ? const Color(0xFF1E1E1E) : Colors.white)
                   .border(color: isDark ? Colors.white12 : Colors.grey.shade200, width: 1.5)
                   .rounded(20)
                   .mb(20)
                   .py(20),

                  Fx.input(
                    signal: controller.password,
                    placeholder: 'Password',
                    icon: Icons.lock_outline,
                    obscureText: true,
                  ).bg(isDark ? const Color(0xFF1E1E1E) : Colors.white)
                   .border(color: isDark ? Colors.white12 : Colors.grey.shade200, width: 1.5)
                   .rounded(20)
                   .mb(8)
                   .py(20),

                  if (!isSignUp)
                    Fx.row(
                      justify: MainAxisAlignment.end,
                      children: [
                        Fx.text('Forgot Password?')
                          .font.sm()
                          .bold()
                          .color(Colors.blueAccent)
                          .pointer()
                          .onTap(() {})
                      ]
                    ).mb(32)
                  else
                    Fx.gap(32),

                  // Auth Button
                  if (loading)
                    const AppShimmer(
                      borderRadius: BorderRadius.all(Radius.circular(24)),
                    ).wFull().h(68)
                  else
                    Fx.box()
                      .wFull()
                      .py(20)
                      .rounded(24)
                      .shadowMedium()
                      .pointer()
                      .bg(const Color(0xFF0D0D2B))
                      .center()
                      .child(
                        Fx.text(isSignUp ? 'Sign Up' : 'Sign In').font.xl().bold().color(Colors.white)
                      )
                      .onTap(() => controller.handleAuth(context)),

                  Fx.gap(24),

                  // Divider
                  Fx.row(
                    children: [
                      Fx.box().h(1).bg(isDark ? Colors.white12 : Colors.grey.shade300).expanded(),
                      Fx.text(' OR ').font.sm().bold().color(Colors.grey.shade500).px(12),
                      Fx.box().h(1).bg(isDark ? Colors.white12 : Colors.grey.shade300).expanded(),
                    ]
                  ).mb(24),

                  // OAuth Buttons
                  Fx.row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      _buildSocialButton(Icons.g_mobiledata_rounded, Colors.redAccent, isDark),
                      Fx.gap(16),
                      _buildSocialButton(Icons.apple, isDark ? Colors.white : Colors.black, isDark),
                      Fx.gap(16),
                      _buildSocialButton(Icons.facebook, Colors.blue, isDark),
                    ]
                  ).mb(32),

                  // Toggle mode
                  Fx.row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Fx.text(isSignUp ? 'Already have an account? ' : 'Don\'t have an account? ')
                        .font.md()
                        .color(isDark ? Colors.grey.shade400 : Colors.grey.shade600),
                      Fx.text(isSignUp ? 'Sign In' : 'Sign Up')
                        .font.md()
                        .bold()
                        .color(Colors.blueAccent)
                        .pointer()
                        .onTap(controller.toggleMode),
                    ]
                  )
                ],
              ).px(24).scrollable().expanded(),
            ],
          ).hFull(),
        ),
      );
    });
  }

  Widget _buildSocialButton(IconData icon, Color color, bool isDark) {
    return Fx.box()
      .w(64).h(64)
      .rounded(20)
      .bg(isDark ? const Color(0xFF1E1E1E) : Colors.white)
      .border(color: isDark ? Colors.white12 : Colors.grey.shade200, width: 1.5)
      .shadowSmall()
      .center()
      .pointer()
      .child(Icon(icon, color: color, size: 36))
      .onTap(() {});
  }
}

Auth Controller (lib/features/auth/auth.controller.dart)

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

class AuthController extends FluxController {
  final email = flux<String>("");
  final fullName = flux<String>("");
  final password = flux<String>("");
  final isLoading = flux<bool>(false);
  final isSignUpMode = flux<bool>(false);

  Future<void> handleAuth(BuildContext context) async {
    if (email.value.isEmpty || password.value.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Please enter all fields.'), backgroundColor: Colors.redAccent)
      );
      return;
    }

    isLoading.value = true;
    
    // Simulate auth network request
    await Future.delayed(const Duration(seconds: 2));
    
    isLoading.value = false;
    
    // Auth successful -> go to home
    Fluxy.offAll('/home');
  }

  void toggleMode() {
    isSignUpMode.value = !isSignUpMode.value;
  }
}

📱 Visual Examples

Sign In ScreenSign In DarkSign Up ScreenSign Up Dark

On this page