fadsync_email_validator 1.0.0 copy "fadsync_email_validator: ^1.0.0" to clipboard
fadsync_email_validator: ^1.0.0 copied to clipboard

Superfast email validation, disposable email detection, and anti-fraud auth guard SDK for Flutter. Secure login and signup flows with 1 click.

example/lib/main.dart

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

void main() {
  runApp(const FadSyncDemoApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FadSync Email Validator Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF00C49F),
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      home: const MainScreen(),
    );
  }
}

class MainScreen extends StatefulWidget {
  const MainScreen({super.key});

  @override
  State<MainScreen> createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  int _selectedTab = 0;
  String _apiKey = 'demo_free_key';
  late FadSyncEmailValidator _validator;

  @override
  void initState() {
    super.initState();
    _initValidator();
  }

  void _initValidator() {
    _validator = FadSyncEmailValidator(
      apiKey: _apiKey,
      baseUrl: 'https://mailcheck.fadsync.com',
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(6),
              decoration: BoxDecoration(
                color: const Color(0xFF00C49F).withValues(alpha: 0.2),
                borderRadius: BorderRadius.circular(8),
              ),
              child: const Icon(Icons.shield_rounded, color: Color(0xFF00C49F)),
            ),
            const SizedBox(width: 10),
            const Text(
              'FadSync Security',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
            ),
          ],
        ),
        actions: [
          IconButton(
            icon: const Icon(Icons.key_rounded),
            tooltip: 'Configure API Key',
            onPressed: _showApiKeyDialog,
          ),
        ],
      ),
      body: IndexedStack(
        index: _selectedTab,
        children: [
          SignupProtectionView(validator: _validator),
          LiveInspectorView(validator: _validator),
        ],
      ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _selectedTab,
        onDestinationSelected: (index) => setState(() => _selectedTab = index),
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.app_registration_rounded),
            label: '1-Click Auth Guard',
          ),
          NavigationDestination(
            icon: Icon(Icons.analytics_rounded),
            label: 'Email Inspector',
          ),
        ],
      ),
    );
  }

  void _showApiKeyDialog() {
    final controller = TextEditingController(text: _apiKey);
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: const Text('FadSync API Key'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              'Get your free API Key from https://mailcheck.fadsync.com/',
              style: TextStyle(fontSize: 13, color: Colors.grey),
            ),
            const SizedBox(height: 12),
            TextField(
              controller: controller,
              decoration: const InputDecoration(
                labelText: 'API Key',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.vpn_key_rounded),
              ),
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () {
              setState(() {
                _apiKey = controller.text.trim();
                _initValidator();
              });
              Navigator.pop(ctx);
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('API Key updated!')),
              );
            },
            child: const Text('Save Key'),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// TAB 1: 1-Click Secure Signup Flow
// ─────────────────────────────────────────────────────────────────────────────
class SignupProtectionView extends StatefulWidget {
  final FadSyncEmailValidator validator;

  const SignupProtectionView({super.key, required this.validator});

  @override
  State<SignupProtectionView> createState() => _SignupProtectionViewState();
}

class _SignupProtectionViewState extends State<SignupProtectionView> {
  final _emailController = TextEditingController();
  final _nameController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _isLoading = false;
  final List<String> _registeredUsersDb = [];

  Future<void> _handleSignup() async {
    final email = _emailController.text.trim();
    final name = _nameController.text.trim();

    if (name.isEmpty || email.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Please fill all required fields.')),
      );
      return;
    }

    setState(() => _isLoading = true);

