instant_request_handler 0.0.1
instant_request_handler: ^0.0.1 copied to clipboard
Reusable Flutter plugin for immediate incoming request notification experiences.
instant_request_handler #
Reusable Flutter plugin for real-time incoming request experiences. It lets an app register notification types, match incoming FCM/data payloads, show a custom widget immediately, fetch details after the UI is visible, and recover pending requests after background or killed-app launches.
The package is generic. It does not include app controllers, repositories, API clients, GetX, or ride-specific business logic.
Installation #
dependencies:
instant_request_handler: ^0.0.1
firebase_core: ^4.2.1
firebase_messaging: ^16.0.4
Run:
flutter pub get
Android Setup #
Add the permissions your app needs to android/app/src/main/AndroidManifest.xml.
The plugin manifest also declares them, but keeping them in the app manifest
makes the requirements explicit.
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
Set minSdk to 24 or higher.
android {
defaultConfig {
minSdk = 24
}
}
Use singleTop or a similar launch mode for your main activity so native
notification intents are delivered to the existing Flutter activity when
possible.
Firebase Setup #
Configure Firebase normally for your app:
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(instantRequestBackgroundHandler);
FirebaseMessaging.onMessage.listen(
InstantRequestHandler.handleForegroundMessage,
);
Send FCM data payloads that include either action or type, plus the request
id key you registered.
Registering Types #
await InstantRequestHandler.initialize(
notificationTypes: [
InstantNotificationType<Map<String, dynamic>>(
action: 'new_ride_request',
type: 'new_ride_request',
idKey: 'ride_request_id',
timeoutSeconds: 30,
presentation: InstantRequestPresentation.fullScreenOrSheet,
onFetchData: (requestId, payload) async {
return fetchDetailsFromBackend(requestId);
},
onAccept: (requestId, data) async {
await acceptRequest(requestId);
},
onReject: (requestId, data, reason) async {
await rejectRequest(requestId, reason);
},
builder: (context, state) {
return MyIncomingRequestWidget(state: state);
},
),
],
);
Attach the package navigator key to your app:
MaterialApp(
navigatorKey: InstantRequestHandler.navigatorKey,
home: const AppHome(),
);
You may pass your own GlobalKey<NavigatorState> to initialize.
Foreground Handling #
FirebaseMessaging.onMessage.listen(
InstantRequestHandler.handleForegroundMessage,
);
Foreground payloads are matched against registered types. The UI is shown with
isLoading == true, the countdown starts immediately, and onFetchData runs
after the widget is visible.
Background Handler #
@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) {
return instantRequestBackgroundHandler(message);
}
Register it before runApp:
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
The background handler asks Android native code to match the data payload, store the pending request, create a high-priority/full-screen notification, briefly wake the device, and open the host activity when Android allows it.
Killed App / Native Behavior #
The Android side includes:
InstantFirebaseMessagingServiceInstantRequestHandlerPluginInstantRequestLauncher- MethodChannel and EventChannel bridge
- Android
SharedPreferencespending request storage - High-importance notification channel
- Full-screen intent notification
- Wake lock
- Intent recovery for app launches and
onNewIntent
When Flutter starts, initialize configures native matchers and recovers pending
requests from both native and Flutter storage.
Custom Widget Builder #
The builder receives InstantRequestState<T>:
builder: (context, state) {
if (state.isLoading) return const CircularProgressIndicator();
return Column(
children: [
Text('${state.secondsRemaining}s'),
FilledButton(
onPressed: state.accept,
child: const Text('Accept'),
),
OutlinedButton(
onPressed: state.reject,
child: const Text('Reject'),
),
],
);
}
state.retry() re-runs onFetchData. state.close() dismisses the UI and calls
onReject with InstantRequestCloseReason.dismissed.
Permission Helpers #
final status = await InstantRequestHandler.checkPermissions();
await InstantRequestHandler.requestNotificationPermission();
await InstantRequestHandler.openOverlaySettings();
await InstantRequestHandler.openBatteryOptimizationSettings();
await InstantRequestHandler.openFullScreenIntentSettings();
The package also includes InstantRequestPermissionOnboarding for a generic
permission setup screen.
Local Simulation #
await InstantRequestHandler.handlePayload({
'action': 'new_ride_request',
'type': 'new_ride_request',
'ride_request_id': '123',
});
Testing With ADB #
adb shell am start \
-n your.package.name/.MainActivity \
--es action new_ride_request \
--es ride_request_id 12345
Use a fresh request id for repeated tests. Completed request ids are remembered for duplicate protection, so reusing the same id can intentionally do nothing.
For FCM tests, send a data-only payload containing the same keys.
Android Limitations #
Android controls background activity launches, full-screen intent eligibility, notification permission, OEM battery rules, and lock-screen behavior. This plugin requests the strongest allowed path for incoming request alerts, but it cannot guarantee that every device will wake, unlock, or show full screen.
Full-screen intent access is user-controllable on newer Android versions. Battery optimization and overlay permissions also require user approval.
For v1, if a request is active and another request arrives, the new request is ignored. Duplicate request ids are ignored while active or after completion.
Example #
See example/ for a Flutter app that registers a new_ride_request type,
simulates payloads locally, shows a custom ride request sheet, and includes
Android manifest setup.