Route reminder notification taps into app UI

This commit is contained in:
Rijad Zuzo
2026-02-15 23:58:12 +01:00
parent c20c29550a
commit 85ca9fbeaf
7 changed files with 146 additions and 6 deletions
@@ -0,0 +1,18 @@
import 'dart:async';
/// Broadcasts reminder notification tap intents to the UI layer.
class ReminderNotificationIntentBus {
final StreamController<String> _controller =
StreamController<String>.broadcast();
Stream<String> get intents => _controller.stream;
void emitTap(String reminderId) {
if (reminderId.trim().isEmpty) {
return;
}
_controller.add(reminderId.trim());
}
Future<void> close() => _controller.close();
}
@@ -0,0 +1,67 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:relationship_saver/features/reminders/reminders_view.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler_provider.dart';
/// Listens for reminder notification taps and opens reminder management UI.
class ReminderNotificationListener extends ConsumerStatefulWidget {
const ReminderNotificationListener({required this.child, super.key});
final Widget child;
@override
ConsumerState<ReminderNotificationListener> createState() =>
_ReminderNotificationListenerState();
}
class _ReminderNotificationListenerState
extends ConsumerState<ReminderNotificationListener> {
StreamSubscription<String>? _subscription;
@override
void initState() {
super.initState();
_subscription = ref
.read(reminderNotificationIntentBusProvider)
.intents
.listen(_handleIntent);
}
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) => widget.child;
Future<void> _handleIntent(String reminderId) async {
if (!mounted) {
return;
}
await Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (BuildContext context) =>
_ReminderNotificationScreen(reminderId: reminderId),
),
);
}
}
class _ReminderNotificationScreen extends StatelessWidget {
const _ReminderNotificationScreen({required this.reminderId});
final String reminderId;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Reminder $reminderId')),
body: const RemindersView(),
);
}
}
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:relationship_saver/features/local/local_models.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_notification_intent_bus.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler.dart';
import 'package:timezone/timezone.dart' as tz;
@@ -14,7 +15,10 @@ class LocalNotificationsReminderScheduler implements ReminderScheduler {
LocalNotificationsReminderScheduler({
ReminderNotificationsDriver? driver,
DateTime Function()? now,
}) : _driver = driver ?? FlutterReminderNotificationsDriver(),
ReminderNotificationIntentBus? intentBus,
}) : _driver =
driver ??
FlutterReminderNotificationsDriver(onTapPayload: intentBus?.emitTap),
_now = now ?? DateTime.now;
final ReminderNotificationsDriver _driver;
@@ -122,10 +126,14 @@ abstract class ReminderNotificationsDriver {
class FlutterReminderNotificationsDriver
implements ReminderNotificationsDriver {
FlutterReminderNotificationsDriver({FlutterLocalNotificationsPlugin? plugin})
: _plugin = plugin ?? FlutterLocalNotificationsPlugin();
FlutterReminderNotificationsDriver({
FlutterLocalNotificationsPlugin? plugin,
void Function(String payload)? onTapPayload,
}) : _plugin = plugin ?? FlutterLocalNotificationsPlugin(),
_onTapPayload = onTapPayload;
final FlutterLocalNotificationsPlugin _plugin;
final void Function(String payload)? _onTapPayload;
Future<void>? _initializeFuture;
@override
@@ -204,6 +212,13 @@ class FlutterReminderNotificationsDriver
guid: 'ee23c0af-5898-4d96-b599-cf2d7d0d37d7',
),
),
onDidReceiveNotificationResponse: (NotificationResponse response) {
final String? payload = response.payload;
if (payload == null || payload.isEmpty) {
return;
}
_onTapPayload?.call(payload);
},
);
}
@@ -1,13 +1,30 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:relationship_saver/core/config/app_config.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_notification_intent_bus.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler_local_notifications.dart';
/// Broadcasts reminder notification tap intents to interested UI listeners.
final Provider<ReminderNotificationIntentBus>
reminderNotificationIntentBusProvider = Provider<ReminderNotificationIntentBus>(
(Ref ref) {
final ReminderNotificationIntentBus bus = ReminderNotificationIntentBus();
ref.onDispose(() {
unawaited(bus.close());
});
return bus;
},
);
/// Provides reminder delivery scheduler implementation.
final Provider<ReminderScheduler> reminderSchedulerProvider =
Provider<ReminderScheduler>((Ref ref) {
if (AppConfig.enableLocalNotifications) {
return LocalNotificationsReminderScheduler();
return LocalNotificationsReminderScheduler(
intentBus: ref.watch(reminderNotificationIntentBusProvider),
);
}
return const NoopReminderScheduler();
});