欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

在这里插入图片描述

前言

在构建大型 Flutter 应用时,依赖注入 (Dependency Injection, DI) 是绕不开的话题。

  • ViewModel 依赖 Service。
  • Service 依赖 Repository。
  • Repository 依赖 HttpClient 和 Database。

如果全靠手动 new 和传参,代码会变成一团乱麻(Dependency Hell)。

Flutter 社区有很多 DI 解决方案,如 Provider, GetIt, Riverpod。而 Kiwi 是一个非常轻量级且强大的选择。它最大的特点是支持 代码生成 (Code Generation),这大大减少了手写注册代码的工作量,并且在编译时就能发现依赖错误,而不是等到运行时崩给你看。

对于 OpenHarmony 应用,Kiwi 的纯 Dart 特性使其能够无缝运行,帮助我们将业务逻辑与 UI、原生实现解耦。

一、核心概念:IoC 容器

Kiwi 的核心是一个 IoC (Inversion of Control) 容器。你把所有的工具类、服务类都扔进这个容器里,需要用的时候找容器要。

它支持两种生命周期:

  1. Factory (工厂模式): 每次请求都创建一个新的实例。
  2. Singleton (单例模式): 整个 App 生命周期只创建一次,后续请求返回同一个实例。

请求实例

检查类型

工厂模式

单例模式

Flutter App

Kiwi 容器

注册表

新实例

缓存实例

二、集成与基础用法

2.1 添加依赖

Kiwi 分为运行时核心库和编译时生成器。

dependencies:
  kiwi: ^5.0.1

dev_dependencies:
  build_runner: ^2.4.0
  kiwi_generator: ^5.0.1

2.2 定义注入器 (Injector)

我们需要定义一个抽象类(或接口)来描述“如何注册依赖”。

// injector.dart
import 'package:kiwi/kiwi.dart';

abstract class Injector {
  // 定义一个 setup 方法,用于注册所有依赖
  void setup();
  
  // 也可以定义获取实例的 helper 方法
  static final KiwiContainer container = KiwiContainer();
  static void resolve<T>() => container.resolve<T>();
}

2.3 使用注解自动生成代码

这是 Kiwi 的精髓。使用 @Register.factory@Register.singleton 注解。

// injector.dart
import 'package:kiwi/kiwi.dart';
import 'services/user_service.dart';
import 'services/api_client.dart';

part 'injector.g.dart'; // 这一行很重要!

abstract class Injector {
  .singleton(ApiClient)
  .factory(UserService)
  void setup();
}

class $Injector extends Injector {
  // 这个方法会被生成代码覆盖
  
  void setup() {
    // 自动生成的代码会放在这里
    _$Injector().setup();
  }
}

// 对应的 Service 类
class ApiClient {
  ApiClient();
}

class UserService {
  final ApiClient client;
  // Kiwi 会自动分析构造函数,把 container 中的 ApiClient 注入进来
  UserService(this.client);
}

运行生成命令:

dart run build_runner build

生成后的 injector.g.dart 会自动处理依赖关系:UserService 需要 ApiClient,Kiwi 会自动先解析 ApiClient 并传给 UserService

在这里插入图片描述

2.4 初始化与调用

main.dart 中:

void main() {
  // 1. 初始化容器
  var injector = _$Injector();
  injector.setup();

  // 2. 获取实例
  var userService = KiwiContainer().resolve<UserService>();
  
  runApp(MyApp());
}

在这里插入图片描述

三、OpenHarmony 适配与实战:平台差异化注入

在鸿蒙开发中,我们经常遇到这种情况:

  • 在 Android/iOS 上,使用 plugins_a
  • 在 OpenHarmony 上,因为插件暂不支持,我们需要自己写一个简易版的 Dart 实现,或者调用鸿蒙原生 MethodChannel。

Kiwi 可以通过 命名构造函数子类 完美解决这个问题。

3.1 场景:文件存储服务

假设我们有一个 StorageService

abstract class StorageService {
  Future<void> save(String key, String value);
}

class MobileStorageService implements StorageService {
  
  Future<void> save(String key, String value) {
    print('使用标准移动端存储');
    // ...
    return Future.value();
  }
}

class OhosStorageService implements StorageService {
  
  Future<void> save(String key, String value) {
    print('使用 OpenHarmony 专属沙箱存储');
    // ... 调用 path_provider 获取 el2 加密区
    return Future.value();
  }
}

3.2 根据平台注册 (Conditional Registration)

虽然 Kiwi 的注解是在编译时运行的(无法知道运行时平台),但我们可以在 setup 方法中加入逻辑判断。

