From 2d0e46293fbda14888a1d90936056caaa73d38c6 Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:08:03 +0100 Subject: [PATCH 01/10] feat: add global logging configuration - Set root logger level to ALL - Add log record listener to print log messages with level, time, logger name, and message --- lib/bootstrap.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 6836de66..a45a1308 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -39,6 +39,10 @@ Future bootstrap( app_config.AppConfig appConfig, app_config.AppEnvironment environment, ) async { + Logger.root.level = Level.ALL; + Logger.root.onRecord.listen((record) { + print(''''${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}''''); + }); WidgetsFlutterBinding.ensureInitialized(); Bloc.observer = const AppBlocObserver(); final logger = Logger('bootstrap'); From d213969e4851f300603e8804227b4d54ab487d61 Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:09:35 +0100 Subject: [PATCH 02/10] refactor(router): replace prints with logger in router configuration --- lib/router/router.dart | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/router/router.dart b/lib/router/router.dart index 5444b791..dd949487 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -48,6 +48,7 @@ import 'package:flutter_news_app_mobile_client_full_source_code/settings/view/se import 'package:flutter_news_app_mobile_client_full_source_code/settings/view/theme_settings_page.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/shared/services/feed_decorator_service.dart'; import 'package:go_router/go_router.dart'; +import 'package:logging/logging.dart'; /// Creates and configures the GoRouter instance for the application. /// @@ -76,6 +77,7 @@ GoRouter createRouter({ required AdService adService, required GlobalKey navigatorKey, required InlineAdCacheService inlineAdCacheService, + required Logger logger, }) { // Instantiate FeedDecoratorService once to be shared final feedDecoratorService = FeedDecoratorService( @@ -101,7 +103,7 @@ GoRouter createRouter({ final appStatus = context.read().state.status; final currentLocation = state.matchedLocation; - print( + logger.info( 'GoRouter Redirect Check:\n' ' Current Location (Matched): $currentLocation\n' ' AppStatus: $appStatus', @@ -118,24 +120,24 @@ GoRouter createRouter({ // If the user is unauthenticated, they must be on an auth path. // If they try to go anywhere else, they are redirected to the sign-in page. if (appStatus == AppLifeCycleStatus.unauthenticated) { - print(' Redirect: User is unauthenticated.'); + logger.info(' Redirect: User is unauthenticated.'); return isGoingToAuth ? null : authenticationPath; } // --- Case 2: Anonymous User --- // An anonymous user is partially authenticated. They can browse the app. if (appStatus == AppLifeCycleStatus.anonymous) { - print(' Redirect: User is anonymous.'); + logger.info(' Redirect: User is anonymous.'); // Block anonymous users from the main sign-in page. if (isGoingToAuth) { - print( + logger.info( ' Action: Anonymous user on auth path. Redirecting to feed.', ); return feedPath; } // If at the root, send them to the feed. if (currentLocation == rootPath) { - print(' Action: User at root. Redirecting to feed.'); + logger.info(' Action: User at root. Redirecting to feed.'); return feedPath; } // Allow navigation to other pages, including the new linking page. @@ -145,23 +147,23 @@ GoRouter createRouter({ // --- Case 3: Authenticated User --- // A fully authenticated user should be blocked from all auth/linking pages. if (appStatus == AppLifeCycleStatus.authenticated) { - print(' Redirect: User is authenticated.'); + logger.info(' Redirect: User is authenticated.'); if (isGoingToAuth || isGoingToLinking) { - print( + logger.info( ' Action: Authenticated user on auth/linking path. Redirecting to feed.', ); return feedPath; } // If at the root, send them to the feed. if (currentLocation == rootPath) { - print(' Action: User at root. Redirecting to feed.'); + logger.info(' Action: User at root. Redirecting to feed.'); return feedPath; } } // --- Fallback --- // For any other case (or if no conditions are met), allow navigation. - print(' Redirect: No condition met. Allowing navigation.'); + logger.info(' Redirect: No condition met. Allowing navigation.'); return null; }, // --- Authentication Routes --- @@ -551,8 +553,8 @@ GoRouter createRouter({ ); } else { // Handle case where user is unexpectedly null. - print( - 'ShellRoute/SettingsBloc: User ID is null when creating SettingsBloc. Settings will not be loaded.', + logger.warning( + 'User ID is null when creating SettingsBloc. Settings will not be loaded.', ); } return settingsBloc; From 7841e40e8ff658134986b777a26df1ee2c834f26 Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:09:48 +0100 Subject: [PATCH 03/10] feat(app): instantiate and provide logger to router --- lib/app/view/app.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 452b1907..d21eb6ca 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -18,6 +18,7 @@ import 'package:flutter_news_app_mobile_client_full_source_code/router/router.da import 'package:flutter_news_app_mobile_client_full_source_code/status/view/view.dart'; import 'package:go_router/go_router.dart'; import 'package:kv_storage_service/kv_storage_service.dart'; +import 'package:logging/logging.dart'; import 'package:ui_kit/ui_kit.dart'; /// {@template app_widget} @@ -223,6 +224,7 @@ class _AppViewState extends State<_AppView> { late final GoRouter _router; late final ValueNotifier _statusNotifier; AppStatusService? _appStatusService; + final _routerLogger = Logger('GoRouter'); @override void initState() { @@ -257,6 +259,7 @@ class _AppViewState extends State<_AppView> { adService: widget.adService, navigatorKey: widget.navigatorKey, inlineAdCacheService: widget.inlineAdCacheService, + logger: _routerLogger, ); } From e1103f20ea8fd47bb424017df24a0332ccef1858 Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:10:05 +0100 Subject: [PATCH 04/10] chore(router): improve log messages in go_router_observer --- lib/router/go_router_observer.dart | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lib/router/go_router_observer.dart diff --git a/lib/router/go_router_observer.dart b/lib/router/go_router_observer.dart new file mode 100644 index 00000000..bf11f0da --- /dev/null +++ b/lib/router/go_router_observer.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; + +class GoRouterObserver extends NavigatorObserver { + GoRouterObserver({required this.logger}); + + final Logger logger; + + @override + void didPush(Route route, Route? previousRoute) { + logger.info( + 'Pushed: ${route.settings.name} | from: ${previousRoute?.settings.name}', + ); + } + + @override + void didPop(Route route, Route? previousRoute) { + logger.info( + 'Popped: ${route.settings.name} | to: ${previousRoute?.settings.name}', + ); + } + + @override + void didRemove(Route route, Route? previousRoute) { + logger.info( + 'Removed: ${route.settings.name} | previous: ${previousRoute?.settings.name}', + ); + } + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + logger.info( + 'Replaced: ${oldRoute?.settings.name} | with: ${newRoute?.settings.name}', + ); + } +} From 880c64d8814521b8642adc243af7d8cd1f579fea Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:14:32 +0100 Subject: [PATCH 05/10] style(bootstrap): update Logger root listener syntax - Refactor Logger root listener to use arrow function syntax - Improve code readability and maintainability --- lib/bootstrap.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index a45a1308..21fc97ea 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -40,9 +40,11 @@ Future bootstrap( app_config.AppEnvironment environment, ) async { Logger.root.level = Level.ALL; - Logger.root.onRecord.listen((record) { - print(''''${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}''''); - }); + Logger.root.onRecord.listen( + (record) => print( + '${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}', + ), + ); WidgetsFlutterBinding.ensureInitialized(); Bloc.observer = const AppBlocObserver(); final logger = Logger('bootstrap'); From 6bfec181d3d3683ab87d69a098c8600aa25fa083 Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:20:22 +0100 Subject: [PATCH 06/10] fix(router): integrate go_router_observer into router configuration --- lib/router/router.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/router/router.dart b/lib/router/router.dart index dd949487..3efbf537 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -2,6 +2,7 @@ import 'package:auth_repository/auth_repository.dart'; import 'package:core/core.dart' hide AppStatus; import 'package:data_repository/data_repository.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_news_app_mobile_client_full_source_code/router/go_router_observer.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/account/view/account_page.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/account/view/manage_followed_items/countries/add_country_to_follow_page.dart'; @@ -93,9 +94,7 @@ GoRouter createRouter({ initialLocation: '/', debugLogDiagnostics: true, navigatorKey: navigatorKey, - observers: [ - // Add any other necessary observers here. If none, this can be an empty list. - ], + observers: [GoRouterObserver(logger: logger)], // --- Redirect Logic --- // This function is the single source of truth for route protection. // It's driven by the AppBloc's AppLifeCycleStatus. From 06ffd3b87dc686304fe1e9c970f576a5eeaccfeb Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:25:23 +0100 Subject: [PATCH 07/10] feat(bootstrap): add robust logging to bootstrap process --- lib/bootstrap.dart | 72 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 21fc97ea..cbeda251 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -39,20 +39,34 @@ Future bootstrap( app_config.AppConfig appConfig, app_config.AppEnvironment environment, ) async { - Logger.root.level = Level.ALL; - Logger.root.onRecord.listen( - (record) => print( + // Setup logging + Logger.root.level = environment == app_config.AppEnvironment.production + ? Level.INFO + : Level.ALL; + Logger.root.onRecord.listen((record) { + print( '${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}', - ), - ); + ); + if (record.error != null) { + print('Error: ${record.error}'); + } + if (record.stackTrace != null) { + print('Stack Trace: ${record.stackTrace}'); + } + }); + final logger = Logger('bootstrap'); + logger.config('--- Starting Bootstrap Process ---'); + logger.config('App Environment: $environment'); + WidgetsFlutterBinding.ensureInitialized(); Bloc.observer = const AppBlocObserver(); - final logger = Logger('bootstrap'); timeago.setLocaleMessages('en', EnTimeagoMessages()); timeago.setLocaleMessages('ar', ArTimeagoMessages()); + logger.info('1. Initializing KV Storage Service...'); // 1. Initialize KV Storage Service first, as it's a foundational dependency. final kvStorage = await KVStorageSharedPreferences.getInstance(); + logger.fine('KV Storage Service initialized (SharedPreferences).'); // Initialize InlineAdCacheService early as it's a singleton and needs AdService. // It will be fully configured once AdService is available. @@ -60,6 +74,7 @@ Future bootstrap( // 2. Initialize HttpClient. Its tokenProvider now directly reads from // kvStorage, breaking the circular dependency with AuthRepository. + logger.info('2. Initializing HttpClient...'); // This HttpClient instance is used for all subsequent API calls, including // the initial unauthenticated fetch of RemoteConfig. final httpClient = HttpClient( @@ -68,11 +83,14 @@ Future bootstrap( kvStorage.readString(key: StorageKey.authToken.stringValue), logger: logger, ); + logger.fine('HttpClient initialized for base URL: ${appConfig.baseUrl}'); // 3. Initialize RemoteConfigClient and Repository, and fetch RemoteConfig. + logger.info('3. Initializing RemoteConfig client and repository...'); // This is done early because RemoteConfig is now publicly accessible (unauthenticated). late DataClient remoteConfigClient; if (appConfig.environment == app_config.AppEnvironment.demo) { + logger.fine('Using in-memory client for RemoteConfig.'); remoteConfigClient = DataInMemory( toJson: (i) => i.toJson(), getId: (i) => i.id, @@ -80,6 +98,7 @@ Future bootstrap( logger: logger, ); } else { + logger.fine('Using API client for RemoteConfig.'); // For development and production environments, use DataApi. remoteConfigClient = DataApi( httpClient: httpClient, @@ -92,6 +111,7 @@ Future bootstrap( final remoteConfigRepository = DataRepository( dataClient: remoteConfigClient, ); + logger.fine('RemoteConfig repository initialized.'); // Fetch the initial RemoteConfig. This is a critical step to determine // the app's global status (e.g., maintenance mode, update required) @@ -99,31 +119,28 @@ Future bootstrap( RemoteConfig? initialRemoteConfig; HttpException? initialRemoteConfigError; + logger.info('4. Fetching initial RemoteConfig...'); try { initialRemoteConfig = await remoteConfigRepository.read( id: kRemoteConfigId, ); - logger.info('[bootstrap] Initial RemoteConfig fetched successfully.'); + logger.fine('Initial RemoteConfig fetched successfully.'); } on HttpException catch (e) { - logger.severe( - '[bootstrap] Failed to fetch initial RemoteConfig (HttpException): $e', - ); + logger.severe('Failed to fetch initial RemoteConfig (HttpException): $e'); initialRemoteConfigError = e; } catch (e, s) { - logger.severe( - '[bootstrap] Unexpected error fetching initial RemoteConfig.', - e, - s, - ); + logger.severe('Unexpected error fetching initial RemoteConfig.', e, s); initialRemoteConfigError = UnknownException(e.toString()); } // 4. Conditionally initialize Auth services based on environment. // This is done after RemoteConfig is fetched, as Auth services might depend - // on configurations defined in RemoteConfig (though not directly in this case). + // on configurations defined in RemoteConfig. + logger.info('5. Initializing Authentication services...'); late final AuthClient authClient; late final AuthRepository authenticationRepository; if (appConfig.environment == app_config.AppEnvironment.demo) { + logger.fine('Using in-memory client for Authentication.'); // In-memory authentication for demo environment. authClient = AuthInmemory(); authenticationRepository = AuthRepository( @@ -131,6 +148,7 @@ Future bootstrap( storageService: kvStorage, ); } else { + logger.fine('Using API client for Authentication.'); // Now that httpClient is available, initialize AuthApi and AuthRepository. authClient = AuthApi(httpClient: httpClient); authenticationRepository = AuthRepository( @@ -138,14 +156,17 @@ Future bootstrap( storageService: kvStorage, ); } + logger.fine('Authentication repository initialized.'); // 5. Initialize AdProvider and AdService. + logger.info('6. Initializing Ad providers and AdService...'); late final Map adProviders; // Conditionally instantiate ad providers based on the application environment. // This ensures that only the relevant ad providers are available for the // current environment, preventing unintended usage. if (appConfig.environment == app_config.AppEnvironment.demo || kIsWeb) { + logger.fine('Using DemoAdProvider for all ad platforms.'); final demoAdProvider = DemoAdProvider(logger: logger); adProviders = { // In the demo environment or on the web, all ad platform types map to @@ -157,6 +178,7 @@ Future bootstrap( AdPlatformType.demo: demoAdProvider, }; } else { + logger.fine('Using AdMobAdProvider and LocalAdProvider.'); // For development and production environments (non-web), use real ad providers. adProviders = { // AdMob provider for Google Mobile Ads. @@ -186,13 +208,17 @@ Future bootstrap( logger: logger, ); await adService.initialize(); + logger.fine('AdService initialized.'); // Initialize InlineAdCacheService with the created AdService. inlineAdCacheService = InlineAdCacheService(adService: adService); + logger.fine('InlineAdCacheService initialized.'); // Fetch the initial user from the authentication repository. // This ensures the AppBloc starts with an accurate authentication status. + logger.info('7. Fetching initial user...'); final initialUser = await authenticationRepository.getCurrentUser(); + logger.fine('Initial user fetched: ${initialUser?.id ?? 'none'}.'); // Create a GlobalKey for the NavigatorState to be used by AppBloc // and InterstitialAdManager for BuildContext access. @@ -200,8 +226,10 @@ Future bootstrap( // Initialize PackageInfoService final packageInfoService = PackageInfoServiceImpl(logger: logger); + logger.fine('PackageInfoService initialized.'); // 6. Initialize all other DataClients and Repositories. + logger.info('8. Initializing Data clients and repositories...'); // These now also have a guaranteed valid httpClient. late final DataClient headlinesClient; late final DataClient topicsClient; @@ -212,6 +240,7 @@ Future bootstrap( late final DataClient userClient; late final DataClient localAdClient; if (appConfig.environment == app_config.AppEnvironment.demo) { + logger.fine('Using in-memory clients for all data repositories.'); headlinesClient = DataInMemory( toJson: (i) => i.toJson(), getId: (i) => i.id, @@ -286,6 +315,7 @@ Future bootstrap( logger: logger, ); } else if (appConfig.environment == app_config.AppEnvironment.development) { + logger.fine('Using API clients for all data repositories (Development).'); headlinesClient = DataApi( httpClient: httpClient, modelName: 'headline', @@ -343,6 +373,7 @@ Future bootstrap( logger: logger, ); } else { + logger.fine('Using API clients for all data repositories (Production).'); // Default to API clients for production headlinesClient = DataApi( httpClient: httpClient, @@ -401,6 +432,7 @@ Future bootstrap( logger: logger, ); } + logger.fine('All data clients instantiated.'); final headlinesRepository = DataRepository( dataClient: headlinesClient, @@ -419,6 +451,7 @@ Future bootstrap( dataClient: userAppSettingsClient, ); final userRepository = DataRepository(dataClient: userClient); + logger.fine('All data repositories initialized.'); // Conditionally instantiate DemoDataMigrationService final demoDataMigrationService = @@ -428,6 +461,9 @@ Future bootstrap( userContentPreferencesRepository: userContentPreferencesRepository, ) : null; + logger.fine( + 'DemoDataMigrationService initialized: ${demoDataMigrationService != null}', + ); // Conditionally instantiate DemoDataInitializerService // This service is responsible for ensuring that essential user-specific data @@ -441,7 +477,11 @@ Future bootstrap( userContentPreferencesRepository: userContentPreferencesRepository, ) : null; + logger.fine( + 'DemoDataInitializerService initialized: ${demoDataInitializerService != null}', + ); + logger.info('--- Bootstrap Process Complete. Returning App widget. ---'); return App( authenticationRepository: authenticationRepository, headlinesRepository: headlinesRepository, From dd2ef00003b79399d875a1d867ca07377d1b92cb Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:28:36 +0100 Subject: [PATCH 08/10] refactor(logger): improve log message formatting and structure - Consolidate log message construction using a StringBuffer - Format error and stack trace information consistently - Enhance readability of log output by structuring the message --- lib/bootstrap.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index cbeda251..d4c8d704 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -43,17 +43,20 @@ Future bootstrap( Logger.root.level = environment == app_config.AppEnvironment.production ? Level.INFO : Level.ALL; + Logger.root.onRecord.listen((record) { - print( + final message = StringBuffer( '${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}', ); if (record.error != null) { - print('Error: ${record.error}'); + message.write('\nError: ${record.error}'); } if (record.stackTrace != null) { - print('Stack Trace: ${record.stackTrace}'); + message.write('\nStack Trace: ${record.stackTrace}'); } + print(message.toString()); }); + final logger = Logger('bootstrap'); logger.config('--- Starting Bootstrap Process ---'); logger.config('App Environment: $environment'); From 48b1da6b516a7a2961b783e72868e885c152bdf4 Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:29:37 +0100 Subject: [PATCH 09/10] refactor(bootstrap): improve logger usage and code readability - Chaining logger methods for better readability - Removing unnecessary print statement - Consistent use of string interpolation - Removing extra empty lines --- lib/bootstrap.dart | 54 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index d4c8d704..040f2e7a 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -43,7 +43,7 @@ Future bootstrap( Logger.root.level = environment == app_config.AppEnvironment.production ? Level.INFO : Level.ALL; - + Logger.root.onRecord.listen((record) { final message = StringBuffer( '${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}', @@ -54,12 +54,12 @@ Future bootstrap( if (record.stackTrace != null) { message.write('\nStack Trace: ${record.stackTrace}'); } - print(message.toString()); + print(message); }); - final logger = Logger('bootstrap'); - logger.config('--- Starting Bootstrap Process ---'); - logger.config('App Environment: $environment'); + final logger = Logger('bootstrap') + ..config('--- Starting Bootstrap Process ---') + ..config('App Environment: $environment'); WidgetsFlutterBinding.ensureInitialized(); Bloc.observer = const AppBlocObserver(); @@ -86,10 +86,10 @@ Future bootstrap( kvStorage.readString(key: StorageKey.authToken.stringValue), logger: logger, ); - logger.fine('HttpClient initialized for base URL: ${appConfig.baseUrl}'); - - // 3. Initialize RemoteConfigClient and Repository, and fetch RemoteConfig. - logger.info('3. Initializing RemoteConfig client and repository...'); + logger + ..fine('HttpClient initialized for base URL: ${appConfig.baseUrl}') + // 3. Initialize RemoteConfigClient and Repository, and fetch RemoteConfig. + ..info('3. Initializing RemoteConfig client and repository...'); // This is done early because RemoteConfig is now publicly accessible (unauthenticated). late DataClient remoteConfigClient; if (appConfig.environment == app_config.AppEnvironment.demo) { @@ -159,10 +159,10 @@ Future bootstrap( storageService: kvStorage, ); } - logger.fine('Authentication repository initialized.'); - - // 5. Initialize AdProvider and AdService. - logger.info('6. Initializing Ad providers and AdService...'); + logger + ..fine('Authentication repository initialized.') + // 5. Initialize AdProvider and AdService. + ..info('6. Initializing Ad providers and AdService...'); late final Map adProviders; // Conditionally instantiate ad providers based on the application environment. @@ -215,11 +215,11 @@ Future bootstrap( // Initialize InlineAdCacheService with the created AdService. inlineAdCacheService = InlineAdCacheService(adService: adService); - logger.fine('InlineAdCacheService initialized.'); - - // Fetch the initial user from the authentication repository. - // This ensures the AppBloc starts with an accurate authentication status. - logger.info('7. Fetching initial user...'); + logger + ..fine('InlineAdCacheService initialized.') + // Fetch the initial user from the authentication repository. + // This ensures the AppBloc starts with an accurate authentication status. + ..info('7. Fetching initial user...'); final initialUser = await authenticationRepository.getCurrentUser(); logger.fine('Initial user fetched: ${initialUser?.id ?? 'none'}.'); @@ -229,10 +229,10 @@ Future bootstrap( // Initialize PackageInfoService final packageInfoService = PackageInfoServiceImpl(logger: logger); - logger.fine('PackageInfoService initialized.'); - - // 6. Initialize all other DataClients and Repositories. - logger.info('8. Initializing Data clients and repositories...'); + logger + ..fine('PackageInfoService initialized.') + // 6. Initialize all other DataClients and Repositories. + ..info('8. Initializing Data clients and repositories...'); // These now also have a guaranteed valid httpClient. late final DataClient headlinesClient; late final DataClient topicsClient; @@ -480,11 +480,11 @@ Future bootstrap( userContentPreferencesRepository: userContentPreferencesRepository, ) : null; - logger.fine( - 'DemoDataInitializerService initialized: ${demoDataInitializerService != null}', - ); - - logger.info('--- Bootstrap Process Complete. Returning App widget. ---'); + logger + ..fine( + 'DemoDataInitializerService initialized: ${demoDataInitializerService != null}', + ) + ..info('--- Bootstrap Process Complete. Returning App widget. ---'); return App( authenticationRepository: authenticationRepository, headlinesRepository: headlinesRepository, From 604737765ebf895c3196f6a208c7eb9aafdc2d7d Mon Sep 17 00:00:00 2001 From: fulleni Date: Tue, 7 Oct 2025 07:29:48 +0100 Subject: [PATCH 10/10] fix(router): resolve import path issue and update observer registration - Remove relative import path for go_router_observer.dart - Add absolute import path for go_router_observer.dart - Ensure GoRouterObserver is correctly imported and used in the Router --- lib/router/router.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/router/router.dart b/lib/router/router.dart index 3efbf537..330f7a8b 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -2,7 +2,6 @@ import 'package:auth_repository/auth_repository.dart'; import 'package:core/core.dart' hide AppStatus; import 'package:data_repository/data_repository.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_news_app_mobile_client_full_source_code/router/go_router_observer.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/account/view/account_page.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/account/view/manage_followed_items/countries/add_country_to_follow_page.dart'; @@ -38,6 +37,7 @@ import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/v import 'package:flutter_news_app_mobile_client_full_source_code/headlines-search/bloc/headlines_search_bloc.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/headlines-search/view/headlines_search_page.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart'; +import 'package:flutter_news_app_mobile_client_full_source_code/router/go_router_observer.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/router/routes.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/settings/bloc/settings_bloc.dart'; import 'package:flutter_news_app_mobile_client_full_source_code/settings/view/appearance_settings_page.dart';