get_it_lazyload

Pub Version License: MIT Test Coverage

A Flutter package providing optimized lazy loading extensions for GetIt dependency injection with comprehensive support for all registration types and async dependencies.

✨ Features

  • πŸš€ Optimized Lazy Loading: Efficient dependency registration with automatic instance creation
  • πŸ”„ Multiple Registration Types: Support for factory, singleton, and lazy singleton patterns
  • ⚑ Async Support: Full support for asynchronous dependency initialization
  • 🎯 Performance Optimized: Minimal overhead with smart registration checking
  • πŸ›‘οΈ Type Safe: Full type safety with generic methods
  • πŸ§ͺ Well Tested: 100% test coverage ensuring reliability

πŸ“¦ Installation

Add this to your package's pubspec.yaml file:

dependencies:
  get_it_lazyload: ^0.0.3
  get_it: ^8.2.0

Then run:

flutter pub get

πŸš€ Quick Start

import 'package:get_it/get_it.dart';
import 'package:get_it_lazyload/get_it_lazyload.dart';

final getIt = GetIt.instance;

// Register a service with automatic lazy loading
final service = getIt.getOrRegister<MyService>(
  () => MyService(), 
  RegisterAs.singleton
);

πŸ“š Usage

Basic Usage

import 'package:get_it/get_it.dart';
import 'package:get_it_lazyload/get_it_lazyload.dart';

final getIt = GetIt.instance;

// Register a service with automatic lazy loading
getIt.getOrRegister<MyService>(() => MyService(), RegisterAs.singleton);

// Get the service (will be created if not registered)
final service = getIt.get<MyService>();

Lazy Loading Pattern for Use Cases and Repositories

The main benefit of this package is avoiding eager registration of dependencies at app startup. Instead, dependencies are loaded and registered only when they're actually required:

// UseCase with Factory pattern (creates new instance each time)
class AnonymousAuthUseCase {
  final AuthRepository _repository;

  AnonymousAuthUseCase(this._repository);

  Future<FirebaseUserModel> execute() {
    return _repository.signInAnonymously();
  }

  static AnonymousAuthUseCase get() {
    return GetIt.instance.getOrRegister<AnonymousAuthUseCase>(
        () => AnonymousAuthUseCase(AuthRepository.get()), 
        RegisterAs.factory);
  }
}

// Repository with LazySingleton pattern (reuses instance)
class AuthRepository {
  final DatabaseService _database;

  AuthRepository(this._database);

  Future<FirebaseUserModel> signInAnonymously() async {
    return _database.createAnonymousUser();
  }

  static AuthRepository get() {
    return GetIt.instance.getOrRegister<AuthRepository>(
        () => AuthRepository(DatabaseService.get()), 
        RegisterAs.lazySingleton);
  }
}

// Service with Singleton pattern (created immediately when first accessed)
class DatabaseService {
  static DatabaseService get() {
    return GetIt.instance.getOrRegister<DatabaseService>(
        () => DatabaseService(), 
        RegisterAs.singleton);
  }
}

Benefits of this pattern:

  • βœ… Dependencies are created only when needed
  • βœ… No eager registration at app startup
  • βœ… Automatic dependency resolution
  • βœ… Clean separation of concerns
  • βœ… Easy to test and mock

Registration Types

Singleton

Creates instance immediately and reuses it:

getIt.getOrRegister<ConfigService>(
  () => ConfigService(), 
  RegisterAs.singleton
);

Factory

Creates new instance each time:

getIt.getOrRegister<DataModel>(
  () => DataModel(), 
  RegisterAs.factory
);

Lazy Singleton

Creates instance on first use, then reuses it:

getIt.getOrRegister<ExpensiveService>(
  () => ExpensiveService(), 
  RegisterAs.lazySingleton
);

Async Dependencies

Async Factory

final service = await getIt.getOrRegisterAsync<AsyncService>(
  () async => await AsyncService.initialize(), 
  RegisterAs.factoryAsync
);

Async Lazy Singleton

final service = await getIt.getOrRegisterAsync<AsyncService>(
  () async => await AsyncService.initialize(), 
  RegisterAs.lazySingletonAsync
);

Async Singleton

final service = await getIt.getOrRegisterAsync<AsyncService>(
  () async => await AsyncService.initialize(), 
  RegisterAs.singletonAsync
);

