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

A delightful, child-friendly Flutter authentication package featuring login, registration, forgot password, and password reset screens, plus managed auth flows, backend adapters, and large accessible [...]

example/lib/main.dart

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

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'kids_auth Example',
      debugShowCheckedModeBanner: false,
      home: ThemePickerScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final themes = {
      'Default 🌈': const KidsAuthTheme(),
      'Candy 🍬': KidsAuthTheme.candy(),
      'Ocean 🌊': KidsAuthTheme.ocean(),
      'Jungle 🌿': KidsAuthTheme.jungle(),
      'Space 🚀': KidsAuthTheme.space(),
    };

    return Scaffold(
      body: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            colors: [Color(0xFFEDE7F6), Color(0xFFFCE4EC)],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
        ),
        child: SafeArea(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 28),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const SizedBox(height: 40),
                const Text(
                  'kids_auth',
                  style: TextStyle(
                    fontSize: 32,
                    fontWeight: FontWeight.w900,
                    color: Color(0xFF6C63FF),
                    letterSpacing: 1.5,
                  ),
                ),
                const SizedBox(height: 8),
                const Text(
                  'Pick a theme to preview the connected flow',
                  style: TextStyle(color: Colors.black54, fontSize: 15),
                ),
                const SizedBox(height: 40),
                Expanded(
                  child: ListView(
                    children: themes.entries
                        .map(
                          (entry) => Padding(
                            padding: const EdgeInsets.only(bottom: 16),
                            child: _ThemeCard(
                              label: entry.key,
                              theme: entry.value,
                            ),
                          ),
                        )
                        .toList(),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _ThemeCard extends StatelessWidget {
  final String label;
  final KidsAuthTheme theme;

  const _ThemeCard({required this.label, required this.theme});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.push(
        context,
        MaterialPageRoute(
          builder: (_) => _DemoShell(theme: theme, name: label),
        ),
      ),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(18),
          boxShadow: [
            BoxShadow(
              color: theme.primaryColor.withValues(alpha: 0.2),
              blurRadius: 12,
              offset: const Offset(0, 4),
            ),
          ],
        ),
        child: Row(
          children: [
            Container(
              width: 32,
              height: 32,
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [theme.primaryColor, theme.secondaryColor],
                ),
                shape: BoxShape.circle,
              ),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Text(
                label,
                style: TextStyle(
                  fontWeight: FontWeight.w700,
                  fontSize: 16,
                  color: theme.primaryColor,
                ),
              ),
            ),
            Icon(
              Icons.arrow_forward_ios,
              size: 16,
              color: theme.primaryColor,
            ),
          ],
        ),
      ),
    );
  }
}

class _DemoShell extends StatefulWidget {
  final KidsAuthTheme theme;
  final String name;

  const _DemoShell({required this.theme, required this.name});

  @override
  State<_DemoShell> createState() => _DemoShellState();
}

class _DemoShellState extends State<_DemoShell> {
  late final _FakeAuthAdapter _adapter;
  late final _MemoryPersistenceStore _store;
  bool _loggedIn = false;
  KidsAuthFlowStep _step = KidsAuthFlowStep.login;
  String _email = '';
  String _status = 'Ready to test connected auth flow';

