Skip to content

Commit 674fd34

Browse files
authored
Clean up example app and release v1.0.0 (#15)
Filter FCM metadata from notification tap payloads, clean up example app, and bump to v1.0.0 stable release.
1 parent 027b780 commit 674fd34

File tree

7 files changed

+83
-210
lines changed

7 files changed

+83
-210
lines changed

CHANGELOG.md

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,3 @@
1-
## 1.0.0-dev.5
1+
## 1.0.0
22

3-
- Initial stable release of PNTA Flutter plugin.
4-
5-
## 1.0.0-dev.4
6-
7-
- Fix parameter signature mismatch in updateMetadata method
8-
- Remove unnecessary hex token conversion in iOS identify handler
9-
- Fix Android memory leak in permission handler
10-
- Remove redundant notification channel creation
11-
- Add error handling for event stream casting errors
12-
- Add retry logic with exponential backoff for network requests
13-
- Standardize error codes across platforms (NO_DEVICE_TOKEN, FCM_REGISTRATION_FAILED)
14-
- Add comprehensive input validation for projectId and metadata
15-
- Improve error handling in LinkHandler with return status
16-
- Standardize debug logging with consistent PNTA prefixing
17-
18-
## 1.0.0-dev.3
19-
20-
Stable version with a bunch of improvements
3+
- Initial stable release of PNTA Flutter plugin.

README.md

Lines changed: 2 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ A Flutter plugin for requesting push notification permissions and handling notif
1717
- [Android Setup](#android-setup)
1818
- [Quick Start Guide](#quick-start-guide)
1919
- [API Reference](#api-reference)
20-
- [Simple Example](#simple-example)
21-
- [Troubleshooting](#troubleshooting)
20+
- [Example](#example)
2221

2322
## Installation & Setup
2423

@@ -296,114 +295,6 @@ await PntaFlutter.initialize('prj_XXXXXXXXX', metadata: UserMetadata.current);
296295
await PntaFlutter.updateMetadata(UserMetadata.current);
297296
```
298297

299-
## Simple Example
300-
301-
```dart
302-
import 'package:flutter/material.dart';
303-
import 'package:pnta_flutter/pnta_flutter.dart';
304-
305-
void main() async {
306-
WidgetsFlutterBinding.ensureInitialized();
307-
308-
// One-line setup - handles permission, registration, and configuration
309-
await PntaFlutter.initialize(
310-
'prj_XXXXXXXXX', // Your project ID from app.pnta.io
311-
metadata: {
312-
'user_id': '123',
313-
'user_email': 'user@example.com',
314-
},
315-
);
316-
317-
// Optional: Get the device token if you need it for your backend
318-
final deviceToken = PntaFlutter.deviceToken;
319-
if (deviceToken != null) {
320-
print('Device registered successfully! Token: $deviceToken');
321-
}
322-
323-
runApp(MyApp());
324-
}
325-
326-
class MyApp extends StatelessWidget {
327-
@override
328-
Widget build(BuildContext context) {
329-
return MaterialApp(
330-
navigatorKey: PntaFlutter.navigatorKey, // Required for deep linking
331-
home: HomePage(),
332-
);
333-
}
334-
}
335-
336-
class HomePage extends StatefulWidget {
337-
@override
338-
_HomePageState createState() => _HomePageState();
339-
}
340-
341-
class _HomePageState extends State<HomePage> {
342-
@override
343-
void initState() {
344-
super.initState();
345-
_setupNotifications();
346-
}
347-
348-
void _setupNotifications() async {
349-
// If you used initialize() with requestPermission: true, this is already done!
350-
// Otherwise, for delayed permission scenarios:
351-
// await PntaFlutter.requestPermission(
352-
// metadata: {
353-
// 'user_id': '123',
354-
// 'user_email': 'user@example.com',
355-
// },
356-
// );
357-
358-
// Listen for foreground notifications
359-
PntaFlutter.foregroundNotifications.listen((payload) {
360-
ScaffoldMessenger.of(context).showSnackBar(
361-
SnackBar(content: Text('Received: ${payload['title']}')),
362-
);
363-
});
364-
365-
// Listen for background notification taps
366-
PntaFlutter.onNotificationTap.listen((payload) {
367-
print('User tapped notification: ${payload['title']}');
368-
});
369-
}
370-
371-
@override
372-
Widget build(BuildContext context) {
373-
return Scaffold(
374-
appBar: AppBar(title: Text('PNTA Example')),
375-
body: Center(child: Text('Ready for notifications!')),
376-
);
377-
}
378-
}
379-
```
298+
## Example
380299

381300
For a complete working example with all features, see the `example/` app in the plugin repository.
382-
383-
## Troubleshooting
384-
385-
### Common Issues
386-
387-
**Permission not granted on Android:**
388-
389-
- Ensure `POST_NOTIFICATIONS` permission is in AndroidManifest.xml
390-
- For Android 13+, permission must be requested at runtime
391-
392-
**Firebase issues:**
393-
394-
- Verify `google-services.json` is in the correct location
395-
- Check that Firebase project is properly configured
396-
- Ensure Google Services plugin is applied
397-
398-
**Deep links not working:**
399-
400-
- Verify `navigatorKey` is assigned to MaterialApp
401-
- Check that routes are properly defined
402-
- For external URLs, ensure `<queries>` block is in AndroidManifest.xml
403-
404-
**iOS build issues:**
405-
406-
- Clean and rebuild: `flutter clean && flutter pub get`
407-
- Update Podfile and run `cd ios && pod install`
408-
409-
For more examples and advanced usage, see the `example/` directory in the plugin repository.

android/src/main/kotlin/io/pnta/pnta_flutter/PntaFlutterPlugin.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ class PntaFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Plugin
128128
if (extras != null && !extras.isEmpty) {
129129
val payload = mutableMapOf<String, Any>()
130130
for (key in extras.keySet()) {
131+
if (key.startsWith("google.") || key.startsWith("gcm.") ||
132+
key == "from" || key == "collapse_key") {
133+
continue
134+
}
131135
val value = extras.get(key)
132136
when (value) {
133137
is String, is Int, is Boolean, is Double, is Float, is Long -> payload[key] = value
@@ -138,6 +142,6 @@ class PntaFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Plugin
138142
NotificationTapHandler.sendTapPayload(payload)
139143
}
140144
}
141-
return false // allow other listeners to process
145+
return false
142146
}
143147
}

example/lib/main.dart

Lines changed: 71 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,108 @@
11
import 'package:flutter/material.dart';
22
import 'package:pnta_flutter/pnta_flutter.dart';
33

4-
void main() {
5-
runApp(const MyApp());
4+
void main() async {
5+
WidgetsFlutterBinding.ensureInitialized();
6+
7+
await PntaFlutter.initialize(
8+
'prj_k3e0Givq', // replace with your project id
9+
metadata: {
10+
'user_id': '123',
11+
'user_email': 'user@example.com',
12+
},
13+
autoHandleLinks: true,
14+
showSystemUI: true,
15+
);
16+
17+
runApp(MyApp());
618
}
719

820
class MyApp extends StatelessWidget {
921
const MyApp({super.key});
22+
1023
@override
1124
Widget build(BuildContext context) {
1225
return MaterialApp(
1326
navigatorKey: PntaFlutter.navigatorKey,
14-
home: const HomeScreen(),
27+
home: HomePage(),
28+
routes: {
29+
'/profile': (context) => ProfilePage(),
30+
},
1531
);
1632
}
1733
}
1834

19-
class HomeScreen extends StatefulWidget {
20-
const HomeScreen({super.key});
35+
class HomePage extends StatefulWidget {
36+
const HomePage({super.key});
37+
2138
@override
22-
State<HomeScreen> createState() => _HomeScreenState();
39+
State<HomePage> createState() => _HomePageState();
2340
}
2441

25-
class _HomeScreenState extends State<HomeScreen> {
26-
String? _deviceToken;
27-
String? _lastNotificationTap;
28-
String? _foregroundLink;
29-
final List<Map<String, dynamic>> _foregroundNotifications = [];
30-
bool _loading = true;
42+
class _HomePageState extends State<HomePage> {
43+
String? _lastNotification;
3144

3245
@override
3346
void initState() {
3447
super.initState();
35-
_initPnta();
48+
3649
PntaFlutter.foregroundNotifications.listen((payload) {
37-
setState(() {
38-
_foregroundNotifications.insert(0, payload);
39-
final link = payload['link_to'] as String?;
40-
if (link != null && link.isNotEmpty) {
41-
_foregroundLink = link;
42-
}
43-
});
50+
setState(() => _lastNotification = 'Foreground: ${payload.toString()}');
4451
});
52+
4553
PntaFlutter.onNotificationTap.listen((payload) {
46-
setState(() {
47-
_lastNotificationTap = payload.toString();
48-
});
54+
setState(() => _lastNotification = 'Tap: ${payload.toString()}');
4955
});
5056
}
5157

52-
Future<void> _initPnta() async {
53-
await PntaFlutter.initialize(
54-
'prj_k3e0Givq',
55-
metadata: {
56-
'user_id': '123',
57-
'email': 'user@example.com',
58-
'role': 'tester',
59-
},
60-
autoHandleLinks: false, // We'll handle links manually for demo
58+
@override
59+
Widget build(BuildContext context) {
60+
return Scaffold(
61+
appBar: AppBar(title: Text('PNTA Example')),
62+
body: Center(
63+
child: Column(
64+
mainAxisAlignment: MainAxisAlignment.center,
65+
children: [
66+
Icon(Icons.notifications, size: 64, color: Colors.blue),
67+
SizedBox(height: 16),
68+
Text(
69+
'Push notifications ready!',
70+
style: Theme.of(context).textTheme.headlineSmall,
71+
),
72+
SizedBox(height: 8),
73+
Text(
74+
'Device token: ${PntaFlutter.deviceToken != null ? "Available" : "Not available"}'),
75+
if (PntaFlutter.deviceToken != null)
76+
SelectableText(
77+
PntaFlutter.deviceToken!,
78+
style: TextStyle(fontSize: 10, fontFamily: 'monospace'),
79+
),
80+
if (_lastNotification != null) ...[
81+
SizedBox(height: 16),
82+
Padding(
83+
padding: EdgeInsets.symmetric(horizontal: 24),
84+
child: SelectableText(
85+
_lastNotification!,
86+
style: TextStyle(fontSize: 10, fontFamily: 'monospace'),
87+
),
88+
),
89+
],
90+
],
91+
),
92+
),
6193
);
62-
setState(() {
63-
_deviceToken = PntaFlutter.deviceToken;
64-
_loading = false;
65-
});
6694
}
95+
}
96+
97+
class ProfilePage extends StatelessWidget {
98+
const ProfilePage({super.key});
6799

68100
@override
69101
Widget build(BuildContext context) {
70102
return Scaffold(
71-
appBar: AppBar(title: const Text('PNTA Metadata & Foreground Demo')),
72-
body: Padding(
73-
padding: const EdgeInsets.all(24),
74-
child: _loading
75-
? const Center(child: CircularProgressIndicator())
76-
: Column(
77-
crossAxisAlignment: CrossAxisAlignment.start,
78-
children: [
79-
const Text('Device Token:'),
80-
SelectableText(_deviceToken ?? 'No token (denied or error)'),
81-
const SizedBox(height: 24),
82-
const Text('Foreground Notifications:'),
83-
if (_foregroundNotifications.isEmpty) const Text('None'),
84-
for (final notif in _foregroundNotifications)
85-
Card(
86-
child: Padding(
87-
padding: const EdgeInsets.all(8.0),
88-
child: Text(notif.toString()),
89-
),
90-
),
91-
const SizedBox(height: 24),
92-
if (_foregroundLink != null) ...[
93-
ElevatedButton(
94-
onPressed: () async {
95-
final link = _foregroundLink;
96-
if (link != null) {
97-
await PntaFlutter.handleLink(link);
98-
setState(() {
99-
_foregroundLink = null;
100-
});
101-
}
102-
},
103-
child: const Text('Open Foreground Link'),
104-
),
105-
const SizedBox(height: 16),
106-
],
107-
const Text('Last Notification Tap Payload:'),
108-
SelectableText(_lastNotificationTap ?? 'None'),
109-
],
110-
),
103+
appBar: AppBar(title: Text('Profile')),
104+
body: Center(
105+
child: Text('Profile page opened via deep link!'),
111106
),
112107
);
113108
}

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ packages:
189189
path: ".."
190190
relative: true
191191
source: path
192-
version: "1.0.0-dev.5"
192+
version: "1.0.0"
193193
process:
194194
dependency: transitive
195195
description:

lib/src/version.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/// PNTA Flutter SDK version
22
/// This should match the version in pubspec.yaml
3-
const String kPntaSdkVersion = '1.0.0-dev.5';
3+
const String kPntaSdkVersion = '1.0.0';

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: pnta_flutter
22
description: "Official PNTA Flutter plugin to make push notifications suck less."
3-
version: 1.0.0-dev.5
3+
version: 1.0.0
44
homepage: https://pnta.io
55
repository: https://github.com/pnta-io/pnta-flutter-plugin
66
documentation: https://docs.pnta.io

0 commit comments

Comments
 (0)