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.
- 🚀 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
Add this to your package's pubspec.yaml file:
dependencies:
pushfire_sdk: ^1.0.0Then run:
flutter pub getThis 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.dartfile with your Firebase configuration - Download and add the configuration files:
google-services.jsonfor Android (place inandroid/app/)GoogleService-Info.plistfor iOS (place inios/Runner/)
- Follow the FlutterFire setup guide for platform-specific configuration
Add the following to your android/app/build.gradle:
dependencies {
implementation 'com.google.firebase:firebase-messaging:23.0.0'
}Enable push notifications in your iOS project:
- Open
ios/Runner.xcworkspacein Xcode - Select your project target
- Go to "Signing & Capabilities"
- Add "Push Notifications" capability
- Add "Background Modes" capability and enable "Background processing" and "Remote notifications"
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());
}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.
- Firebase Authentication (
AuthProvider.firebase) - Supabase Authentication (
AuthProvider.supabase) - None (
AuthProvider.none) - Default, manual subscriber management
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()
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key-here',
authProvider: AuthProvider.supabase,
),
);When Supabase Authentication is configured:
- The SDK automatically listens for
onAuthStateChangeevents 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()
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.
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');
}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');
}try {
await PushFireSDK.instance.logoutSubscriber();
print('Subscriber logged out');
} catch (e) {
print('Logout failed: $e');
}// 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}');
}// 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 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 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');
}Execute automated workflows for targeted push notifications:
// 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');
}// 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');
}// 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');
}// 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');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
}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');
}// Reset SDK and clear all data
await PushFireSDK.instance.reset();// Dispose SDK resources (call when app is shutting down)
PushFireSDK.dispose();if (PushFireSDK.isInitialized) {
print('SDK is ready to use');
} else {
print('SDK needs to be initialized');
}| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey |
String | Yes | - | Your PushFire API key |
baseUrl |
String | No | https://api.pushfire.app/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 |
⚠️ Logging & privacy: WhenenableLoggingistrue, the SDK logs full request and response bodies — including subscriber PII such asphone,name, andmetadata. Logging is off by default and usesdart:developer(generally not emitted in release builds), but if you forward SDK logs anywhere (for example a crash reporter or log aggregator), do not do so in production builds with logging enabled.
The PushFire SDK provides flexible notification permission handling to accommodate different app requirements and user experience strategies.
By default, the SDK automatically requests notification permissions during initialization:
await PushFireSDK.initialize(
config: PushFireConfig(
apiKey: 'your-api-key',
requestNotificationPermission: true, // Default behavior
),
);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');
}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
- 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
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
PushFireException- Base exception classPushFireApiException- API-related errorsPushFireNotInitializedException- SDK not initializedPushFireConfigurationException- Configuration errorsPushFireDeviceException- Device registration errorsPushFireSubscriberException- Subscriber operation errorsPushFireTagException- Tag operation errorsPushFireNetworkException- Network connectivity errors
- 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: trueduring development for debugging
-
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
Enable logging to see detailed information about SDK operations:
await PushFireSDK.initialize(
PushFireConfig(
apiKey: 'your-api-key',
enableLogging: true, // Enable this for debugging
),
);For issues and questions:
- Check the troubleshooting section
- Review the API documentation
- Contact support at support@pushfire.com
This project is licensed under the MIT License - see the LICENSE file for details.