Skip to content

Objetos Asíncronos

Descripción General

GetIt proporciona soporte comprehensivo para creación e inicialización asíncrona de objetos. Esto es esencial para objetos que necesitan realizar operaciones async durante la creación (conexiones a base de datos, llamadas de red, I/O de archivos) o que dependen de que otros objetos async estén listos primero.

Capacidades clave:

Referencia Rápida

Métodos de Registro Asíncrono

MétodoCuándo se CreaCuántas InstanciasTiempo de VidaMejor Para
registerFactoryAsyncCada getAsync()MuchasPor solicitudOperaciones async en cada acceso
registerCachedFactoryAsyncPrimer acceso + después del GCReutilizado mientras está en memoriaHasta que es recolectado por el GCOptimización de rendimiento para operaciones async costosas
registerSingletonAsyncInmediatamente en el registroUnaPermanenteServicios a nivel de app con configuración async
registerLazySingletonAsyncPrimer getAsync()UnaPermanenteServicios async costosos no siempre necesarios
registerSingletonWithDependenciesDespués de que las dependencias estén listasUnaPermanenteServicios que dependen de otros servicios

Async Factories

Las async factories crean una nueva instancia en cada llamada a getAsync() ejecutando una función factory asíncrona.

registerFactoryAsync

Crea una nueva instancia cada vez que llamas getAsync<T>().

dart
void registerFactoryAsync<T extends Object>(
  FactoryFuncAsync<T> factoryFunc, {
  String? instanceName,
});

Parámetros:

  • factoryFunc - Función async que crea y devuelve la instancia
  • instanceName - Nombre opcional para registrar múltiples factories del mismo tipo

Ejemplo:

dart
void main() async {
  // Register async factory
  getIt.registerFactoryAsync<DatabaseConnection>(
    () async {
      final conn = DatabaseConnection();
      await conn.connect();
      return conn;
    },
  );

  // Usage - creates new instance each time
  final db1 = await getIt.getAsync<DatabaseConnection>();
  final db2 = await getIt.getAsync<DatabaseConnection>(); // Different instance

  print('db1 == db2: ${identical(db1, db2)}'); // false - different instances
}

registerCachedFactoryAsync

Como registerFactoryAsync, pero cachea la instancia con una referencia débil. Devuelve la instancia cacheada si todavía está en memoria; de lo contrario crea una nueva.

dart
void registerCachedFactoryAsync<T extends Object>(
  FactoryFuncAsync<T> factoryFunc, {
  String? instanceName,
});

Ejemplo:

dart
void main() async {
  // Cached async factory
  getIt.registerCachedFactoryAsync<HeavyResource>(
    () async {
      final resource = HeavyResource();
      await resource.initialize();
      return resource;
    },
  );

  // First access - creates new instance
  final resource1 = await getIt.getAsync<HeavyResource>();

  // While still in memory - returns cached instance
  final resource2 = await getIt.getAsync<HeavyResource>();

  print(
      'resource1 == resource2: ${identical(resource1, resource2)}'); // true - same instance
}

Async Factories con Parámetros

Como las factories regulares, las async factories pueden aceptar hasta dos parámetros.

dart
void registerFactoryParamAsync<T, P1, P2>(
  FactoryFuncParamAsync<T, P1, P2> factoryFunc, {
  String? instanceName,
});

void registerCachedFactoryParamAsync<T, P1, P2>(
  FactoryFuncParamAsync<T, P1, P2> factoryFunc, {
  String? instanceName,
});

Ejemplo:

dart
// Register async factory with two parameters
getIt.registerFactoryParamAsync<UserViewModel, String, int>(
  (userId, age) async {
    // Simulate async initialization (e.g., fetch from API)
    await Future.delayed(Duration(milliseconds: 100));
    return UserViewModel(userId, age: age);
  },
);

// Access with parameters
final vm = await getIt.getAsync<UserViewModel>(
  param1: 'user-123',
  param2: 25,
);

Async Singletons

Los async singletons se crean una vez con inicialización asíncrona y viven durante el tiempo de vida del registro (hasta que se desregistren o se haga pop del scope).

registerSingletonAsync

Registra un singleton con una función factory async que se ejecuta inmediatamente. El singleton se marca como listo cuando la función factory se completa (a menos que signalsReady sea true).

dart
void registerSingletonAsync<T extends Object>(
  FactoryFuncAsync<T> factoryFunc, {
  String? instanceName,
  Iterable<Type>? dependsOn,
  bool? signalsReady,
  DisposingFunc<T>? dispose,
  void Function(T instance)? onCreated,
});

Parámetros:

  • factoryFunc - Función async que crea la instancia singleton
  • instanceName - Nombre opcional para registrar múltiples singletons del mismo tipo
  • dependsOn - Lista de tipos de los que depende este singleton (espera a que estén listos primero)
  • signalsReady - Si es true, debes llamar manualmente a signalReady() para marcar como listo
  • dispose - Función de limpieza opcional llamada al desregistrar o resetear
  • onCreated - Callback opcional invocado después de que la instancia es creada

Ejemplo:

dart
class RestService {
  Future<RestService> init() async {
    // do your async initialisation...
    await Future.delayed(Duration(seconds: 2));
    return this;
  }
}

Future<void> setup() async {
  // Pattern: Create instance and call init() in one expression
  getIt.registerSingletonAsync<RestService>(() async => RestService().init());

  // Wait for all async singletons to be ready
  await getIt.allReady();

  // Now access normally
  final service = getIt<RestService>();
}
Error Común: Usando signalsReady con registerSingletonAsync

La mayoría del tiempo, NO necesitas signalsReady: true con registerSingletonAsync.

La completación de la factory async automáticamente señaliza ready cuando retorna. Solo usa signalsReady: true si necesitas inicialización multi-etapa donde la factory se completa pero tienes trabajo async adicional antes de que la instancia esté verdaderamente lista.

Patrón de error común:

dart
class MyService {
  Future<void> initialize() async {
    await Future.delayed(Duration(milliseconds: 100));
  }
}

void configureIncorrect() {
  // ❌ This will throw: "This instance is not available in GetIt"
  getIt.registerSingletonAsync<MyService>(
    () async {
      final service = MyService();
      await service.initialize();

      // ❌ ERROR: Instance not in GetIt yet!
      // The factory hasn't returned, so GetIt doesn't know about this instance
      getIt.signalReady(service);

      return service;
    },
    signalsReady: true,
  );
}

Por qué falla: No puedes llamar signalReady(instance) desde dentro de la factory porque la instancia aún no está registrada.

Alternativas correctas:

Opción 1 - Dejar que la async factory auto-señalice (recomendado):

dart
void configure() {
  // ✅ Correct - no signalsReady needed
  getIt.registerSingletonAsync<MyService>(
    () async {
      final service = MyService();
      await service.initialize();
      return service; // Automatically signals ready
    },
  );
}

Opción 2 - Usar registerSingleton para señalización post-registro:

dart
class MyService {
  MyService() {
    _initialize();
  }

  Future<void> _initialize() async {
    await loadData();
    GetIt.instance.signalReady(this); // ✅ Now it's in GetIt
  }

  Future<void> loadData() async {
    await Future.delayed(Duration(milliseconds: 100));
  }
}

void configure() {
  // ✅ Correct - instance registered immediately
  getIt.registerSingleton<MyService>(
    MyService(),
    signalsReady: true, // Must manually signal
  );
}

Opción 3 - Implementar la interfaz WillSignalReady:

dart
class MyService implements WillSignalReady {
  MyService() {
    _initialize();
  }

  Future<void> _initialize() async {
    await loadData();
    GetIt.instance.signalReady(this);
  }

  Future<void> loadData() async {
    await Future.delayed(Duration(milliseconds: 100));
  }
}

void configure() {
  // ✅ Correct - interface-based signaling
  getIt.registerSingleton<MyService>(MyService());
  // No signalsReady parameter needed - interface detected
}

Mira Señalización Manual de Ready para más detalles.

registerLazySingletonAsync

Registra un singleton con una función factory async que se ejecuta en el primer acceso (cuando llamas getAsync<T>() por primera vez).

dart
void registerLazySingletonAsync<T extends Object>(
  FactoryFuncAsync<T> factoryFunc, {
  String? instanceName,
  DisposingFunc<T>? dispose,
  void Function(T instance)? onCreated,
  bool useWeakReference = false,
});

Parámetros:

  • factoryFunc - Función async que crea la instancia singleton
  • instanceName - Nombre opcional para registrar múltiples singletons del mismo tipo
  • dispose - Función de limpieza opcional llamada al desregistrar o resetear
  • onCreated - Callback opcional invocado después de que la instancia es creada
  • useWeakReference - Si es true, usa referencia débil (permite recolección de basura si no se usa)

Ejemplo:

dart
void configureDependencies() {
  // Lazy async singleton - created on first access
  getIt.registerLazySingletonAsync<CacheService>(
    () async {
      final cache = CacheService();
      await cache.loadFromDisk();
      return cache;
    },
  );

  // With weak reference - allows GC when not in use
  getIt.registerLazySingletonAsync<ImageCache>(
    () async => ImageCache.load(),
    useWeakReference: true,
  );
}

Future<void> main() async {
  configureDependencies();

  // First access - triggers creation
  final cache = await getIt.getAsync<CacheService>();

  // Subsequent access - returns existing instance
  final cache2 = await getIt.getAsync<CacheService>(); // Same instance
}

Lazy Async Singletons y allReady()

registerLazySingletonAsync no bloquea allReady() porque la función factory no se llama hasta el primer acceso. Sin embargo, una vez accedido, puedes usar isReady() para esperar a su completación.

Accediendo a Objetos Async

Los Objetos Async se Vuelven Normales Después de la Inicialización

Una vez que un async singleton ha completado la inicialización (has hecho await de allReady() o isReady<T>()), puedes accederlo como un singleton regular usando get<T>() en lugar de getAsync<T>(). Los métodos async solo se necesitan durante la fase de inicialización o cuando accedes a async factories.

dart
// Durante el inicio - espera la inicialización
await getIt.allReady();

// Después de estar listo - accede normalmente (no se necesita await)
final database = getIt<Database>();  // ¡No getAsync!
final apiClient = getIt<ApiClient>();

getAsync()

Recupera una instancia creada por una async factory o espera a que un async singleton complete la inicialización.

dart
Future<T> getAsync<T>({
  String? instanceName,
  dynamic param1,
  dynamic param2,
  Type? type,
})

Ejemplo:

dart
// Get async factory instance
final conn = await getIt.getAsync<DatabaseConnection>();

// Get async singleton (waits if still initializing)
final api = await getIt.getAsync<ApiClient>();

// Get named instance
final cache = await getIt.getAsync<CacheService>(instanceName: 'user-cache');

// Get with parameters (async factory param)
final report = await getIt.getAsync<Report>(
  param1: 'user-123',
  param2: DateTime.now(),
);

Obteniendo Múltiples Instancias Async

Si necesitas recuperar múltiples registros async del mismo tipo, mira el capítulo Registros Múltiples para la documentación de getAllAsync().

Gestión de Dependencias

Usando dependsOn

El parámetro dependsOn asegura el orden de inicialización. Cuando registras un singleton con dependsOn, su función factory no se ejecutará hasta que todas las dependencias listadas hayan señalizado ready.

Ejemplo - Inicialización secuencial:

dart
void configureDependencies() {
  // 1. Config loads first (no dependencies)
  getIt.registerSingletonAsync<ConfigService>(
    () async {
      final config = ConfigService();
      await config.loadFromFile();
      return config;
    },
  );

  // 2. API client waits for config
  getIt.registerSingletonAsync<ApiClient>(
    () async {
      final apiUrl = getIt<ConfigService>().apiUrl;
      final client = ApiClient(apiUrl);
      await client.authenticate();
      return client;
    },
    dependsOn: [ConfigService],
  );

  // 3. Database waits for config
  getIt.registerSingletonAsync<Database>(
    () async {
      final dbPath = getIt<ConfigService>().databasePath;
      final db = Database(dbPath);
      await db.initialize();
      return db;
    },
    dependsOn: [ConfigService],
  );

  // 4. App model waits for everything
  getIt.registerSingletonWithDependencies<AppModel>(
    () => AppModel.withParams(
        api: getIt<ApiClient>(),
        db: getIt<Database>(),
        config: getIt<ConfigService>()),
    dependsOn: [ConfigService, ApiClient, Database],
  );
}

Singletons Sincrónicos con Dependencias

A veces tienes un singleton regular (sync) que depende de que otros async singletons estén listos primero. Usa registerSingletonWithDependencies para este patrón.

dart
void registerSingletonWithDependencies<T>(
  FactoryFunc<T> factoryFunc, {
  String? instanceName,
  required Iterable<Type>? dependsOn,
  bool? signalsReady,
  DisposingFunc<T>? dispose,
})

Parámetros:

  • factoryFunc - Función sync que crea la instancia singleton (llamada después de que las dependencias estén listas)
  • instanceName - Nombre opcional para registrar múltiples singletons del mismo tipo
  • dependsOn - Lista de tipos de los que depende este singleton (espera a que estén listos primero)
  • signalsReady - Si es true, debes llamar manualmente a signalReady() para marcar como listo
  • dispose - Función de limpieza opcional llamada al desregistrar o resetear

