Skip to content

Commit 131c223

Browse files
committed
refactor(bootstrap): restructure app initialization process
- Rearrange initialization order to eliminate circular dependencies - Introduce conditional initialization based on environment - Separated KV Storage initialization as a foundational step - Reorganized AdProvider and DataClient initializations - Improved HttpClient and AuthRepository setup
1 parent 34916e8 commit 131c223

File tree

1 file changed

+51
-34
lines changed

1 file changed

+51
-34
lines changed

lib/bootstrap.dart

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'package:flutter_news_app_mobile_client_full_source_code/app/services/dem
2121
import 'package:flutter_news_app_mobile_client_full_source_code/bloc_observer.dart';
2222
import 'package:flutter_news_app_mobile_client_full_source_code/shared/data/clients/country_inmemory_client.dart';
2323
import 'package:http_client/http_client.dart';
24+
import 'package:kv_storage_service/kv_storage_service.dart';
2425
import 'package:kv_storage_shared_preferences/kv_storage_shared_preferences.dart';
2526
import 'package:logging/logging.dart';
2627
import 'package:timeago/timeago.dart' as timeago;
@@ -32,22 +33,57 @@ Future<Widget> bootstrap(
3233
) async {
3334
WidgetsFlutterBinding.ensureInitialized();
3435
Bloc.observer = const AppBlocObserver();
35-
36+
final logger = Logger('bootstrap');
3637
timeago.setLocaleMessages('en', EnTimeagoMessages());
3738
timeago.setLocaleMessages('ar', ArTimeagoMessages());
3839

39-
final logger = Logger('bootstrap');
40-
40+
// 1. Initialize KV Storage Service first, as it's a foundational dependency.
4141
final kvStorage = await KVStorageSharedPreferences.getInstance();
4242

43+
// 2. Conditionally initialize HttpClient and Auth services based on environment.
44+
// This ensures HttpClient is available before any DataApi or AdProvider
45+
// that depends on it.
4346
late final AuthClient authClient;
4447
late final AuthRepository authenticationRepository;
45-
HttpClient? httpClient;
48+
late final HttpClient httpClient;
49+
if (appConfig.environment == app_config.AppEnvironment.demo) {
50+
// In-memory authentication for demo environment.
51+
authClient = AuthInmemory();
52+
authenticationRepository = AuthRepository(
53+
authClient: authClient,
54+
storageService: kvStorage,
55+
);
56+
// For demo, httpClient is not strictly needed for DataApi,
57+
// but we initialize a dummy one to satisfy non-nullable requirements
58+
// if any part of the code path expects it.
59+
// In a real scenario, DataApi would not be used in demo mode.
60+
httpClient = HttpClient(
61+
baseUrl: appConfig.baseUrl,
62+
tokenProvider: () async => null, // No token needed for demo
63+
logger: logger,
64+
);
65+
} else {
66+
// For production and development environments, an HTTP client is needed.
67+
// Initialize HttpClient first. Its tokenProvider now directly reads from
68+
// kvStorage, breaking the circular dependency with AuthRepository.
69+
httpClient = HttpClient(
70+
baseUrl: appConfig.baseUrl,
71+
tokenProvider: () =>
72+
kvStorage.readString(key: StorageKey.authToken.stringValue),
73+
logger: logger,
74+
);
4675

47-
// Initialize AdProvider and AdService
48-
// Initialize AdProvider based on platform.
49-
// On web, use a No-Op provider to prevent MissingPluginException,
50-
// as Google Mobile Ads SDK does not support native ads on web.
76+
// Now that httpClient is available, initialize AuthApi and AuthRepository.
77+
authClient = AuthApi(httpClient: httpClient);
78+
authenticationRepository = AuthRepository(
79+
authClient: authClient,
80+
storageService: kvStorage,
81+
);
82+
}
83+
84+
// 3. Initialize AdProvider and AdService.
85+
// These now have a guaranteed valid httpClient (for DataApi-based LocalAdProvider)
86+
// or can proceed independently (AdMobAdProvider).
5187
final adProviders = <AdPlatformType, AdProvider>{
5288
AdPlatformType.admob: AdMobAdProvider(logger: logger),
5389
AdPlatformType.local: LocalAdProvider(
@@ -60,7 +96,8 @@ Future<Widget> bootstrap(
6096
logger: logger,
6197
)
6298
: DataApi<LocalAd>(
63-
httpClient: httpClient!,
99+
httpClient:
100+
httpClient, // httpClient is now guaranteed to be non-null
64101
modelName: 'local_ad',
65102
fromJson: LocalAd.fromJson,
66103
toJson: LocalAd.toJson,
@@ -74,31 +111,12 @@ Future<Widget> bootstrap(
74111
final adService = AdService(adProviders: adProviders, logger: logger);
75112
await adService.initialize(); // Initialize all selected AdProviders early
76113

77-
if (appConfig.environment == app_config.AppEnvironment.demo) {
78-
authClient = AuthInmemory();
79-
authenticationRepository = AuthRepository(
80-
authClient: authClient,
81-
storageService: kvStorage,
82-
);
83-
} else {
84-
// For production and development environments, an HTTP client is needed.
85-
httpClient = HttpClient(
86-
baseUrl: appConfig.baseUrl,
87-
tokenProvider: () => authenticationRepository.getAuthToken(),
88-
logger: logger,
89-
);
90-
authClient = AuthApi(httpClient: httpClient);
91-
authenticationRepository = AuthRepository(
92-
authClient: authClient,
93-
storageService: kvStorage,
94-
);
95-
}
96-
97114
// Fetch the initial user from the authentication repository.
98115
// This ensures the AppBloc starts with an accurate authentication status.
99116
final initialUser = await authenticationRepository.getCurrentUser();
100117

101-
// Conditional data client instantiation based on environment
118+
// 4. Initialize all other DataClients and Repositories.
119+
// These now also have a guaranteed valid httpClient.
102120
DataClient<Headline> headlinesClient;
103121
DataClient<Topic> topicsClient;
104122
DataClient<Country> countriesClient;
@@ -107,8 +125,7 @@ Future<Widget> bootstrap(
107125
DataClient<UserAppSettings> userAppSettingsClient;
108126
DataClient<RemoteConfig> remoteConfigClient;
109127
DataClient<User> userClient;
110-
DataClient<LocalAd> localAdClient; // Declare localAdClient
111-
128+
DataClient<LocalAd> localAdClient;
112129
if (appConfig.environment == app_config.AppEnvironment.demo) {
113130
headlinesClient = DataInMemory<Headline>(
114131
toJson: (i) => i.toJson(),
@@ -192,7 +209,7 @@ Future<Widget> bootstrap(
192209
);
193210
} else if (appConfig.environment == app_config.AppEnvironment.development) {
194211
headlinesClient = DataApi<Headline>(
195-
httpClient: httpClient!,
212+
httpClient: httpClient,
196213
modelName: 'headline',
197214
fromJson: Headline.fromJson,
198215
toJson: (headline) => headline.toJson(),
@@ -257,7 +274,7 @@ Future<Widget> bootstrap(
257274
} else {
258275
// Default to API clients for production
259276
headlinesClient = DataApi<Headline>(
260-
httpClient: httpClient!,
277+
httpClient: httpClient,
261278
modelName: 'headline',
262279
fromJson: Headline.fromJson,
263280
toJson: (headline) => headline.toJson(),

0 commit comments

Comments
 (0)