sms_insights_flutter 0.0.1-dev.1 copy "sms_insights_flutter: ^0.0.1-dev.1" to clipboard
sms_insights_flutter: ^0.0.1-dev.1 copied to clipboard

Flutter plugin wrapping the PayU SMS Insights Android SDK (core + optional logger). Reads transactional SMS for credit assessment with consent-based permission flow.

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sms_insights_flutter/sms_insights_flutter.dart';

// Pass credentials at build time:
//   flutter run --dart-define=CLIENT_EMAIL=you@co.com --dart-define=ACCESS_KEY=key
const _clientEmail = String.fromEnvironment('CLIENT_EMAIL');
const _accessKey   = String.fromEnvironment('ACCESS_KEY');

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

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

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'SMS Insights Flutter Demo',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(useMaterial3: false),
        home: const HomeScreen(),
      );
}

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final _customerIdCtrl = TextEditingController();

  String _customerId      = 'customer-123';
  bool   _initEnabled     = true;
  bool   _postInitEnabled = false;
  bool   _busy            = false;

  StreamSubscription<SdkEvent>? _sub;

  @override
  void initState() {
    super.initState();
    _sub = SmsSdkWrapper.instance.events.listen(_onEvent);
  }

  @override
  void dispose() {
    _sub?.cancel();
    _customerIdCtrl.dispose();
    super.dispose();
  }

  // ── event listener ────────────────────────────────────────────────────────
  void _onEvent(SdkEvent event) {
    if (event is InitSuccessEvent) {
      setState(() {
        _postInitEnabled = true;
        _initEnabled = false;
      });
      _toast('Init Success');
    } else if (event is InitFailureEvent) {
      _toast('Init Failure (${event.code}): ${event.message}');
    } else if (event is SmsSyncSuccessEvent) {
      _toast(event.message);
    } else if (event is NoMessagesToSyncEvent) {
      _toast(event.message);
    } else if (event is AuthFailureEvent) {
      _toast('Auth failure (${event.code}): ${event.message}');
    } else if (event is UploadFailureEvent) {
      _toast('Upload failure: ${event.message}');
    } else if (event is PermissionNotAvailableEvent) {
      _toast('Permission not available: ${event.message}');
    } else if (event is InitNotCalledEvent) {
      _toast('Init not called: ${event.message}');
    } else if (event is ForgetUserFailureEvent) {
      _toast('${event.code} ${event.message}');
    } else if (event is MatchDeviceFailureEvent) {
      _toast('Device match failure: ${event.message}');
    } else if (event is SdkConfigFailureEvent) {
      _toast(event.message);
    }
  }

  // ── actions ───────────────────────────────────────────────────────────────
  void _submitCustomerId() {
    final val = _customerIdCtrl.text.trim();
    if (val.isEmpty) {
      _toast('Customer Id should not be empty.');
      return;
    }
    setState(() => _customerId = val);
    _customerIdCtrl.clear();
    FocusScope.of(context).unfocus();
  }

  Future<void> _checkPermissionAndInit() async {
    final status = await Permission.sms.status;
    if (!status.isGranted) {
      if (!mounted) return;
      final proceed = await showDialog<bool>(
        context: context,
        builder: (_) => AlertDialog(
          title: const Text('SMS Permission'),
          content: const Text(
            'SMS access is needed to analyse your transactions. '
            'You can still continue without granting permission.',
          ),
          actions: [
            TextButton(
              onPressed: () async {
                await Permission.sms.request();
                if (mounted) Navigator.pop(context, true);
              },
              child: const Text('GRANT PERMISSION'),
            ),
            TextButton(
              onPressed: () => Navigator.pop(context, true),
              child: const Text('NO THANKS'),
            ),
          ],
        ),
      );
      if (proceed != true) return;
    }
    await _initSdk();
  }

  Future<void> _initSdk() async {
    if (_clientEmail.isEmpty || _accessKey.isEmpty) {
      _toast('Credentials not set. Pass --dart-define=CLIENT_EMAIL=... --dart-define=ACCESS_KEY=...');
      return;
    }
    setState(() => _busy = true);
    try {
      await SmsSdkWrapper.instance.init(
        SdkClientConfig(
          clientEmail: _clientEmail,
          accessId:    _accessKey,
          customerId:  _customerId,
          upperLimit:  190,
          salaryInRs:  100000,
          companyName: 'PayU',
        ),
      );
    } on SmsInsightsException catch (e) {
      _toast('${e.code}: ${e.message}');
    } finally {
      setState(() => _busy = false);
    }
  }

  Future<void> _deviceMatch() async {
    setState(() => _busy = true);
    try {
      await SmsSdkWrapper.instance.setDeviceMatch(
        const DeviceMatch(phoneNo: '7307140364', email: 'rahularora'),
      );
      _toast('setDeviceMatch called');
    } on SmsInsightsException catch (e) {
      _toast(e.message);
    } finally {
      setState(() => _busy = false);
    }
  }

  Future<void> _startSync() async {
    setState(() => _busy = true);
    try {
      await SmsSdkWrapper.instance.startSyncAndHandlePermissions(
        onPermissionsRequested: _requestPermissions,
      );
    } on SmsInsightsException catch (e) {
      _toast(e.message);
    } finally {
      setState(() => _busy = false);
    }
  }

  Future<void> _forgetUser() async {
    setState(() => _busy = true);
    try {
      await SmsSdkWrapper.instance.forgetUser();
      setState(() {
        _postInitEnabled = false;
        _initEnabled = true;
      });
    } on SmsInsightsException catch (e) {
      _toast(e.message);
    } finally {
      setState(() => _busy = false);
    }
  }

  Future<List<String>> _requestPermissions(List<String> requested) async {
    // Build Permission → [android name, ...] mapping.
    // ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION both map to Permission.location,
    // so if location is granted we return both strings (SDK expects both in the list).
    final mapping = <Permission, List<String>>{};
    for (final p in requested) {
      final handler = switch (p) {
        'android.permission.READ_SMS'               => Permission.sms,
        'android.permission.ACCESS_FINE_LOCATION'   => Permission.location,
        'android.permission.ACCESS_COARSE_LOCATION' => Permission.location,
        'android.permission.READ_PHONE_STATE'       => Permission.phone,
        'android.permission.READ_PHONE_NUMBERS'     => Permission.phone,
        _ => null,
      };
      if (handler != null) (mapping[handler] ??= []).add(p);
    }
    if (mapping.isEmpty) return [];
    // Single batch request — one system dialog, same as Kotlin's
    // requestPermissionsLauncher.launch(permissions.toTypedArray())
    final statuses = await mapping.keys.toList().request();
    return [
      for (final entry in statuses.entries)
        if (entry.value.isGranted) ...?mapping[entry.key],
    ];
  }

  void _toast(String msg) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(msg), duration: const Duration(seconds: 3)),
    );
  }

  // ── UI ────────────────────────────────────────────────────────────────────
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              // Customer ID row
              TextField(
                controller: _customerIdCtrl,
                textInputAction: TextInputAction.done,
                onSubmitted: (_) => _submitCustomerId(),
                decoration: const InputDecoration(
                  hintText: 'Enter customerId',
                  enabledBorder: UnderlineInputBorder(),
                  focusedBorder: UnderlineInputBorder(),
                ),
              ),
              const SizedBox(height: 12),
              Align(
                alignment: Alignment.centerRight,
                child: TextButton(
                  onPressed: _submitCustomerId,
                  child: const Text('Set Customer ID'),
                ),
              ),

              const SizedBox(height: 40),

              _SdkButton(
                label: 'INIT SDK',
                enabled: _initEnabled && !_busy,
                onPressed: _checkPermissionAndInit,
              ),
              const SizedBox(height: 16),

              _SdkButton(
                label: 'DEVICE MATCH',
                enabled: _postInitEnabled && !_busy,
                onPressed: _deviceMatch,
              ),
              const SizedBox(height: 16),

              _SdkButton(
                label: 'START SYNC',
                enabled: _postInitEnabled && !_busy,
                onPressed: _startSync,
              ),
              const SizedBox(height: 16),

              _SdkButton(
                label: 'FORGET USER',
                enabled: _postInitEnabled && !_busy,
                onPressed: _forgetUser,
              ),

              if (_busy) ...[
                const SizedBox(height: 32),
                const Center(child: CircularProgressIndicator()),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

class _SdkButton extends StatelessWidget {
  final String label;
  final bool enabled;
  final VoidCallback onPressed;

  const _SdkButton({
    required this.label,
    required this.enabled,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: enabled ? onPressed : null,
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(vertical: 14),
        textStyle: const TextStyle(
          fontSize: 14,
          fontWeight: FontWeight.w600,
          letterSpacing: 1.2,
        ),
      ),
      child: Text(label),
    );
  }
}
0
likes
0
points
15
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter plugin wrapping the PayU SMS Insights Android SDK (core + optional logger). Reads transactional SMS for credit assessment with consent-based permission flow.

Topics

#sms #android #sdk #payu #credit

License

unknown (license)

Dependencies

flutter

More

Packages that depend on sms_insights_flutter

Packages that implement sms_insights_flutter