Ejemplo:

dart
void configureDependencies() {
  // Async singletons
  getIt.registerSingletonAsync<ConfigService>(
    () async => ConfigService.load(),
  );

  getIt.registerSingletonAsync<ApiClient>(
    () async => ApiClient.create(),
  );

  // Sync singleton that depends on async singletons
  getIt.registerSingletonWithDependencies<UserRepository>(
    () => UserRepository(getIt<ApiClient>(), getIt<Database>()),
    dependsOn: [ConfigService, ApiClient],
  );
}

Future<void> main() async {
  configureDependencies();

  // Wait for all to be ready
  await getIt.allReady();

  // Now safe to access - dependencies are guaranteed ready
  final userRepo = getIt<UserRepository>();
}

Orquestación de Inicio

GetIt proporciona varias funciones para coordinar la inicialización async y esperar a que los servicios estén listos.

allReady()

Devuelve un Future<void> que se completa cuando todos los async singletons y singletons con signalsReady han completado su inicialización.

dart
Future<void> allReady({
  Duration? timeout,
  bool ignorePendingAsyncCreation = false,
})

Parámetros:

  • timeout - Timeout opcional; lanza WaitingTimeOutException si no está listo a tiempo
  • ignorePendingAsyncCreation - Si es true, solo espera señales manuales, ignora async singletons

Ejemplo con FutureBuilder:

dart
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: getIt.allReady(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          // All services ready - show main app
          return HomePage();
        } else {
          // Still initializing - show splash screen
          return SplashScreen();
        }
      },
    );
  }
}

Ejemplo con timeout:

dart
Future<void> main() async {
  setupDependencies();

  try {
    await getIt.allReady(timeout: Duration(seconds: 10));
    runApp(MyApp());
  } on WaitingTimeOutException catch (e) {
    print('Initialization timeout!');
    print('Not ready: ${e.notReadyYet}');
    print('Already ready: ${e.areReady}');
    print('Waiting chain: ${e.areWaitedBy}');
  }
}

Llamando a allReady() múltiples veces:

Puedes llamar allReady() múltiples veces. Después de que el primer allReady() se complete, si registras nuevos async singletons, puedes hacer await de allReady() otra vez para esperar a los nuevos.

dart
// Register first batch of services
getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());
getIt.registerSingletonAsync<Logger>(() async => Logger.initialize());

// Wait for first batch
await getIt.allReady();
print('Core services ready');

// Register second batch based on config
final config = getIt<ConfigService>();
if (config.enableFeatureX) {
  getIt.registerSingletonAsync<FeatureX>(() async => FeatureX.initialize());
}

// Wait for second batch
await getIt.allReady();
print('All services ready');

runApp(MyApp());

Este patrón es especialmente útil con scopes donde cada scope necesita su propia inicialización:

dart
Future<void> main() async {
  // Initialize base scope
  await getIt.allReady();

  // Push new scope with its own async services
  getIt.pushNewScope(scopeName: 'user-session');
  getIt.registerSingletonAsync<UserService>(() async => UserService.load());

  // Wait for new scope to be ready
  await getIt.allReady();
}

isReady()

Devuelve un Future<void> que se completa cuando un singleton específico está listo.

dart
Future<void> isReady<T>({
  Object? instance,
  String? instanceName,
  Duration? timeout,
  Object? callee,
})

Parámetros:

  • T - Tipo del singleton a esperar
  • instance - Alternativamente, esperar a un objeto de instancia específico
  • instanceName - Esperar a registro con nombre
  • timeout - Timeout opcional; lanza WaitingTimeOutException si no está listo a tiempo
  • callee - Parámetro opcional para depuración (ayuda a identificar quién está esperando)

Ejemplo:

dart
Future<void> main() async {
  // setupDependencies(); // Setup your dependencies first

  // Wait for specific service
  await getIt.isReady<Database>();

  // Now safe to use
  final db = getIt<Database>();

  // Wait for named instance
  await getIt.isReady<ApiClient>(instanceName: 'production');
}

isReadySync()

Verifica si un singleton está listo sin esperar (devuelve inmediatamente).

dart
bool isReadySync<T>({
  Object? instance,
  String? instanceName,
})

Ejemplo:

dart
void checkStatus() {
  if (getIt.isReadySync<Database>()) {
    print('Database is ready');
  } else {
    print('Database still initializing...');
  }
}

allReadySync()