Convenience Methods

For common use cases, you can use these convenience methods:

// Factory registration
getIt.getOrRegisterFactory<DataModel>(() => DataModel());

// Lazy singleton registration
getIt.getOrRegisterLazySingleton<Service>(() => Service());

// Singleton registration
getIt.getOrRegisterSingleton<Config>(() => Config());

// Async variants
await getIt.getOrRegisterFactoryAsync<AsyncService>(
  () async => await AsyncService.initialize()
);

await getIt.getOrRegisterLazySingletonAsync<AsyncService>(
  () async => await AsyncService.initialize()
);

await getIt.getOrRegisterSingletonAsync<AsyncService>(
  () async => await AsyncService.initialize()
);

πŸ“– API Reference

RegisterAs Enum

  • RegisterAs.singleton - Creates instance immediately and reuses it
  • RegisterAs.factory - Creates new instance each time
  • RegisterAs.lazySingleton - Creates instance on first use, then reuses it
  • RegisterAs.factoryAsync - Creates new async instance each time
  • RegisterAs.lazySingletonAsync - Creates async instance on first use, then reuses it
  • RegisterAs.singletonAsync - Creates async instance immediately and reuses it

GetItExtension Methods

  • getOrRegister<T>() - Gets or registers a dependency
  • getOrRegisterAsync<T>() - Gets or registers an async dependency
  • getOrRegisterFactory<T>() - Gets or registers as factory
  • getOrRegisterLazySingleton<T>() - Gets or registers as lazy singleton
  • getOrRegisterSingleton<T>() - Gets or registers as singleton
  • getOrRegisterFactoryAsync<T>() - Gets or registers as async factory
  • getOrRegisterLazySingletonAsync<T>() - Gets or registers as async lazy singleton
  • getOrRegisterSingletonAsync<T>() - Gets or registers as async singleton

πŸš€ Performance Benefits

  • Single Registration Check: Only checks if dependency is registered once per call
  • Optimized Registration: Direct function registration without extra wrapping
  • Lazy Initialization: Dependencies are only created when needed
  • Memory Efficient: Reuses instances where appropriate
  • Startup Performance: No eager registration means faster app startup

⚠️ Important Notes

Async Registration Behavior

Due to GetIt's internal implementation of async registrations, there are some limitations to be aware of:

  1. Async Factory: Works as expected - creates new instances each time
  2. Async Lazy Singleton: May create new instances on subsequent calls due to GetIt's async registration behavior
  3. Async Singleton: Works as expected - creates instance once and reuses it

For the most reliable behavior with async dependencies, consider using RegisterAs.singletonAsync when you need consistent instance reuse.

Best Practices

  • Use sync registration methods when possible for better performance
  • Reserve async registration for dependencies that truly require async initialization
  • Test your dependency injection setup thoroughly, especially with async services
  • Consider using RegisterAs.singletonAsync for services that should be shared across your app
  • Use the lazy loading pattern for UseCases and Repositories to avoid eager registration
  • Only register core dependencies (like config) at app startup

πŸ“± Example

Check out the example app for a complete working demonstration of the lazy loading pattern.

import 'package:get_it/get_it.dart';
import 'package:get_it_lazyload/get_it_lazyload.dart';

class MyApp {
  static void setupDependencies() {
    final getIt = GetIt.instance;
    
    // Only register core dependencies that are needed immediately
    getIt.getOrRegister<ConfigService>(
      () => ConfigService(), 
      RegisterAs.singleton
    );
    
    // Other dependencies will be registered lazily when first accessed
    // No need to register AuthRepository, AnonymousAuthUseCase, etc. here
  }
}

πŸ§ͺ Testing

The package includes comprehensive tests with 100% coverage:

# Run tests
flutter test

# Run tests with coverage
flutter test --coverage

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Built on top of the excellent GetIt package
  • Inspired by clean architecture principles and dependency injection best practices
  • Community feedback and contributions

πŸ“Š Package Health

  • βœ… Test Coverage: 100%
  • βœ… Static Analysis: Passing
  • βœ… Documentation: Complete
  • βœ… Examples: Included
  • βœ… License: MIT
  • βœ… Platform Support: Flutter (iOS, Android, Web, Desktop)

Libraries

get_it_lazyload
A Flutter package providing optimized lazy loading extensions for GetIt dependency injection.