boot static method

Future<void> boot({
  1. String? flavor,
  2. BloomBootstrapper? bootstrapper,
  3. String? envContent,
  4. String? configYaml,
  5. BloomObservabilityConfig? observability,
  6. 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 .env string to load directly without file I/O.
  • configYaml: Optional raw bloom.yaml string 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.');
}