get_it icon indicating copy to clipboard operation
get_it copied to clipboard

Add `findAll`

Open Abion47 opened this issue 8 months ago • 0 comments

The existing getAll method returns a list of all services registered under a particular type, but there is not currently a way to get all of the registered services that match a particular type.

The impetus for this issue was this question. While that particular use case would be better resolved using scopes, their naive implementation was to register all of their singletons under a custom Disposable type so they could later use getAll to iterate over them and trigger disposal (not the right way to do this, I know, but stay with me here). The problem with this implementation was that they would have to fetch their services by name and manually cast them to the service type they wanted, which is problematic for several reasons. A "solution" that remained in the spirit of their approach, however, would require a way to query all registered singletons and find the ones that extended Disposable, not just the ones that were registered under Disposable.

A more realistic use case for this might be the following:

abstract interface class IOutput {
  void write(String message);
}

class ConsoleOutput implements IOutput {
  @override
  void write(String message) {
    // Write to console
  }
}

class FileOutput implements IOutput {
  @override
  void write(String message) {
    // Write to local file
  }

  void clearFile() {
    // Truncate local file
  }
}

class RemoteLoggingOutput implements IOutput {
  @override
  void write(String message) {
    // Write to remote logging service
  }

  void setTarget(String url) {
    // Configure the target of the logging service
  }
}

...

void initGetIt() {
  final getIt = GetIt.I;
  
  // Ideal
  getIt.registerSingleton(ConsoleOutput());
  getIt.registerSingleton(FileOutput());
  getIt.registerSingleton(RemoteLoggingOutput());

  // Not ideal for this use case
  getIt.registerSingleton<IOutput>(ConsoleOutput(), instanceName: 'console');
  getIt.registerSingleton<IOutput>(FileOutput(), instanceName: 'file');
  getIt.registerSingleton<IOutput>(RemoteLoggingOutput(), instanceName: 'remoteLogging');
}

Using the first approach, I can access the individual services by type and type safety would be maintained without my needing to worry about getting them by name and casting them to the type I want.

// This is good
GetIt.I.get<FileOutput>().clearFile();
GetIt.I.get<RemoteLoggingOutput>().setTarget('https://api.example.com/log');

// This is verbose and error-prone
(GetIt.I.get<IOutput>(instanceName: 'file') as FileOutput).clearFile();
(GetIt.I.get<IOutput>(instanceName: 'remoteLogging') as RemoteLoggingOutput).setTarget('https://api.example.com/log');

But if I wanted to write something to all of the services, there's no way to do that with the first approach. I would expect to be able to do something like this:

void messageAll(String message) {
  for (final output in GetIt.I.findAll<IOutput>()) {
    output.write(message);
  }
}

Abion47 avatar May 15 '25 23:05 Abion47