whatsapp_share_plus 2.0.0 copy "whatsapp_share_plus: ^2.0.0" to clipboard
whatsapp_share_plus: ^2.0.0 copied to clipboard

Share text, images, video and documents to WhatsApp and WhatsApp Business. Target a contact by phone number, send multiple files, and install sticker packs.

example/lib/main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:whatsapp_share_plus/whatsapp_share_plus.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'whatsapp_share_plus',
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF25D366),
        useMaterial3: true,
      ),
      home: const SharePage(),
    );
  }
}

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

  @override
  State<SharePage> createState() => _SharePageState();
}

class _SharePageState extends State<SharePage> {
  final _messageController = TextEditingController(
    text: 'Sent from whatsapp_share_plus',
  );
  final _phoneController = TextEditingController();
  final _picker = ImagePicker();

  final _attachments = <ShareFile>[];
  Set<WhatsAppTarget> _installed = const <WhatsAppTarget>{};
  WhatsAppTarget _target = WhatsAppTarget.standard;

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

  @override
  void dispose() {
    _messageController.dispose();
    _phoneController.dispose();
    super.dispose();
  }

  Future<void> _refreshInstalledTargets() async {
    final installed = await WhatsAppShare.installedTargets();
    if (!mounted) return;
    setState(() => _installed = installed);
  }

  Future<void> _pickImages() async {
    try {
      final picked = await _picker.pickMultiImage();
      if (picked.isEmpty) return;

      final files = <ShareFile>[
        for (final image in picked)
          // A browser has no file system path to share from, so on web the
          // bytes have to travel with the file. Everywhere else a path is
          // cheaper — it avoids copying the image through Dart.
          if (kIsWeb)
            ShareFile.fromBytes(
              await image.readAsBytes(),
              name: image.name,
              mimeType: image.mimeType,
            )
          else
            ShareFile.fromXFile(image),
      ];

      setState(() => _attachments.addAll(files));
    } on MissingPluginException {
      _report('Picking images is not supported on this platform.');
    }
  }

  /// Runs a share and turns any failure into a readable message.
  ///
  /// Every call in this package throws a [WhatsAppShareException] subtype, so
  /// one handler covers the lot — this is the pattern to copy into a real app.
  Future<void> _run(Future<ShareResult> Function() action) async {
    try {
      final result = await action();
      _report(switch (result.status) {
        ShareStatus.opened =>
          'Opened ${result.target?.displayName ?? 'WhatsApp'}.',
        ShareStatus.sheetPresented => 'Shared through the system share sheet.',
        ShareStatus.fallback =>
          'WhatsApp is not installed — opened wa.me instead.',
        ShareStatus.dismissed => 'You closed the share sheet.',
      });
    } on WhatsAppNotInstalledException catch (error) {
      _report('${error.target.displayName} is not installed.');
    } on InvalidShareArgumentException catch (error) {
      _report(error.message);
    } on ShareFileException catch (error) {
      _report('That file could not be shared: ${error.message}');
    } on UnsupportedShareException catch (error) {
      _report(error.message);
    } on WhatsAppShareException catch (error) {
      _report(error.message);
    }
  }

  /// The anchor an iPad or Mac share sheet points at.
  ///
  /// Without it the popover lands in the corner with its arrow pointing at
  /// nothing, and on some iPad configurations UIKit refuses to present at all.
  Rect _shareOrigin() {
    final box = context.findRenderObject() as RenderBox?;
    if (box == null) return Rect.zero;
    return box.localToGlobal(Offset.zero) & box.size;
  }

  Future<void> _share() => _run(
        () => WhatsAppShare.share(
          text: _messageController.text.trim().isEmpty
              ? null
              : _messageController.text.trim(),
          files: _attachments,
          phone: _phoneController.text.trim().isEmpty
              ? null
              : _phoneController.text.trim(),
          target: _target,
          sharePositionOrigin: _shareOrigin(),
        ),
      );

  Future<void> _openChat() => _run(
        () => WhatsAppShare.openChat(
          phone: _phoneController.text.trim(),
          text: _messageController.text.trim(),
          target: _target,
        ),
      );