  @override
  void initState() {
    super.initState();
    _adapter = _FakeAuthAdapter(
      onStatusChanged: (status) {
        if (!mounted) return;
        setState(() => _status = status);
      },
      onLoggedIn: (email) {
        if (!mounted) return;
        setState(() {
          _email = email;
          _loggedIn = true;
        });
      },
    );
    _store = _MemoryPersistenceStore(
      const KidsAuthSavedLoginState(
        email: 'saved@kid.example',
        rememberMe: true,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    if (_loggedIn) {
      return _HomeScreen(
        theme: widget.theme,
        email: _email,
        status: _status,
        onLogout: () {
          setState(() {
            _loggedIn = false;
            _step = KidsAuthFlowStep.login;
            _status = 'Logged out';
          });
        },
        onPreviewReset: () {
          setState(() {
            _loggedIn = false;
            _step = KidsAuthFlowStep.resetPassword;
            _status = 'Previewing reset password flow';
          });
        },
        onPreviewRegister: () {
          setState(() {
            _loggedIn = false;
            _step = KidsAuthFlowStep.register;
            _status = 'Previewing sign-up flow';
          });
        },
      );
    }

    return KidsAuthConnectedFlow(
      theme: widget.theme,
      config: KidsAuthConfig(
        appName: widget.name,
        appTagline: 'Testing a connected auth flow',
      ),
      adapter: _adapter,
      persistenceStore: _store,
      initialStep: _step,
      resetToken: 'demo-reset-token',
      onStepChanged: (step) => setState(() => _step = step),
      onLoginSuccess: () {
        setState(() {
          _loggedIn = true;
          _step = KidsAuthFlowStep.login;
        });
      },
      onResetPasswordSuccess: () {
        setState(() => _status = 'Password reset successful');
      },
      onTermsOfServiceTap: () => _showLegalDialog(
        context,
        title: 'Terms & Conditions',
        body:
            'This demo uses fake sign-in logic for preview purposes only. No real account is created, and no personal data is sent to a backend.',
      ),
      onPrivacyPolicyTap: () => _showLegalDialog(
        context,
        title: 'Privacy Policy',
        body:
            'This demo stores the remembered email only in local in-memory state while the app is running. Closing the app resets the sample data.',
      ),
    );
  }

  void _showLegalDialog(
    BuildContext context, {
    required String title,
    required String body,
  }) {
    showDialog<void>(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(title),
        content: Text(body),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }
}

class _HomeScreen extends StatelessWidget {
  final KidsAuthTheme theme;
  final String email;
  final String status;
  final VoidCallback onLogout;
  final VoidCallback onPreviewReset;
  final VoidCallback onPreviewRegister;

  const _HomeScreen({
    required this.theme,
    required this.email,
    required this.status,
    required this.onLogout,
    required this.onPreviewReset,
    required this.onPreviewRegister,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: theme.backgroundGradient,
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
        ),
        child: SafeArea(
          child: Center(
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 28),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  KidsCharacterAvatar(
                    theme: theme,
                    size: 120,
                    mood: KidsCharacterMood.success,
                  ),
                  const SizedBox(height: 24),
                  Text(
                    'Welcome! 🎉',
                    style: TextStyle(
                      fontSize: 32,
                      fontWeight: FontWeight.w900,
                      color: theme.primaryColor,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Text(
                    email,
                    style: TextStyle(
                      fontSize: 15,
                      color: theme.textColor.withValues(alpha: 0.6),
                    ),
                  ),
                  const SizedBox(height: 16),
                  Container(
                    padding: const EdgeInsets.all(16),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(20),
                    ),
                    child: Text(
                      status,
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.w600,
                        color: theme.textColor,
                      ),
                    ),
                  ),
                  const SizedBox(height: 32),
                  KidsAuthButton(
                    label: 'Preview Sign Up',
                    theme: theme,
                    onPressed: onPreviewRegister,
                    leading: const KidsAuthIcon(
                      type: KidsAuthIconType.star,
                      size: 22,
                      color: Colors.white,
                    ),
                  ),
                  const SizedBox(height: 16),
                  KidsAuthButton(
                    label: 'Preview Reset Flow',
                    theme: theme,
                    onPressed: onPreviewReset,
                    leading: const KidsAuthIcon(
                      type: KidsAuthIconType.key,
                      size: 22,
                      color: Colors.white,
                    ),
                  ),
                  const SizedBox(height: 16),
                  KidsAuthButton(
                    label: 'Log Out',
                    theme: theme,
                    isSecondary: true,
                    onPressed: onLogout,
                    leading: KidsAuthIcon(
                      type: KidsAuthIconType.logout,
                      size: 22,
                      color: theme.primaryColor,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _FakeAuthAdapter implements KidsAuthAdapter {
  final ValueChanged<String> onStatusChanged;
  final ValueChanged<String> onLoggedIn;

  _FakeAuthAdapter({
    required this.onStatusChanged,
    required this.onLoggedIn,
  });

  @override
  Future<void> login(String email, String password) async {
    onStatusChanged('Signing in as $email...');
    await Future.delayed(const Duration(milliseconds: 900));
    if (password.length < 6) {
      throw Exception('Wrong password, try again! 🙈');
    }
    onLoggedIn(email);
    onStatusChanged('Signed in successfully');
  }

  @override
  Future<void> register(String email, String password) async {
    onStatusChanged('Creating account for $email...');
    await Future.delayed(const Duration(milliseconds: 900));
    if (password.length < 6) {
      throw Exception('That password is too short 🙈');
    }
    onLoggedIn(email);
    onStatusChanged('Account created successfully');
  }

  @override
  Future<void> resetPassword(String newPassword, String? resetToken) async {
    onStatusChanged('Resetting password with token: ${resetToken ?? 'none'}');
    await Future.delayed(const Duration(milliseconds: 900));
    if (newPassword.length < 6) {
      throw Exception('That password is too short 🙈');
    }
    onStatusChanged('Password reset complete');
  }

  @override
  Future<void> sendResetLink(String email) async {
    onStatusChanged('Sending reset link to $email');
    await Future.delayed(const Duration(milliseconds: 900));
  }
}

class _MemoryPersistenceStore implements KidsAuthPersistenceStore {
  KidsAuthSavedLoginState? _state;

  _MemoryPersistenceStore(this._state);

  @override
  Future<void> clearLoginState() async {
    _state = null;
  }

  @override
  Future<KidsAuthSavedLoginState?> readLoginState() async => _state;

  @override
  Future<void> writeLoginState(KidsAuthSavedLoginState state) async {
    _state = state;
  }
}
0
likes
140
points
94
downloads

Documentation

API reference

Publisher

verified publishershop.stripedape.tech

Weekly Downloads

A delightful, child-friendly Flutter authentication package featuring login, registration, forgot password, and password reset screens, plus managed auth flows, backend adapters, and large accessible UI components for Flutter apps.

Repository (GitHub)
View/report issues

Topics

#flutter #authentication #ui #login #kids

License

MIT (license)

Dependencies

flutter

More

Packages that depend on kids_auth