Flutter依赖注入插件qinject的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter依赖注入插件qinject的使用

Qinject简介

Qinject是一个快速、灵活的Dart和Flutter应用程序开发的IoC(控制反转)库。它可以帮助您轻松地使用依赖注入(DI)和服务定位器(Service Locator)方法来开发应用程序。

主要特性

  • 支持依赖注入(DI)
  • 支持服务定位器
  • 隐式依赖链解析;无需定义和维护“依赖于”关系
  • 简单但极其灵活的依赖注册和解析机制
  • 可以注册任何类型的依赖,从Flutter小部件和函数到简单的类
  • 简单但强大的单元测试工具

安装

Qinject包可以在pub.dev上找到,并可以通过以下命令安装:

dart pub add qinject

或者

flutter pub add qinject

快速开始

下面的例子展示了如何在一个基本应用中同时使用DI和服务定位器模式,以及单例和解析器依赖注册。这些主题在本文档后面会有更详细的介绍。

main() {
  // 使用Qinject实例支持DI,通过将其作为构造函数参数传递给定义依赖项的类
  final qinjector = Qinject.instance();

  // 注册一个单例
  Qinject.registerSingleton(() => stdout);
  
  // 注册一个解析器
  Qinject.register((_) => DateTimeReader());
  
  // 注册Greeter类,需要Qinjector实例
  Qinject.register((_) => Greeter(qinjector));

  // 解析Greeter实例并调用其greet方法
  Qinject.use<void, Greeter>().greet();
}

class Greeter {
  // 使用服务定位器风格的解析
  final Stdout _stdout = Qinject.use<Greeter, Stdout>();

  // 使用DI风格的依赖解析
  final DateTimeReader _dateTimeReader;

  Greeter(Qinjector qinjector)
      : _dateTimeReader = qinjector.use<Greeter, DateTimeReader>();

  void greet() => _stdout.writeln("Right now it's ${_dateTimeReader.now()}");
}

class DateTimeReader {
  DateTime now() => DateTime.now();
}

依赖注册

有两种高层次的依赖注册类型:单例(Singleton)和解析器(Resolver)。单例在第一次解析时被评估一次,然后在应用程序的生命周期内返回相同的实例。

单例注册示例如下:

Qinject.registerSingleton(() => MyClass());

use<TConsumer, MyClass>()首次调用时,将返回一个新的MyClass实例。随后调用use<TConsumer, MyClass>()时,将返回同一原始实例。

依赖使用

所有类型的依赖都通过相同的方法use<TConsumer, TDependency>进行解析。这可以从Qinject服务定位器或通过注入到类中的Qinjector实例调用。两种方法可以互换使用,因为任何已注册的依赖都可以通过任一路由访问。

使用服务定位器

服务定位器是一种简单地解耦依赖的模式。其使用基于全局可访问的服务定位器实例,在Qinject的情况下,这是Qinject类型本身,它公开了一个静态的use<TConsumer, TDependency>方法。

可以从任何可以访问Qinject类型的任何地方以如下方式解析依赖:

final DependencyType _dependency = Qinject.use<ConsumerType, DependencyType>();

对于一些复杂的应用程序,使用服务定位器可能会使测试变得困难,特别是在并发执行逻辑时。这是因为单一的全局实例需要配置适合给定测试运行的所有测试的测试替身,或者反复配置和清理。对于这样的应用程序,DI可能是更好的选择。

使用DI(依赖注入)

采用DI时,类在其构造函数中声明其依赖项,并暴露这些变量的setter。这些变量由外部实体设置,该实体负责选择、创建和管理这些抽象依赖类型的具体实现的生命周期。

下面是一个示例,说明了如何在构造函数中声明依赖项并通过注入的Qinjector实例填充它们。

main() {
    // 获取Qinjector实例
    final qinjector = Qinject.instance();

    // 注册ConsumerClass及其依赖项
    Qinject.register((_) => ConsumerClass(qinjector));
    Qinject.register((_) => DependencyA(qinjector));
    Qinject.register((_) => DependencyB());
    Qinject.register((_) => DependencyC(qinjector));
    
    final consumerClass = Qinject.use<void, ConsumerClass>(); // 解析ConsumerClass实例

    // 对consumerClass进行操作
}

class ConsumerClass {
  final DependencyA _dependencyA;
  final DependencyB _dependencyB;
  final DependencyC _dependencyC;

