simple_service_locator 0.1.3
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>()orscope<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>()(defaultexactTypeMatch: 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; withoutonManyit throwsMultipleInstancesFoundExceptionon ambiguous matches.put<T>(instance)registers underT, and by default also underinstance.runtimeType.- Set
registerRuntimeType: falseto 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,
InstanceNotFoundExceptionincludes requested type, scope, and tag. - Closing a scope disposes registered instances once, even when they were registered under multiple type aliases.