Module 5
Topic 3

Dependency Injection

Managing dependencies in Flutter using GetIt โ€” a simple, type-safe service locator.

GetIt Package Flutter Architecture Guide
What Is Dependency Injection?

Dependency Injection (DI) is a design pattern where objects receive their dependencies from external sources rather than creating them internally. This makes your code more testable , maintainable , and flexible .

๐Ÿ’ก Key Concept

Instead of a class creating its own dependencies (tight coupling), dependencies are injected from the outside. This follows the Dependency Inversion Principle โ€” high-level modules should not depend on low-level modules, both should depend on abstractions.

โŒ Without DI (Tight Coupling)

  • Class creates its own dependencies
  • Hard to test (can't mock)
  • Difficult to change implementations
  • Violates Single Responsibility
  • Hidden dependencies

โœ… With DI (Loose Coupling)

  • Dependencies are passed from outside
  • Easy to test (mock dependencies)
  • Swap implementations easily
  • Clear, explicit dependencies
  • Follows SOLID principles
Service Locator vs Dependency Injection

While Dependency Injection and Service Locator are often used together, they serve different purposes:

Dependency Injection

  • Dependencies are passed via constructor
  • Dependencies are explicit and visible
  • Better for testability
  • Follows Inversion of Control
  • Dependencies are known at compile time

Service Locator (GetIt)

  • Dependencies are requested from a central registry
  • Dependencies can be resolved at runtime
  • Convenient for Flutter apps
  • No BuildContext required
  • O(1) lookups

โœ… The Flutter Approach

In Flutter, we often use GetIt (service locator) together with constructor injection . Services are registered with GetIt and then injected into classes that need them. This gives us the best of both worlds.

GetIt Package

GetIt is a simple, type-safe service locator for Dart and Flutter that makes dependency management simple. It gives you O(1) access to your objects from anywhere in your app โ€” no BuildContext required, no code generation.

โšก
Blazing Fast
O(1) lookups using Dart's native Maps
๐ŸŽฏ
Type Safe
Full compile-time type checking with generics
๐Ÿงช
Test Friendly
Easily swap real implementations for mocks
๐ŸŒณ
No BuildContext
Access objects from anywhere
๐Ÿ“ฆ
Framework Agnostic
Works in Flutter, Dart, server-side, CLI
๐Ÿ”ง
Zero Boilerplate
No code generation, no build_runner
Registration Types

GetIt provides three main registration types to control the lifetime of your dependencies:

๐Ÿ”’
Singleton
Create once, share everywhere
Perfect for services that maintain state
โณ
Lazy Singleton
Create on first access
Delays initialization until needed
๐Ÿญ
Factory
New instance every time
Stateless services or short-lived objects

๐Ÿ’ก Which Registration Type to Use?

  • Singleton โ€“ Services that maintain state (e.g., ApiClient, Database)
  • Lazy Singleton โ€“ Services that are expensive to create and may not always be used
  • Factory โ€“ Stateless services or objects with short lifetimes
  • Async โ€“ Dependencies that require async initialization (e.g., opening a database)
Accessing Dependencies

Once registered, dependencies can be accessed from anywhere in your app:

Testing with GetIt

One of the biggest advantages of dependency injection is testability . GetIt makes it easy to swap real implementations with mocks in tests.

โœ… Testing Benefits

  • Easy mocking โ€“ Replace real implementations with mocks
  • Test isolation โ€“ reset() clears all registrations between tests
  • Constructor injection โ€“ Use optional constructor parameters to inject mocks
  • No BuildContext โ€“ Test business logic without UI setup
Async Initialization

GetIt supports async initialization for dependencies that need to be set up asynchronously (e.g., opening a database, initializing a client).

Step-by-Step Implementation

Follow these steps to implement dependency injection in your Flutter app:

1.
Add GetIt dependency โ€“ Add get_it: ^8.0.2 to pubspec.yaml
2.
Create service locator file โ€“ Create lib/di/service_locator.dart
3.
Register dependencies โ€“ Register all your services, repositories, and use cases
4.
Initialize in main โ€“ Call setupServiceLocator() before runApp()
5.
Use in classes โ€“ Access dependencies with getIt<MyService>()
6.
Test โ€“ Use reset() and mock dependencies in tests
Common Mistakes
โŒ Mistake 1: Using service_role key in client-side code

The service_role key bypasses all security policies. Never expose it in your Flutter app.

โœ… Correct: Use anon key for client-side

Always use the anon key in your Flutter app. It's safe and respects Row Level Security policies.

โŒ Mistake 2: Not handling error responses properly

Failing to check for error responses or parse error messages correctly leads to confusing user experiences.

โœ… Correct: Parse and handle errors

Always check the response status code and parse error messages from the API response body.

โŒ Mistake 3: Hardcoding API URLs

Hardcoding API URLs makes it difficult to switch between development, staging, and production environments.

โœ… Correct: Use environment configuration

Use environment variables or configuration files to manage different API endpoints for different environments.

๐ŸŽฏ Key Takeaway

Dependency Injection is essential for building testable, maintainable Flutter apps. Using GetIt as a service locator provides a simple, type-safe way to manage dependencies with O(1) access from anywhere in your app. Register dependencies with the appropriate lifetime (Singleton, LazySingleton, Factory) and use constructor injection for maximum testability.