序言
依赖注入(IOC)就是通过容器,将当前这个类所需的对象实例化,而不需要这个类自身去实例化这个对象。目的是为了类的解耦。在小项目里面可能无法体现依赖注入的价值,但是在大型多人合作的项目里面,依赖注入能让整个项目更加健壮和易于维护。
Inject
说起依赖注入,最大名鼎鼎的莫过于 Java 的 Spring 系列。在 Flutter 开发中也有很多的依赖注入框架,其中官方推荐的框架就是本文的主角 Inject
导入
由于 Inject 不支持导包的形式,因此只能通过导入源码的方式引入。
在 lib 同级别目录新建 vendor 文件夹
在 vendor 文件夹里导入 inject 源码
git clone https://github.com/google/inject.dart.git
然后在 pubspec.yaml 文件中引用
注意 Inject 依赖于 build runner,因此也需要引入 build runner
dependencies:
inject:
path: ./vendor/inject.dart/package/inject
dev_dependencies:
build_runner: ^1.0.0
inject_generator:
path: ./vendor/inject.dart/package/inject_generator
之后在项目根目录下创建一个名为 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
为了隐藏生成的文件,请导航至 Android Studio
-> Preferences
-> Editor
-> File Types
并将以下代码粘贴在 ignore files and folders
部分下
*.inject.summary;*.inject.dart;*.g.dart;
在 Visual Studio Code 中,导航到 Preferences
-> Settings
并搜索 Files:Exclude
。添加以下代码:
**/*.inject.summary
**/*.inject.dart
**/*.g.dart
简单入门
直接说原理和概念容易让人一头雾水,因此先展示一个简单的示例
首先写一个非常简单的 Presenter
@provide
class UserPresenter{
String getUserName() {
return "Nike";
}
}
可以看到我们给这个类加了一个 @provide 的注解
provide 这个注解用于告诉 Inject,当前这个类是需要让 Inject 来帮助我们实例化的,当我们编译时 Inject 会帮我们把这个类放入注射器里面,当有类需要用到 UserPresenter 的实例化对象时,Inject 会再帮我们从注射器里将 UserPresenter 对象注入到该类。
接下来需要一个 Widget 来使用我们的 Presenter
@provide
class UserWidget extends StatelessWidget {
UserPresenter _userPresenter;
UserWidget(this._userPresenter);
@override
Widget build(BuildContext context) {
return Text(_userPresenter.getUserName());
}
}
使用这个 Presenter 也很简单,只需要在构造函数里传入即可,并且给该类加上 @provide 的注解。
这么写很好理解,但是下一个问题就来了,UserPresenter
UserWidget
这两个对象既然不能通过直接 new 来实例化,那它们到底是在哪里被实例化的呢?
按照正常的 Ioc 来说,必然是有一个注射器来注入实例化对象的,Inject 当然也不例外,这里就要用到另外一个注解 @Injector()
来配置我们需要的注射器。
import 'app_injector.inject.dart' as g;
@Injector()
abstract class AppInjector {
@provide
UserWidget get userWidget;
static Future<AppInjector> create() {
return g.AppInjector$Injector.create();
}
}
由于我们还没有编译,会出现找不到 app_injector.inject.dart
的错误提示
@Injector()
这个注解告诉 Inject,当前这个类是一个注射器,我们需要的实例都可以通过注射器来获取。比如当前我们需要 UserWidget
的实例。我们需要这么一行代码
@provide
UserWidget get userWidget;
现在基本的代码已经完成了,我们编译一下生成所需的代码
在命令行执行
flutter packages pub run build_runner build
最后 在 main 函数里使用该注射器
void main() async {
final container = await AppInjector.create();
runApp( MaterialApp(
home: Scaffold(
body: Center(child: container.userWidget),
),
));
}
可以看到,整个依赖注入已经完成了。
进阶
上面是比较常规的使用,Inject 还有别的注解
@singleton
这个注解的含义比较简单,让当前类成为一个单例,只会实例化一次。
@singleton
@provide
class UserWidget extends StatelessWidget {
我们给之前的 UserWidget 增加一个 singleton 注解,重新 build 之后
我们进入到自动生成的 app_injector.inject.dart 代码里面
_i2.UserWidget _singletonUserWidget;
_i2.UserWidget _createUserWidget() =>
_singletonUserWidget ??= _i2.UserWidget(_createUserPresenter());
可以看到当 UserWidget
不为空时,会重用之前的对象。
@module
平时开发经常会有一个 module 里面包含多个 Repository 的写法,如果像上面一样把所有东西扔到 Injector
会使 Injector
变得很臃肿。我们可以通过 @module
注解来解决这个问题
首先 写两个有依赖的 Repository
@provide
class UserRepository {
GoodRepository _goodRepository;
UserRepository(this._goodRepository);
Future<List<String>> getAllUsers() {
print("getAllUsers");
_goodRepository.getAllGoods();
return null;
}
}
@provide
class GoodRepository {
Future<List<String>> getAllGoods() {
print("getAllGoods");
return null;
}
}
然后 需要一个 module 来提供 Repository
@module
class UserService{
@provide
UserRepository userRepository(GoodRepository goodRepository) => UserRepository(goodRepository);
}
最后,在上面的 UserPresenter
里使用这个 Repository
@provide
class UserPresenter{
UserRepository _userRepository;
UserPresenter(this._userRepository);
String getUserName() {
_userRepository.getAllUsers();
return "Nike";
}
}
重新运行后,可以看到 UserRepository 也已经被成功注入了
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于