  ConsumerClass(Qinjector qinjector)
      : _dependencyA = qinjector.use<ConsumerClass, DependencyA>(),
        _dependencyB = qinjector.use<ConsumerClass, DependencyB>(),
        _dependencyC = qinjector.use<ConsumerClass, DependencyC>();

  // 对依赖项进行操作
}

单元测试与Qinjector

可以使用TestQinjector实例注册依赖项的测试替身,以帮助进行单元测试。TestQinjector实例可以代替默认的Qinjector实例传递给依赖项消费者。

例如,如果测试上述的Qinjector应用程序,则可以定义如下:

main() {
    test('ConsumerClass behaves as expected', () {
        // 创建一个TestQinjector实例,实现Qinjector接口
        final qinjector = TestQinjector();

        // 在TestQinjector实例上注册存根或模拟
        qinjector.registerTestDouble<DependencyA>((_) => DependencyAStub());
        qinjector.registerTestDouble<DependencyB>((_) => DependencyBMock()); 
        qinjector.registerTestDouble<DependencyC>((_) => DependencyCStub());
        
        final consumerClass = ConsumerClass(qinjector); // 使用TestQInjector创建ConsumerClass实例

        // 对consumerClass进行断言
    });
}

示例代码

以下是从示例项目中提取的主要文件示例:

import 'dart:io';

import 'package:qinject/qinject.dart';

import 'app.dart';
import 'datetime_reader.dart';
import 'greeter.dart';
import 'name_formatter.dart';

main() {
  final qinjector = Qinject.instance();

  // 注册一个Transient Resolver
  Qinject.register((_) => NameFormatter());

  // 注册一个Factory Resolver
  Qinject.register((_) => (String target) => Greeter(qinjector, target));

  // 注册一个Type Sensitive Resolver
  Qinject.register((Type consumer) => consumer.runtimeType == Greeter
      ? DateTimeReaderNow()
      : DateTimeReaderTomorrow());

  // 注册单例
  Qinject.registerSingleton(() => stdout);
  Qinject.registerSingleton(() => stdin);

  App(qinjector).run();
}

更多关于Flutter依赖注入插件qinject的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter依赖注入插件qinject的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用qinject插件进行依赖注入的代码示例。qinject是一个轻量级的依赖注入框架,它允许你在Flutter应用中轻松管理和注入依赖。

首先,确保你的pubspec.yaml文件中已经添加了qinject依赖:

dependencies:
  flutter:
    sdk: flutter
  qinject: ^最新版本号  # 请替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来,我们将创建一个简单的示例,展示如何使用qinject进行依赖注入。

1. 创建一个服务类

首先,我们创建一个简单的服务类,比如一个数据服务DataService

// data_service.dart
class DataService {
  String fetchData() {
    return "Hello from DataService!";
  }
}

2. 配置依赖注入容器

然后,我们需要配置qinject的依赖注入容器。这通常在你的应用入口文件(比如main.dart)中进行:

// main.dart
import 'package:flutter/material.dart';
import 'package:qinject/qinject.dart';
import 'data_service.dart';

void main() {
  // 创建依赖注入容器
  final container = Container();

  // 注册依赖
  container.registerSingleton<DataService>(DataService());

  // 使用依赖注入的应用
  runApp(MyApp(container: container));
}

class MyApp extends StatelessWidget {
  final Container container;

  MyApp({required this.container});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(container: container),
    );
  }
}

3. 在组件中使用依赖注入

现在,我们可以在我们的组件中使用依赖注入来获取DataService的实例:

// my_home_page.dart
import 'package:flutter/material.dart';
import 'package:qinject/qinject.dart';
import 'data_service.dart';

class MyHomePage extends StatelessWidget {
  final Container container;

  MyHomePage({required this.container});

  @override
  Widget build(BuildContext context) {
    // 获取DataService实例
    final dataService = container.resolve<DataService>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Text(dataService.fetchData()),
      ),
    );
  }
}

完整代码结构

你的项目结构可能类似于这样:

lib/
├── main.dart
├── data_service.dart
└── my_home_page.dart

总结

以上代码展示了如何在Flutter项目中使用qinject进行依赖注入。我们首先创建了一个服务类DataService,然后在main.dart中配置了依赖注入容器并注册了服务,最后在组件中使用依赖注入来获取服务实例。

请根据你的实际需求调整代码,并确保qinject插件的版本是最新的,以避免潜在的兼容性问题。

回到顶部