PNLight SDK - Flutter Plugin
A Flutter plugin that provides iOS integration with the PNLight SDK.
Installation
Add this to your package's pubspec.yaml file:
dependencies:
pnlight_sdk: ^0.1.0
Then run:
flutter pub get
Requirements
- Flutter: >=3.3.0
- Dart: >=2.17.0 <4.0.0
- iOS: 15.0+
This package is iOS-only. Android is not supported.
iOS Setup
DivKit Pod Source
If you use RemoteUiView, add the DivKit CocoaPods source to your app's ios/Podfile before the default CocoaPods source:
source 'https://github.com/divkit/divkit-ios.git'
source 'https://cdn.cocoapods.org/'
Then run:
cd ios
pod install
Required Frameworks
Make sure the host iOS app links the frameworks required by PNLight:
- StoreKit.framework
- AdSupport.framework
- AppTrackingTransparency.framework
For apps that need IDFA tracking, add NSUserTrackingUsageDescription to Info.plist:
<key>NSUserTrackingUsageDescription</key>
<string>This app uses device tracking to provide analytics and improve user experience.</string>
Usage
Initialization
Initialize PNLight before using analytics, attribution, or Remote UI:
import 'package:flutter/widgets.dart';
import 'package:pnlight_sdk/pnlight_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await PNLightSDK.initialize('your-api-key');
runApp(MyApp());
}
Event Logging
import 'package:pnlight_sdk/pnlight_sdk.dart';
await PNLightSDK.logEvent('purchase_completed', {
'product_id': 'premium_subscription',
'amount': 9.99,
'currency': 'USD',
});
Attribution
Send attribution data from external providers before requesting UI config.
import 'package:pnlight_sdk/pnlight_sdk.dart';
final success = await PNLightSDK.addAttribution(
provider: 'appsFlyer',
data: {'af_status': 'Non-organic'},
identifier: 'your-appsflyer-id',
);
AppsFlyer Integration Example
PNLight does not depend on the AppsFlyer initialization order — it only needs the conversion data, delivered via addAttribution. Make sure the conversion ("attribution success") callback is not processed before PNLight is initialized: if it can fire earlier, store the conversion data in memory and call addAttribution once initialize completes.
AppsFlyer requires the ATT prompt to complete before it starts. If PNLight is initialized before ATT authorization, call updateIdfa() after the prompt completes so PNLight receives the granted IDFA.
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:appsflyer_sdk/appsflyer_sdk.dart';
import 'package:pnlight_sdk/pnlight_sdk.dart';
Future<void> startSdks() async {
await PNLightSDK.initialize('your-api-key');
// Request ATT, then pass the granted IDFA to PNLight.
await AppTrackingTransparency.requestTrackingAuthorization();
await PNLightSDK.updateIdfa();
// Start AppsFlyer after ATT completes (an AppsFlyer requirement).
final appsFlyerSdk = AppsflyerSdk(AppsFlyerOptions(
afDevKey: 'your-appsflyer-dev-key',
appId: 'your-ios-app-id',
showDebug: false,
));
appsFlyerSdk.onInstallConversionData((conversionData) async {
final appsFlyerId = await appsFlyerSdk.getAppsFlyerUID();
final payload = conversionData['payload'] ?? conversionData;
await PNLightSDK.addAttribution(
provider: 'appsFlyer',
data: Map<String, dynamic>.from(payload as Map),
identifier: appsFlyerId,
);
});
await appsFlyerSdk.initSdk(
registerConversionDataCallback: true,
registerOnAppOpenAttributionCallback: false,
registerOnDeepLinkingCallback: false,
);
}
Supported providers:
appsFlyerfirebasefacebook
User Identity
import 'package:pnlight_sdk/pnlight_sdk.dart';
final userId = await PNLightSDK.getUserId();
In-App Purchases
PNLight wraps StoreKit 2 for fetching products (price, offers, trial info),
purchasing, restoring, and checking entitlements. The product ids are configured
on the backend — fetchProducts resolves them against the App Store.
import 'package:pnlight_sdk/pnlight_sdk.dart';
// Load the configured products with their App Store price/offer info.
final products = await PNLightSDK.fetchProducts();
for (final product in products) {
print('${product.displayName}: ${product.displayPrice}');
final offer = product.subscription?.introductoryOffer;
if (offer != null && product.subscription!.isEligibleForIntroOffer) {
// e.g. pay-as-you-go: "$0.99/month for 6 months"
print('Offer: ${offer.displayPrice} (${offer.paymentMode.name}, '
'${offer.periodCount} × ${offer.period.value} ${offer.period.unit.name})');
}
}
// Purchase.
final result = await PNLightSDK.purchase('your.product.id');
if (result == PNLightPurchaseResult.success) {
// Unlock content.
}
// Entitlement checks (local StoreKit entitlements, work offline).
final premium = await PNLightSDK.isPremium();
final eligible = await PNLightSDK.isEligibleForTrial('your.product.id');
// Restore previous purchases.
await PNLightSDK.restorePurchases();
RemoteUiView - Server-driven UI
RemoteUiView fetches and renders a server-driven layout from PNLight for a given placement. It calls getUIConfig(placement) internally, renders the native view, and emits action events to Dart.
When using external attribution providers such as AppsFlyer, send attribution as early as possible (see the AppsFlyer example above). getUIConfig waits for attribution data internally when attributionRequired is true (the default), so no manual delay is needed.
import 'package:flutter/widgets.dart';
import 'package:pnlight_sdk/pnlight_sdk.dart';
class PaywallScreen extends StatelessWidget {
const PaywallScreen({super.key});
void _handleAction(RemoteUiActionEvent event) {
if (event.logId == 'purchase_button') {
final productId = event.params['id'];
// Start purchase flow for productId
} else if (event.logId == 'close_button') {
// Close the screen
}
}
@override
Widget build(BuildContext context) {
return RemoteUiView(
placement: 'paywall',
onAction: _handleAction,
);
}
}
Manual Config Fetching
Use getUIConfig if you need to fetch the placement configuration yourself. When attributionRequired is true (the default), the SDK waits for attribution data internally before returning:
import 'package:pnlight_sdk/pnlight_sdk.dart';
final config = await PNLightSDK.getUIConfig('paywall');
final configWithoutAttributionWait = await PNLightSDK.getUIConfig(
'paywall',
attributionRequired: false,
);
API Reference
PNLightSDK
| Method | Description |
|---|---|
initialize(apiKey, baseDomain?) |
Initialize the SDK with your API key and optional base domain |
logEvent(eventName, eventArgs?) |
Log a custom event with optional arguments |
addAttribution(provider, data?, identifier?) |
Send attribution data from AppsFlyer, Firebase, or Facebook |
getUserId() |
Get or create a stable user identifier |
updateIdfa() |
Send the current IDFA to PNLight after the ATT prompt completes |
prefetchUIConfig(placement) |
Prefetch a UI config into the in-memory cache |
getUIConfig(placement, attributionRequired?) |
Fetch a UI config; waits for attribution by default |
fetchProducts() |
Load configured products with App Store price/offer/trial info |
purchase(productId) |
Purchase a product; returns a PNLightPurchaseResult |
restorePurchases() |
Restore previous purchases by syncing with the App Store |
isPremium() |
Whether the user has an active entitlement to any configured product |
isPurchased(productId) |
Whether the user has an active entitlement to a specific product |
isEligibleForTrial(productId) |
Whether the user is eligible for a product's introductory offer |
getAppleReceipt() |
Base64 App Store receipt for server-side validation, or null if absent |
RemoteUiView
| Parameter | Type | Description |
|---|---|---|
placement |
String |
PNLight placement identifier |
cardId |
String? |
Card identifier; defaults to pnlight_<placement> |
secure |
bool |
Deprecated. Secure rendering is controlled by the backend response. |
preventRecording |
bool |
Deprecated. Capture blocking is controlled by the backend. |
onAction |
void Function(RemoteUiActionEvent)? |
Called when a custom action is triggered |
RemoteUiActionEvent
| Property | Type | Description |
|---|---|---|
url |
String |
Full URL string of the triggered action |
scheme |
String |
URL scheme |
path |
String |
URL path component |
params |
Map<String, String> |
Query parameters extracted from the URL |
logId |
String? |
Log ID for the triggered action |
action |
String? |
Raw action value when provided by the native view |
UIConfig
| Property | Type | Description |
|---|---|---|
config |
String? |
Remote UI JSON config |
parameters |
Map<String, dynamic>? |
Placement parameters returned by PNLight |
debug |
bool? |
Debug flag returned by PNLight |
Support
For support and questions, visit docs.pnlight.app.