Verifica si todos los async singletons están listos sin esperar.

dart
bool allReadySync([bool ignorePendingAsyncCreation = false])

Ejemplo:

dart
void showUI() {
  if (getIt.allReadySync()) {
    // Show main UI
  } else {
    // Show loading indicator
  }
}

Dependencias con Nombre usando InitDependency

Si tienes registros con nombre, usa InitDependency para especificar tanto el tipo como el nombre de instancia.

dart
void configureDependencies() {
  // Register multiple API clients
  getIt.registerSingletonAsync<ApiClient>(
    () async => ApiClient.create('https://api-v1.example.com'),
    instanceName: 'api-v1',
  );

  getIt.registerSingletonAsync<ApiClient>(
    () async => ApiClient.create('https://api-v2.example.com'),
    instanceName: 'api-v2',
  );

  // Depend on specific named instance
  getIt.registerSingletonWithDependencies<DataSync>(
    () => DataSync(getIt<ApiClient>(instanceName: 'api-v2')),
    dependsOn: [InitDependency(ApiClient, instanceName: 'api-v2')],
  );
}

Señalización Manual de Ready

A veces necesitas más control sobre cuándo un singleton señaliza que está listo. Esto es útil cuando la inicialización involucra múltiples pasos o callbacks.

Usando el Parámetro signalsReady

Cuando estableces signalsReady: true durante el registro, GetIt no marcará automáticamente el singleton como listo. Debes llamar manualmente a signalReady().

Ejemplo:

dart
class ConfigService {
  bool isReady = false;

  ConfigService() {
    _initialize();
  }

  Future<void> _initialize() async {
    // Complex async initialization
    await loadRemoteConfig();
    await validateConfig();
    await setupConnections();

    isReady = true;
    // Signal that we're ready
    GetIt.instance.signalReady(this);
  }

  Future<void> loadRemoteConfig() async {}
  Future<void> validateConfig() async {}
  Future<void> setupConnections() async {}
}

void configureDependencies() {
  getIt.registerSingleton<ConfigService>(
    ConfigService(),
    signalsReady: true, // Must manually signal ready
  );
}

Future<void> main() async {
  configureDependencies();
  // Wait for ready signal
  await getIt.isReady<ConfigService>();
}

Usando la Interfaz WillSignalReady

En lugar de pasar signalsReady: true, implementa la interfaz WillSignalReady. GetIt detecta esto automáticamente y espera la señalización manual.

dart
class ConfigService implements WillSignalReady {
  bool isReady = false;

  ConfigService() {
    _initialize();
  }

  Future<void> _initialize() async {
    await loadConfig();
    isReady = true;
    GetIt.instance.signalReady(this);
  }

  Future<void> loadConfig() async {}
}

void configureDependencies() {
  // No signalsReady parameter needed - interface handles it
  getIt.registerSingleton<ConfigService>(ConfigService());
}

signalReady()

Señaliza manualmente que un singleton está listo.

dart
void signalReady(Object? instance)

Parámetros:

  • instance - La instancia que está lista (pasar null es legacy y no se recomienda)

Ejemplo:

dart
class DatabaseService {
  DatabaseService() {
    _init();
  }

  Future<void> _init() async {
    await connectToDatabase();
    await runMigrations();

    // Signal this instance is ready
    GetIt.instance.signalReady(this);
  }

  Future<void> connectToDatabase() async {}
  Future<void> runMigrations() async {}
}

Característica Legacy

signalReady(null) (señal ready global sin una instancia) es una característica legacy de versiones anteriores de GetIt. Se recomienda usar registros async (registerSingletonAsync, etc.) o señalización específica de instancia en su lugar. El enfoque de señal global es menos claro sobre qué se está inicializando y no se integra bien con la gestión de dependencias.

Nota: El signalReady(null) global lanzará un error si tienes cualquier registro async o instancias con signalsReady: true que aún no hayan señalizado. La señalización específica de instancia funciona bien junto con registros async.

Mejores Prácticas

1. Prefiere registerSingletonAsync para Inicialización de App

Para servicios necesarios al inicio de la app, usa registerSingletonAsync (no lazy) para que comiencen a inicializarse inmediatamente.

dart
void configureDependencies() {
  // Good - starts initializing immediately
  getIt.registerSingletonAsync<Database>(() async => Database.connect());

  // Less ideal - won't initialize until first access
  getIt.registerLazySingletonAsync<Database>(() async => Database.connect());
}
2. Usa dependsOn para Expresar Dependencias