    // 🛡️ 1-CLICK AUTH GUARD INTEGRATION
    await FadSyncAuthGuard.protect(
      client: widget.validator,
      email: email,
      // 1. SAFE & ALLOWED -> Save user to database
      onAllowed: (result) async {
        setState(() {
          _isLoading = false;
          _registeredUsersDb.add('${name.trim()} (${result.email})');
          _emailController.clear();
          _nameController.clear();
          _passwordController.clear();
        });

        _showSuccessDialog(result);
      },
      // 2. BLOCKED OR DISPOSABLE -> Show friendly error alert
      onBlocked: (errorMessage, result) {
        setState(() => _isLoading = false);
        _showBlockedDialog(errorMessage, result);
      },
      // 3. TYPO FOUND -> Offer 1-tap autocorrect
      onTypoFound: (suggestedEmail, result) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Did you mean $suggestedEmail?'),
            action: SnackBarAction(
              label: 'Apply',
              onPressed: () {
                setState(() {
                  _emailController.text = suggestedEmail;
                });
              },
            ),
          ),
        );
      },
    );
  }

  void _showSuccessDialog(ValidationResult result) {
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        icon: const Icon(Icons.check_circle_rounded, color: Color(0xFF00C49F), size: 48),
        title: const Text('Account Created! 🎉'),
        content: Text(
          'Email "${result.email}" was verified by FadSync and saved to the database.\n\nRisk Score: ${result.riskScore}/100',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('Great!'),
          ),
        ],
      ),
    );
  }

  void _showBlockedDialog(String errorMessage, ValidationResult result) {
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        icon: const Icon(Icons.block_rounded, color: Colors.redAccent, size: 48),
        title: const Text('Registration Blocked'),
        content: Text(
          '$errorMessage\n\n(Disposable: ${result.isDisposable}, Risk: ${result.riskScore})',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }

  void _fillPreset(String email) {
    setState(() {
      _emailController.text = email;
      _nameController.text = 'Demo User';
      _passwordController.text = 'Password123!';
    });
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Card(
            color: const Color(0xFF1E293B),
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Row(
                    children: [
                      Icon(Icons.shield_outlined, color: Color(0xFF00C49F)),
                      SizedBox(width: 8),
                      Text(
                        '1-Click Signup Security Guard',
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                      ),
                    ],
                  ),
                  const SizedBox(height: 6),
                  const Text(
                    'Try creating an account with a legitimate email, a disposable email, or a domain with a typo to see the SDK in action.',
                    style: TextStyle(color: Colors.white70, fontSize: 13),
                  ),
                  const SizedBox(height: 12),
                  Wrap(
                    spacing: 8,
                    runSpacing: 6,
                    children: [
                      ActionChip(
                        label: const Text('✅ Valid: alex@fadsync.com'),
                        onPressed: () => _fillPreset('alex@fadsync.com'),
                      ),
                      ActionChip(
                        label: const Text('🚫 Disposable: test@10minutemail.com'),
                        onPressed: () => _fillPreset('test@10minutemail.com'),
                      ),
                      ActionChip(
                        label: const Text('💡 Typo: user@gamil.com'),
                        onPressed: () => _fillPreset('user@gamil.com'),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 20),
          TextField(
            controller: _nameController,
            decoration: const InputDecoration(
              labelText: 'Full Name',
              border: OutlineInputBorder(),
              prefixIcon: Icon(Icons.person_outline),
            ),
          ),
          const SizedBox(height: 14),
          EmailValidatorFormField(
            client: widget.validator,
            controller: _emailController,
            triggerMode: ValidationTriggerMode.onChangedDebounced,
            debounceDuration: const Duration(milliseconds: 500),
            showTypoSuggestion: true,
            decoration: const InputDecoration(
              labelText: 'Email Address',
              border: OutlineInputBorder(),
              prefixIcon: Icon(Icons.email_outlined),
            ),
          ),
          const SizedBox(height: 14),
          TextField(
            controller: _passwordController,
            obscureText: true,
            decoration: const InputDecoration(
              labelText: 'Password',
              border: OutlineInputBorder(),
              prefixIcon: Icon(Icons.lock_outline),
            ),
          ),
          const SizedBox(height: 20),
          SizedBox(
            width: double.infinity,
            height: 50,
            child: ElevatedButton.icon(
              onPressed: _isLoading ? null : _handleSignup,
              icon: _isLoading
                  ? const SizedBox(
                      width: 20,
                      height: 20,
                      child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black),
                    )
                  : const Icon(Icons.lock_outline),
              label: Text(
                _isLoading ? 'Validating Email...' : 'Create Secure Account',
                style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
              ),
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFF00C49F),
                foregroundColor: Colors.black,
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
              ),
            ),
          ),
          const SizedBox(height: 30),
          const Text(
            'Simulated Database (Users Saved):',
            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
          ),
          const SizedBox(height: 8),
          if (_registeredUsersDb.isEmpty)
            const Text(
              'No users saved yet. Sign up above to persist clean accounts.',
              style: TextStyle(color: Colors.grey, fontSize: 13),
            )
          else
            ..._registeredUsersDb.map(
              (u) => Container(
                margin: const EdgeInsets.only(bottom: 6),
                padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                decoration: BoxDecoration(
                  color: const Color(0xFF1E293B),
                  borderRadius: BorderRadius.circular(6),
                ),
                child: Row(
                  children: [
                    const Icon(Icons.check_circle, size: 16, color: Color(0xFF00C49F)),
                    const SizedBox(width: 8),
                    Text(u, style: const TextStyle(fontSize: 13)),
                  ],
                ),
              ),
            ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// TAB 2: Live Email Inspector
// ─────────────────────────────────────────────────────────────────────────────
class LiveInspectorView extends StatefulWidget {
  final FadSyncEmailValidator validator;

  const LiveInspectorView({super.key, required this.validator});

  @override
  State<LiveInspectorView> createState() => _LiveInspectorViewState();
}

class _LiveInspectorViewState extends State<LiveInspectorView> {
  final _controller = TextEditingController(text: 'demo@mailinator.com');
  ValidationResult? _result;
  bool _isLoading = false;
  String? _errorMessage;

  Future<void> _analyzeEmail() async {
    final email = _controller.text.trim();
    if (email.isEmpty) return;

    setState(() {
      _isLoading = true;
      _errorMessage = null;
    });

    try {
      final res = await widget.validator.verify(email);
      setState(() {
        _result = res;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _errorMessage = e.toString();
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              labelText: 'Email to Inspect',
              border: const OutlineInputBorder(),
              prefixIcon: const Icon(Icons.search_rounded),
              suffixIcon: IconButton(
                icon: const Icon(Icons.arrow_forward_rounded),
                onPressed: _isLoading ? null : _analyzeEmail,
              ),
            ),
            onSubmitted: (_) => _analyzeEmail(),
          ),
          const SizedBox(height: 16),
          SizedBox(
            width: double.infinity,
            child: ElevatedButton.icon(
              onPressed: _isLoading ? null : _analyzeEmail,
              icon: const Icon(Icons.radar_rounded),
              label: const Text('Inspect Email Health & Fraud Risk'),
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFF00C49F),
                foregroundColor: Colors.black,
              ),
            ),
          ),
          const SizedBox(height: 24),
          if (_isLoading)
            const Center(child: CircularProgressIndicator())
          else if (_errorMessage != null)
            Card(
              color: Colors.red.shade900.withValues(alpha: 0.4),
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text('Error: $_errorMessage'),
              ),
            )
          else if (_result != null)
            _buildResultCard(_result!),
        ],
      ),
    );
  }

  Widget _buildResultCard(ValidationResult r) {
    final isBlocked = r.isBlocked;
    final statusColor = isBlocked ? Colors.redAccent : const Color(0xFF00C49F);

    return Card(
      color: const Color(0xFF1E293B),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  r.email,
                  style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                ),
                Chip(
                  label: Text(
                    r.recommendation.name.toUpperCase(),
                    style: TextStyle(fontWeight: FontWeight.bold, color: statusColor),
                  ),
                  backgroundColor: statusColor.withValues(alpha: 0.15),
                ),
              ],
            ),
            const Divider(height: 24),
            _infoRow('Safe to Register', r.isSafeToRegister ? 'YES ✅' : 'NO ❌',
                r.isSafeToRegister ? Colors.green : Colors.redAccent),
            _infoRow('Disposable / Burner', r.isDisposable ? 'YES (High Risk)' : 'NO (Clean)',
                r.isDisposable ? Colors.redAccent : Colors.green),
            _infoRow('Syntax Format', r.isValidFormat ? 'Valid RFC' : 'Invalid Syntax',
                r.isValidFormat ? Colors.green : Colors.redAccent),
            _infoRow('MX Mail Servers', r.domainDetails.hasValidMx ? 'Active' : 'Missing / Dead',
                r.domainDetails.hasValidMx ? Colors.green : Colors.redAccent),
            _infoRow('Free Provider', r.isFreeProvider ? 'Yes (Public)' : 'No (Custom Domain)',
                Colors.white70),
            _infoRow('Role / Department Account', r.isRoleAccount ? 'Yes (support/admin)' : 'No (Personal)',
                Colors.white70),
            _infoRow('Risk Score', '${r.riskScore} / 100',
                r.riskScore > 60 ? Colors.redAccent : Colors.green),
            if (r.hasTypoSuggestion)
              _infoRow('Typo Suggestion', r.typoFix!, Colors.amberAccent),
            const SizedBox(height: 12),
            Container(
              padding: const EdgeInsets.all(10),
              decoration: BoxDecoration(
                color: Colors.black26,
                borderRadius: BorderRadius.circular(6),
              ),
              child: Text(
                'End-User Friendly UI Message:\n"${r.userFriendlyMessage}"',
                style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _infoRow(String label, String value, Color valueColor) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label, style: const TextStyle(color: Colors.white70, fontSize: 13)),
          Text(
            value,
            style: TextStyle(fontWeight: FontWeight.bold, color: valueColor, fontSize: 13),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
26
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Superfast email validation, disposable email detection, and anti-fraud auth guard SDK for Flutter. Secure login and signup flows with 1 click.

Homepage
Repository (GitHub)
View/report issues

Topics

#email #validation #security #authentication #disposable-email

License

MIT (license)

Dependencies

flutter, http

More

Packages that depend on fadsync_email_validator