# injectable: a Dart code generator that writes your get_it registrations for you

> injectable turns annotated Dart classes into get_it registration code, so a service locator stops being a hand-maintained list of factories. The generator writes an extension with an init function, supports scopes, environments, named factories, and can hand back a static accessor method instead of a get call.

**Milad-Akarie/injectable** — Code Generator for get_it

- Repository: https://github.com/Milad-Akarie/injectable
- Stars: 625 · Forks: 191
- Language: Dart
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/milad-akarie-injectable

## The problem is a service locator with a hand-written registry

get_it is a service locator for Dart and Flutter, and the pattern it enables is straightforward: you register factories, singletons, and scopes in one place, then resolve them by type at the point of use. The pain point arrives quickly. Every dependency has to be registered by hand, in the right order, with the right scope, and a forgotten registration is a runtime crash rather than a compile error. injectable is a code generator for get_it, and it removes that manual registry. You annotate a class with `@injectable`, the generator reads your annotations, and it writes the registration calls for you into a generated Dart file. The audience is a Dart or Flutter team already using get_it and finding the boilerplate unmanageable, and the change is incremental because the runtime you depend on does not change.

## Setup is an annotation, a generated import, and a call

The README's setup has four steps. You create a new Dart file and define a global variable for your GetIt instance. You define a top-level function, for example `configureDependencies`, and annotate it with `@injectableInit`. You import the generated Dart file, which follows the name of the file holding the annotated function, so `file_name.config.dart`. Finally you call the generated extension function `getIt.init()`, or your own initializer name, inside the configure function. The example for version 2 and later is:

```dart
@InjectableInit(generateAccessors: true)
void configureDependencies() => getIt.init();
```

The four properties shown in the README's fuller example are the defaults: `initializerName` is `init`, `preferRelativeImports` is true, `asExtension` is true, and `generateAccessors` is false. You then call `configureDependencies()` in your main function before running the app, which is what guarantees the registrations exist before anything resolves them. The `generateForDir` property of `@injectableInit` narrows what the generator looks at, so a test-only tree can be processed on its own.

## The generator writes an extension with one line per dependency

The point of the whole exercise is what comes out. The README shows the inside of a generated file, and it is short enough to read at a glance. The output is an extension on the GetIt type, with an `init` method that takes an optional environment string and an environment filter, builds a `GetItHelper`, and registers each dependency. The registration lines are the interesting part, because they encode the dependency graph you would otherwise have to write yourself. A class with no dependencies becomes a factory that constructs it, and a class whose constructor takes another registered type becomes a factory that resolves that dependency first. That resolution of `ServiceA` inside the `ServiceB` registration is exactly the part people get wrong by hand, and it is generated from the constructor signature rather than from a list you maintain.

## Annotations carry cache, scope, and environment decisions

The annotations do more than mark a class as injectable. Cached factories are registered by passing true to the `cache` property inside the `@Injectable` annotation, and the README links to the GetIt documentation for what that means in practice. Beyond caching, the annotation set covers the decisions that used to live in the manual registry: registering singletons, disposing of singletons, multiple registrations of the same type, `@FactoryMethod` and `@PostConstruct` annotations, registering asynchronous injectables, pre-resolving futures, and passing parameters to factories. There is also binding abstract classes to implementations, registering under different environments, and using named factories with static create functions. Two features deserve a note. Async injectables and pre-resolving futures matter for applications that must not block on initialization, and binding an abstract class to an implementation is what keeps the generated code decoupled from a concrete type.

## Accessors replace the get call when you ask for them

By default, dependencies are resolved with the standard get_it syntax, `getIt.get<Type>()`. The README describes an alternative in the `generateAccessors` property of `@InjectableInit`, which generates convenient getter methods for a cleaner, more concise API. The generated code is described in the README as follows:

```dart
// Without generateAccessors
var userService = getIt.get<UserService>();
var database = getIt.get<Database>();
```

