Skip to content

Instantly share code, notes, and snippets.

@maharatha
Created December 15, 2025 18:04
Show Gist options
  • Select an option

  • Save maharatha/985963431418349ae52f70c4889db88b to your computer and use it in GitHub Desktop.

Select an option

Save maharatha/985963431418349ae52f70c4889db88b to your computer and use it in GitHub Desktop.
FA Login Experience
import 'package:flutter/material.dart';
void main() {
runApp(const AuthStoryboardApp());
}
class AuthStoryboardApp extends StatelessWidget {
const AuthStoryboardApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'FA Auth Storyboard',
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: const Color(0xFF2563EB),
scaffoldBackgroundColor: const Color(0xFFF6F7FB),
),
home: const EntryScreen(),
);
}
}
/* ---------- SHARED UI ---------- */
class PageShell extends StatelessWidget {
final String title;
final Widget body;
final bool showBack;
const PageShell({
super.key,
required this.title,
required this.body,
this.showBack = true,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w700)),
leading: showBack
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
)
: null,
),
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
child: Padding(
padding: const EdgeInsets.all(16),
child: body,
),
),
),
);
}
}
Widget primaryButton(BuildContext c, String label, Widget page) {
return FilledButton(
onPressed: () => Navigator.push(c, MaterialPageRoute(builder: (_) => page)),
style: FilledButton.styleFrom(minimumSize: const Size.fromHeight(48)),
child: Text(label),
);
}
Widget section(String title, String subtitle) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
const SizedBox(height: 6),
Text(subtitle, style: TextStyle(color: Colors.black.withOpacity(0.6))),
],
);
}
/* ---------- SCREENS ---------- */
class EntryScreen extends StatelessWidget {
const EntryScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'First Alert App',
showBack: false,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Welcome', 'Choose how you want to sign in'),
const SizedBox(height: 24),
primaryButton(context, 'Login with Email', const EmailLoginScreen()),
const SizedBox(height: 12),
primaryButton(context, 'Login with OTP', const OtpEmailScreen()),
const SizedBox(height: 12),
primaryButton(context, 'Login with SSO', const SsoScreen()),
const SizedBox(height: 12),
OutlinedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const CreateAccountScreen()),
),
child: const Text('Create Account'),
),
],
),
);
}
}
/* ---------- EMAIL LOGIN ---------- */
class EmailLoginScreen extends StatelessWidget {
const EmailLoginScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Login with Email',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Enter Email', 'Password will be entered on Auth0'),
const SizedBox(height: 16),
const TextField(decoration: InputDecoration(labelText: 'Email')),
const SizedBox(height: 24),
primaryButton(context, 'Continue', const Auth0PasswordScreen()),
],
),
);
}
}
class Auth0PasswordScreen extends StatelessWidget {
const Auth0PasswordScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Auth0 – Password',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Enter Password', 'This screen is hosted by Auth0'),
const SizedBox(height: 16),
const TextField(
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
),
const SizedBox(height: 24),
primaryButton(context, 'Login', const DashboardScreen()),
],
),
);
}
}
/* ---------- OTP LOGIN ---------- */
class OtpEmailScreen extends StatelessWidget {
const OtpEmailScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Login with OTP',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Enter Email', 'We will send a one-time code'),
const SizedBox(height: 16),
const TextField(decoration: InputDecoration(labelText: 'Email')),
const SizedBox(height: 24),
primaryButton(context, 'Send Code', const OtpCodeScreen()),
],
),
);
}
}
class OtpCodeScreen extends StatelessWidget {
const OtpCodeScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Enter OTP',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Check your email', 'Enter the 6-digit code'),
const SizedBox(height: 16),
const TextField(decoration: InputDecoration(labelText: 'OTP Code')),
const SizedBox(height: 24),
primaryButton(context, 'Verify', const ProfileScreen()),
],
),
);
}
}
/* ---------- SSO ---------- */
class SsoScreen extends StatelessWidget {
const SsoScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Social Login',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Continue with SSO', 'Google or Apple'),
const SizedBox(height: 24),
primaryButton(context, 'Continue with Google', const ProfileScreen()),
const SizedBox(height: 12),
primaryButton(context, 'Continue with Apple', const ProfileScreen()),
],
),
);
}
}
/* ---------- CREATE ACCOUNT ---------- */
class CreateAccountScreen extends StatelessWidget {
const CreateAccountScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Create Account',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Create Account', 'No password required'),
const SizedBox(height: 16),
const TextField(decoration: InputDecoration(labelText: 'First Name')),
const SizedBox(height: 8),
const TextField(decoration: InputDecoration(labelText: 'Last Name')),
const SizedBox(height: 8),
const TextField(decoration: InputDecoration(labelText: 'Email')),
const SizedBox(height: 24),
primaryButton(context, 'Continue', const CheckEmailScreen()),
],
),
);
}
}
class CheckEmailScreen extends StatelessWidget {
const CheckEmailScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Check Your Email',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Next Steps', 'If eligible, you will receive a temporary password'),
const SizedBox(height: 24),
primaryButton(context, 'Login with Temp Password', const Auth0PasswordChangeScreen()),
],
),
);
}
}
class Auth0PasswordChangeScreen extends StatelessWidget {
const Auth0PasswordChangeScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Auth0 – Change Password',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Set New Password', 'Required before login'),
const SizedBox(height: 16),
const TextField(obscureText: true, decoration: InputDecoration(labelText: 'New Password')),
const SizedBox(height: 8),
const TextField(obscureText: true, decoration: InputDecoration(labelText: 'Confirm Password')),
const SizedBox(height: 24),
primaryButton(context, 'Save', const DashboardScreen()),
],
),
);
}
}
/* ---------- PROFILE ---------- */
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Complete Profile',
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('Profile Info', 'Required before continuing'),
const SizedBox(height: 16),
const TextField(decoration: InputDecoration(labelText: 'First Name')),
const SizedBox(height: 8),
const TextField(decoration: InputDecoration(labelText: 'Last Name')),
const SizedBox(height: 8),
const TextField(decoration: InputDecoration(labelText: 'Location')),
const SizedBox(height: 24),
primaryButton(context, 'Continue', const DashboardScreen()),
],
),
);
}
}
/* ---------- DASHBOARD ---------- */
class DashboardScreen extends StatelessWidget {
const DashboardScreen({super.key});
@override
Widget build(BuildContext context) {
return PageShell(
title: 'Dashboard',
showBack: false,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
section('You’re logged in', 'This represents FA App post-login'),
const SizedBox(height: 24),
FilledButton(
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const EntryScreen()),
(_) => false,
);
},
child: const Text('Sign Out'),
),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment