reddit_pixel 1.0.0
reddit_pixel: ^1.0.0 copied to clipboard
A privacy-centric, backend-agnostic Flutter library for Reddit Conversions API (CAPI) v3. Supports direct and proxy transport modes with offline-first persistence.
reddit_pixel #
A privacy-centric, backend-agnostic Flutter library for Reddit Conversions API (CAPI) v3. Track conversion events with offline-first persistence, automatic retry, and PII hashing.
Features #
- Privacy-First: No tracking dependencies by default. IDFA/AAID support requires explicit opt-in
- Backend-Agnostic: Use proxy mode (recommended) or direct mode for development
- Offline-First: Events are queued locally with Hive and sent when online
- Performance: PII normalization and SHA-256 hashing run in isolates
- Automatic Retry: Exponential backoff for failed requests
- All Event Types: Purchase, SignUp, Lead, AddToCart, AddToWishlist, Search, ViewContent, PageVisit, and Custom events
See the example for runnable examples of various usages.
Platform Support #
| Android | iOS | macOS | Web | Linux | Windows |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Features Supported #
| Feature | Android | iOS | macOS | Web | Linux | Windows |
|---|---|---|---|---|---|---|
| Proxy Transport | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Direct Transport | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Offline Queue | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| IDFA/AAID Support | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
Installation #
Add the dependency to your pubspec.yaml:
dependencies:
reddit_pixel: ^0.0.1
Then run:
flutter pub get
Configuration #
Proxy Server Setup (Recommended) #
For production, use a proxy server to keep your Reddit API token secure. Your server should:
- Receive the event payload from this library
- Add the Reddit API authorization header
- Forward the request to Reddit's Conversions API
- Return the response
Example proxy endpoint (Node.js/Express):
app.post('/api/reddit-events/:pixelId', async (req, res) => {
const response = await fetch(
`https://ads-api.reddit.com/api/v3/pixels/${req.params.pixelId}/conversion_events`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.REDDIT_API_TOKEN}`,
},
body: JSON.stringify(req.body),
}
);
res.status(response.status).json(await response.json());
});
iOS Configuration #
If you plan to use IDFA tracking, add to your Info.plist:
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to measure advertising effectiveness.</string>
Android Configuration #
No special configuration required. For AAID tracking, Google Play Services must be available on the device.
Usage #
Basic Usage #
import 'package:reddit_pixel/reddit_pixel.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize with proxy mode (recommended)
await RedditPixel.initialize(
pixelId: 'your-pixel-id',
proxyUrl: 'https://your-server.com/api/reddit-events',
);
runApp(MyApp());
}
// Track a purchase anywhere in your app
await RedditPixel.instance.trackPurchase(
value: 99.99,
currency: 'USD',
itemCount: 2,
userData: RedditUserData(
email: 'customer@example.com',
externalId: 'user-123',
),
);
Advanced Usage #
Direct Mode (Development Only)
⚠️ Warning: Direct mode embeds your Reddit API token in the app binary. Use only for development.
await RedditPixel.initialize(
pixelId: 'your-pixel-id',
token: 'your-reddit-api-token',
testMode: true, // Events won't affect production data
debug: true, // Enable debug logging
);
Custom Identity Provider
To enable IDFA/AAID tracking, implement RedditIdentityProvider:
import 'package:advertising_id/advertising_id.dart';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
class AppIdentityProvider implements RedditIdentityProvider {
@override
Future<String?> getAdvertisingId() async {
try {
if (Platform.isIOS) {
final status = await AppTrackingTransparency.trackingAuthorizationStatus;
if (status != TrackingStatus.authorized) return null;
}
return await AdvertisingId.id(true);
} catch (_) {
return null;
}
}
@override
Future<bool> isTrackingEnabled() async {
if (Platform.isIOS) {
final status = await AppTrackingTransparency.trackingAuthorizationStatus;
return status == TrackingStatus.authorized;
}
return true;
}
}
// Use it during initialization
await RedditPixel.initialize(
pixelId: 'your-pixel-id',
proxyUrl: 'https://your-server.com/api/reddit-events',
identityProvider: AppIdentityProvider(),
);
All Event Types
// Standard events
await RedditPixel.instance.trackPurchase(value: 99.99, currency: 'USD');
await RedditPixel.instance.trackSignUp();
await RedditPixel.instance.trackLead();
await RedditPixel.instance.trackAddToCart(value: 49.99, currency: 'USD');
await RedditPixel.instance.trackAddToWishlist(value: 199.99, currency: 'USD');
await RedditPixel.instance.trackSearch(searchString: 'wireless headphones');
await RedditPixel.instance.trackViewContent(contentId: 'product-123');
await RedditPixel.instance.trackPageVisit(pageUrl: '/checkout');
// Custom events
await RedditPixel.instance.trackCustom(
'VideoWatched',
customData: {'video_id': 'vid-123', 'duration': 120},
);
// Generic tracking with event objects
await RedditPixel.instance.track(
PurchaseEvent(
value: 99.99,
currency: 'USD',
userData: RedditUserData(email: 'user@example.com'),
),
);
Queue Management
// Get pending event count
final count = await RedditPixel.instance.pendingEventCount;
// Force immediate flush of queued events
await RedditPixel.instance.flush();
// Dispose when done (e.g., app termination)
await RedditPixel.instance.dispose();
API Reference #
RedditPixel #
| Method | Description |
|---|---|
initialize() |
Initialize the library with configuration |
track(event) |
Track any RedditEvent |
trackPurchase() |
Track a purchase conversion |
trackSignUp() |
Track a sign-up conversion |
trackLead() |
Track a lead generation |
trackAddToCart() |
Track add-to-cart action |
trackAddToWishlist() |
Track add-to-wishlist action |
trackSearch() |
Track a search action |
trackViewContent() |
Track content view |
trackPageVisit() |
Track page/screen visit |
trackCustom() |
Track custom events |
flush() |
Force send queued events |
dispose() |
Release resources |
RedditUserData #
| Field | Type | Description |
|---|---|---|
email |
String? |
User's email (hashed before sending) |
externalId |
String? |
Your system's user ID (hashed) |
uuid |
String? |
UUID for cross-device attribution |
idfa |
String? |
iOS advertising ID (hashed) |
aaid |
String? |
Android advertising ID (hashed) |
ipAddress |
String? |
User's IP address |
userAgent |
String? |
Browser/app user agent |
clickId |
String? |
Reddit click ID (rdt_cid parameter) |
Security #
PII Handling #
All personally identifiable information (PII) is automatically normalized and SHA-256 hashed before transmission:
- Email: Trimmed, lowercased, then hashed
- Phone: Normalized to digits only, then hashed
- IDFA: Uppercased, then hashed
- AAID: Lowercased, then hashed
Transport Security #
| Mode | Token Location | Recommendation |
|---|---|---|
| Proxy | Server-side only | ✅ Production |
| Direct | Embedded in app | ⚠️ Development only |
Issues #
Please file issues, bugs, or feature requests in our issue tracker.
Contributing #
If you wish to contribute, please review our contribution guidelines and open a pull request.
License #
This project is licensed under the BSD-3-Clause License - see the LICENSE file for details.
Made with ❤️ by TPN Labs