boot static method
Future<void>
boot({
- String? flavor,
- BloomBootstrapper? bootstrapper,
- String? envContent,
- String? configYaml,
- BloomObservabilityConfig? observability,
- BloomEnvironmentSchema? environmentSchema,
Main boot pipeline for Bloom applications.
Initializes Flutter bindings, loads and validates configuration and environment variables,
registers DI bindings, configures deep links, starts devtools and observability,
and runs the user bootstrapper.
Parameters:
flavor: Optional build flavor name.bootstrapper: Optional custom bootstrap lifecycle handler.envContent: Optional raw.envstring to load directly without file I/O.configYaml: Optional rawbloom.yamlstring to load directly.observability: Custom telemetry and crash reporting options.environmentSchema: Strict schema validation rules for environment variables.
Implementation
static Future<void> boot({
String? flavor,
BloomBootstrapper? bootstrapper,
String? envContent,
String? configYaml,
BloomObservabilityConfig? observability,
BloomEnvironmentSchema? environmentSchema,
}) async {
if (_isBooted) {
logger.warn('Bloom.boot() was called multiple times. Skipping duplicate initialization.');
return;
}
// 1. Ensure Flutter binding is initialized
WidgetsFlutterBinding.ensureInitialized();
// 2. Parse configuration
if (configYaml != null) {
_config = BloomConfig.fromYaml(configYaml);
} else {
try {
final assetConfig = await rootBundle.loadString('bloom.yaml');
_config = BloomConfig.fromYaml(assetConfig);
} catch (_) {
_config = const BloomConfig();
}
}
// 3. Resolve active flavor
_activeFlavor = flavor ??
(const bool.hasEnvironment('BLOOM_FLAVOR')
? const String.fromEnvironment('BLOOM_FLAVOR')
: null);
// 4. Load environment variables (.env, .env.local, and flavor-specific envFiles in order)
if (envContent != null) {
BloomEnv.loadContent(envContent);
} else {
final envFilesToLoad = <String>[];
if (_config.envFiles.isNotEmpty) {
envFilesToLoad.addAll(_config.envFiles);
} else {
envFilesToLoad.addAll(['.env', '.env.local']);
}
// Add flavor-specific env file if active
if (_activeFlavor != null && _config.flavors.containsKey(_activeFlavor)) {
final flavorEnv = _config.flavors[_activeFlavor]!.envFile ?? '.env.$_activeFlavor';
if (!envFilesToLoad.contains(flavorEnv)) {
envFilesToLoad.add(flavorEnv);
}
}
for (final envFile in envFilesToLoad) {
try {
final assetEnv = await rootBundle.loadString(envFile);
if (assetEnv.isNotEmpty) {
BloomEnv.loadContent(assetEnv, overwrite: true);
logger.debug('BloomEnv: Loaded environment file: $envFile');
}
} catch (_) {}
}
}
// 4b. Enforce strict environment schema validation if specified
if (environmentSchema != null) {
BloomEnv.validate(environmentSchema);
}
// 4c. Register feature flags from config
final customFlags = _config.custom['feature_flags'] ?? _config.custom['features'];
if (customFlags is Map) {
_features.registerAll(Map<String, dynamic>.from(customFlags));
}
// 5. Configure logger
logger.info('Booting Bloom application "${_config.name}" (v${_config.version})${_activeFlavor != null ? ' [Flavor: $_activeFlavor]' : ''}');
// 6. Register core framework bindings in DI
container.provideValue<BloomConfig>(_config);
// 7. Initialize Deep Links listener
if (_config.deepLinks.enabled) {
await BloomDeepLinks.initialize(
routeMappings: _config.deepLinks.routeMappings,
);
}
// 8. Auto-register VM DevTools Service extensions & start cache GC
BloomDevToolsService.register();
BloomData.startGarbageCollector();
// 9. Initialize OTA Code-Push if enabled
if (_config.deployment.shorebird.enabled) {
await BloomOTA.initialize();
if (_config.deployment.shorebird.autoCheckUpdate) {
unawaited(BloomOTA.checkForUpdate());
}
}
// 10. Initialize Error Observability & Telemetry SDK
final runtimeFp = BloomRuntimeFingerprint.fromConfig(_config).computeHash();
final obsConfig = observability != null
? BloomObservabilityConfig(
enabled: observability.enabled,
sampleRate: observability.sampleRate,
autoCaptureFlutterErrors: observability.autoCaptureFlutterErrors,
autoCaptureZoneErrors: observability.autoCaptureZoneErrors,
autoCaptureNativeCrashes: observability.autoCaptureNativeCrashes,
maxBreadcrumbs: observability.maxBreadcrumbs,
transport: observability.transport,
beforeSend: observability.beforeSend,
appInfo: {
'name': _config.name,
'version': _config.version,
'buildNumber': _config.buildNumber,
...observability.appInfo,
},
tags: observability.tags,
runtimeFingerprint: observability.runtimeFingerprint ?? runtimeFp,
bloomVersion: observability.bloomVersion ?? _config.version,
flutterVersion: observability.flutterVersion ?? '3.27.0',
channel: observability.channel ?? _activeFlavor ?? 'production',
activePatchId: observability.activePatchId ??
BloomOTA.activePatchId ??
BloomUpdates.activePatchId,
buildNumber: observability.buildNumber ?? _config.buildNumber,
)
: BloomObservabilityConfig(
enabled: true,
bloomVersion: _config.version,
flutterVersion: '3.27.0',
channel: _activeFlavor ?? 'production',
activePatchId: BloomOTA.activePatchId ?? BloomUpdates.activePatchId,
buildNumber: _config.buildNumber,
runtimeFingerprint: runtimeFp,
appInfo: {
'name': _config.name,
'version': _config.version,
'buildNumber': _config.buildNumber,
},
);
await BloomObservability.initialize(obsConfig);
// 11. Execute user bootstrapper if provided
if (bootstrapper != null) {
await bootstrapper.onBoot(container);
}
_isBooted = true;
logger.info('Bloom boot completed successfully.');
}