我们目前正在与Flutter一起进行实验,同时开发我们的副项目以应对同事的挑战。 这个辅助项目也应该被视为一个游乐场,在这里我们可以检查是否可以在更严重的项目中使用Flutter。 这就是为什么我们要在那里使用一些方法的原因,而这种方法看起来像是对如此小的项目的过度设计。
因此,第一个问题是我们可以使用什么进行依赖注入。 在互联网上进行的快速搜索显示,有2个库得到了好评: get_it和kiwi 。 由于get_it
原来是服务定位器(并且我不喜欢这种模式),所以我打算使用奇异果,它看起来更有前途,但是后来我找到了另一个库: inject.dart 。 它在很大程度上受到Dagger库的启发,在我们使用其他Android项目中的最新库时,我决定深入研究它。
值得一提的是,尽管该库位于Google GitHub存储库中,但它不是Google的官方库,因此目前不提供支持:
该库当前是按现状提供(开发人员预览),因为它是从Google内部的内部存储库中开源的。 因此,我们目前无法处理错误或功能请求。
尽管如此,看起来该库已完成我们现在需要的所有工作,因此我想分享一些有关如何在项目中使用该库的信息。
安装方式
由于官方存储库中没有软件包,因此我们必须手动安装。 我更喜欢将其作为git子模块来执行,因此我在项目源目录中创建了一个文件夹vendor
,并从该目录运行以下命令:
git submodule add https://github.com/google/inject.dart
现在我们可以通过pubspec.yaml
添加到pubspec.yaml
:
dependencies: // other dependencies here inject: path: ./vendor/inject.dart/package/inject dev_dependencies: // other dev_dependencies here build_runner: ^1.0.0 inject_generator: path: ./vendor/inject.dart/package/inject_generator
使用方法
我们通常期望DI库具有哪些功能? 让我们看一些常见的用例:
混凝土类注射
可以这样简单:
import 'package:inject/inject.dart'; @provide class StepService { // implementation }
我们可以将其与Flutter小部件一起使用,例如:
@provide class SomeWidget extends StatelessWidget { final StepService _service; SomeWidget(this._service); }
接口注入
首先,我们需要定义一个带有一些实现类的抽象类,例如:
abstract class UserRepository { Future<List<User>> allUsers(); } class FirestoreUserRepository implements UserRepository { @override Future<List<User>> allUsers() { // implementation } }
现在,我们可以在模块中提供依赖项:
import 'package:inject/inject.dart'; @module class UsersServices { @provide UserRepository userRepository() => FirestoreUserRepository(); }
提供者
如果我们不需要注入某个类的实例,而需要注入一个提供者,那将给我们每次该类的新实例,该怎么办? 还是如果我们需要懒散地解决依赖关系,而不是在构造函数中获取具体实例呢? 我既没有在文档中找到它(因为根本没有文档),也没有在提供的示例中找到它,但是它实际上以这种方式工作,您可以请求一个返回所需实例的函数,并将其正确注入。
我们甚至可以这样定义一个帮助程序类型:
typedef Provider<T> = T Function();
并在我们的课程中使用它:
@provide class SomeWidget extends StatelessWidget { final Provider<StepService> _service; SomeWidget(this._service); void _someFunction() { final service = _service(); // use service } }
辅助注射
没有内置功能来注入仅需要在运行时知道参数的对象,因此在这种情况下,我们可以将通用模式与工厂一起使用:创建一个工厂类,该类在构造函数中获取所有编译时的依赖项并将其注入,并提供一个带有运行时参数的工厂方法,它将创建所需的实例。
单例,限定符和异步注入
是的,该库支持所有这些。 官方示例中实际上有一个很好的解释。
接线
为了使一切正常工作的最后一步是创建一个注入器(又名Dagger组件),例如:
import 'main.inject.dart' as g; @Injector(const [UsersServices, DateResultsServices]) abstract class Main { @provide MyApp get app; static Future<Main> create( UsersServices usersModule, DateResultsServices dateResultsModule, ) async { return await g.Main$Injector.create( usersModule, dateResultsModule, ); } }
这里的UserServices
和DateResultsServices
是先前定义的模块, MyApp
是应用程序的根窗口小部件, main.inject.dart
是自动生成的文件(稍后会对此进行详细介绍)。
现在,我们可以这样定义我们的主要功能:
void main() async { var container = await Main.create( UsersServices(), DateResultsServices(), ); runApp(container.app); }
跑步
当inject
与代码生成一起工作时,我们需要使用构建运行器来生成所需的代码。 我们可以使用以下命令:
flutter packages pub run build_runner build
或watch
命令以保持源代码自动同步:
flutter packages pub run build_runner watch
但是这里有一个重要的时刻:默认情况下,代码将生成到cache
文件夹中,Flutter当前不支持此功能(尽管为了解决此问题正在进行中)。 因此,我们需要添加具有以下内容的文件inject_generator.build.yaml
:
builders: inject_generator: target: ":inject_generator" import: "package:inject_generator/inject_generator.dart" builder_factories: - "summarizeBuilder" - "generateBuilder" build_extensions: ".dart": - ".inject.summary" - ".inject.dart" auto_apply: dependents build_to: source
它的内容实际上与文件vendor/inject.dart/package/inject_generator/build.yaml
相同,除了以下一行: build_to: cache
已替换为build_to: source
。
现在我们可以运行build_runner
,它将生成所需的代码(如果某些依赖项build_runner
,则会提供错误消息),然后我们可以照常运行Flutter build。
获利
仅此而已。 您还应该检查库本身提供的示例 ,如果您对Dagger库有一定的经验,那么inject
对您真的很熟悉。