sms_sender_plus 0.2.0 copy "sms_sender_plus: ^0.2.0" to clipboard
sms_sender_plus: ^0.2.0 copied to clipboard

Send SMS from Flutter with Android SIM selection, delivery status, batch recipients, and iOS composer support.

example/lib/main.dart

import 'dart:async';

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'sms_sender_plus example',
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.teal),
      home: const SmsExampleScreen(),
    );
  }
}

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

  @override
  State<SmsExampleScreen> createState() => _SmsExampleScreenState();
}

class _SmsExampleScreenState extends State<SmsExampleScreen> {
  final _recipientController = TextEditingController();
  final _batchController = TextEditingController();
  final _messageController = TextEditingController(
    text: 'Hello from sms_sender_plus',
  );
  final _events = <String>[];

  List<SimCard> _simCards = const [];
  int? _selectedSlot;
  bool _deliveryReport = true;
  bool _busy = false;
  StreamSubscription<SmsStatusEvent>? _statusSubscription;

  @override
  void initState() {
    super.initState();
    _statusSubscription = SmsSenderPlus.instance.statusEvents.listen((event) {
      _addEvent(
        '${event.messageId}: ${event.status.name} ${event.recipient ?? ''}',
      );
    });
    _loadSimCards();
  }

  @override
  void dispose() {
    _statusSubscription?.cancel();
    _recipientController.dispose();
    _batchController.dispose();
    _messageController.dispose();
    super.dispose();
  }

  Future<void> _loadSimCards() async {
    final hasPhoneStatePermission = await _ensurePhoneStatePermission();
    if (!hasPhoneStatePermission) {
      _addEvent('READ_PHONE_STATE permission is required to load SIM cards.');
      return;
    }
    await _run(() async {
      final simCards = await SmsSenderPlus.instance.getSimCards();
      setState(() => _simCards = simCards);
    });
  }

  Future<bool> _ensureSmsPermission() async {
    final hasPermission = await SmsSenderPlus.instance.checkSmsPermission();
    if (hasPermission) {
      return true;
    }
    return SmsSenderPlus.instance.requestSmsPermission();
  }

  Future<bool> _ensurePhoneStatePermission() async {
    final hasPermission =
        await SmsSenderPlus.instance.checkPhoneStatePermission();
    if (hasPermission) {
      return true;
    }
    return SmsSenderPlus.instance.requestPhoneStatePermission();
  }

  Future<void> _sendSingle() async {
    final hasSmsPermission = await _ensureSmsPermission();
    if (!hasSmsPermission) {
      _addEvent('SEND_SMS permission is required to send a message.');
      return;
    }
    if (_selectedSlot != null) {
      final hasPhoneStatePermission = await _ensurePhoneStatePermission();
      if (!hasPhoneStatePermission) {
        _addEvent(
          'READ_PHONE_STATE permission is required to select a SIM slot.',
        );
        return;
      }
    }
    await _run(() async {
      final result = await SmsSenderPlus.instance.sendTextMessage(
        recipient: _recipientController.text,
        message: _messageController.text,
        simSlot: _selectedSlot,
        deliveryReport: _deliveryReport,
      );
      _addEvent('single result: ${result.state.name} ${result.messageId}');
    });
  }

  Future<void> _sendBatch() async {
    final hasSmsPermission = await _ensureSmsPermission();
    if (!hasSmsPermission) {
      _addEvent('SEND_SMS permission is required to send messages.');
      return;
    }
    if (_selectedSlot != null) {
      final hasPhoneStatePermission = await _ensurePhoneStatePermission();
      if (!hasPhoneStatePermission) {
        _addEvent(
          'READ_PHONE_STATE permission is required to select a SIM slot.',
        );
        return;
      }
    }
    await _run(() async {
      final result = await SmsSenderPlus.instance.sendTextMessages(
        recipients: _batchController.text.split(','),
        message: _messageController.text,
        simSlot: _selectedSlot,
      );
      _addEvent('batch result: ${result.state.name} ${result.messageId}');
    });
  }

  Future<void> _run(Future<void> Function() action) async {
    setState(() => _busy = true);
    try {
      await action();
    } on SmsSenderPlusException catch (error) {
      _addEvent('${error.code}: ${error.message}');
    } finally {
      if (mounted) {
        setState(() => _busy = false);
      }
    }
  }

  void _addEvent(String message) {
    if (!mounted) {
      return;
    }
    setState(() => _events.insert(0, message));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('sms_sender_plus')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          TextField(
            controller: _recipientController,
            keyboardType: TextInputType.phone,
            decoration: const InputDecoration(labelText: 'Single recipient'),
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _batchController,
            keyboardType: TextInputType.phone,
            decoration: const InputDecoration(
              labelText: 'Batch recipients, comma separated',
            ),
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _messageController,
            minLines: 3,
            maxLines: 5,
            decoration: const InputDecoration(labelText: 'Message'),
          ),
          const SizedBox(height: 12),
          DropdownButtonFormField<int?>(
            initialValue: _selectedSlot,
            decoration: const InputDecoration(labelText: 'SIM slot'),
            items: [
              const DropdownMenuItem<int?>(
                value: null,
                child: Text('Device default'),
              ),
              ..._simCards.map(
                (sim) => DropdownMenuItem<int?>(
                  value: sim.simSlotIndex,
                  child: Text(
                    'Slot ${sim.simSlotIndex}: '
                    '${sim.carrierName ?? sim.displayName ?? sim.subscriptionId}',
                  ),
                ),
              ),
            ],
            onChanged: (value) => setState(() => _selectedSlot = value),
          ),
          SwitchListTile(
            value: _deliveryReport,
            title: const Text('Delivery report for single SMS'),
            onChanged: (value) => setState(() => _deliveryReport = value),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _busy ? null : _sendSingle,
            child: const Text('Send single'),
          ),
          OutlinedButton(
            onPressed: _busy ? null : _sendBatch,
            child: const Text('Send batch'),
          ),
          const SizedBox(height: 24),
          Text('Events', style: Theme.of(context).textTheme.titleMedium),
          const SizedBox(height: 8),
          for (final event in _events) Text(event),
        ],
      ),
    );
  }
}
2
likes
140
points
137
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Send SMS from Flutter with Android SIM selection, delivery status, batch recipients, and iOS composer support.

Repository (GitHub)
View/report issues

Topics

#sms #telephony #android #ios #dual-sim

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on sms_sender_plus

Packages that implement sms_sender_plus