Core Methods
initialize()
PntaFlutter.initialize(String projectId, {
Map<String, dynamic>? metadata,
bool registerDevice = true,
bool autoHandleLinks = false,
bool showSystemUI = false,
})
Initializes the plugin and optionally registers the device. When registerDevice: true (default), this method will prompt the user for notification permission and register the device with PNTA. Use registerDevice: false for delayed registration if you don’t want to prompt the user immediately.
Parameters:
projectId: Your PNTA project ID (format: prj_XXXXXXXXX)
metadata (optional): Device metadata as key-value pairs
registerDevice (optional): Whether to register device immediately. Default: true
autoHandleLinks (optional): Automatically handle link_to URLs when notifications are tapped from background/terminated state. Default: false
showSystemUI (optional): Show system notification banner/sound when app is in foreground. Default: false
Returns: Future<void>
Examples:
// Minimal - handles everything automatically
await PntaFlutter.initialize('prj_XXXXXXXXX');
// With metadata and configuration
await PntaFlutter.initialize(
'prj_XXXXXXXXX',
metadata: {
'user_id': '123',
'user_email': '[email protected]',
},
autoHandleLinks: true, // Optional: enable auto-handling of links
showSystemUI: true, // Optional: show system UI in foreground
);
// Delayed registration scenario
await PntaFlutter.initialize(
'prj_XXXXXXXXX',
metadata: {
'user_id': '123',
'user_email': '[email protected]',
},
registerDevice: false, // Don't register immediately
autoHandleLinks: true,
);
// Register later with registerDevice()
registerDevice()
PntaFlutter.registerDevice()
Registers the device with your PNTA project using metadata from initialize(). Only needed when using delayed registration (when initialize() was called with registerDevice: false).
Returns: Future<void>
Example:
// First initialize with metadata but don't register
await PntaFlutter.initialize(
'prj_XXXXXXXXX',
metadata: {
'user_id': '123',
'user_email': '[email protected]',
},
registerDevice: false,
);
// Later, register when ready (uses stored metadata)
await PntaFlutter.registerDevice();
// Get device token after registration
final token = PntaFlutter.deviceToken;
if (token != null) {
print('Device registered with token: $token');
}
Must call initialize() first. Only use this method when you initialized with registerDevice: false.
PntaFlutter.updateMetadata(Map<String, dynamic> metadata)
Updates device metadata without re-registering.
Parameters:
metadata: Updated metadata as key-value pairs
Returns: Future<void>
Example:
await PntaFlutter.updateMetadata({
'last_active': DateTime.now().toIso8601String(),
});
handleLink()
PntaFlutter.handleLink(String link)
Manually handles a link using the plugin’s routing logic.
Parameters:
Returns: void
Example:
PntaFlutter.handleLink('/profile');
PntaFlutter.handleLink('https://example.com');
Properties
deviceToken
Gets the current device token after successful initialization or registration.
Type: String?
Returns: Device token string if registration was successful, null otherwise.
Example:
await PntaFlutter.initialize('prj_XXXXXXXXX');
final token = PntaFlutter.deviceToken;
if (token != null) {
print('Device token: $token');
} else {
print('Device registration failed or not completed');
}
navigatorKey
Global navigator key for internal route navigation. Must be assigned to your MaterialApp.
Type: GlobalKey<NavigatorState>
Example:
MaterialApp(
navigatorKey: PntaFlutter.navigatorKey,
// ... rest of your app
)
foregroundNotifications
PntaFlutter.foregroundNotifications
Stream of notification payloads received when app is in foreground.
Type: Stream<Map<String, dynamic>>
Payload Structure:
Both iOS and Android send identical flat payload structures:
{
"title": "Rain expected this afternoon",
"body": "Showers from 2–5 PM in your area...",
"link_to": "https://example.com/weather",
"custom_field": "custom_value"
}
Example:
PntaFlutter.foregroundNotifications.listen((payload) {
final title = payload['title'];
final body = payload['body'];
final link = payload['link_to'];
print('Received: $title');
// Show custom UI
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$title: $body')),
);
});
onNotificationTap
PntaFlutter.onNotificationTap
Stream of notification payloads when user taps a notification from background/terminated state.
Type: Stream<Map<String, dynamic>>
Payload Structure:
Contains the link_to field and any custom data fields. System fields (title, body) are not available on Android due to platform limitations.
{
"link_to": "https://example.com/weather"
}
Example:
PntaFlutter.onNotificationTap.listen((payload) {
print('User tapped notification: $payload');
// Access link_to field (available on both platforms)
final linkTo = payload['link_to'] as String?;
// Track analytics, navigate to specific screen, etc.
});
Link Handling
The plugin handles notification links automatically using smart URL detection:
- External URLs (with scheme) → Opens in system browser/apps
- Examples:
https://example.com, mailto:[email protected], tel:+1234567890, sms:+1234567890
- Works automatically with no additional configuration required
- Internal routes (without scheme) → Navigates using Flutter’s Navigator
- Examples:
/profile, /settings, /dashboard
- Requires
navigatorKey setup (covered in Quick Start)
Implementing Notification Listeners
App-Level
Global listeners in main.dart. No disposal needed since the app doesn’t get disposed.
void main() async {
await PntaFlutter.initialize('prj_XXXXXXXXX');
PntaFlutter.foregroundNotifications.listen((payload) {
print('Got notification: ${payload['title']}');
});
runApp(MyApp());
}
Listeners in StatefulWidgets. Disposal required to prevent memory leaks.
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
StreamSubscription? _sub;
@override
void initState() {
super.initState();
_sub = PntaFlutter.foregroundNotifications.listen((payload) {
// Handle notification
});
}
@override
void dispose() {
_sub?.cancel(); // Required to prevent memory leaks
super.dispose();
}
}