With the flag enabled, the generator creates getter methods instead, so call sites read as a property access rather than a lookup. The difference matters in a codebase with many resolutions, because `getIt.get<T>()` is a runtime lookup that hides both the dependency and the type from a reader skimming the call site. The property defaults to false, so the accessor style is opt in per project, and the README notes it is also what the accessor generation documentation covers, which suggests the two are kept in step.

## The build step is build_runner, with LeanBuilder as an option

Running the generator is a build step, and the README documents the classic route. The `watch` flag watches the file system for edits and rebuilds as necessary, and the one-time variant runs the generator and exits. The README also describes a LeanBuilder path marked as experimental, supported by injectable_generator 2.12.0 and later, which it presents as a way to get faster incremental builds. The trade-off there is that you can disable the build_runner generators from the `build.yaml` file, listing `injectable_generator:injectable_builder` and `injectable_generator:injectable_config_builder` under the default target and setting them to false. That configuration block is the piece worth reading carefully, because running both builders at once is the obvious way to end up generating the same file twice. The repository is split into `injectable/` and `injectable_generator/` packages, so the runtime and the generator ship separately and version independently.

## The scope system and the external module support are where the edges are

Two features in the README's table of contents are worth reading before you commit, because they decide whether the tool covers your project. The first is scopes, which is how you register a set of dependencies with a lifetime that is not the whole app, such as a session that must be disposed of when the user logs out. The second is including microPackages and external modules, which is what makes the generator usable in a monorepo where a feature lives in its own package with its own `pubspec.yaml`. Both are extensions of the annotation model rather than separate subsystems, so they scale with the rest of the tool, and both add configuration that a project has to think about deliberately rather than discover later. There is also an auto registering option and a manual order option for the cases where you need to control the sequence in which registrations are made.

## Conclusion

injectable suits a Flutter or Dart team that has adopted get_it and now has a long, hand-written list of registrations to maintain, especially where the dependency graph has grown past what a human can keep in sync. It is the wrong tool if you are still deciding between service location and constructor injection, since the generator commits you to get_it, and it is the wrong tool if you want dependency injection that works without annotations or code generation, because every registration still lives in generated Dart you have to rebuild. Before adopting it, read the generated file once so the shape of the output is familiar, then pick one of the `build_runner` or the experimental LeanBuilder path rather than running both.

## FAQ

### What does the injectable code generator produce?

It generates a Dart file, named after the file containing your `@InjectableInit` function, containing an extension on GetIt with an `init` method. That method registers each annotated dependency through a `GetItHelper`, constructing each class with its resolved constructor dependencies.

### How do I install injectable in a Dart project?

Add `injectable` and `get_it` to your dependencies and `injectable_generator` plus `build_runner` to your dev_dependencies, as shown in the README's pubspec snippet. The generator package and the runtime package are separate, since the repository holds an `injectable/` package and an `injectable_generator/` package.

### How do I run the injectable code generator?

The README documents the build_runner route, where the watch flag rebuilds on file changes and the build variant runs once and exits. It also describes an experimental LeanBuilder route, supported by injectable_generator 2.12.0 and later, which you enable by adding lean_builder to dev_dependencies and disabling the build_runner builders in build.yaml.

### What is the difference between getIt.get and generated accessors in injectable?

By default dependencies are resolved with the standard get_it syntax, `getIt.get<Type>()`. Setting `generateAccessors: true` in the `@InjectableInit` annotation makes the generator create getter methods instead, so call sites read as property access. The property defaults to false.

### Can I limit which folders the injectable generator scans?

Yes. The `generateForDir` property inside `@injectableInit` tells injectable which directories to generate for, and the README gives an example that only processes files inside the test folder.

## Sources

- [Issues](https://github.com/Milad-Akarie/injectable/issues)
- [License: MIT](https://github.com/Milad-Akarie/injectable/blob/master/LICENSE)
- [Milad-Akarie/injectable on GitHub](https://github.com/Milad-Akarie/injectable)
- [README](https://github.com/Milad-Akarie/injectable/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/milad-akarie-injectable
