This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
get_it is a simple, fast (O(1)) Service Locator for Dart and Flutter projects. It provides dependency injection capabilities without code generation or reflection.
Key characteristics:
- Pure Dart package (no Flutter dependency required)
- No code generation or build_runner
- Extremely fast O(1) lookup using Dart Maps
- Type-safe with compile-time checking
- Supports multiple registration types: factories, singletons, lazy singletons
# Run all tests
flutter test
# Run specific test file
flutter test test/get_it_test.dart
flutter test test/async_test.dart
flutter test test/scope_test.dart
# Run tests with verbose output
flutter test --verbose# Analyze code
flutter analyze
# Format code
dart format .
# Publish dry-run (check before publishing)
flutter pub publish --dry-run# Run the example app
cd example
flutter run
# Return to package root
cd ..GetIt uses a hierarchical scope system with type-based registration:
- Base scope - Default scope, always exists
- Pushed scopes - Stack of scopes that can shadow lower scope registrations
- Type registration - Each type can have multiple registrations (unnamed or named instances)
_GetItImplementation- Singleton implementation of the GetIt interface_Scope- Represents a registration scope with its own object registry_ObjectRegistration<T, P1, P2>- Holds registration metadata and instances- Stores creation functions (sync/async, with/without parameters)
- Manages instance lifecycle (creation, disposal, ready state)
- Tracks dependencies and ready signals
_TypeRegistration- Maps types to their object registrations within a scope
enum ObjectRegistrationType {
alwaysNew, // Factory - creates new instance on every get()
constant, // Singleton - single instance passed at registration
lazy, // Lazy singleton - created on first get()
cachedFactory, // Factory with weak reference caching
}When get<T>() is called:
- Start from topmost scope
- Look for type
Twith optionalinstanceName - If not found, move down to parent scope
- Repeat until found or base scope exhausted
- If still not found, throw error
This enables shadowing: registering the same type in a higher scope overrides lower scope registrations.
GetIt supports complex async initialization patterns:
registerSingletonAsync- Async factory that runs immediatelyregisterLazySingletonAsync- Async factory that runs on first accessdependsOn- Declare initialization dependenciessignalsReady- Manual ready signaling for complex initializationallReady()- Wait for all async singletons to completeisReady<T>()- Wait for specific singleton to be ready
Scopes enable managing different object lifetimes (e.g., app-level vs session-level):
// Push new scope (e.g., on user login)
GetIt.I.pushNewScope(scopeName: 'userSession');
// Register objects in this scope
GetIt.I.registerSingleton<UserService>(LoggedInUserService());
// Pop scope when done (e.g., on logout) - disposes all objects in scope
await GetIt.I.popScope();Objects can be disposed when unregistered or when scopes are popped:
- Disposable interface - Implement
Disposable.onDispose()for automatic disposal - Dispose function - Pass
disposeparameter during registration - Scope dispose - Pass
disposecallback when pushing scope
- Regular instances stored in
_ObjectRegistration._instance - Weak references supported via
weakReferenceInstancefor cached factories and lazy singletons - Instance retrieval checks weak reference first, then falls back to strong reference
- Each registration has a
_readyCompleterthat tracks initialization state shouldSignalReadyflag determines if manual signaling is requiredpendingResultstores the Future for ongoing async creationobjectsWaitingtracks which types depend on this registration
For recursive scenarios (e.g., pushing same page multiple times):
registerSingletonIfAbsentincrements reference count if already registeredreleaseInstancedecrements count and only disposes when reaching zero- Prevents premature disposal in nested navigation scenarios
Each test file focuses on a specific feature area:
get_it_test.dart- Core registration and retrievalasync_test.dart- Async singletons and initializationscope_test.dart- Scope management and shadowingskip_double_registration_test.dart- Double registration behavior
void main() {
setUp(() async {
// Reset GetIt before each test
await GetIt.I.reset();
});
test('description', () {
// Test code
});
}// Test factory registration
GetIt.I.registerFactory<MyClass>(() => MyClass());
expect(GetIt.I<MyClass>(), isA<MyClass>());
expect(GetIt.I<MyClass>(), isNot(same(GetIt.I<MyClass>()))); // Different instances
// Test singleton registration
final instance = MyClass();
GetIt.I.registerSingleton(instance);
expect(GetIt.I<MyClass>(), same(instance)); // Same instance
// Test async initialization
GetIt.I.registerSingletonAsync<MyService>(() async => MyService());
await GetIt.I.allReady();
expect(GetIt.I.isReadySync<MyService>(), true);lib/
get_it.dart # Public API and interfaces
get_it_impl.dart # Implementation (part of get_it.dart)
test/
get_it_test.dart # Core functionality tests
async_test.dart # Async initialization tests
scope_test.dart # Scope management tests
example/
lib/main.dart # Example Flutter app
- Type-based access - Primary access via generics:
GetIt.I<MyType>() - Optional callable class - Can call GetIt instance directly:
getIt<MyType>() - Named registrations - Optional
instanceNamefor multiple instances of same type - Runtime type support - Rare
typeparameter for dynamic type access - No BuildContext required - Unlike InheritedWidget/Provider, accessible from anywhere
final getIt = GetIt.instance;
void setupLocator() {
// Register services
getIt.registerLazySingleton<ApiService>(() => ApiService());
getIt.registerSingleton<ConfigService>(ConfigService());
// Register with dependencies
getIt.registerSingletonWithDependencies<AppModel>(
() => AppModel(getIt<ApiService>()),
dependsOn: [ApiService],
);
}// In production code
class UserManager {
final ApiService api;
UserManager({ApiService? api}) : api = api ?? GetIt.I<ApiService>();
}
// In tests - inject mock
final mockApi = MockApiService();
final userManager = UserManager(api: mockApi);// Enable feature first
getIt.enableRegisteringMultipleInstancesOfOneType();
// Register multiple implementations
getIt.registerLazySingleton<PaymentProcessor>(() => StripeProcessor());
getIt.registerLazySingleton<PaymentProcessor>(() => PayPalProcessor());
// Get all implementations
final processors = getIt.getAll<PaymentProcessor>();Current version: 8.2.0
See CHANGELOG.md for detailed version history and breaking changes.
- watch_it - State management built on get_it (reactive observers)
- command_it - Command pattern with automatic loading/error states
- listen_it - ValueListenable operators for reactive programming
- README.md - Comprehensive usage documentation
- https://flutter-it.dev - Official documentation site
- https://www.burkharts.net/apps/blog/one-to-find-them-all-how-to-use-service-locators-with-flutter/ - Detailed blog post
- don't commit without first run dart analyse to make sure no warnings exist