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

Hawcx Flutter SDK plugin providing V5 auth, session, and push flows via native Hawcx SDKs.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hawcx_flutter_sdk/hawcx_flutter_sdk.dart';

import 'hawcx_config.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hawcx Flutter Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
        useMaterial3: true,
      ),
      home: const HawcxExampleHome(),
    );
  }
}

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

  @override
  State<HawcxExampleHome> createState() => _HawcxExampleHomeState();
}

class _HawcxExampleHomeState extends State<HawcxExampleHome> {
  final HawcxClient _client = HawcxClient();

  final TextEditingController _userIdController =
      TextEditingController(text: 'user@example.com');
  final TextEditingController _otpController = TextEditingController();

  StreamSubscription<HawcxEvent>? _eventsSub;

  bool _initialized = false;
  bool _authInProgress = false;
  bool _otpRequired = false;
  String? _initError;
  String? _statusMessage;
  bool _statusIsError = false;
  HawcxAuthHandle? _authHandle;
  final List<String> _logs = <String>[];

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

    if (!kIsWeb &&
        (defaultTargetPlatform == TargetPlatform.android ||
            defaultTargetPlatform == TargetPlatform.iOS)) {
      _eventsSub = _client.events.listen(
        _onEvent,
        onError: (Object error, StackTrace stack) {
          _appendLog('Event stream error: $error');
        },
      );
      if (defaultHawcxConfig == null) {
        _initError =
            'Missing Hawcx config. Update example/lib/hawcx_config.dart.';
        _appendLog(_initError!);
        _setStatus(_initError!, isError: true);
      } else {
        _initialize();
      }
    } else {
      _appendLog(
        'This example is intended for iOS/Android. Current platform: '
        '${kIsWeb ? 'web' : defaultTargetPlatform}',
      );
    }
  }

  @override
  void dispose() {
    _eventsSub?.cancel();
    _userIdController.dispose();
    _otpController.dispose();
    super.dispose();
  }

  void _appendLog(String line) {
    setState(() {
      final timestamp = DateTime.now().toIso8601String();
      _logs.insert(0, '[$timestamp] $line');
    });
  }

  void _onEvent(HawcxEvent event) {
    _appendLog(_describeEvent(event));

    switch (event) {
      case AuthOtpRequiredEvent():
        setState(() => _otpRequired = true);
        _setStatus('OTP required', isError: false);
        break;
      case AuthSuccessEvent():
        _setStatus('Authentication complete (tokens stored by SDK).',
            isError: false);
        _endAuthFlow();
        break;
      case AuthErrorEvent(:final payload):
        _setStatus('Auth error: ${payload.message}', isError: true);
        _endAuthFlow();
        break;
      case AuthorizationCodeEvent():
        _setStatus(
          'Authentication complete. Authorization code received — exchange via backend.',
          isError: false,
        );
        _cancelAuthFlow();
        break;
      case AdditionalVerificationRequiredEvent(:final payload):
        _setStatus(
          'Additional verification required: ${payload.sessionId}',
          isError: true,
        );
        _cancelAuthFlow();
        break;
      default:
        break;
    }
  }

  String _describeEvent(HawcxEvent event) {
    switch (event) {
      case AuthOtpRequiredEvent():
        return 'Auth: otp_required';
      case AuthSuccessEvent(:final payload):
        return 'Auth: auth_success isLoginFlow=${payload.isLoginFlow} '
            'accessToken=${payload.accessToken != null ? '<present>' : '<null>'} '
            'refreshToken=${payload.refreshToken != null ? '<present>' : '<null>'}';
      case AuthErrorEvent(:final payload):
        return 'Auth: auth_error code=${payload.code} message=${payload.message}';
      case AuthorizationCodeEvent(:final payload):
        return 'Auth: authorization_code code=${payload.code} expiresIn=${payload.expiresIn}';
      case AdditionalVerificationRequiredEvent(:final payload):
        return 'Auth: additional_verification_required sessionId=${payload.sessionId} detail=${payload.detail}';
      case SessionSuccessEvent():
        return 'Session: session_success';
      case SessionErrorEvent(:final payload):
        return 'Session: session_error code=${payload.code} message=${payload.message}';
      case PushLoginRequestEvent(:final payload):
        return 'Push: push_login_request requestId=${payload.requestId}';
      case PushErrorEvent(:final payload):
        return 'Push: push_error code=${payload.code} message=${payload.message}';
      case HawcxUnknownEvent(:final type):
        return 'Unknown: $type';
    }
  }

  Future<void> _runGuarded(Future<void> Function() operation) async {
    try {
      await operation();
    } catch (error) {
      _appendLog('Error: $error');
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(error.toString())),
      );
    }
  }

  Future<void> _initialize() {
    return _runGuarded(() async {
      final config = defaultHawcxConfig;
      if (config == null) {
        _initError = 'Missing Hawcx config.';
        _appendLog(_initError!);
        _setStatus(_initError!, isError: true);
        return;
      }
      await _client.initialize(config);
      setState(() => _initialized = true);
      _appendLog('Initialized');
      _setStatus('Initialized', isError: false);
    });
  }

  Future<void> _authenticate() {
    return _runGuarded(() async {
      if (_authInProgress) {
        _appendLog('Auth already in progress.');
        _setStatus('Auth already in progress.', isError: true);
        return;
      }
      if (_initError != null) {
        _appendLog(_initError!);
        _setStatus(_initError!, isError: true);
        return;
      }
      if (!_initialized) {
        await _initialize();
      }
      final userId = _userIdController.text;
      setState(() {
        _authInProgress = true;
        _otpRequired = false;
        _statusMessage = 'Authenticating...';
        _statusIsError = false;
      });
      _otpController.clear();
      _authHandle?.cancel();
      _authHandle = _client.authenticate(
        userId: userId,
        onOtpRequired: () {
          _appendLog('Auth: OTP required');
          setState(() => _otpRequired = true);
          _setStatus('OTP required', isError: false);
        },
        onAuthorizationCode: (payload) {
          _appendLog('Auth: Authorization code received (${payload.code})');
          _setStatus(
            'Authentication complete. Authorization code received — exchange via backend.',
            isError: false,
          );
        },
        onAdditionalVerificationRequired: (payload) {
          _appendLog(
              'Auth: Additional verification required (${payload.sessionId})');
          _setStatus(
            'Additional verification required: ${payload.sessionId}',
            isError: true,
          );
        },
      );
      _appendLog('Auth: started for userId=$userId');
    });
  }

  Future<void> _submitOtp() {
    return _runGuarded(() async {
      await _client.submitOtp(_otpController.text);
      _appendLog('Auth: submitOtp called');
      _setStatus('OTP submitted. Awaiting verification...', isError: false);
    });
  }

  void _endAuthFlow() {
    setState(() {
      _authInProgress = false;
      _otpRequired = false;
    });
    _authHandle = null;
  }

  void _cancelAuthFlow() {
    _authHandle?.cancel();
    _authHandle = null;
    setState(() {
      _authInProgress = false;
      _otpRequired = false;
    });
  }

  void _setStatus(String message, {required bool isError}) {
    setState(() {
      _statusMessage = message;
      _statusIsError = isError;
    });
  }

  @override
  Widget build(BuildContext context) {
    final isMobile = !kIsWeb &&
        (defaultTargetPlatform == TargetPlatform.android ||
            defaultTargetPlatform == TargetPlatform.iOS);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Hawcx Flutter Example'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          IconButton(
            tooltip: 'Clear log',
            onPressed: () => setState(_logs.clear),
            icon: const Icon(Icons.delete_outline),
          ),
        ],
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              if (!isMobile)
                Card(
                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: Text(
                      'This example is intended for iOS/Android.\n'
                      'Current platform: ${kIsWeb ? 'web' : defaultTargetPlatform}',
                    ),
                  ),
                ),
              _section(
                title: 'Authenticate',
                children: [
                  TextField(
                    controller: _userIdController,
                    decoration: const InputDecoration(
                      labelText: 'User ID',
                      hintText: 'user@example.com',
                    ),
                    autocorrect: false,
                  ),
                  const SizedBox(height: 12),
                  ElevatedButton(
                    onPressed: isMobile && _initialized && !_authInProgress
                        ? _authenticate
                        : null,
                    child: const Text('Authenticate'),
                  ),
                  if (_statusMessage != null) ...[
                    const SizedBox(height: 12),
                    Text(
                      _statusMessage!,
                      style: TextStyle(
                        color: _statusIsError
                            ? Theme.of(context).colorScheme.error
                            : Theme.of(context).colorScheme.primary,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                  ],
                  if (_otpRequired) ...[
                    const SizedBox(height: 12),
                    TextField(
                      controller: _otpController,
                      decoration: const InputDecoration(
                        labelText: 'OTP',
                        hintText: '123456',
                      ),
                      autofillHints: const [AutofillHints.oneTimeCode],
                      keyboardType: TextInputType.number,
                    ),
                    const SizedBox(height: 12),
                    OutlinedButton(
                      onPressed: isMobile && _initialized ? _submitOtp : null,
                      child: const Text('Submit OTP'),
                    ),
                  ],
                ],
              ),
              _section(
                title: 'Log',
                children: [
                  Container(
                    padding: const EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      border: Border.all(color: Theme.of(context).dividerColor),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: _logs.isEmpty
                        ? const Text('No events yet.')
                        : SelectableText(_logs.take(80).join('\n\n')),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _section({required String title, required List<Widget> children}) {
    return Card(
      margin: const EdgeInsets.only(bottom: 16),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(title, style: Theme.of(context).textTheme.titleMedium),
            const SizedBox(height: 12),
            ...children,
          ],
        ),
      ),
    );
  }
}
0
likes
0
points
271
downloads

Publisher

unverified uploader

Weekly Downloads

Hawcx Flutter SDK plugin providing V5 auth, session, and push flows via native Hawcx SDKs.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on hawcx_flutter_sdk

Packages that implement hawcx_flutter_sdk