Dependency Injection in Flutter: A Beginner’s Guide

Dependency Injection (DI) is a crucial concept for managing app dependencies efficiently, Dependency Injection in Flutter makes your applications more scalable and maintainable. This guide will help you understand DI and how to implement it in Flutter.


What is Dependency Injection?

Dependency Injection is a design pattern that allows you to separate the creation of dependencies from their usage, making your code more modular, testable, and maintainable. In the context of Flutter development, it helps manage state and services efficiently, improving scalability and enabling better testability by decoupling the components of your app. Instead of a class creating its dependencies, these are provided by an external entity, enhancing testability and modularity.


Why Use Dependency Injection in Flutter?

  1. Improves Code Maintainability: By decoupling dependencies, your code becomes easier to manage and refactor.
  2. Enhances Testability: Mock dependencies can be injected during testing.
  3. Simplifies State Management: Shared dependencies like services and repositories can be efficiently managed.

Top Dependency Injection Packages for Flutter in 2025

  1. Provider: A lightweight and straightforward DI tool.
  2. Get It: A service locator that allows you to register and retrieve objects.
  3. Riverpod: A robust and modern state management tool with DI capabilities.

Step-by-Step Implementation

Step 1: Install Dependencies

Add the desired DI package to your pubspec.yaml file. We’ll use Get It in this tutorial.

dependencies:
  get_it: ^7.6.0

Run flutter pub get to install the package.


Step 2: Set Up the Dependency Injector

Create a file named service_locator.dart to register your dependencies.

import 'package:get_it/get_it.dart';

// Initialize GetIt instance
final GetIt getIt = GetIt.instance;

// Register services
void setupLocator() {
  getIt.registerSingleton<CounterService>(CounterService());
}

// Example Service
class CounterService {
  int value = 0;

  void increment() => value++;
}

Step 3: Register Dependencies

Call setupLocator() in the main() function before running the app.

import 'package:flutter/material.dart';
import 'service_locator.dart';

void main() {
  setupLocator(); // Register dependencies
  runApp(MyApp());
}

Step 4: Access Dependencies

Retrieve dependencies wherever needed in your app using Get It.

import 'package:flutter/material.dart';
import 'service_locator.dart';

class HomePage extends StatelessWidget {
  final CounterService counterService = getIt<CounterService>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Dependency Injection')),
      body: Center(
        child: Text('Counter: ${counterService.value}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counterService.increment();
          print('Counter incremented: ${counterService.value}');
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Advanced DI with Scoped Instances

You can register factories or lazy singletons for scoped or delayed dependency creation.

getIt.registerFactory(() => SomeService());
getIt.registerLazySingleton(() => AnotherService());

Testing with Dependency Injection

Mock services can replace real services during testing by overriding the registration.

void setupTestLocator() {
  getIt.registerSingleton<CounterService>(MockCounterService());
}

class MockCounterService extends CounterService {
  @override
  void increment() {
    value += 2; // Mocked behavior
  }
}

Conclusion

Dependency Injection is a powerful pattern that simplifies app development and testing in Flutter. By using packages like Get It or Provider, you can efficiently manage your app’s dependencies and make your code cleaner and more modular.

Ready to try Dependency Injection? Share your experiences or questions in the comments!