whatsapp_cloud_flutter 1.0.0
whatsapp_cloud_flutter: ^1.0.0 copied to clipboard
A comprehensive Flutter package providing type-safe WhatsApp Cloud API integration. Send messages, handle webhooks, manage media, and build chat interfaces with ease.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:whatsapp_cloud_flutter/whatsapp_cloud_flutter.dart' as whatsapp;
void main() {
runApp(const WhatsAppExampleApp());
}
class WhatsAppExampleApp extends StatelessWidget {
const WhatsAppExampleApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WhatsApp Cloud API Example',
theme: ThemeData(
primarySwatch: Colors.green,
useMaterial3: true,
),
home: const ExampleScreen(),
);
}
}
class ExampleScreen extends StatefulWidget {
const ExampleScreen({Key? key}) : super(key: key);
@override
State<ExampleScreen> createState() => _ExampleScreenState();
}
class _ExampleScreenState extends State<ExampleScreen> {
final _phoneNumberIdController = TextEditingController();
final _accessTokenController = TextEditingController();
final _recipientController = TextEditingController();
final _messageController = TextEditingController();
whatsapp.WhatsAppCloudClient? _client;
String _status = 'Not connected';
List<String> _logs = [];
@override
void initState() {
super.initState();
// You can set default values here for testing
_phoneNumberIdController.text = 'YOUR_PHONE_NUMBER_ID';
_accessTokenController.text = 'YOUR_ACCESS_TOKEN';
_recipientController.text = '+1234567890'; // Example recipient
}
@override
void dispose() {
_phoneNumberIdController.dispose();
_accessTokenController.dispose();
_recipientController.dispose();
_messageController.dispose();
super.dispose();
}
void _addLog(String message) {
setState(() {
_logs.insert(0, '${DateTime.now().toIso8601String()}: $message');
if (_logs.length > 10) _logs.removeLast();
});
}
void _initializeClient() {
try {
_client = whatsapp.WhatsAppCloudClient(
phoneNumberId: _phoneNumberIdController.text.trim(),
accessToken: _accessTokenController.text.trim(),
config: const whatsapp.WhatsAppApiConfig(
logLevel: whatsapp.LogLevel.debug,
environment: whatsapp.Environment.production,
connectTimeout: Duration(seconds: 30),
retryPolicy: whatsapp.RetryPolicy(
maxRetries: 3,
initialBackoff: Duration(seconds: 1),
),
),
);
setState(() {
_status = 'Connected';
});
_addLog('WhatsApp client initialized successfully');
} catch (e) {
setState(() {
_status = 'Connection failed: $e';
});
_addLog('Failed to initialize client: $e');
}
}
Future<void> _sendTextMessage() async {
if (_client == null) {
_addLog('Please initialize the client first');
return;
}
final message = _messageController.text.trim();
final recipient = _recipientController.text.trim();
if (message.isEmpty || recipient.isEmpty) {
_addLog('Please enter both message and recipient');
return;
}
try {
_addLog('Sending text message...');
final response = await _client!.messageService.sendTextMessage(
recipient: recipient,
text: message,
previewUrl: true,
);
if (response.successful) {
_addLog('✅ Message sent successfully! ID: ${response.messageId}');
_messageController.clear();
} else {
_addLog('❌ Failed to send message: ${response.errorMessage}');
}
} catch (e) {
_addLog('❌ Error sending message: $e');
}
}
Future<void> _sendLocationMessage() async {
if (_client == null) {
_addLog('Please initialize the client first');
return;
}
try {
_addLog('Sending location message...');
final response = await _client!.messageService.sendLocationMessage(
recipient: _recipientController.text.trim(),
latitude: 37.7749,
longitude: -122.4194,
name: 'San Francisco',
address: 'San Francisco, CA, USA',
);
if (response.successful) {
_addLog('✅ Location sent successfully! ID: ${response.messageId}');
} else {
_addLog('❌ Failed to send location: ${response.errorMessage}');
}
} catch (e) {
_addLog('❌ Error sending location: $e');
}
}
Future<void> _sendInteractiveMessage() async {
if (_client == null) {
_addLog('Please initialize the client first');
return;
}
try {
_addLog('Sending simple text message with URL preview...');
final response = await _client!.messageService.sendTextMessage(
recipient: _recipientController.text.trim(),
text: 'Check out this website: https://flutter.dev',
previewUrl: true,
);
if (response.successful) {
_addLog('✅ Message with URL preview sent! ID: ${response.messageId}');
} else {
_addLog('❌ Failed to send message: ${response.errorMessage}');
}
} catch (e) {
_addLog('❌ Error sending message: $e');
}
}
Future<void> _markAsRead() async {
if (_client == null) {
_addLog('Please initialize the client first');
return;
}
try {
_addLog('Marking message as read...');
// This would typically use a message ID from an incoming webhook
const messageId = 'example_message_id';
final response = await _client!.messageService.markMessageAsRead(messageId: messageId);
if (response.successful) {
_addLog('✅ Message marked as read');
} else {
_addLog('❌ Failed to mark as read: ${response.errorMessage}');
}
} catch (e) {
_addLog('❌ Error marking as read: $e');
}
}
void _testWebhookVerification() {
try {
_addLog('Testing webhook functionality...');
// Example of how webhook verification would work
const verifyToken = 'your_verify_token';
const mode = 'subscribe';
const challenge = '1234567890';
// In a real app, this would be in your webhook endpoint
if (mode == 'subscribe' && verifyToken == 'your_verify_token') {
_addLog('✅ Webhook verification would succeed with challenge: $challenge');
} else {
_addLog('❌ Webhook verification would fail');
}
_addLog('📖 Note: In production, configure webhook URL in Meta Developer Console');
} catch (e) {
_addLog('❌ Webhook test error: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('WhatsApp Cloud API Example'),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Configuration Section
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Configuration',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
TextField(
controller: _phoneNumberIdController,
decoration: const InputDecoration(
labelText: 'Phone Number ID',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
TextField(
controller: _accessTokenController,
decoration: const InputDecoration(
labelText: 'Access Token',
border: OutlineInputBorder(),
),
obscureText: true,
),
const SizedBox(height: 8),
TextField(
controller: _recipientController,
decoration: const InputDecoration(
labelText: 'Recipient Phone (+1234567890)',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
Row(
children: [
ElevatedButton(
onPressed: _initializeClient,
child: const Text('Initialize Client'),
),
const SizedBox(width: 12),
Text('Status: $_status'),
],
),
],
),
),
),
const SizedBox(height: 16),
// Message Section
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Send Messages',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
TextField(
controller: _messageController,
decoration: const InputDecoration(
labelText: 'Message Text',
border: OutlineInputBorder(),
),
maxLines: 2,
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton.icon(
onPressed: _sendTextMessage,
icon: const Icon(Icons.message),
label: const Text('Send Text'),
),
ElevatedButton.icon(
onPressed: _sendLocationMessage,
icon: const Icon(Icons.location_on),
label: const Text('Send Location'),
),
ElevatedButton.icon(
onPressed: _sendInteractiveMessage,
icon: const Icon(Icons.link),
label: const Text('URL Preview'),
),
ElevatedButton.icon(
onPressed: _markAsRead,
icon: const Icon(Icons.mark_as_unread),
label: const Text('Mark Read'),
),
ElevatedButton.icon(
onPressed: _testWebhookVerification,
icon: const Icon(Icons.webhook),
label: const Text('Test Webhook'),
),
],
),
],
),
),
),
const SizedBox(height: 16),
// Logs Section
Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Activity Logs',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
TextButton(
onPressed: () => setState(() => _logs.clear()),
child: const Text('Clear'),
),
],
),
const SizedBox(height: 8),
Expanded(
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8),
),
child: _logs.isEmpty
? const Text(
'No activity yet. Initialize the client and try sending a message.',
style: TextStyle(color: Colors.grey),
)
: ListView.builder(
itemCount: _logs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Text(
_logs[index],
style: const TextStyle(
fontSize: 12,
fontFamily: 'monospace',
),
),
);
},
),
),
),
],
),
),
),
),
],
),
),
);
}
}