CodeBlanks Logo Icon CodeBlanks

Dependency injection

Learn dependency injection.

Dependency injection

Dependency injection (DI) means an object receives its dependencies from outside instead of creating them. They are injected via the constructor; this makes testing and swapping easier.

Example: class OrderService { final PaymentService payment; OrderService(this.payment); }. OrderService does not create PaymentService itself; it receives it. In tests a fake service is provided.

With DI you depend on an interface, not a concrete class. PaymentService interface can take StripePayment or PaypalPayment; OrderService does not care.

The DI container

A Container registers and resolves dependencies. container.register('math', MathService()) registers; container.resolve<MathService>('math') resolves.

The container also manages lifetime: a new instance per call (transient) or a single instance (singleton). This collects object creation logic in one place.

In large apps the container registers all dependencies at startup. At runtime only resolve is called; creation details stay hidden.

Practice Define PaymentService and OrderService

Define an abstract class PaymentService with bool pay(int amount);. Define a class StripePayment implements PaymentService with @override bool pay(int amount) => true;. Define a class OrderService with final PaymentService payment; OrderService(this.payment); String checkout(int amount) { final ok = payment.pay(amount); return ok ? 'paid: $amount' : 'failed'; }. In main create final order = OrderService(StripePayment()); and add print(order.checkout(100));.

Starter code

main.dart
void main() {
  // define an abstract PaymentService (pay method)
  // define a StripePayment (applies the interface)
  // define an OrderService class (payment field, checkout method)
  // create an OrderService; print checkout(100)
}

Sample solution

main.dart
abstract class PaymentService {
  bool pay(int amount);
}

class StripePayment implements PaymentService {
  @override
  bool pay(int amount) => true;
}

class OrderService {
  final PaymentService payment;
  OrderService(this.payment);
  String checkout(int amount) {
    final ok = payment.pay(amount);
    return ok ? 'paid: $amount' : 'failed';
  }
}

void main() {
  final order = OrderService(StripePayment());
  print(order.checkout(100));
}

Expected output

paid: 100

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What does dependency injection make easier?

  • UI drawing
  • File reading
  • Testing and swapping Correct answer
  • Network listening

Explanation: DI makes providing fake dependencies in tests and swapping easier.

2 Match the DI parts

Term Meaning
constructor Injection point
inject Provide dependency
Container Provider
resolve Resolve

3 Constructor injection

Question
class OrderService {
  final ____ payment;
  OrderService(this.payment);
}
Answer
class OrderService {
  final PaymentService payment;
  OrderService(this.payment);
}

Explanation: the dependency is declared with its interface type.

All lessons in Expert