xpush_flutter 1.0.1
xpush_flutter: ^1.0.1 copied to clipboard
Flutter plugin for xNotify/XPush push notifications, in-app notifications, unread counts, Android notification tap handling, and deep linking.
XPush Flutter SDK #
Flutter plugin for integrating xNotify/XPush push notifications, in-app notifications, notification center, unread notification count, Android notification tap handling, app lifecycle handling, user session handling, and deep linking.
⚠️ Android 13+ requires runtime notification permission.
The XPush Flutter SDK does not request notification permission automatically. The host application is responsible for requesting and managing notification permission before initializing the SDK.
Features #
- Android push notification support
- In-app notification popup support
- Notification center support
- Unread notification count support
- Foreground, background, and terminated state handling
- Notification tap handling
- Deep linking support
- User login/contact update support
- Logout/session clearing support
- Android 13+ notification permission compatible, host app managed
Installation #
Add the package in the host Flutter app pubspec.yaml:
dependencies:
xpush_flutter: ^1.0.0
Then run:
flutter pub get
Android Setup #
1. Add JitPack Repository #
Open:
android/settings.gradle
Add JitPack inside both repository blocks:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
For older Android projects, add JitPack in:
android/build.gradle
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
2. Kotlin SDK Dependency #
The native Android SDK dependency used by this Flutter plugin is:
implementation("com.github.dilawarabbas1:kotlin-sdk-v2:stg-4.2.0")
This dependency should be added inside the plugin Android module:
android/build.gradle
Example:
dependencies {
implementation("com.github.dilawarabbas1:kotlin-sdk-v2:stg-4.2.0")
}
3. Android Permissions #
Open:
android/app/src/main/AndroidManifest.xml
Add these permissions above the <application> tag:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
POST_NOTIFICATIONS is required for Android 13 and above.
4. Android 13+ Notification Permission #
Android 13, API level 33, requires runtime notification permission before push notifications can be displayed.
The XPush Flutter SDK does not request notification permission automatically.
The host application is responsible for:
- Requesting notification permission
- Handling permission denial
- Redirecting users to settings if permission is permanently denied
Example using permission_handler
Add this dependency in the host app:
dependencies:
permission_handler: ^11.3.1
Import:
import 'package:permission_handler/permission_handler.dart';
Create this method:
Future<void> requestNotificationPermission() async {
final status = await Permission.notification.request();
if (status.isGranted) {
debugPrint("Notification permission granted");
} else {
debugPrint("Notification permission denied");
}
}
Call it before SDK initialization:
Future<void> initializeApp() async {
await requestNotificationPermission();
await XpushSdkController.initializeSdk();
await XpushSdkController.onForeground();
}
Important: If notification permission is not granted on Android 13+, push notifications may not be displayed even though the SDK is initialized correctly.
5. MainApplication Setup #
Create or update:
android/app/src/main/kotlin/YOUR_PACKAGE_NAME/MainApplication.kt
Example:
package YOUR_PACKAGE_NAME
import android.app.Application
import com.tencent.mmkv.MMKV
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
MMKV.initialize(this)
}
}
Register it in:
android/app/src/main/AndroidManifest.xml
Inside the <application> tag:
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher">
</application>
6. Firebase Messaging Service #
Inside the <application> tag in:
android/app/src/main/AndroidManifest.xml
Add:
<service
android:name="com.xpush.sdk.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
This service is required for receiving push notifications from Firebase Cloud Messaging.
7. MainActivity Setup #
In:
android/app/src/main/AndroidManifest.xml
Make sure MainActivity uses:
android:launchMode="singleTop"
Example:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
singleTop is required so notification tap and deep link intents can be handled correctly through the existing activity.
Flutter Usage #
Import the SDK controller.
For the package/example project:
import 'package:xpush_flutter_example/sdk/xpush_sdk_controller.dart';
For pub.dev package usage, the recommended import should be:
import 'package:xpush_flutter/sdk/xpush_sdk_controller.dart';
Note: Before publishing, make sure
xpush_sdk_controller.dartis available inside the packagelib/folder and not only inside theexample/app.
SDK Initialization #
Initialize the SDK after app startup or after splash:
await XpushSdkController.initializeSdk();
Example:
Future<void> initSdk() async {
try {
await XpushSdkController.initializeSdk();
await XpushSdkController.onForeground();
} catch (e) {
debugPrint("XPush SDK init failed: $e");
}
}
Recommended flow:
await requestNotificationPermission();
await XpushSdkController.initializeSdk();
await XpushSdkController.onForeground();
Contact Update After Login #
After user login, call contactUpdate with the user's phone number:
await XpushSdkController.contactUpdate(phoneNumber);
Example:
if (isLoggedIn && phoneNumber != null) {
await XpushSdkController.contactUpdate(phoneNumber);
}
This links the current device/subscriber with the logged-in user.
Open Notification Center #
To open the SDK notification center:
await XpushSdkController.openNotifications();
Example:
IconButton(
icon: const Icon(Icons.notifications),
onPressed: () async {
await XpushSdkController.openNotifications();
},
)
Get Unread Notification Count #
To get unread notification count:
final unreadCount = await XpushSdkController.getUnreadCount();
Example:
int unreadCount = 0;
Future<void> refreshUnreadCount() async {
final count = await XpushSdkController.getUnreadCount();
setState(() {
unreadCount = count;
});
}
Example badge UI:
Stack(
children: [
IconButton(
icon: const Icon(Icons.notifications),
onPressed: () async {
await XpushSdkController.openNotifications();
await refreshUnreadCount();
},
),
if (unreadCount > 0)
Positioned(
right: 6,
top: 6,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text(
unreadCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
),
),
],
)
App Lifecycle Handling #
The host Flutter app should notify the SDK when the app enters foreground or background.
Example:
class XBankApp extends StatefulWidget {
const XBankApp({super.key});
@override
State<XBankApp> createState() => _XBankAppState();
}
class _XBankAppState extends State<XBankApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
Future.microtask(() async {
await requestNotificationPermission();
await XpushSdkController.initializeSdk();
await XpushSdkController.onForeground();
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
XpushSdkController.onForeground();
} else if (state == AppLifecycleState.paused ||
state == AppLifecycleState.inactive ||
state == AppLifecycleState.detached) {
XpushSdkController.onBackground();
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
Deep Linking Setup #
The SDK sends deep link events from native Android/iOS to Flutter through this MethodChannel:
MethodChannel('xpush_flutter_deeplink')
Native channel name:
let deepLinkChannel = FlutterMethodChannel(
name: "xpush_flutter_deeplink",
binaryMessenger: registrar.messenger()
)
Add the MethodChannel handler inside your main Flutter app widget.
Example:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:xpush_flutter/sdk/xpush_sdk_controller.dart';
import 'deep_link_router.dart';
import 'screens/splash_screen.dart';
import 'screens/login_screen.dart';
import 'screens/dashboard_screen.dart';
import 'screens/payment_screen.dart';
class XBankApp extends StatefulWidget {
const XBankApp({super.key});
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
@override
State<XBankApp> createState() => _XBankAppState();
}
class _XBankAppState extends State<XBankApp> with WidgetsBindingObserver {
static const MethodChannel _deepLinkChannel =
MethodChannel('xpush_flutter_deeplink');
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_deepLinkChannel.setMethodCallHandler((call) async {
if (call.method == 'routeDeepLink') {
final args = Map<String, dynamic>.from(call.arguments ?? {});
final deepLink = args['deepLink']?.toString();
final messageId = args['messageId']?.toString();
if (deepLink != null && deepLink.isNotEmpty) {
DeepLinkRouter.route(
navigatorKey: XBankApp.navigatorKey,
deepLink: deepLink,
messageId: messageId,
);
}
}
});
Future.microtask(() async {
try {
await requestNotificationPermission();
await XpushSdkController.initializeSdk();
await XpushSdkController.onForeground();
} catch (e) {
debugPrint("XPush SDK init failed: $e");
}
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
XpushSdkController.onForeground();
} else if (state == AppLifecycleState.paused ||
state == AppLifecycleState.inactive ||
state == AppLifecycleState.detached) {
XpushSdkController.onBackground();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: XBankApp.navigatorKey,
debugShowCheckedModeBanner: false,
home: const SplashScreen(),
routes: {
'/login': (_) => const LoginScreen(),
'/dashboard': (_) => const DashboardScreen(),
'/payment': (_) => const PaymentScreen(),
},
);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
DeepLinkRouter Example #
Create:
lib/deep_link_router.dart
Add:
import 'package:flutter/material.dart';
class DeepLinkRouter {
static void route({
required GlobalKey<NavigatorState> navigatorKey,
required String deepLink,
String? messageId,
}) {
final uri = Uri.tryParse(deepLink);
if (uri == null) return;
if (uri.scheme == 'xpush' && uri.host == 'pay') {
final paymentId = uri.queryParameters['id'];
navigatorKey.currentState?.pushNamed(
'/payment',
arguments: {
'paymentId': paymentId,
'messageId': messageId,
},
);
return;
}
if (uri.scheme == 'xpush' && uri.host == 'notifications') {
navigatorKey.currentState?.pushNamed('/dashboard');
return;
}
}
}
Example deep link:
xpush://pay?id=12
This navigates to:
/payment
with arguments:
{
"paymentId": "12",
"messageId": "MESSAGE_ID"
}
Logout #
On logout, call:
await XpushSdkController.logout();
Example:
Future<void> logout() async {
await XpushSdkController.logout();
Navigator.pushReplacementNamed(context, '/login');
}
This clears the current user session and prevents notifications from being shown for the logged-out user.
Notification Tap Handling #
Android notification tap handling is managed by the SDK.
The SDK handles:
- App foreground state
- App background state
- App terminated state
- Notification body tap
- Notification action button tap
- Deep link payload
- Dialog fallback when no valid deep link exists
If a valid deep link is received, it is sent to Flutter through:
MethodChannel('xpush_flutter_deeplink')
If no valid deep link exists, the SDK opens the notification dialog.
Required SDK Methods #
| Method | Description |
|---|---|
initializeSdk() |
Initializes XPush SDK |
contactUpdate(phoneNumber) |
Updates logged-in user/contact |
openNotifications() |
Opens notification center |
getUnreadCount() |
Returns unread notification count |
onForeground() |
Sets app state as foreground |
onBackground() |
Sets app state as background |
logout() |
Clears user/session data |
restartSocket() |
Restarts socket if required |
handlePushTap() |
Handles Android notification tap |
Recommended Integration Flow #
App Start #
await requestNotificationPermission();
await XpushSdkController.initializeSdk();
await XpushSdkController.onForeground();
User Login #
await XpushSdkController.contactUpdate(phoneNumber);
Open Notification Center #
await XpushSdkController.openNotifications();
Refresh Unread Count #
final count = await XpushSdkController.getUnreadCount();
App Resume #
await XpushSdkController.onForeground();
App Background #
await XpushSdkController.onBackground();
Logout #
await XpushSdkController.logout();
Troubleshooting #
Push notification is not received on Android 13+ #
Check that this permission exists:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Also make sure runtime notification permission is requested and granted.
await Permission.notification.request();
Notification tap opens app but does not route #
Check that MainActivity has:
android:launchMode="singleTop"
Also check that the Flutter app has registered:
MethodChannel('xpush_flutter_deeplink')
Deep link is received but screen does not open #
Make sure the app has a valid navigatorKey:
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
And pass it to MaterialApp:
MaterialApp(
navigatorKey: XBankApp.navigatorKey,
)
Unread count is always zero #
Make sure contactUpdate(phoneNumber) is called after login:
await XpushSdkController.contactUpdate(phoneNumber);
Also refresh unread count when app resumes:
await XpushSdkController.getUnreadCount();
In-app notification appears on login screen after logout #
Make sure logout is called:
await XpushSdkController.logout();
Also clear the local login session from the host app.
Package Publishing Checklist #
Before publishing to pub.dev, make sure the package root contains:
README.md
CHANGELOG.md
LICENSE
pubspec.yaml
lib/
android/
ios/
example/
Run:
flutter pub get
dart pub publish --dry-run
If dry run succeeds:
dart pub publish
License #
This package is distributed under the license mentioned in the LICENSE file.