@@ -39,21 +39,45 @@ Future<Widget> bootstrap(
3939 app_config.AppConfig appConfig,
4040 app_config.AppEnvironment environment,
4141) async {
42+ // Setup logging
43+ Logger .root.level = environment == app_config.AppEnvironment .production
44+ ? Level .INFO
45+ : Level .ALL ;
46+
47+ Logger .root.onRecord.listen ((record) {
48+ final message = StringBuffer (
49+ '${record .level .name }: ${record .time }: ${record .loggerName }: ${record .message }' ,
50+ );
51+ if (record.error != null ) {
52+ message.write ('\n Error: ${record .error }' );
53+ }
54+ if (record.stackTrace != null ) {
55+ message.write ('\n Stack Trace: ${record .stackTrace }' );
56+ }
57+ print (message);
58+ });
59+
60+ final logger = Logger ('bootstrap' )
61+ ..config ('--- Starting Bootstrap Process ---' )
62+ ..config ('App Environment: $environment ' );
63+
4264 WidgetsFlutterBinding .ensureInitialized ();
4365 Bloc .observer = const AppBlocObserver ();
44- final logger = Logger ('bootstrap' );
4566 timeago.setLocaleMessages ('en' , EnTimeagoMessages ());
4667 timeago.setLocaleMessages ('ar' , ArTimeagoMessages ());
4768
69+ logger.info ('1. Initializing KV Storage Service...' );
4870 // 1. Initialize KV Storage Service first, as it's a foundational dependency.
4971 final kvStorage = await KVStorageSharedPreferences .getInstance ();
72+ logger.fine ('KV Storage Service initialized (SharedPreferences).' );
5073
5174 // Initialize InlineAdCacheService early as it's a singleton and needs AdService.
5275 // It will be fully configured once AdService is available.
5376 InlineAdCacheService ? inlineAdCacheService;
5477
5578 // 2. Initialize HttpClient. Its tokenProvider now directly reads from
5679 // kvStorage, breaking the circular dependency with AuthRepository.
80+ logger.info ('2. Initializing HttpClient...' );
5781 // This HttpClient instance is used for all subsequent API calls, including
5882 // the initial unauthenticated fetch of RemoteConfig.
5983 final httpClient = HttpClient (
@@ -62,18 +86,22 @@ Future<Widget> bootstrap(
6286 kvStorage.readString (key: StorageKey .authToken.stringValue),
6387 logger: logger,
6488 );
65-
66- // 3. Initialize RemoteConfigClient and Repository, and fetch RemoteConfig.
89+ logger
90+ ..fine ('HttpClient initialized for base URL: ${appConfig .baseUrl }' )
91+ // 3. Initialize RemoteConfigClient and Repository, and fetch RemoteConfig.
92+ ..info ('3. Initializing RemoteConfig client and repository...' );
6793 // This is done early because RemoteConfig is now publicly accessible (unauthenticated).
6894 late DataClient <RemoteConfig > remoteConfigClient;
6995 if (appConfig.environment == app_config.AppEnvironment .demo) {
96+ logger.fine ('Using in-memory client for RemoteConfig.' );
7097 remoteConfigClient = DataInMemory <RemoteConfig >(
7198 toJson: (i) => i.toJson (),
7299 getId: (i) => i.id,
73100 initialData: remoteConfigsFixturesData,
74101 logger: logger,
75102 );
76103 } else {
104+ logger.fine ('Using API client for RemoteConfig.' );
77105 // For development and production environments, use DataApi.
78106 remoteConfigClient = DataApi <RemoteConfig >(
79107 httpClient: httpClient,
@@ -86,60 +114,62 @@ Future<Widget> bootstrap(
86114 final remoteConfigRepository = DataRepository <RemoteConfig >(
87115 dataClient: remoteConfigClient,
88116 );
117+ logger.fine ('RemoteConfig repository initialized.' );
89118
90119 // Fetch the initial RemoteConfig. This is a critical step to determine
91120 // the app's global status (e.g., maintenance mode, update required)
92121 // before proceeding with other initializations.
93122 RemoteConfig ? initialRemoteConfig;
94123 HttpException ? initialRemoteConfigError;
95124
125+ logger.info ('4. Fetching initial RemoteConfig...' );
96126 try {
97127 initialRemoteConfig = await remoteConfigRepository.read (
98128 id: kRemoteConfigId,
99129 );
100- logger.info ( '[bootstrap] Initial RemoteConfig fetched successfully.' );
130+ logger.fine ( ' Initial RemoteConfig fetched successfully.' );
101131 } on HttpException catch (e) {
102- logger.severe (
103- '[bootstrap] Failed to fetch initial RemoteConfig (HttpException): $e ' ,
104- );
132+ logger.severe ('Failed to fetch initial RemoteConfig (HttpException): $e ' );
105133 initialRemoteConfigError = e;
106134 } catch (e, s) {
107- logger.severe (
108- '[bootstrap] Unexpected error fetching initial RemoteConfig.' ,
109- e,
110- s,
111- );
135+ logger.severe ('Unexpected error fetching initial RemoteConfig.' , e, s);
112136 initialRemoteConfigError = UnknownException (e.toString ());
113137 }
114138
115139 // 4. Conditionally initialize Auth services based on environment.
116140 // This is done after RemoteConfig is fetched, as Auth services might depend
117- // on configurations defined in RemoteConfig (though not directly in this case).
141+ // on configurations defined in RemoteConfig.
142+ logger.info ('5. Initializing Authentication services...' );
118143 late final AuthClient authClient;
119144 late final AuthRepository authenticationRepository;
120145 if (appConfig.environment == app_config.AppEnvironment .demo) {
146+ logger.fine ('Using in-memory client for Authentication.' );
121147 // In-memory authentication for demo environment.
122148 authClient = AuthInmemory ();
123149 authenticationRepository = AuthRepository (
124150 authClient: authClient,
125151 storageService: kvStorage,
126152 );
127153 } else {
154+ logger.fine ('Using API client for Authentication.' );
128155 // Now that httpClient is available, initialize AuthApi and AuthRepository.
129156 authClient = AuthApi (httpClient: httpClient);
130157 authenticationRepository = AuthRepository (
131158 authClient: authClient,
132159 storageService: kvStorage,
133160 );
134161 }
135-
136- // 5. Initialize AdProvider and AdService.
162+ logger
163+ ..fine ('Authentication repository initialized.' )
164+ // 5. Initialize AdProvider and AdService.
165+ ..info ('6. Initializing Ad providers and AdService...' );
137166 late final Map <AdPlatformType , AdProvider > adProviders;
138167
139168 // Conditionally instantiate ad providers based on the application environment.
140169 // This ensures that only the relevant ad providers are available for the
141170 // current environment, preventing unintended usage.
142171 if (appConfig.environment == app_config.AppEnvironment .demo || kIsWeb) {
172+ logger.fine ('Using DemoAdProvider for all ad platforms.' );
143173 final demoAdProvider = DemoAdProvider (logger: logger);
144174 adProviders = {
145175 // In the demo environment or on the web, all ad platform types map to
@@ -151,6 +181,7 @@ Future<Widget> bootstrap(
151181 AdPlatformType .demo: demoAdProvider,
152182 };
153183 } else {
184+ logger.fine ('Using AdMobAdProvider and LocalAdProvider.' );
154185 // For development and production environments (non-web), use real ad providers.
155186 adProviders = {
156187 // AdMob provider for Google Mobile Ads.
@@ -180,22 +211,28 @@ Future<Widget> bootstrap(
180211 logger: logger,
181212 );
182213 await adService.initialize ();
214+ logger.fine ('AdService initialized.' );
183215
184216 // Initialize InlineAdCacheService with the created AdService.
185217 inlineAdCacheService = InlineAdCacheService (adService: adService);
186-
187- // Fetch the initial user from the authentication repository.
188- // This ensures the AppBloc starts with an accurate authentication status.
218+ logger
219+ ..fine ('InlineAdCacheService initialized.' )
220+ // Fetch the initial user from the authentication repository.
221+ // This ensures the AppBloc starts with an accurate authentication status.
222+ ..info ('7. Fetching initial user...' );
189223 final initialUser = await authenticationRepository.getCurrentUser ();
224+ logger.fine ('Initial user fetched: ${initialUser ?.id ?? 'none' }.' );
190225
191226 // Create a GlobalKey for the NavigatorState to be used by AppBloc
192227 // and InterstitialAdManager for BuildContext access.
193228 final navigatorKey = GlobalKey <NavigatorState >();
194229
195230 // Initialize PackageInfoService
196231 final packageInfoService = PackageInfoServiceImpl (logger: logger);
197-
198- // 6. Initialize all other DataClients and Repositories.
232+ logger
233+ ..fine ('PackageInfoService initialized.' )
234+ // 6. Initialize all other DataClients and Repositories.
235+ ..info ('8. Initializing Data clients and repositories...' );
199236 // These now also have a guaranteed valid httpClient.
200237 late final DataClient <Headline > headlinesClient;
201238 late final DataClient <Topic > topicsClient;
@@ -206,6 +243,7 @@ Future<Widget> bootstrap(
206243 late final DataClient <User > userClient;
207244 late final DataClient <LocalAd > localAdClient;
208245 if (appConfig.environment == app_config.AppEnvironment .demo) {
246+ logger.fine ('Using in-memory clients for all data repositories.' );
209247 headlinesClient = DataInMemory <Headline >(
210248 toJson: (i) => i.toJson (),
211249 getId: (i) => i.id,
@@ -280,6 +318,7 @@ Future<Widget> bootstrap(
280318 logger: logger,
281319 );
282320 } else if (appConfig.environment == app_config.AppEnvironment .development) {
321+ logger.fine ('Using API clients for all data repositories (Development).' );
283322 headlinesClient = DataApi <Headline >(
284323 httpClient: httpClient,
285324 modelName: 'headline' ,
@@ -337,6 +376,7 @@ Future<Widget> bootstrap(
337376 logger: logger,
338377 );
339378 } else {
379+ logger.fine ('Using API clients for all data repositories (Production).' );
340380 // Default to API clients for production
341381 headlinesClient = DataApi <Headline >(
342382 httpClient: httpClient,
@@ -395,6 +435,7 @@ Future<Widget> bootstrap(
395435 logger: logger,
396436 );
397437 }
438+ logger.fine ('All data clients instantiated.' );
398439
399440 final headlinesRepository = DataRepository <Headline >(
400441 dataClient: headlinesClient,
@@ -413,6 +454,7 @@ Future<Widget> bootstrap(
413454 dataClient: userAppSettingsClient,
414455 );
415456 final userRepository = DataRepository <User >(dataClient: userClient);
457+ logger.fine ('All data repositories initialized.' );
416458
417459 // Conditionally instantiate DemoDataMigrationService
418460 final demoDataMigrationService =
@@ -422,6 +464,9 @@ Future<Widget> bootstrap(
422464 userContentPreferencesRepository: userContentPreferencesRepository,
423465 )
424466 : null ;
467+ logger.fine (
468+ 'DemoDataMigrationService initialized: ${demoDataMigrationService != null }' ,
469+ );
425470
426471 // Conditionally instantiate DemoDataInitializerService
427472 // This service is responsible for ensuring that essential user-specific data
@@ -435,7 +480,11 @@ Future<Widget> bootstrap(
435480 userContentPreferencesRepository: userContentPreferencesRepository,
436481 )
437482 : null ;
438-
483+ logger
484+ ..fine (
485+ 'DemoDataInitializerService initialized: ${demoDataInitializerService != null }' ,
486+ )
487+ ..info ('--- Bootstrap Process Complete. Returning App widget. ---' );
439488 return App (
440489 authenticationRepository: authenticationRepository,
441490 headlinesRepository: headlinesRepository,
0 commit comments