Skip to content

Commit 956707b

Browse files
committed
refactor(app): improve push notification handling and code structure
- Add HeadlineTapHandler import and use in push notification listener - Refactor push notification handling for better async gap management - Improve code formatting and readability
1 parent d481734 commit 956707b

File tree

1 file changed

+38
-43
lines changed

1 file changed

+38
-43
lines changed

lib/app/view/app.dart

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:flutter_news_app_mobile_client_full_source_code/app/services/app
1616
import 'package:flutter_news_app_mobile_client_full_source_code/feed_decorators/services/feed_decorator_service.dart';
1717
import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/services/feed_cache_service.dart';
1818
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart';
19+
import 'package:flutter_news_app_mobile_client_full_source_code/shared/widgets/feed_core/headline_tap_handler.dart';
1920
import 'package:flutter_news_app_mobile_client_full_source_code/notifications/services/push_notification_service.dart';
2021
import 'package:flutter_news_app_mobile_client_full_source_code/router/router.dart';
2122
import 'package:flutter_news_app_mobile_client_full_source_code/router/routes.dart';
@@ -50,7 +51,7 @@ class App extends StatelessWidget {
5051
required DataRepository<RemoteConfig> remoteConfigRepository,
5152
required DataRepository<AppSettings> appSettingsRepository,
5253
required DataRepository<UserContentPreferences>
53-
userContentPreferencesRepository,
54+
userContentPreferencesRepository,
5455
required AppEnvironment environment,
5556
required DataRepository<InAppNotification> inAppNotificationRepository,
5657
required InlineAdCacheService inlineAdCacheService,
@@ -60,23 +61,23 @@ class App extends StatelessWidget {
6061
required GlobalKey<NavigatorState> navigatorKey,
6162
required PushNotificationService pushNotificationService,
6263
super.key,
63-
}) : _authenticationRepository = authenticationRepository,
64-
_headlinesRepository = headlinesRepository,
65-
_topicsRepository = topicsRepository,
66-
_countriesRepository = countriesRepository,
67-
_sourcesRepository = sourcesRepository,
68-
_userRepository = userRepository,
69-
_remoteConfigRepository = remoteConfigRepository,
70-
_appSettingsRepository = appSettingsRepository,
71-
_userContentPreferencesRepository = userContentPreferencesRepository,
72-
_pushNotificationService = pushNotificationService,
73-
_inAppNotificationRepository = inAppNotificationRepository,
74-
_environment = environment,
75-
_adService = adService,
76-
_feedDecoratorService = feedDecoratorService,
77-
_feedCacheService = feedCacheService,
78-
_navigatorKey = navigatorKey,
79-
_inlineAdCacheService = inlineAdCacheService;
64+
}) : _authenticationRepository = authenticationRepository,
65+
_headlinesRepository = headlinesRepository,
66+
_topicsRepository = topicsRepository,
67+
_countriesRepository = countriesRepository,
68+
_sourcesRepository = sourcesRepository,
69+
_userRepository = userRepository,
70+
_remoteConfigRepository = remoteConfigRepository,
71+
_appSettingsRepository = appSettingsRepository,
72+
_userContentPreferencesRepository = userContentPreferencesRepository,
73+
_pushNotificationService = pushNotificationService,
74+
_inAppNotificationRepository = inAppNotificationRepository,
75+
_environment = environment,
76+
_adService = adService,
77+
_feedDecoratorService = feedDecoratorService,
78+
_feedCacheService = feedCacheService,
79+
_navigatorKey = navigatorKey,
80+
_inlineAdCacheService = inlineAdCacheService;
8081

8182
/// The initial user, pre-fetched during startup.
8283
final User? user;
@@ -99,7 +100,7 @@ class App extends StatelessWidget {
99100
final DataRepository<RemoteConfig> _remoteConfigRepository;
100101
final DataRepository<AppSettings> _appSettingsRepository;
101102
final DataRepository<UserContentPreferences>
102-
_userContentPreferencesRepository;
103+
_userContentPreferencesRepository;
103104
final AppEnvironment _environment;
104105
final DataRepository<InAppNotification> _inAppNotificationRepository;
105106
final AdService _adService;
@@ -202,8 +203,8 @@ class _AppViewState extends State<_AppView> {
202203
// This stream is the single source of truth for the user's auth state
203204
// and drives the entire app lifecycle by dispatching AppUserChanged events.
204205
_userSubscription = context.read<AuthRepository>().authStateChanges.listen(
205-
(user) => context.read<AppBloc>().add(AppUserChanged(user)),
206-
);
206+
(user) => context.read<AppBloc>().add(AppUserChanged(user)),
207+
);
207208

208209
// Subscribe to foreground push notifications. When a message is received,
209210
// dispatch an event to the AppBloc to update the UI state (e.g., show an
@@ -214,29 +215,23 @@ class _AppViewState extends State<_AppView> {
214215

215216
// Subscribe to notifications that are tapped and open the app.
216217
// This is the core of the deep-linking functionality.
217-
_onMessageOpenedAppSubscription =
218-
pushNotificationService.onMessageOpenedApp.listen((payload) {
219-
_routerLogger.fine(
220-
'Notification opened app with payload: $payload',
221-
);
222-
final contentType = payload.contentType;
223-
final contentId = payload.contentId;
224-
final notificationId = payload.notificationId;
218+
_onMessageOpenedAppSubscription = pushNotificationService.onMessageOpenedApp
219+
.listen((payload) async {
220+
_routerLogger.fine('Notification opened app with payload: $payload');
221+
final contentType = payload.contentType;
222+
final contentId = payload.contentId;
225223

226-
if (contentType == ContentType.headline && contentId.isNotEmpty) {
227-
// Use pushNamed instead of goNamed.
228-
// goNamed replaces the entire navigation stack, which causes issues
229-
// when the app is launched from a terminated state. The new page
230-
// would lack the necessary ancestor widgets (like RepositoryProviders).
231-
// pushNamed correctly pushes the details page on top of the existing
232-
// stack (e.g., the feed), ensuring a valid context.
233-
_router.pushNamed(
234-
Routes.globalArticleDetailsName,
235-
pathParameters: {'id': contentId},
236-
extra: {'notificationId': notificationId},
237-
);
238-
}
239-
});
224+
if (contentType == ContentType.headline && contentId.isNotEmpty) {
225+
// Guard against using BuildContext across async gaps by checking
226+
// if the widget is still mounted before using its context.
227+
if (mounted) {
228+
await HeadlineTapHandler.handleTapFromSystemNotification(
229+
context,
230+
contentId,
231+
);
232+
}
233+
}
234+
});
240235
// Instantiate and initialize the AppStatusService.
241236
// This service monitors the app's lifecycle (e.g., resuming from
242237
// background) and periodically triggers remote configuration fetches,

0 commit comments

Comments
 (0)