simple_service_locator 0.3.0
simple_service_locator: ^0.3.0 copied to clipboard
Lightweight hierarchical dependency injection for Flutter with scoped lifecycles and tagged lookups.
0.3.0 #
Breaking Changes #
DiScope.open(..., lookupParentScope: name)now throwsScopeNotFoundExceptionwhennameis given but does not resolve. It previously fell back toRootScope, so a misspelled parent name silently produced a scope wired to the wrong parent and surfaced much later as an unrelatedInstanceNotFoundException. Omitting the argument still falls back toRootScope.contains(),containsType(),isRegistered()andisRegisteredType()now throwStateErroron a closed scope, like every other lookup. They previously answeredfalse, which was indistinguishable from a live scope that simply had no such registration.find(onMany: ...)withoutsearchDescendants: trueis now an assertion error.onManyresolves ambiguity between descendant scopes, so passing it alone was silently ignored.- Registration and lookup keys are now non-nullable. Every generic entry point
(
put,putLazy,putLazyAs,replace,replaceLazy,replaceLazyAs,find,call,findInChildren,contains,isRegistered,locateScopes,evict) is bound asT extends Object, soput<T?>(...)andfind<T?>()no longer compile. A nullable variable passed through inference is rejected as well. This keeps a missing dependency anInstanceNotFoundExceptioninstead of a silentnull. Model "configured, but absent" with an explicit wrapper or sentinel value.
Fixed #
replace()andreplaceLazyAs()are now atomic. All target registration keys are validated before anything is removed, so a rejected replace no longer destroys and disposes the previous registration.find()no longer masks errors raised inside lazy factories. AnInstanceNotFoundExceptionthrown by a factory that cannot resolve its own dependency now propagates with its real requested type instead of being reported as a miss of the outer type. Ancestor and descendant lookups use an internal lookup result instead of exceptions as control flow.ScopeProviderStateno longer throwsDuplicateScopeExceptionwhen Flutter replaces a widget's element. The scope is released indeactivate()and reopened inactivate(), so the scope name is free before a replacing state runsinitState().locateScopes()andverboseTree()no longer materialize lazy registrations, matching the documented guarantee that diagnostics do not invoke factories.- A lazy factory that throws now leaves its registration unmaterialized and retryable instead of caching a partial state.
DiElementtracks materialization explicitly instead of treatingnullas "absent", so an unused lazy registration is no longer materialized just to be disposed, and disposal of a materialized lazy still runs exactly once.- Listener reentrancy is now safe.
ChangeNotifierdispatches listeners synchronously, so a listener that opened a child scope re-entered itself before the first invocation returned and threwDuplicateScopeException. Nested notifications are now suppressed; the mutation still happens and the outer dispatch reports the final state. close()no longer notifies its own listeners and no longer tripsChangeNotifier's "dispose() during notifyListeners()" assertion, so a scope can be closed from its own listener. Parent scopes are still notified.verboseTree()'s misspelledverboseInstacesparameter is now@Deprecated; useverboseInstances. The internal recursion no longer passes the deprecated name.DiScope.toString()is now shallow. It previously printed parents and children recursively, so a single scope dumped the whole tree from both directions.DuplicateInstanceExceptionno longer advises a nonexistentreplace = trueparameter; it points toreplace<T>().
Changed #
ScopeProviderState.scopeis now a getter backed by a nullable field and throws a descriptiveStateErrorwhen read beforeinjectDependencies()or after disposal.scopeNameis documented as read-once for the life of the state.injectDependencies()documentation corrected: it is called aftersuper.initState(), not before.
Internal #
- Split
lib/src/di_scope.dart:DiElementand theDisposeCallbacktypedef moved tolib/src/di_element.dart, and the scope-teardown error collector tolib/src/failure_collector.dart(internal, not exported). The public API surface is unchanged —DiElementandDisposeCallbackare still exported frompackage:simple_service_locator/simple_service_locator.dart.
Notes #
- Dependencies registered in
injectDependencies()are recreated when a state is reinserted through aGlobalKeymove, because the scope is closed on deactivate. Dependencies that must survive such a move belong in a parent scope.
0.2.0 #
Added #
putLazyAs<A, B>()andreplaceLazyAs<A, B>()for lazy registrations with explicit abstraction and implementation keys.
Changed #
- Dependency lookup now resolves only explicit registration keys.
put<A>(B())still registers bothAand concrete typeBby default.DiScope.close()now completes descendant and registration cleanup before rethrowing the first disposal or listener error.- Lazy factories are invoked only by resolution, not by registration checks, replacements, or diagnostics.
- Scope names must be non-empty.
- Closed scopes can no longer be reopened with
reset(). - Enabled strict cast, inference, and raw-type checks in the Dart analyzer.
Fixed #
evictnow supports removal through a concrete runtime-type alias.- Missing-instance errors now report the scope where lookup started.
- Widget scope disposal always invokes
State.dispose(). - Scope-tree traversal uses FIFO queues instead of repeatedly shifting lists.
- Direct
RootScope.close()now rejects the same way ascloseScope('RootScope').
Breaking Changes #
- Removed implicit assignable-type lookup. After
put<B>(B()),find<A>()now throws unlessAwas explicitly registered. - Removed the
exactTypeMatchparameter fromcall,find,findInChildren, andlocateScopes; all lookups now use explicit keys. reset()now closes child scopes and disposes local registrations before keeping the current scope reusable.DiScopeandDiElementnow use identity equality. The previous structural equality and mutable hash codes were removed.- Removed
ScopeConsumerStatefrom the public API. Pass a [DiScope] explicitly or use an explicit lookup by globally unique scope name. ScopeProviderState.scopeNameis now required.parentScopecan be overridden to attach to an explicit parent scope.
0.1.5 #
Added #
DiScopenow extendsChangeNotifierand notifies listeners after scope and registration changes.
0.1.4 #
Added #
- Widget tests covering scope provision, scope consumption, and missing scope errors.
Changed #
- Updated public library docs to reference
ScopeProviderStateandScopeConsumerState.
Breaking Changes #
ScopedWidgetStatewas split intoScopeProviderStateandScopeConsumerState.- Update widget mixin usages and imports to the new names.
0.1.3 #
0.1.2 #
Added #
- Child-scope-only dependency lookup:
DiScope.findInChildren<T>({tag, exactTypeMatch, onMany})- throws
MultipleInstancesFoundExceptionwhen more than one child scope matches and noonManyresolver is provided
- Optional child-tree lookup on
find:DiScope.find<T>({..., searchDescendants: true, onMany})- keeps default behavior unchanged when omitted (
false)
- Scope discovery by local registrations:
DiScope.locateScopes<T>({tag, exactTypeMatch, includeSelf})DiScope.locateScopesByTag(tag, {includeSelf})
Changed #
- Removed internal
_rootScope()traversal and inlined duplicate scope checks againstRootScope. - Added regression tests for child lookup:
- successful resolution in nested descendants
- not-found behavior
- ambiguity details in
MultipleInstancesFoundException find(..., searchDescendants: true)and default non-descendant behavior
0.1.1 #
Added #
- Added publish-ready package example:
example/simple_service_locator_example.dart
- Expanded dartdoc coverage for public API:
- exception constructors
DiElementpublic fields
Changed #
- Improved
DiScope.verboseTreeparameter naming:- added
verboseInstances - kept
verboseInstacesfor backward compatibility
- added
0.1.0 #
Added #
- Regression test for abstraction-chain resolution:
- register as base contract
- resolve as intermediate contract
- resolve as concrete implementation
- Pub.dev-oriented README with practical setup and usage examples.
AGENTS.mdwith repository contribution/release notes for coding agents.
Changed #
- Package renamed to
simple_service_locator. - Primary public entrypoint is now
lib/simple_service_locator.dart. - Updated package imports in library sources and tests to:
package:simple_service_locator/simple_service_locator.dart
- Fixed descendant lookup in
DiScope.find<T>()for aliased runtime registrations. - Improved
pubspec.yamlmetadata for pub.dev (description, links, topics). - Lint cleanup across source/tests.
Breaking Changes #
- Import path changed:
package:flutter_di/flutter_di.dart- to
package:simple_service_locator/simple_service_locator.dart
- Package dependency name changed:
flutter_di- to
simple_service_locator
0.0.1 #
- Initial release.