<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Suhail</title>
    <description>The latest articles on DEV Community by Suhail (@suhail_5174a94aa879d67478).</description>
    <link>https://dev.to/suhail_5174a94aa879d67478</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3551233%2Fc1daf4c3-ee29-4134-ac2e-1e6ff406bc05.jpg</url>
      <title>DEV Community: Suhail</title>
      <link>https://dev.to/suhail_5174a94aa879d67478</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/suhail_5174a94aa879d67478"/>
    <language>en</language>
    <item>
      <title>Introducing Pure DI: Lightweight, Dependency Injection for Dart Without Compromise</title>
      <dc:creator>Suhail</dc:creator>
      <pubDate>Tue, 07 Oct 2025 13:36:38 +0000</pubDate>
      <link>https://dev.to/suhail_5174a94aa879d67478/introducing-pure-di-lightweight-dependency-injection-for-dart-without-compromise-4jin</link>
      <guid>https://dev.to/suhail_5174a94aa879d67478/introducing-pure-di-lightweight-dependency-injection-for-dart-without-compromise-4jin</guid>
      <description>&lt;h2&gt;
  
  
  Why Dependency Injection Matters in Dart and Flutter
&lt;/h2&gt;

&lt;p&gt;Modern software development is increasingly modular. Applications grow ever more complex, and organizing code to allow for reusability, scalability, and testability has become a necessity rather than a luxury. Dependency Injection (DI) is a well-established pattern that helps manage dependencies between components, making code easier to maintain and extend.&lt;br&gt;
Flutter developers may know packages like get_it or injectable, but these solutions often come with additional dependencies or are tightly coupled to the Flutter framework. For pure Dart applications—CLI tools, server-side backends, scripts—there hasn't been a lightweight, standalone solution. That’s why I created Pure DI, a minimal DI library built entirely in Dart, with no external dependencies whatsoever.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Makes Pure DI Unique?
&lt;/h2&gt;

&lt;p&gt;While designing Pure DI, my core goals were simplicity, zero dependency footprint, and maximum flexibility. Pure DI is not a framework; it’s a service locator and DI utility you can drop into any Dart project. Here are some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero External Dependencies&lt;/strong&gt;: No reliance on third-party libraries. You get complete control and minimal package size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Locator Pattern&lt;/strong&gt;: Offers a streamlined API for registering and retrieving dependencies—both singletons and factories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy Singletons&lt;/strong&gt;: Create instances only when needed, saving memory and startup time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped Services&lt;/strong&gt;: Manage sets of dependencies in grouped contexts (e.g., isolate resources for a request or background job).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Disposal&lt;/strong&gt;: Clean up resources effortlessly with the Disposable interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Type Safety&lt;/strong&gt;: Errors are caught at compile time, not runtime, reducing bugs and refactoring risks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform Support&lt;/strong&gt;: Use Pure DI in server, CLI, Flutter, and web applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Getting Started with Pure DI
&lt;/h2&gt;

&lt;p&gt;Setting up Pure DI is easy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add this line to your pubspec.yaml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  pure_di: ^0.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run dart pub get to fetch the package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering Services
&lt;/h2&gt;

&lt;p&gt;Here's a basic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:pure_di/pure_di.dart';

class DatabaseService {
  void query(String sql) =&amp;gt; print('Running: $sql');
}

class UserRepository {
  final DatabaseService db;
  UserRepository(this.db);

  void getUser(String id) =&amp;gt; db.query('SELECT * FROM users WHERE id = $id');
}

void main() {
  // Register database as a singleton
  locator.registerSingleton&amp;lt;DatabaseService&amp;gt;(DatabaseService());

  // Register repository as a factory that depends on DatabaseService
  locator.register&amp;lt;UserRepository&amp;gt;(() =&amp;gt; UserRepository(locator.get&amp;lt;DatabaseService&amp;gt;()));

  // Use your service
  final repo = locator.get&amp;lt;UserRepository&amp;gt;();
  repo.getUser('42');

  // When finished, dispose all resources
  locator.dispose();
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lazy Singletons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Need a resource that is expensive to create? Pure DI supports lazy singletons out of the box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;locator.registerLazySingleton&amp;lt;AnalyticsService&amp;gt;(() {
  print('Initializing Analytics');
  return AnalyticsService();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your service will only be instantiated when&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;locator.get&amp;lt;AnalyticsService&amp;gt;()&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 is called the first time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scoped Instances&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scopes are ideal for isolating resources, such as managing per-request state in a backend server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final requestScope = locator.createScope('request-123');
requestScope.registerSingleton&amp;lt;SessionService&amp;gt;(SessionService());

// In this scope, you can register and resolve dependencies independently
final session = requestScope.get&amp;lt;SessionService&amp;gt;();
Dispose scopes when done:
dart
locator.disposeScope('request-123');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Automatic Disposal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implement Pure DI's disposable interface to be cleaned up when unregistered or disposed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class FileService implements Disposable {
  final File file;
  FileService(this.file);

  @override
  void dispose() =&amp;gt; file.close();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you call locator.dispose() or locator.unregister(), the dispose method is automatically called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;CLI &amp;amp; Scripting: Pure DI is perfect for CLI tools and scripts where you want separation of concerns and simple DI without Flutter or web dependencies.&lt;/li&gt;
&lt;li&gt;Server-Side Dart: Great for creating web servers, handling API requests with scoped dependencies, database connections, and efficient resource management.&lt;/li&gt;
&lt;li&gt;Testable Code: Easily swap real services for mocks in unit and integration tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Register at Startup&lt;/strong&gt;: Register all your dependencies at the application entry point for clarity and maintainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Favour Factories for Stateful Objects&lt;/strong&gt;: Objects like HTTP clients or database connections often need fresh instances—use factories instead of singletons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Scopes&lt;/strong&gt;: Use scopes for lifecycles tied to specific tasks, requests, or background jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disposable Resources&lt;/strong&gt;: Implement the Disposable interface on classes that manage streams, files, or other external resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Project Walkthrough
&lt;/h2&gt;

&lt;p&gt;Suppose you’re building a Dart HTTP server. Here’s how Pure DI makes dependency management easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:pure_di/pure_di.dart';

void handleRequest(HttpRequest request) {
  final reqScope = locator.createScope('req-${request.hashCode}');
  reqScope.registerSingleton&amp;lt;RequestContext&amp;gt;(RequestContext(request));
  reqScope.register&amp;lt;UserController&amp;gt;(
    () =&amp;gt; UserController(reqScope.get&amp;lt;RequestContext&amp;gt;())
  );

  try {
    final controller = reqScope.get&amp;lt;UserController&amp;gt;();
    controller.process();
  } finally {
    locator.disposeScope('req-${request.hashCode}');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each request gets isolated, clean resources, which are cleaned up automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Test with Pure DI
&lt;/h2&gt;

&lt;p&gt;Testing is seamless. Reset your service locator on every test run for a clean environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:test/test.dart';
import 'package:pure_di/pure_di.dart';

void main() {
  setUp(() =&amp;gt; ServiceLocator.reset());

  test('UserRepository fetches user', () {
    locator.registerSingleton&amp;lt;DatabaseService&amp;gt;(MockDatabase());
    locator.register&amp;lt;UserRepository&amp;gt;(() =&amp;gt; UserRepository(locator.get&amp;lt;DatabaseService&amp;gt;()));

    final repo = locator.get&amp;lt;UserRepository&amp;gt;();
    expect(repo.getUser('1'), equals('user-details'));
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;Pure DI’s design is intentionally minimal—but future features may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named instance registration (instanceName)&lt;/li&gt;
&lt;li&gt;Asynchronous factory support (registerAsync())&lt;/li&gt;
&lt;li&gt;Lifecycle hooks for advanced initialization/disposal (onInit, onDispose)&lt;/li&gt;
&lt;li&gt;Optional diagnostics and runtime logging for development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow updates and contribute on &lt;a href="https://github.com/suhail7cb/pure_di" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you’re looking for a clean, lightweight dependency injection solution for Dart, Pure DI delivers simplicity and power without the baggage of external dependencies. Whether you’re writing scripts, services, or scalable backend applications, give Pure DI a try. It’s open source, free, and ready for your next project.&lt;/p&gt;

&lt;p&gt;Find Pure DI on pub.dev: &lt;a href="https://pub.dev/packages/pure_di" rel="noopener noreferrer"&gt;https://pub.dev/packages/pure_di&lt;/a&gt;&lt;br&gt;
Source &amp;amp; Issues: &lt;a href="https://github.com/suhail7cb/pure_di" rel="noopener noreferrer"&gt;https://github.com/suhail7cb/pure_di&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Made with ❤️ for the Dart community.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>architecture</category>
      <category>designpatterns</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