注意:kiwi_generator 目前主要做静态分析。对于动态逻辑,混合使用 手动注册 是最佳实践。

import 'dart:io'; // 用于 Platform.isFuchsia 等判断
import 'package:kiwi/kiwi.dart';

class AppInjector {
  void setup() {
    final container = KiwiContainer();
    
    // 1. 生成部分(通用依赖)
    _$SharedInjector().setup();
    
    // 2. 手动部分(平台特定依赖)
    // 注意:OpenHarmony 的 dart:io Platform 检测可能还在完善中
    // 实际项目中建议结合 device_info_plus 或自定义 flag
    if (Platform.operatingSystem == 'ohos' || Platform.isFuchsia) { // 假设 ohos 标识
      container.registerFactory<StorageService>((c) => OhosStorageService());
    } else {
      container.registerFactory<StorageService>((c) => MobileStorageService());
    }
  }
}

// 通用的部分依然用 generating
abstract class SharedInjector {
  .singleton(ApiClient)
  void setup();
}

这样,当你的 App 跑在鸿蒙设备上时,UI 获取到的 StorageService 自动就是 OhosStorageService,业务层代码完全不需要改动。

在这里插入图片描述

四、进阶技巧

4.1 命名实例 (Named Instances)

有时候你需要同一个类的两个不同实例(例如:两个 HttpClient,一个连内网,一个连外网)。

.singleton(HttpClient, name: 'internal')
.singleton(HttpClient, name: 'external')
void setup();

// 获取
var client = container.resolve<HttpClient>('internal');

4.2 模块化 (Modules)

不要把所有依赖都写在一个 Injector 类里。可以按功能模块拆分:

  • UserInjector
  • ProductInjector
  • CartInjector

然后在 main 中依次调用它们的 setup

4.3 配合 Flutter Bloc

Kiwi 和 Bloc 是绝配。

// 在 Bloc 初始化时注入 Service
class UserBloc extends Bloc<UserEvent, UserState> {
  final UserService _service;
  
  // 默认构造函数让 Kiwi 自动注入
  UserBloc(this._service) : super(InitialState());
}

// 注册 Bloc
.factory(UserBloc)
void setup();

// 在 UI 中使用
BlocProvider(
  create: (context) => KiwiContainer().resolve<UserBloc>(),
  child: UserPage(),
);

五、总结

Kiwi 是一个显得“大巧不工”的库。它没有复杂的 Context 依赖(像 Provider),也没有全局的状态污染。它只是单纯地做好 对象创建与管理 这一件事。

对于 OpenHarmony 开发者,Kiwi 是实现 架构整洁 (Clean Architecture) 的有力帮手。它帮助我们将鸿蒙特有的底层实现(如基于 arkui plugin 的实现)隔离在 DI 层,保证上层 Dart 业务逻辑的纯粹性与可移植性。

最佳实践

  1. 构造函数注入:尽量使用构造函数参数来声明依赖,利用 Kiwi 的自动解析能力。
  2. 区分 Factory 与 Singleton:无状态的服务(Service/Repo)用 Singleton,有状态的对象(Bloc/ViewModel)用 Factory。
  3. 重新生成:每次修改了依赖关系,记得重新运行 build_runner

六、完整实战示例

import 'package:kiwi/kiwi.dart';

// === 业务类定义 ===
abstract class ApiService { void fetch(); }
class RealApiService extends ApiService { 
   void fetch() => print('Calling Real API...'); 
}

abstract class Repository { void getData(); }
class DataRepository extends Repository { 
  final ApiService api; 
  DataRepository(this.api); // 依赖注入点
  
   void getData() { 
    print('Repo getting data...'); 
    api.fetch(); 
  }
}

// === 注入器定义 ===
abstract class Injector {
  // 标记这是一个 Singleton,且 Kiwi 会自动递归解析其依赖 (ApiService)
  .singleton(Repository, from: DataRepository)
  .singleton(ApiService, from: RealApiService)
  void configure(); 
}

// === 生成的代码部分 (通常在 .g.dart 中,这里手动模拟) ===
class _$Injector extends Injector {
  
  void configure() {
    final container = KiwiContainer();
    container.registerSingleton((c) => RealApiService());
    // 自动注入 ApiService
    container.registerSingleton((c) => DataRepository(c.resolve<ApiService>()));
  }
}

void main() {
  // 1. 初始化 (App启动时)
  var injector = _$Injector();
  injector.configure();

  // 2. 使用 (通常在 Provider 或 Bloc 初始化时)
  var repo = KiwiContainer().resolve<Repository>();
  repo.getData(); 
  
  // Output: 
  // Repo getting data...
  // Calling Real API...
}

在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