PushFire Flutter SDK
A Flutter SDK for integrating with the PushFire push notification service. This SDK provides easy-to-use APIs for device registration, subscriber management, and tag operations.
Features
- π Automatic Device Registration: Seamlessly registers devices with FCM tokens
- π€ Subscriber Management: Login, update, and logout subscribers
- π·οΈ Tag Management: Add, update, and remove subscriber tags
- π± Cross-Platform: Works on both iOS and Android
- π§ Configurable: Customizable API endpoints and settings
- π Logging: Built-in logging for debugging
- π Event Streams: Real-time updates via streams
- π‘οΈ Error Handling: Comprehensive exception handling
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
pushfire_sdk: ^1.0.0
Then run:
flutter pub get
Setup
1. Firebase Setup
This SDK uses Firebase Cloud Messaging (FCM) for push notifications. You need to:
- Create a Firebase project at Firebase Console
- Add your Android/iOS app to the project
- Install FlutterFire CLI and configure Firebase:
dart pub global activate flutterfire_cli flutterfire configure
- This will generate
firebase_options.dart
file with your Firebase configuration - Download and add the configuration files:
google-services.json
for Android (place inandroid/app/
)GoogleService-Info.plist
for iOS (place inios/Runner/
)
- Follow the FlutterFire setup guide for platform-specific configuration
2. Platform Configuration
Android
Add the following to your android/app/build.gradle
:
dependencies {
implementation 'com.google.firebase:firebase-messaging:23.0.0'
}
iOS
Enable push notifications in your iOS project:
- Open
ios/Runner.xcworkspace
in Xcode - Select your project target
- Go to "Signing & Capabilities"
- Add "Push Notifications" capability
- Add "Background Modes" capability and enable "Background processing" and "Remote notifications"
Usage
1. Initialize the SDK
Initialize the SDK in your app's main()
function:
import 'package:pushfire_sdk/pushfire_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize PushFire SDK
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key-here',
baseUrl: 'https://api.pushfire.com', // Optional: defaults to production
enableLogging: true, // Optional: enable for debugging
timeoutSeconds: 30, // Optional: request timeout
authProvider: AuthProvider.firebase, // Optional: authentication provider
),
);
runApp(MyApp());
}
2. Authentication Provider Integration
The PushFire SDK supports automatic subscriber management through authentication providers. When configured, the SDK automatically handles subscriber login/logout based on the user's authentication state.
Supported Authentication Providers
- Firebase Authentication (
AuthProvider.firebase
) - Supabase Authentication (
AuthProvider.supabase
) - None (
AuthProvider.none
) - Default, manual subscriber management
Firebase Authentication Integration
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key-here',
authProvider: AuthProvider.firebase,
),
);
When Firebase Authentication is configured:
- The SDK automatically listens for
authStateChanges()
fromFirebaseAuth.instance
- When a user signs in, the SDK automatically calls
loginSubscriber()
with:externalId
: User's UIDemail
: User's email (if available)name
: User's display name or 'Guest' if not availablephone
: User's phone number (if available)
- When a user signs out, the SDK automatically calls
logoutSubscriber()
Supabase Authentication Integration
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key-here',
authProvider: AuthProvider.supabase,
),
);
When Supabase Authentication is configured:
- The SDK automatically listens for
onAuthStateChange
events fromSupabase.instance.client.auth
- When a user signs in (
AuthChangeEvent.signedIn
), the SDK automatically callsloginSubscriber()
with:externalId
: User's IDemail
: User's email (if available)name
: User's full_name from metadata or 'Guest' if not availablephone
: User's phone number (if available)
- When a user signs out (
AuthChangeEvent.signedOut
), the SDK automatically callslogoutSubscriber()
Manual Subscriber Management
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key-here',
authProvider: AuthProvider.none, // Default
),
);
When no authentication provider is configured, you must manually manage subscribers using the SDK methods.
3. Subscriber Management
Login/Register a Subscriber
try {
final subscriber = await PushFireSDK.instance.loginSubscriber(
externalId: 'user123', // Your user ID
name: 'John Doe',
email: 'john@example.com',
phone: '+1234567890',
);
print('Subscriber logged in: ${subscriber.id}');
} catch (e) {
print('Login failed: $e');
}
Update Subscriber
try {
final updatedSubscriber = await PushFireSDK.instance.updateSubscriber(
name: 'John Smith',
email: 'johnsmith@example.com',
);
print('Subscriber updated: ${updatedSubscriber.name}');
} catch (e) {
print('Update failed: $e');
}
Logout Subscriber
try {
await PushFireSDK.instance.logoutSubscriber();
print('Subscriber logged out');
} catch (e) {
print('Logout failed: $e');
}
Check Subscriber Status
// Check if subscriber is logged in
final isLoggedIn = await PushFireSDK.instance.isSubscriberLoggedIn();
// Get current subscriber
final subscriber = await PushFireSDK.instance.getCurrentSubscriber();
if (subscriber != null) {
print('Current subscriber: ${subscriber.name}');
}
3. Tag Management
Add Tags
// Add single tag
try {
final tag = await PushFireSDK.instance.addTag('user_type', 'premium');
print('Tag added: ${tag.tagId} = ${tag.value}');
} catch (e) {
print('Failed to add tag: $e');
}
// Add multiple tags
try {
final tags = await PushFireSDK.instance.addTags({
'user_type': 'premium',
'subscription_plan': 'yearly',
'region': 'us-west',
});
print('Added ${tags.length} tags');
} catch (e) {
print('Failed to add tags: $e');
}
Update Tags
// Update single tag
try {
final tag = await PushFireSDK.instance.updateTag('user_type', 'enterprise');
print('Tag updated: ${tag.tagId} = ${tag.value}');
} catch (e) {
print('Failed to update tag: $e');
}
// Update multiple tags
try {
final tags = await PushFireSDK.instance.updateTags({
'user_type': 'enterprise',
'subscription_plan': 'monthly',
});
print('Updated ${tags.length} tags');
} catch (e) {
print('Failed to update tags: $e');
}
Remove Tags
// Remove single tag
try {
await PushFireSDK.instance.removeTag('user_type');
print('Tag removed');
} catch (e) {
print('Failed to remove tag: $e');
}
// Remove multiple tags
try {
await PushFireSDK.instance.removeTags(['user_type', 'region']);
print('Tags removed');
} catch (e) {
print('Failed to remove tags: $e');
}
4. Workflow Execution
Execute automated workflows for targeted push notifications:
Create Immediate Workflow
// Execute workflow immediately for specific subscribers
try {
await PushFireSDK.instance.createImmediateWorkflowForSubscribers(
workflowId: 'welcome-series',
subscriberIds: ['sub1', 'sub2', 'sub3'],
);
print('Workflow executed for subscribers');
} catch (e) {
print('Workflow execution failed: $e');
}
// Execute workflow immediately for segments
try {
await PushFireSDK.instance.createImmediateWorkflowForSegments(
workflowId: 'promotion-campaign',
segmentIds: ['premium-users', 'active-users'],
);
print('Workflow executed for segments');
} catch (e) {
print('Workflow execution failed: $e');
}
Create Scheduled Workflow
// Schedule workflow for future execution
final scheduledFor = DateTime.now().add(Duration(hours: 2));
try {
await PushFireSDK.instance.createScheduledWorkflowForSubscribers(
workflowId: 'reminder-series',
subscriberIds: ['sub1', 'sub2'],
scheduledFor: scheduledFor,
);
print('Workflow scheduled for subscribers');
} catch (e) {
print('Workflow scheduling failed: $e');
}
try {
await PushFireSDK.instance.createScheduledWorkflowForSegments(
workflowId: 'weekly-digest',
segmentIds: ['newsletter-subscribers'],
scheduledFor: scheduledFor,
);
print('Workflow scheduled for segments');
} catch (e) {
print('Workflow scheduling failed: $e');
}
Advanced Workflow Execution
// Create custom workflow execution request
final request = WorkflowExecutionRequest(
workflowId: 'custom-workflow',
type: WorkflowExecutionType.scheduled,
scheduledFor: DateTime.now().add(Duration(days: 1)),
target: WorkflowTarget(
type: WorkflowTargetType.subscribers,
values: ['sub1', 'sub2'],
),
);
try {
await PushFireSDK.instance.createWorkflowExecution(request);
print('Custom workflow execution created');
} catch (e) {
print('Custom workflow execution failed: $e');
}
5. Device Information
// Get current device
final device = PushFireSDK.instance.currentDevice;
if (device != null) {
print('Device ID: ${device.id}');
print('FCM Token: ${device.fcmToken}');
print('OS: ${device.os} ${device.osVersion}');
}
// Get device ID
final deviceId = await PushFireSDK.instance.getDeviceId();
print('Device ID: $deviceId');
// Get subscriber ID
final subscriberId = await PushFireSDK.instance.getSubscriberId();
print('Subscriber ID: $subscriberId');
6. Event Streams
Listen to real-time events:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late StreamSubscription _deviceSubscription;
late StreamSubscription _subscriberSubscription;
late StreamSubscription _fcmSubscription;
@override
void initState() {
super.initState();
_setupEventListeners();
}
void _setupEventListeners() {
// Listen to device registration events
_deviceSubscription = PushFireSDK.instance.onDeviceRegistered.listen(
(device) {
print('Device registered: ${device.id}');
},
);
// Listen to subscriber login events
_subscriberSubscription = PushFireSDK.instance.onSubscriberLoggedIn.listen(
(subscriber) {
print('Subscriber logged in: ${subscriber.name}');
},
);
// Listen to FCM token refresh events
_fcmSubscription = PushFireSDK.instance.onFcmTokenRefresh.listen(
(token) {
print('FCM token refreshed: $token');
},
);
}
@override
void dispose() {
_deviceSubscription.cancel();
_subscriberSubscription.cancel();
_fcmSubscription.cancel();
super.dispose();
}
// ... rest of your widget
}
7. Error Handling
The SDK provides specific exception types for different error scenarios:
try {
await PushFireSDK.instance.loginSubscriber(externalId: 'user123');
} on PushFireNotInitializedException {
print('SDK not initialized');
} on PushFireSubscriberException catch (e) {
print('Subscriber error: ${e.message}');
} on PushFireApiException catch (e) {
print('API error: ${e.message} (${e.statusCode})');
} on PushFireNetworkException catch (e) {
print('Network error: ${e.message}');
} catch (e) {
print('Unknown error: $e');
}
8. Advanced Usage
Reset SDK
// Reset SDK and clear all data
await PushFireSDK.instance.reset();
Dispose SDK
// Dispose SDK resources (call when app is shutting down)
PushFireSDK.dispose();
Check Initialization Status
if (PushFireSDK.isInitialized) {
print('SDK is ready to use');
} else {
print('SDK needs to be initialized');
}
Configuration Options
PushFireConfig
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
apiKey |
String | Yes | - | Your PushFire API key |
baseUrl |
String | No | https://jojnoebcqoqjlshwzmjm.supabase.co/functions/v1/ |
API base URL |
enableLogging |
bool | No | false |
Enable debug logging |
timeoutSeconds |
int | No | 30 |
Request timeout in seconds |
authProvider |
AuthProvider | No | AuthProvider.none |
Authentication provider for automatic subscriber management |
requestNotificationPermission |
bool | No | true |
Automatically request notification permissions during SDK initialization |
Notification Permissions
The PushFire SDK provides flexible notification permission handling to accommodate different app requirements and user experience strategies.
Automatic Permission Request
By default, the SDK automatically requests notification permissions during initialization:
await PushFireSDK.initialize(
config: PushFireConfig(
apiKey: 'your-api-key',
requestNotificationPermission: true, // Default behavior
),
);
Manual Permission Request
For apps that prefer to request permissions at a more appropriate time in the user journey:
// Initialize without automatic permission request
await PushFireSDK.initialize(
config: PushFireConfig(
apiKey: 'your-api-key',
requestNotificationPermission: false,
),
);
// Later, when appropriate for your UX
bool permissionGranted = await PushFireSDK.requestNotificationPermission();
if (permissionGranted) {
print('Notification permission granted');
} else {
print('Notification permission denied');
}
Platform-Specific Behavior
The SDK handles platform-specific permission requirements:
- iOS: Uses Firebase Messaging's permission system with appropriate settings for alerts, badges, and sounds
- Android: Handles runtime permissions for Android 13+ (API level 33+) and gracefully handles older versions
- Web: Requests browser notification permissions through Firebase Messaging
Best Practices for Permissions
- Context Matters: Request permissions when users understand the value of notifications
- Graceful Degradation: Your app should work even if permissions are denied
- Re-request Strategy: Use
requestNotificationPermission()
to re-request if initially denied - User Education: Explain the benefits before requesting permissions
Permission Status Handling
The SDK automatically:
- Logs permission request outcomes for debugging
- Continues device registration even if permissions are denied
- Supports manual permission grants through device settings
- Re-registers the device when permissions are granted via manual request
Error Types
PushFireException
- Base exception classPushFireApiException
- API-related errorsPushFireNotInitializedException
- SDK not initializedPushFireConfigurationException
- Configuration errorsPushFireDeviceException
- Device registration errorsPushFireSubscriberException
- Subscriber operation errorsPushFireTagException
- Tag operation errorsPushFireNetworkException
- Network connectivity errors
Best Practices
- Initialize Early: Call
PushFireSDK.initialize()
as early as possible in your app lifecycle - Handle Errors: Always wrap SDK calls in try-catch blocks
- Check Status: Use
isSubscriberLoggedIn()
before performing subscriber operations - Listen to Events: Use event streams to react to SDK state changes
- Dispose Resources: Call
PushFireSDK.dispose()
when your app shuts down - Enable Logging: Use
enableLogging: true
during development for debugging
Troubleshooting
Common Issues
-
SDK Not Initialized
- Ensure you call
PushFireSDK.initialize()
before using any other methods - Check that initialization completes successfully
- Ensure you call
-
Device Registration Fails
- Verify Firebase setup is correct
- Check that FCM is properly configured
- Ensure device has internet connectivity
-
API Errors
- Verify your API key is correct
- Check that the base URL is accessible
- Ensure your PushFire account is active
-
Subscriber Operations Fail
- Make sure a subscriber is logged in before performing operations
- Verify the external ID is valid
Debug Logging
Enable logging to see detailed information about SDK operations:
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key',
enableLogging: true, // Enable this for debugging
),
);
Support
For issues and questions:
- Check the troubleshooting section
- Review the API documentation
- Contact support at support@pushfire.com
License
This project is licensed under the MIT License - see the LICENSE file for details.