Deja que GetIt gestione el orden de inicialización en lugar de orquestar manualmente con isReady().

dart
// Good - clear dependency chain
void configureDependencies() {
  getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());

  getIt.registerSingletonAsync<ApiClient>(
    () async => ApiClient(getIt<ConfigService>().apiUrl),
    dependsOn: [ConfigService],
  );
}

// Less ideal - manual orchestration
void configureDependenciesManual() {
  getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());

  getIt.registerSingletonAsync<ApiClient>(() async {
    await getIt.isReady<ConfigService>(); // Manual waiting
    return ApiClient(getIt<ConfigService>().apiUrl);
  });
}
3. Usa FutureBuilder para Pantallas de Splash

Muestra una pantalla de carga mientras los servicios se inicializan.

dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FutureBuilder(
        future: getIt.allReady(),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return ErrorScreen(snapshot.error!);
          }

          if (snapshot.hasData) {
            return HomePage();
          }

          return SplashScreen();
        },
      ),
    );
  }
}
4. Siempre Establece Timeouts para allReady()

Previene que tu app se cuelgue indefinidamente si la inicialización falla.

dart
Future<void> main() async {
  try {
    await getIt.allReady(timeout: Duration(seconds: 30));
    runApp(MyApp());
  } on WaitingTimeOutException catch (e) {
    // Handle timeout - log error, show error screen, etc.
    runApp(ErrorApp(error: e));
  }
}

Patrones Comunes

Patrón 1: Inicialización en Capas

dart
void configureDependencies() {
  // Layer 1: Core infrastructure
  getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());
  getIt.registerSingletonAsync<Logger>(() async => Logger.initialize());

  // Layer 2: Network and data access
  getIt.registerSingletonAsync<ApiClient>(
    () async => ApiClient(getIt<ConfigService>().apiUrl),
    dependsOn: [ConfigService],
  );

  getIt.registerSingletonAsync<Database>(
    () async => Database(getIt<ConfigService>().dbPath),
    dependsOn: [ConfigService],
  );

  // Layer 3: Business logic
  getIt.registerSingletonWithDependencies<UserRepository>(
    () => UserRepository(getIt<ApiClient>(), getIt<Database>()),
    dependsOn: [ApiClient, Database],
  );

  // Layer 4: Application state
  getIt.registerSingletonWithDependencies<AppModel>(
    () => AppModel(getIt<UserRepository>()),
    dependsOn: [UserRepository],
  );
}
Patrón 2: Inicialización Condicional
dart
void configureDependencies({required bool isProduction}) {
  getIt.registerSingletonAsync<ConfigService>(
    () async => ConfigService.load(),
  );

  if (isProduction) {
    getIt.registerSingletonAsync<ApiClient>(
      () async => ApiClient(getIt<ConfigService>().prodUrl),
      dependsOn: [ConfigService],
    );
  } else {
    getIt.registerSingletonAsync<ApiClient>(
      () async => MockApiClient(),
      dependsOn: [ConfigService],
    );
  }
}
Patrón 3: Seguimiento de Progreso
dart
class InitializationProgress extends ChangeNotifier {
  final Map<String, bool> _progress = {};

  void markReady(String serviceName) {
    _progress[serviceName] = true;
    notifyListeners();
  }

  double get percentComplete =>
      _progress.values.where((ready) => ready).length / _progress.length;
}

void configureDependencies(InitializationProgress progress) {
  getIt.registerSingletonAsync<ConfigService>(
    () async => ConfigService.load(),
    onCreated: (_) => progress.markReady('Config'),
  );

  getIt.registerSingletonAsync<Database>(
    () async => Database.connect(),
    dependsOn: [ConfigService],
    onCreated: (_) => progress.markReady('Database'),
  );

  getIt.registerSingletonAsync<ApiClient>(
    () async => ApiClient.create(),
    dependsOn: [ConfigService],
    onCreated: (_) => progress.markReady('API'),
  );
}
Patrón 4: Reintentar en Fallo
dart
Future<T> withRetry<T>(
  Future<T> Function() operation, {
  int maxAttempts = 3,
}) async {
  for (var attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await operation();
    } catch (e) {
      if (attempt == maxAttempts) rethrow;
      await Future.delayed(Duration(seconds: attempt));
    }
  }
  throw StateError('Should never reach here');
}

void configureDependencies() {
  getIt.registerSingletonAsync<ApiClient>(
    () => withRetry(() async => ApiClient.connect()),
  );
}

Lectura Adicional

Publicado bajo la Licencia MIT.