├── .gitignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example └── example.dart ├── lib ├── injector.dart └── src │ ├── injector.dart │ ├── injector_exception.dart │ └── type_factory.dart ├── pre-publish-checks.cmd ├── pubspec.lock ├── pubspec.yaml └── test └── flutter_simple_dependency_injection_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | .idea/ 7 | 8 | build/ 9 | ios/.generated/ 10 | ios/Flutter/Generated.xcconfig 11 | ios/Runner/GeneratedPluginRegistrant.* 12 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Dart", 9 | "program": "example/example.dart", 10 | "request": "launch", 11 | "type": "dart" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.4] - 26/10/2020 2 | 3 | * Various code optimizations. 4 | * Api now allows for method chaining so make mapping more concise. 5 | * Silent removing of non existing mappings (without throwing exceptions). 6 | * The Injector class now has a factory constructor. An Injector instance can be instantiated 7 | in a more concise way than `Injector.getInjector()` which is now marked as deprecated. 8 | 9 | ``` dart 10 | // example with introduced changes 11 | final injector = Injector(); 12 | injector.map((injector) => SomeType("0")) 13 | ..map((injector) => SomeType("1"), key: "One") 14 | ..map((injector) => SomeType("2"), key: "Two"); 15 | ``` 16 | 17 | ## [1.0.3] - 11/08/2020 18 | 19 | * Added the ability to remove a mapping and check if a mapping is present in the injector 20 | 21 | ``` dart 22 | // the new api 23 | final injector = Injector.getInstance(); 24 | injector.map((i) => new SomeType()) 25 | final instance = injector.get(); 26 | injector.removeMapping(); 27 | ``` 28 | 29 | ## [1.0.2+1] - 24/12/2019 30 | 31 | * Fixed various analyzer warnings 32 | 33 | ## [1.0.2] - 18/12/2019 34 | 35 | * Fixed some lint warnings 36 | 37 | ## [1.0.1] - 05/03/2019 38 | 39 | * Removed dependency on flutter 40 | * Updated example to explain how to use dependency injection rather than service location 41 | 42 | ## [0.0.4] - 05/07/2018 43 | 44 | * Added ability to pass in additional arguments in the factory function with a new method call [mapWithParams]. 45 | 46 | ``` dart 47 | final injector = Injector.getInstance(); 48 | injector.mapWithParams((i, p) => new SomeType(p["id"])) 49 | final instance = injector.get(additionalParameters: { "id": "some-id" }); 50 | print(instance.id) // prints 'some-id' 51 | ``` 52 | 53 | * Added ability to get all objects of the same mapped type 54 | 55 | ``` dart 56 | final injector = Injector.getInstance(); 57 | injector.map((injector) => new SomeType("0")); 58 | injector.map((injector) => new SomeType("1"), key: "One"); 59 | injector.map((injector) => new SomeType("2"), key: "Two"); 60 | final instances = injector.getAll(); 61 | print(instances.length); // prints '3' 62 | ``` 63 | 64 | ## [0.0.3] - 01/07/2018. 65 | 66 | * Improved injector interface using generic types instead of a passed in a type to key an object factory 67 | 68 | The new api to map and get a type instance 69 | 70 | ``` dart 71 | // the new api 72 | final injector = Injector.getInstance(); 73 | injector.map((i) => new SomeType()) 74 | final instance = injector.get(); 75 | ``` 76 | 77 | The old api to map and get a type instance 78 | 79 | ``` dart 80 | // the old api 81 | final injector = Injector.getInstance(); 82 | injector.map(SomeType, (i) => new SomeType()) 83 | final instance = injector.get(SomeType); 84 | ``` 85 | 86 | ## [0.0.2] - 28/06/2018. 87 | 88 | * Fixed up linting and file formats 89 | 90 | ## [0.0.1] - 28/06/2018. 91 | 92 | * Initial release 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jon Samwell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_simple_dependency_injection 2 | 3 | A simple dependency injection plugin for [Flutter](https://flutter.io) and Dart. 4 | 5 | This implementation *does not* rely on the dart reflection apis (mirrors) and favours a simple factory based approach. 6 | This increases the performance and simplicity of this implementation. 7 | 8 | * Support for multiple injectors (useful for unit testing or code running in isolates) 9 | * Support for types and named types 10 | * Support for singletons 11 | * Support simple values (useful for configuration parameters like api keys or urls) 12 | 13 | Any help is appreciated! Comment, suggestions, issues, PR's! 14 | 15 | ## Getting Started 16 | 17 | In your flutter or dart project add the dependency: 18 | 19 | ``` yml 20 | dependencies: 21 | ... 22 | flutter_simple_dependency_injection: any 23 | ``` 24 | 25 | For help getting started with Flutter, view the online 26 | [documentation](https://flutter.io/). 27 | 28 | ## Usage example 29 | 30 | Import `flutter_simple_dependency_injection` 31 | 32 | ``` dart 33 | import 'package:flutter_simple_dependency_injection/injector.dart'; 34 | ``` 35 | 36 | ### Injector Configuration 37 | 38 | As this injector relies on factories rather than reflection (as mirrors in not available in Flutter) 39 | each mapped type needs to provide a factory function. In most cases this can just be a simple 40 | new object returned function. In slightly more advanced scenarios where the type to be created relies 41 | on other types an injector instances is passed into the factory function to allow the type of be created 42 | to get other types it depends on (see below for examples). 43 | 44 | ``` dart 45 | import 'package:flutter_simple_dependency_injection/injector.dart'; 46 | 47 | void main() { 48 | // it is best to place all injector initialisation work into one or more modules 49 | // so it can act more like a dependency injector than a service locator. 50 | // The Injector implements a singleton pattern. You can get a singleton injector instance 51 | // just by calling Injector(). 52 | final injector = ModuleContainer().initialise(Injector()); 53 | 54 | // NOTE: it is best to architect your code so that you never need to 55 | // interact with the injector itself. Make this framework act like a dependency injector 56 | // by having dependencies injected into objects in their constructors. That way you avoid 57 | // any tight coupling with the injector itself. 58 | 59 | // Basic usage, however this kind of tight couple and direct interaction with the injector 60 | // should be limited. Instead prefer dependencies injection. 61 | 62 | // simple dependency retrieval and method call 63 | injector.get().doSomething(); 64 | 65 | // get an instance of each of the same mapped types 66 | final instances = injector.getAll(); 67 | print(instances.length); // prints '3' 68 | 69 | // passing in additional arguments when getting an instance 70 | final instance = 71 | injector.get(additionalParameters: {"id": "some-id"}); 72 | print(instance.id); // prints 'some-id' 73 | } 74 | 75 | class ModuleContainer { 76 | Injector initialise(Injector injector) { 77 | injector.map((i) => Logger(), isSingleton: true); 78 | injector.map((i) => "https://api.com/", key: "apiUrl"); 79 | injector.map( 80 | (i) => SomeService(i.get(), i.get(key: "apiUrl"))); 81 | 82 | // note that you can configure mapping in a fluent programming style too 83 | injector.map((injector) => SomeType("0")) 84 | ..map((injector) => SomeType("1"), key: "One") 85 | ..map((injector) => SomeType("2"), key: "Two"); 86 | 87 | injector.mapWithParams((i, p) => SomeOtherType(p["id"])); 88 | 89 | return injector; 90 | } 91 | } 92 | 93 | class Logger { 94 | void log(String message) => print(message); 95 | } 96 | 97 | class SomeService { 98 | final Logger _logger; 99 | final String _apiUrl; 100 | 101 | SomeService(this._logger, this._apiUrl); 102 | 103 | void doSomething() { 104 | _logger.log("Doing something with the api at '$_apiUrl'"); 105 | } 106 | } 107 | 108 | class SomeType { 109 | final String id; 110 | SomeType(this.id); 111 | } 112 | 113 | class SomeOtherType { 114 | final String id; 115 | SomeOtherType(this.id); 116 | } 117 | 118 | ``` 119 | 120 | ### Remove mappings 121 | 122 | You can remove a mapped a factory/instance at any time: 123 | 124 | ``` 125 | injector.removeMapping(); 126 | 127 | injector.removeMapping(key: 'some_key'); 128 | 129 | injector.removeAllMappings(); 130 | ``` 131 | 132 | ### Multiple Injectors 133 | 134 | The Injector class has a factory constructor that by default returns the default singleton instance of the injector. In most cases this will be enough. 135 | However, you can pass a name into this factory constructor to return another isolated injector that is independent from the default injector. Passing in a new 136 | injector name will create the injector if it has not be retrieved before. 137 | 138 | ``` dart 139 | final defaultInjector = Injector(); 140 | final isolatedInjector = Injector("Isolated"); 141 | ``` 142 | 143 | To destroy an injector instance call its `dispose` method. The particular instance of the injector with all mappings will be removed. 144 | 145 | ``` dart 146 | Injector().dispose(); 147 | Injector("Isolated").dispose(); 148 | ``` -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:pedantic/analysis_options.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | linter: 9 | rules: 10 | await_only_futures: true 11 | 12 | # analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** -------------------------------------------------------------------------------- /example/example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_simple_dependency_injection/injector.dart'; 2 | 3 | void main() { 4 | // it is best to place all injector initialisation work into one or more modules 5 | // so it can act more like a dependency injector than a service locator 6 | final injector = ModuleContainer().initialise(Injector()); 7 | 8 | // NOTE: it is best to architect your code so that you never need to 9 | // interact with the injector itself. Make this framework act like a dependency injector 10 | // by having dependencies injected into objects in their constructors. That way you avoid 11 | // any tight coupling with the injector itself. 12 | 13 | // Basic usage, however this kind of tight couple and direct interaction with the injector 14 | // should be limited. Instead prefer dependencies injection. 15 | 16 | // simple dependency retrieval and method call 17 | injector.get().doSomething(); 18 | 19 | // get an instance of each of the same mapped types 20 | final instances = injector.getAll(); 21 | print(instances.length); // prints '3' 22 | 23 | // passing in additional arguments when getting an instance 24 | final instance = 25 | injector.get(additionalParameters: {'id': 'some-id'}); 26 | print(instance.id); // prints 'some-id' 27 | } 28 | 29 | class ModuleContainer { 30 | Injector initialise(Injector injector) { 31 | injector.map((i) => Logger(), isSingleton: true); 32 | injector.map((i) => 'https://api.com/', key: 'apiUrl'); 33 | injector.map( 34 | (i) => SomeService(i.get(), i.get(key: 'apiUrl'))); 35 | 36 | injector.map((injector) => SomeType('0')); 37 | injector.map((injector) => SomeType('1'), key: 'One'); 38 | injector.map((injector) => SomeType('2'), key: 'Two'); 39 | 40 | injector.mapWithParams((i, p) => SomeOtherType(p['id'])); 41 | 42 | return injector; 43 | } 44 | } 45 | 46 | class Logger { 47 | void log(String message) => print(message); 48 | } 49 | 50 | class SomeService { 51 | final Logger _logger; 52 | final String _apiUrl; 53 | 54 | SomeService(this._logger, this._apiUrl); 55 | 56 | void doSomething() { 57 | _logger.log('Doing something with the api at `$_apiUrl`'); 58 | } 59 | } 60 | 61 | class SomeType { 62 | final String id; 63 | SomeType(this.id); 64 | } 65 | 66 | class SomeOtherType { 67 | final String id; 68 | SomeOtherType(this.id); 69 | } 70 | -------------------------------------------------------------------------------- /lib/injector.dart: -------------------------------------------------------------------------------- 1 | library flutter_simple_dependency_injection; 2 | 3 | export 'src/injector_exception.dart'; 4 | export 'src/injector.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/injector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_simple_dependency_injection/src/injector_exception.dart'; 2 | import 'package:flutter_simple_dependency_injection/src/type_factory.dart'; 3 | 4 | typedef ObjectFactoryFn = T Function(Injector injector); 5 | typedef ObjectFactoryWithParamsFn = T Function( 6 | Injector injector, 7 | Map additionalParameters, 8 | ); 9 | 10 | /// A simple injector implementation for use in Flutter projects where conventional reflection (mirrors) 11 | /// is not available. 12 | /// 13 | ///```dart 14 | /// import 'package:flutter_simple_dependency_injection/injector.dart'; 15 | /// 16 | /// void main() { 17 | /// final injector = Injector(); 18 | /// injector.map((i) => Logger(), isSingleton: true); 19 | /// injector.map((i) => 'https://api.com/, key: 'apiUrl'); 20 | /// injector.map((i) => SomeService(i.get(Logger), i.get(String, 'apiUrl'))); 21 | /// 22 | /// injector.get().doSomething(); 23 | /// } 24 | /// 25 | /// class Logger { 26 | /// void log(String message) => print(message); 27 | /// } 28 | /// 29 | /// class SomeService { 30 | /// final Logger _logger; 31 | /// final String _apiUrl; 32 | /// 33 | /// SomeService(this._logger, this._apiUrl); 34 | /// 35 | /// void doSomething() { 36 | /// _logger.log("Doing something with the api at '$_apiUrl'"); 37 | /// } 38 | /// } 39 | ///``` 40 | class Injector { 41 | static final Map _injectors = {}; 42 | final Map> _factories = 43 | >{}; 44 | 45 | /// The name of this injector. 46 | /// 47 | /// Naming injectors enable each app to have multiple atomic injectors. 48 | final String name; 49 | 50 | @Deprecated( 51 | 'Prefer to use the factory constructor Injector([String name = "default"])') 52 | static Injector getInjector([String name = 'default']) { 53 | return Injector(name); 54 | } 55 | 56 | /// Get the instance of the named injector creating an [Injector] instance 57 | /// if the named injector cannot be found. 58 | /// 59 | /// The [name] is optional and if omitted the "default" injector instance 60 | /// will be returned. 61 | /// 62 | /// ```dart 63 | /// final defaultInjector = Injector(); 64 | /// final isolatedInjector = Injector("Isolated"); 65 | /// ``` 66 | factory Injector([String name = 'default']) { 67 | if (!_injectors.containsKey(name)) { 68 | _injectors[name] = Injector._internal(name); 69 | } 70 | 71 | return _injectors[name]; 72 | } 73 | 74 | Injector._internal(this.name); 75 | 76 | String _makeKey(T type, [String key]) => 77 | '${_makeKeyPrefix(type)}${key ?? 'default'}'; 78 | 79 | String _makeKeyPrefix(T type) => '${type.toString()}::'; 80 | 81 | /// Maps the given type to the given factory function. Optionally specify the type as a singleton and give it a named key. 82 | /// 83 | /// [T] The type the [factoryFn] will return an instance of. 84 | /// 85 | /// [factoryFn] is a simple function which takes in an [Injector] and returns an new instance 86 | /// of the type [T]. In this method you can use the injector to get other dependencies 87 | /// this instance depends on (see examples below). 88 | /// 89 | /// When [isSingleton] is true the first returned instances of the object is stored and 90 | /// subsequently return in future calls. 91 | /// 92 | /// When [key] is provided the object is keyed by type name and the given key. 93 | /// 94 | /// Throws an [InjectorException] if the type and or key combination has already been mapped. 95 | /// 96 | /// Returns the current injector instance. 97 | /// 98 | /// ```dart 99 | /// final injector = Injector.getInstance(); 100 | /// injector.map(Logger, (injector) => AppLogger()); 101 | /// injector.map(DbLogger, (injector) => DbLogger(), isSingleton: true); 102 | /// injector.map(AppLogger, (injector) => AppLogger(injector.get(Logger)), key: "AppLogger"); 103 | /// injector.map(String, (injector) => "https://api.com/", key: "ApiUrl"); 104 | /// ``` 105 | /// 106 | /// You can also configure mapping in a fluent programming style: 107 | /// ```dart 108 | /// Injector.getInstance().map(Logger, (injector) => AppLogger()); 109 | /// ..map(DbLogger, (injector) => DbLogger(), isSingleton: true); 110 | /// ..map(AppLogger, (injector) => AppLogger(injector.get(Logger)), key: "AppLogger"); 111 | /// ..map(String, (injector) => "https://api.com/", key: "ApiUrl"); 112 | /// ``` 113 | Injector map(ObjectFactoryFn factoryFn, 114 | {bool isSingleton = false, String key}) { 115 | final objectKey = _makeKey(T, key); 116 | if (_factories.containsKey(objectKey)) { 117 | throw InjectorException("Mapping already present for type '$objectKey'"); 118 | } 119 | _factories[objectKey] = TypeFactory((i, p) => factoryFn(i), isSingleton); 120 | return this; 121 | } 122 | 123 | /// Maps the given type to the given factory function. Optionally give it a named key. 124 | /// 125 | /// [T] The type the [factoryFn] will return an instance of. 126 | /// 127 | /// [factoryFn] is a simple function which takes in an [Injector] and returns an new instance 128 | /// of the type [T]. In this method you can use the injector to get other dependencies 129 | /// this instance depends on (see examples below). 130 | /// 131 | /// When [isSingleton] is true the first returned instances of the object is stored and 132 | /// subsequently return in future calls. 133 | /// 134 | /// When [key] is provided the object is keyed by type name and the given key. 135 | /// 136 | /// Throws an [InjectorException] if the type and or key combination has already been mapped. 137 | /// 138 | /// Returns the current injector instance. 139 | /// 140 | /// ```dart 141 | /// final injector = Injector.getInstance(); 142 | /// injector.map(Logger, (injector, params) => AppLogger(params["logKey"])); 143 | /// injector.map(AppLogger, (injector, params) => AppLogger(injector.get(Logger, params["apiUrl"])), key: "AppLogger"); 144 | /// ``` 145 | Injector mapWithParams(ObjectFactoryWithParamsFn factoryFn, 146 | {String key}) { 147 | final objectKey = _makeKey(T, key); 148 | if (_factories.containsKey(objectKey)) { 149 | throw InjectorException("Mapping already present for type '$objectKey'"); 150 | } 151 | _factories[objectKey] = TypeFactory(factoryFn, false); 152 | return this; 153 | } 154 | 155 | /// Returns true if the given type has been mapped. Optionally give it a named key. 156 | bool isMapped({String key}) { 157 | final objectKey = _makeKey(T, key); 158 | return _factories.containsKey(objectKey); 159 | } 160 | 161 | /// Removes the type mapping from the injector if present. Optionally give it a named key. 162 | /// The remove operation is silent, means no exception is thrown if the type or key combination is not present. 163 | /// 164 | /// Returns the current injector instance. 165 | Injector removeMapping({String key}) { 166 | final objectKey = _makeKey(T, key); 167 | if (_factories.containsKey(objectKey)) { 168 | _factories.remove(objectKey); 169 | } 170 | return this; 171 | } 172 | 173 | /// Removes all the mappings for the given type 174 | /// The remove operation is silent, means no exception is thrown if the type or key combination is not present. 175 | /// 176 | /// Returns the current injector instance. 177 | Injector removeAllMappings() { 178 | final keyForType = _makeKeyPrefix(T); 179 | _factories.removeWhere((key, value) => key.startsWith(keyForType)); 180 | return this; 181 | } 182 | 183 | /// Gets an instance of the given type of [T] and optional given key and parameters. 184 | /// 185 | /// Throws an [InjectorException] if the given type has not been mapped 186 | /// using the map method. 187 | /// 188 | /// Note that instance that are mapped to need additional parameters cannot be singletons 189 | /// 190 | /// ```dart 191 | /// final injector = Injector.getInstance(); 192 | /// // map the type 193 | /// injector.map((injector) => AppLogger()); 194 | /// // get the type 195 | /// injector.get().log("some message"); 196 | /// 197 | /// injector.mapWithParams((i, p) => SomeType(p["id"])) 198 | /// final instance = injector.get(additionalParameters: { "id": "some-id" }); 199 | /// print(instance.id) // prints 'some-id' 200 | /// ``` 201 | T get({String key, Map additionalParameters}) { 202 | final objectKey = _makeKey(T, key); 203 | final objectFactory = _factories[objectKey]; 204 | if (objectFactory == null) { 205 | throw InjectorException("Cannot find object factory for '$objectKey'"); 206 | } 207 | 208 | return objectFactory.get(this, additionalParameters); 209 | } 210 | 211 | /// Gets all the mapped instances of the given type and additional parameters 212 | Iterable getAll({Map additionalParameters}) { 213 | final keyForType = _makeKeyPrefix(T); 214 | return _factories.entries // 215 | .where((entry) => entry.key.startsWith(keyForType)) // 216 | .map((entry) => entry.value.get(this, additionalParameters) as T); 217 | } 218 | 219 | /// Disposes of the injector instance and removes it from the named collection of injectors 220 | void dispose() { 221 | _factories.clear(); 222 | _injectors.remove(name); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /lib/src/injector_exception.dart: -------------------------------------------------------------------------------- 1 | class InjectorException implements Exception { 2 | String message; 3 | 4 | InjectorException(this.message); 5 | 6 | @override 7 | String toString() => 'Injector Exception: $message'; 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/type_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_simple_dependency_injection/src/injector.dart'; 2 | 3 | class TypeFactory { 4 | final bool _isSingleton; 5 | final ObjectFactoryWithParamsFn _factoryFn; 6 | T _instance; 7 | 8 | TypeFactory(this._factoryFn, this._isSingleton); 9 | 10 | T get(Injector injector, Map additionalParameters) { 11 | if (_isSingleton && _instance != null) { 12 | return _instance; 13 | } 14 | 15 | final instance = _factoryFn(injector, additionalParameters); 16 | if (_isSingleton) { 17 | _instance = instance; 18 | } 19 | return instance; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pre-publish-checks.cmd: -------------------------------------------------------------------------------- 1 | CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\dartanalyzer" --options analysis_options.yaml . 2 | CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\dartfmt" . -w 3 | CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\pub" run test . 4 | cd ../ -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "6.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.39.16" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.6.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.4.2" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.0.0" 39 | charcode: 40 | dependency: transitive 41 | description: 42 | name: charcode 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.3" 46 | cli_util: 47 | dependency: transitive 48 | description: 49 | name: cli_util 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "0.1.4" 53 | collection: 54 | dependency: transitive 55 | description: 56 | name: collection 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.14.13" 60 | convert: 61 | dependency: transitive 62 | description: 63 | name: convert 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.1.1" 67 | coverage: 68 | dependency: transitive 69 | description: 70 | name: coverage 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.14.0" 74 | crypto: 75 | dependency: transitive 76 | description: 77 | name: crypto 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.5" 81 | csslib: 82 | dependency: transitive 83 | description: 84 | name: csslib 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "0.16.2" 88 | glob: 89 | dependency: transitive 90 | description: 91 | name: glob 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.2.0" 95 | html: 96 | dependency: transitive 97 | description: 98 | name: html 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "0.14.0+3" 102 | http: 103 | dependency: transitive 104 | description: 105 | name: http 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "0.12.2" 109 | http_multi_server: 110 | dependency: transitive 111 | description: 112 | name: http_multi_server 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "2.2.0" 116 | http_parser: 117 | dependency: transitive 118 | description: 119 | name: http_parser 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "3.1.4" 123 | io: 124 | dependency: transitive 125 | description: 126 | name: io 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "0.3.4" 130 | js: 131 | dependency: transitive 132 | description: 133 | name: js 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "0.6.2" 137 | logging: 138 | dependency: transitive 139 | description: 140 | name: logging 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "0.11.4" 144 | matcher: 145 | dependency: transitive 146 | description: 147 | name: matcher 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "0.12.9" 151 | meta: 152 | dependency: transitive 153 | description: 154 | name: meta 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "1.2.2" 158 | mime: 159 | dependency: transitive 160 | description: 161 | name: mime 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "0.9.6+3" 165 | node_interop: 166 | dependency: transitive 167 | description: 168 | name: node_interop 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "1.1.1" 172 | node_io: 173 | dependency: transitive 174 | description: 175 | name: node_io 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "1.1.1" 179 | node_preamble: 180 | dependency: transitive 181 | description: 182 | name: node_preamble 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "1.4.12" 186 | package_config: 187 | dependency: transitive 188 | description: 189 | name: package_config 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "1.9.3" 193 | path: 194 | dependency: transitive 195 | description: 196 | name: path 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "1.7.0" 200 | pedantic: 201 | dependency: "direct dev" 202 | description: 203 | name: pedantic 204 | url: "https://pub.dartlang.org" 205 | source: hosted 206 | version: "1.9.2" 207 | pool: 208 | dependency: transitive 209 | description: 210 | name: pool 211 | url: "https://pub.dartlang.org" 212 | source: hosted 213 | version: "1.4.0" 214 | pub_semver: 215 | dependency: transitive 216 | description: 217 | name: pub_semver 218 | url: "https://pub.dartlang.org" 219 | source: hosted 220 | version: "1.4.4" 221 | shelf: 222 | dependency: transitive 223 | description: 224 | name: shelf 225 | url: "https://pub.dartlang.org" 226 | source: hosted 227 | version: "0.7.7" 228 | shelf_packages_handler: 229 | dependency: transitive 230 | description: 231 | name: shelf_packages_handler 232 | url: "https://pub.dartlang.org" 233 | source: hosted 234 | version: "2.0.0" 235 | shelf_static: 236 | dependency: transitive 237 | description: 238 | name: shelf_static 239 | url: "https://pub.dartlang.org" 240 | source: hosted 241 | version: "0.2.8" 242 | shelf_web_socket: 243 | dependency: transitive 244 | description: 245 | name: shelf_web_socket 246 | url: "https://pub.dartlang.org" 247 | source: hosted 248 | version: "0.2.3" 249 | source_map_stack_trace: 250 | dependency: transitive 251 | description: 252 | name: source_map_stack_trace 253 | url: "https://pub.dartlang.org" 254 | source: hosted 255 | version: "2.0.0" 256 | source_maps: 257 | dependency: transitive 258 | description: 259 | name: source_maps 260 | url: "https://pub.dartlang.org" 261 | source: hosted 262 | version: "0.10.9" 263 | source_span: 264 | dependency: transitive 265 | description: 266 | name: source_span 267 | url: "https://pub.dartlang.org" 268 | source: hosted 269 | version: "1.7.0" 270 | stack_trace: 271 | dependency: transitive 272 | description: 273 | name: stack_trace 274 | url: "https://pub.dartlang.org" 275 | source: hosted 276 | version: "1.9.5" 277 | stream_channel: 278 | dependency: transitive 279 | description: 280 | name: stream_channel 281 | url: "https://pub.dartlang.org" 282 | source: hosted 283 | version: "2.0.0" 284 | string_scanner: 285 | dependency: transitive 286 | description: 287 | name: string_scanner 288 | url: "https://pub.dartlang.org" 289 | source: hosted 290 | version: "1.0.5" 291 | term_glyph: 292 | dependency: transitive 293 | description: 294 | name: term_glyph 295 | url: "https://pub.dartlang.org" 296 | source: hosted 297 | version: "1.1.0" 298 | test: 299 | dependency: "direct dev" 300 | description: 301 | name: test 302 | url: "https://pub.dartlang.org" 303 | source: hosted 304 | version: "1.15.3" 305 | test_api: 306 | dependency: transitive 307 | description: 308 | name: test_api 309 | url: "https://pub.dartlang.org" 310 | source: hosted 311 | version: "0.2.18" 312 | test_core: 313 | dependency: transitive 314 | description: 315 | name: test_core 316 | url: "https://pub.dartlang.org" 317 | source: hosted 318 | version: "0.3.11" 319 | typed_data: 320 | dependency: transitive 321 | description: 322 | name: typed_data 323 | url: "https://pub.dartlang.org" 324 | source: hosted 325 | version: "1.2.0" 326 | vm_service: 327 | dependency: transitive 328 | description: 329 | name: vm_service 330 | url: "https://pub.dartlang.org" 331 | source: hosted 332 | version: "4.2.0" 333 | watcher: 334 | dependency: transitive 335 | description: 336 | name: watcher 337 | url: "https://pub.dartlang.org" 338 | source: hosted 339 | version: "0.9.7+15" 340 | web_socket_channel: 341 | dependency: transitive 342 | description: 343 | name: web_socket_channel 344 | url: "https://pub.dartlang.org" 345 | source: hosted 346 | version: "1.1.0" 347 | webkit_inspection_protocol: 348 | dependency: transitive 349 | description: 350 | name: webkit_inspection_protocol 351 | url: "https://pub.dartlang.org" 352 | source: hosted 353 | version: "0.7.3" 354 | yaml: 355 | dependency: transitive 356 | description: 357 | name: yaml 358 | url: "https://pub.dartlang.org" 359 | source: hosted 360 | version: "2.2.1" 361 | sdks: 362 | dart: ">=2.7.0 <3.0.0" 363 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_simple_dependency_injection 2 | description: A super simple dependency injection implementation for flutter that behaves like any normal IOC container and does not rely on mirrors 3 | version: 1.0.4 4 | homepage: https://github.com/jonsamwell/flutter_simple_dependency_injection 5 | 6 | dependencies: 7 | 8 | dev_dependencies: 9 | test: 10 | pedantic: 11 | 12 | environment: 13 | sdk: ">=2.0.0 <3.0.0" 14 | -------------------------------------------------------------------------------- /test/flutter_simple_dependency_injection_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_simple_dependency_injection/injector.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | class ObjectWithNoDependencies { 5 | String propertyOne = 'Hello'; 6 | } 7 | 8 | class ObjectWithOneDependency { 9 | ObjectWithNoDependencies dependencyOne; 10 | String propertyOne = 'Hello Jon'; 11 | 12 | ObjectWithOneDependency(this.dependencyOne); 13 | } 14 | 15 | class ObjectWithTwoDependencies { 16 | ObjectWithNoDependencies dependencyOne; 17 | ObjectWithOneDependency dependencyTwo; 18 | String propertyOne = 'Hello Jon!'; 19 | 20 | ObjectWithTwoDependencies(this.dependencyOne, this.dependencyTwo); 21 | } 22 | 23 | class ObjectWithGenerics { 24 | T propertyOfType; 25 | 26 | ObjectWithGenerics(this.propertyOfType); 27 | } 28 | 29 | class ObjectWithSomeConstructorArgDependencies { 30 | final String id; 31 | 32 | ObjectWithSomeConstructorArgDependencies(this.id); 33 | } 34 | 35 | class ObjectC extends ObjectB { 36 | final String idC; 37 | 38 | ObjectC(this.idC, String idB) : super(idB); 39 | } 40 | 41 | class ObjectB { 42 | final String idB; 43 | 44 | ObjectB(this.idB); 45 | } 46 | 47 | void main() { 48 | test('non-generic map test', () async { 49 | final injector = Injector(); 50 | injector.map((i) => ObjectC('C', 'B')); 51 | final instance = injector.get(); 52 | expect(instance != null, true); 53 | }); 54 | 55 | test('can get default injector instance', () async { 56 | final injector = Injector(); 57 | expect(injector != null, true); 58 | }); 59 | 60 | test('can get named injector instance', () async { 61 | final injectorDefault = Injector(); 62 | final injectorNamed = Injector('name'); 63 | expect(injectorDefault != null, true); 64 | expect(injectorNamed != null, true); 65 | expect(injectorNamed != injectorDefault, true); 66 | }); 67 | 68 | test('can have isolated injector instances', () async { 69 | final injectorOne = Injector('one'); 70 | final injectorTwo = Injector('two'); 71 | injectorOne 72 | .map((i) => ObjectWithNoDependencies()); 73 | final instance = injectorOne.get(); 74 | expect(instance is ObjectWithNoDependencies, true); 75 | expect( 76 | () => injectorTwo.get(), 77 | throwsA(predicate((e) => 78 | e is InjectorException && 79 | e.message == 80 | "Cannot find object factory for 'ObjectWithNoDependencies::default'"))); 81 | }); 82 | 83 | test('can map generic types', () async { 84 | final injector = Injector(); 85 | injector.map>( 86 | (injector) => ObjectWithGenerics('Hello')); 87 | injector.map>((injector) => ObjectWithGenerics(10)); 88 | injector 89 | .map>((injector) => ObjectWithGenerics(true)); 90 | final stringInstance = injector.get>(); 91 | final intInstance = injector.get>(); 92 | final boolInstance = injector.get>(); 93 | expect(stringInstance is ObjectWithGenerics, true); 94 | expect(intInstance is ObjectWithGenerics, true); 95 | expect(boolInstance is ObjectWithGenerics, true); 96 | expect(stringInstance.propertyOfType, 'Hello'); 97 | expect(intInstance.propertyOfType, 10); 98 | expect(boolInstance.propertyOfType, true); 99 | injector.dispose(); 100 | }); 101 | 102 | test('can map class factory and get object instance', () async { 103 | final injector = Injector(); 104 | injector.map( 105 | (injector) => ObjectWithNoDependencies()); 106 | final instance = injector.get(); 107 | expect(instance is ObjectWithNoDependencies, true); 108 | expect(instance.propertyOne, 'Hello'); 109 | injector.dispose(); 110 | }); 111 | 112 | test( 113 | 'can map class factory for singleton and always get back the same object', 114 | () async { 115 | final injector = Injector(); 116 | injector.map( 117 | (injector) => ObjectWithNoDependencies(), 118 | isSingleton: true); 119 | final instanceOne = injector.get(); 120 | final instanceTwo = injector.get(); 121 | final instanceThree = injector.get(); 122 | expect(instanceOne is ObjectWithNoDependencies, true); 123 | expect(instanceTwo.hashCode, instanceOne.hashCode); 124 | expect(instanceThree.hashCode, instanceOne.hashCode); 125 | injector.dispose(); 126 | }); 127 | 128 | test('can map class factory and get object simple instance', () async { 129 | final injector = Injector(); 130 | injector.map( 131 | (injector) => ObjectWithNoDependencies()); 132 | injector.map((injector) => 133 | ObjectWithOneDependency(injector.get())); 134 | final instance = injector.get(); 135 | expect(instance is ObjectWithOneDependency, true); 136 | expect(instance.dependencyOne is ObjectWithNoDependencies, true); 137 | expect(instance.propertyOne, 'Hello Jon'); 138 | injector.dispose(); 139 | }); 140 | 141 | test('can map same object with different keys and get instances', () async { 142 | final injector = Injector(); 143 | injector.map( 144 | (injector) => ObjectWithNoDependencies()); 145 | injector.map( 146 | (injector) => ObjectWithNoDependencies(), 147 | key: 'One'); 148 | final instanceOne = injector.get(); 149 | final instanceTwo = injector.get(key: 'One'); 150 | expect(instanceOne is ObjectWithNoDependencies, true); 151 | expect(instanceTwo is ObjectWithNoDependencies, true); 152 | expect(instanceOne != instanceTwo, true); 153 | injector.dispose(); 154 | }); 155 | 156 | test('can map simple named string type', () async { 157 | final injector = Injector(); 158 | injector.map((injector) => 'Jon', key: 'MyName'); 159 | final instanceOne = injector.get(key: 'MyName'); 160 | final instanceTwo = injector.get(key: 'MyName'); 161 | expect(instanceOne is String, true); 162 | expect(instanceOne, 'Jon'); 163 | expect(instanceTwo, instanceOne); 164 | injector.dispose(); 165 | }); 166 | 167 | test('can construct type with additional give parameters', () async { 168 | final injector = Injector(); 169 | injector.mapWithParams( 170 | (injector, p) => ObjectWithSomeConstructorArgDependencies(p['id'])); 171 | final instanceOne = injector.get( 172 | additionalParameters: {'id': 'some-id'}); 173 | expect(instanceOne is ObjectWithSomeConstructorArgDependencies, true); 174 | expect(instanceOne.id, 'some-id'); 175 | injector.dispose(); 176 | }); 177 | 178 | test('can get all instances of type', () async { 179 | final injector = Injector(); 180 | injector.map( 181 | (injector) => ObjectWithSomeConstructorArgDependencies('0')); 182 | injector.map( 183 | (injector) => ObjectWithNoDependencies()); 184 | injector.map( 185 | (injector) => ObjectWithSomeConstructorArgDependencies('1'), 186 | key: 'One'); 187 | injector.map( 188 | (injector) => ObjectWithSomeConstructorArgDependencies('2'), 189 | key: 'Two'); 190 | final instances = 191 | injector.getAll(); 192 | expect(instances.length, 3); 193 | expect(instances.elementAt(0).id, '0'); 194 | expect(instances.elementAt(1).id, '1'); 195 | expect(instances.elementAt(2).id, '2'); 196 | injector.dispose(); 197 | }); 198 | 199 | test('exception thrown when type is not known', () async { 200 | final injector = Injector(); 201 | injector.map((injector) => 202 | ObjectWithOneDependency(injector.get())); 203 | expect( 204 | () => injector.get(), 205 | throwsA(predicate((e) => 206 | e is InjectorException && 207 | e.message == 208 | "Cannot find object factory for 'ObjectWithNoDependencies::default'"))); 209 | injector.dispose(); 210 | }); 211 | 212 | test('exception thrown when keyed type is not known', () async { 213 | final injector = Injector(); 214 | injector.map((injector) => ObjectWithOneDependency( 215 | injector.get(key: 'Key'))); 216 | expect( 217 | () => injector.get(), 218 | throwsA(predicate((e) => 219 | e is InjectorException && 220 | e.message == 221 | "Cannot find object factory for 'ObjectWithNoDependencies::Key'"))); 222 | injector.dispose(); 223 | }); 224 | 225 | test('can remove mapping and map again', () async { 226 | final injector = Injector(); 227 | injector.map( 228 | (injector) => ObjectWithNoDependencies()); 229 | 230 | expect(injector.isMapped(), true); 231 | injector.removeMapping(); 232 | expect(injector.isMapped(), false); 233 | 234 | injector.map( 235 | (injector) => ObjectWithNoDependencies()); 236 | expect(injector.isMapped(), true); 237 | 238 | expect(injector.get() is ObjectWithNoDependencies, 239 | true); 240 | 241 | injector.dispose(); 242 | }); 243 | 244 | test('can remove all mappings of type', () async { 245 | final injector = Injector(); 246 | injector.map( 247 | (injector) => ObjectWithNoDependencies()); 248 | injector.map( 249 | (injector) => ObjectWithNoDependencies(), 250 | key: 'one', 251 | ); 252 | injector.map( 253 | (injector) => ObjectWithNoDependencies(), 254 | key: 'two', 255 | ); 256 | 257 | expect(injector.isMapped(), true); 258 | expect(injector.isMapped(key: 'one'), true); 259 | expect(injector.isMapped(key: 'two'), true); 260 | injector.removeAllMappings(); 261 | expect(injector.isMapped(), false); 262 | expect(injector.isMapped(key: 'one'), false); 263 | expect(injector.isMapped(key: 'two'), false); 264 | 265 | injector.dispose(); 266 | }); 267 | } 268 | --------------------------------------------------------------------------------