  Future<void> _diagnose() async {
    final report = await WhatsAppShare.diagnose();
    if (kDebugMode) debugPrint(report.toString());
    if (!mounted) return;

    showDialog<void>(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(report.isHealthy ? 'Configuration OK' : 'Issues found'),
        content: SingleChildScrollView(
          child: Text(report.toString(), style: const TextStyle(fontSize: 13)),
        ),
        actions: <Widget>[
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  void _report(String message) {
    if (!mounted) return;
    ScaffoldMessenger.of(context)
      ..clearSnackBars()
      ..showSnackBar(SnackBar(content: Text(message)));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('whatsapp_share_plus'),
        actions: <Widget>[
          IconButton(
            onPressed: _diagnose,
            icon: const Icon(Icons.health_and_safety_outlined),
            tooltip: 'Run diagnostics',
          ),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: <Widget>[
          _InstalledTargets(
            installed: _installed,
            onRefresh: _refreshInstalledTargets,
          ),
          const SizedBox(height: 16),
          SegmentedButton<WhatsAppTarget>(
            segments: WhatsAppTarget.values
                .map(
                  (target) => ButtonSegment<WhatsAppTarget>(
                    value: target,
                    label: Text(target.displayName),
                  ),
                )
                .toList(),
            selected: <WhatsAppTarget>{_target},
            onSelectionChanged: (selection) =>
                setState(() => _target = selection.first),
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _messageController,
            maxLines: 3,
            decoration: const InputDecoration(
              labelText: 'Message',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _phoneController,
            keyboardType: TextInputType.phone,
            decoration: const InputDecoration(
              labelText: 'Recipient (optional)',
              helperText: 'Any format: +91 98765 43210, 0091…, 919876543210',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 16),
          _Attachments(
            files: _attachments,
            onAdd: _pickImages,
            onClear: () => setState(_attachments.clear),
          ),
          const SizedBox(height: 24),
          FilledButton.icon(
            onPressed: _share,
            icon: const Icon(Icons.send),
            label: const Text('Share'),
          ),
          const SizedBox(height: 8),
          OutlinedButton.icon(
            onPressed: _openChat,
            icon: const Icon(Icons.chat_bubble_outline),
            label: const Text('Open chat (no attachment)'),
          ),
          const SizedBox(height: 24),
          _LinkPreview(
            phone: _phoneController.text,
            text: _messageController.text,
          ),
        ],
      ),
    );
  }
}

/// Shows which WhatsApp apps the device has.
class _InstalledTargets extends StatelessWidget {
  const _InstalledTargets({required this.installed, required this.onRefresh});

  final Set<WhatsAppTarget> installed;
  final VoidCallback onRefresh;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        leading: Icon(
          installed.isEmpty ? Icons.error_outline : Icons.check_circle_outline,
          color: installed.isEmpty
              ? Theme.of(context).colorScheme.error
              : Theme.of(context).colorScheme.primary,
        ),
        title: Text(
          installed.isEmpty
              ? 'No WhatsApp app detected'
              : installed.map((target) => target.displayName).join(' + '),
        ),
        subtitle: Text(
          installed.isEmpty
              ? 'Shares will fall back to a wa.me link. Tap the shield icon '
                  'above if you expected an app to be found.'
              : 'Ready to share.',
        ),
        trailing: IconButton(
          onPressed: onRefresh,
          icon: const Icon(Icons.refresh),
        ),
      ),
    );
  }
}

/// Lists the files queued for the next share.
class _Attachments extends StatelessWidget {
  const _Attachments({
    required this.files,
    required this.onAdd,
    required this.onClear,
  });

  final List<ShareFile> files;
  final VoidCallback onAdd;
  final VoidCallback onClear;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Row(
          children: <Widget>[
            Text('Attachments', style: Theme.of(context).textTheme.titleSmall),
            const Spacer(),
            TextButton.icon(
              onPressed: onAdd,
              icon: const Icon(Icons.add_photo_alternate_outlined),
              label: const Text('Add'),
            ),
            if (files.isNotEmpty)
              TextButton(onPressed: onClear, child: const Text('Clear')),
          ],
        ),
        if (files.isEmpty)
          const Text('None — the share will be text only.')
        else
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: files
                .map(
                  (file) => Chip(
                    avatar: Icon(
                      file.isImage
                          ? Icons.image_outlined
                          : file.isVideo
                              ? Icons.movie_outlined
                              : Icons.description_outlined,
                      size: 18,
                    ),
                    label: Text(file.name ?? 'attachment'),
                  ),
                )
                .toList(),
          ),
      ],
    );
  }
}

/// Shows the `wa.me` link for the current input.
///
/// [WhatsAppLink] is pure Dart, so this works with no plugin registered — the
/// same call builds links for QR codes, emails, and web pages.
class _LinkPreview extends StatelessWidget {
  const _LinkPreview({required this.phone, required this.text});

  final String phone;
  final String text;

  @override
  Widget build(BuildContext context) {
    final link = WhatsAppLink.chat(
      phone: phone.isEmpty ? null : phone,
      text: text.isEmpty ? null : text,
    );

    return Card(
      color: Theme.of(context).colorScheme.surfaceContainerHighest,
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text('Shareable link',
                style: Theme.of(context).textTheme.titleSmall),
            const SizedBox(height: 4),
            SelectableText(link.toString(),
                style: const TextStyle(fontSize: 12)),
          ],
        ),
      ),
    );
  }
}
2
likes
160
points
646
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Share text, images, video and documents to WhatsApp and WhatsApp Business. Target a contact by phone number, send multiple files, and install sticker packs.

Repository (GitHub)
View/report issues

Topics

#whatsapp #share #sharing #deep-links #stickers

License

MIT (license)

Dependencies

cross_file, flutter, flutter_web_plugins, plugin_platform_interface, web

More

Packages that depend on whatsapp_share_plus

Packages that implement whatsapp_share_plus