Show foreground push notifications on iOS

iOS suppresses an incoming push banner while the app is foregrounded
unless the app opts in. _ensureForegroundDisplay now calls
setForegroundNotificationPresentationOptions on iOS so the OS presents
the push directly, avoiding a double notification / delegate clash with
flutter_local_notifications. Android keeps the onMessage local-notification
path, which it still requires.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-06-09 14:44:44 -04:00
co-authored by Claude Opus 4.8
parent 7241a964ef
commit 8c7e98ae5d
2 changed files with 21 additions and 5 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
- [x] Test in-house for 1 week - [x] Test in-house for 1 week
- [x] Implement push notifications - [x] Implement push notifications
- [x] Release 0.2.0 - [x] Release 0.2.0
- [ ] Install in test server and test app on iPhone - [x] Install in test server and test app on iPhone
- [ ] Test in-house for 3 days - [ ] Test in-house for 3 days
- [ ] Release 0.2.1 (or as many versions as needed) - [ ] Release 0.2.1 (or as many versions as needed)
- [ ] Publish website - [ ] Publish website
+20 -4
View File
@@ -99,17 +99,34 @@ final FlutterLocalNotificationsPlugin _localNotifications =
// request comes from startup (push already enabled) or from enable() on toggle. // request comes from startup (push already enabled) or from enable() on toggle.
bool _foregroundDisplayWired = false; bool _foregroundDisplayWired = false;
// Renders push notifications that arrive while the app is in the foreground (the // Ensures push notifications are shown while the app is in the foreground (the OS
// OS displays backgrounded/terminated ones itself); taps just open the app, so no // displays backgrounded/terminated ones itself); taps just open the app, so no
// tap handler is wired. Idempotent; a no-op on platforms without push. // tap handler is wired. Idempotent; a no-op on platforms without push.
//
// The two platforms diverge in the foreground: iOS suppresses the incoming push
// banner unless we opt in via setForegroundNotificationPresentationOptions, after
// which the OS presents the original push itself (no re-show needed, which also
// avoids a double notification / UNUserNotificationCenter delegate clash with
// flutter_local_notifications). Android never displays foreground `notification`
// messages itself, so there we render them through a local-notification channel
// from onMessage.
Future<void> _ensureForegroundDisplay() async { Future<void> _ensureForegroundDisplay() async {
if (!_pushSupported || _foregroundDisplayWired) return; if (!_pushSupported || _foregroundDisplayWired) return;
_foregroundDisplayWired = true; _foregroundDisplayWired = true;
if (_isIOS) {
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
return;
}
await _localNotifications.initialize( await _localNotifications.initialize(
const InitializationSettings( const InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'), android: AndroidInitializationSettings('@mipmap/ic_launcher'),
iOS: DarwinInitializationSettings(),
), ),
); );
await _localNotifications await _localNotifications
@@ -133,7 +150,6 @@ Future<void> _ensureForegroundDisplay() async {
importance: Importance.high, importance: Importance.high,
priority: Priority.high, priority: Priority.high,
), ),
iOS: const DarwinNotificationDetails(),
), ),
); );
}); });