Monday, January 26, 2026
HomeFlutterWhat is dependency injection in flutter

What is dependency injection in flutter

Dependency Injection (DI) in Flutter (and more generally in software development) is a design pattern used to make your code more modular, testable, and maintainable by separating object creation from object usage.

Let’s break that down 👇


🧩 What It Means

Normally, when a class depends on another class, you might write something like:

class ApiService {
  void fetchData() => print('Fetching data...');
}

class UserRepository {
  final ApiService apiService = ApiService(); // 👈 creating dependency here

  void getUser() {
    apiService.fetchData();
  }
}

Here, UserRepository creates its own ApiService.
That makes it hard to test (you can’t easily mock ApiService) and hard to change (if ApiService needs parameters, every place that creates it must change).


💡 With Dependency Injection

Instead, you inject the dependency from outside:

class UserRepository {
  final ApiService apiService;

  UserRepository(this.apiService); // 👈 dependency is injected

  void getUser() {
    apiService.fetchData();
  }
}

Now you can easily provide any version of ApiService you like (real, mock, etc.) when creating UserRepository:

final userRepo = UserRepository(ApiService());

Or in tests:

final userRepo = UserRepository(MockApiService());

🧰 Common Ways to Do Dependency Injection in Flutter

Flutter doesn’t have built-in DI, but there are several popular packages that make it easy:

1. provider

Simplest way — you can provide dependencies using the widget tree.

void main() {
  runApp(
    Provider(
      create: (_) => ApiService(),
      child: MyApp(),
    ),
  );
}

Then retrieve it anywhere in the widget tree:

final api = Provider.of<ApiService>(context, listen: false);

2. get_it (Service Locator Pattern)

A lightweight and widely used DI library.

final getIt = GetIt.instance;

void setup() {
  getIt.registerLazySingleton<ApiService>(() => ApiService());
  getIt.registerFactory<UserRepository>(() => UserRepository(getIt<ApiService>()));
}

Then anywhere in your code:

final userRepo = getIt<UserRepository>();

This avoids passing dependencies down the widget tree.


3. riverpod

A more modern and powerful alternative to provider, also supports DI patterns:

final apiServiceProvider = Provider((ref) => ApiService());
final userRepoProvider = Provider((ref) => UserRepository(ref.read(apiServiceProvider)));

Then use it like this:

final userRepo = ref.read(userRepoProvider);

✅ Benefits of Dependency Injection

  • Easier testing: You can mock dependencies easily.
  • Better code reuse: Swap out implementations (e.g., fake API vs real API).
  • Decoupling: Classes don’t depend on specific implementations.
  • Cleaner architecture: Promotes SOLID principles.

⚠️ Common Pitfalls

  • Overusing DI can make small apps more complex than necessary.
  • Misusing Service Locators (get_it) can lead to hidden dependencies.
  • It’s best combined with architecture patterns like MVVM, Clean Architecture, or Bloc.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments