simple_service_locator 0.1.3 copy "simple_service_locator: ^0.1.3" to clipboard
simple_service_locator: ^0.1.3 copied to clipboard

Lightweight hierarchical dependency injection for Flutter with scoped lifecycles and tagged lookups.

simple_service_locator #

Lightweight hierarchical dependency injection for Flutter.

simple_service_locator is built around explicit runtime scopes (DiScope) with:

  • parent/child scope resolution
  • tagged registrations
  • lazy factories
  • deterministic disposal
  • type-safe lookup by abstraction and implementation

Why This Package #

Useful when you need:

  • app-wide services in a root scope
  • feature/page-local overrides in child scopes
  • predictable disposal on scope close
  • direct control without code generation

Features #

  • Register direct instances: put<T>(), replace<T>()
  • Register lazy instances: putLazy<T>(), replaceLazy<T>()
  • Resolve dependencies: find<T>() or scope<T>()
  • Resolve in descendants only: findInChildren<T>()
  • Support abstraction + implementation lookup for same object
  • Support tagged registrations (tag)
  • Remove instances (evict<T>())
  • Scope tree lookup by name (locateScope)
  • Scope lookup by registration/type tag (locateScopes, locateScopesByTag)
  • Widget helper mixin (ScopedWidgetState)

Getting Started #

dependencies:
  simple_service_locator: ^0.1.0

Quick Usage #

import 'package:simple_service_locator/simple_service_locator.dart';

abstract interface class UserRepository {}

class UserRepositoryFirebase implements UserRepository {}

void setup() {
  RootScope.replace<UserRepository>(UserRepositoryFirebase());
}

void useIt() {
  final userRepository = RootScope.find<UserRepository>();
  final sameInstanceByImpl = RootScope.find<UserRepositoryFirebase>();
}

Scopes And Overrides #

final appScope = DiScope.open('app');
appScope.put<ApiClient>(ApiClientProd());

final featureScope = DiScope.open('feature', knownParentScope: appScope);
featureScope.put<ApiClient>(ApiClientMock()); // local override

final fromFeature = featureScope.find<ApiClient>(); // ApiClientMock
final fromApp = appScope.find<ApiClient>(); // ApiClientProd

appScope.close(); // closes children and disposes registered instances

Tags #

RootScope.put<String>('https://prod.example.com', tag: 'prod');
RootScope.put<String>('https://staging.example.com', tag: 'staging');

final prod = RootScope.find<String>(tag: 'prod');
final staging = RootScope.find<String>(tag: 'staging');

Lazy Registration #

RootScope.putLazy<ExpensiveService>(() => ExpensiveService());
final service = RootScope.find<ExpensiveService>(); // created on first access

Lookup Behavior #

  • find<T>() (default exactTypeMatch: false) can resolve descendants by runtime type.
  • find<T>(exactTypeMatch: true) restricts lookup to exact registered type keys.
  • find<T>(searchDescendants: true, onMany: ...) also searches child scopes.
  • findInChildren<T>(onMany: ...) searches only child scopes; without onMany it throws MultipleInstancesFoundException on ambiguous matches.
  • put<T>(instance) registers under T, and by default also under instance.runtimeType.
  • Set registerRuntimeType: false to disable runtime-type alias registration.

Advanced Scope Queries #

final root = DiScope.open('root');
final auth = DiScope.open('auth', knownParentScope: root);
final profile = DiScope.open('profile', knownParentScope: root);

auth.put<ApiClient>(ApiClientProd(), tag: 'prod');
profile.put<ApiClient>(ApiClientMock(), tag: 'mock');

final prodOnlyScopes = root.locateScopes<ApiClient>(tag: 'prod');
final taggedScopes = root.locateScopesByTag('mock');

Flutter Widget Scope Helper #

class ProfilePageState extends State<ProfilePage> with ScopedWidgetState<ProfilePage> {
  @override
  String get scopeName => 'profile_scope';

  @override
  void injectDependencies() {
    super.injectDependencies();
    scope.put<ProfileViewModel>(ProfileViewModel(RootScope.find()));
  }
}

Cases That Fit Pub.dev Consumers Well #

  • multi-environment service wiring (prod/stage/dev with tags)
  • per-feature service overrides in large apps
  • test-friendly replacement of interfaces with fakes
  • explicit lifecycle control for expensive resources
  • no-codegen DI for small and medium Flutter projects

Notes #

  • If an instance is missing, InstanceNotFoundException includes requested type, scope, and tag.
  • Closing a scope disposes registered instances once, even when they were registered under multiple type aliases.
0
likes
150
points
13
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Lightweight hierarchical dependency injection for Flutter with scoped lifecycles and tagged lookups.

Repository (GitHub)
View/report issues

Topics

#dependency-injection #flutter #ioc #scoped

License

MIT (license)

Dependencies

flutter

More

Packages that depend on simple_service_locator