Flutter依赖注入插件flutter_ioc_container的使用

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

Flutter依赖注入插件flutter_ioc_container的使用

flutter_ioc_container

ioc_container

flutter_ioc_container 是一个用于Flutter的依赖注入库,它允许你在widget树中管理依赖关系,并可以从BuildContext访问它们。你还可以用测试替身替换依赖关系进行测试。

ioc_container 是一个Dart的依赖注入和服务定位库。你可以像使用GetIt包一样在Flutter中使用它。flutter_ioc_containerioc_container的一个扩展,它在整个widget树中暴露了该库的功能,使你可以像使用Provider一样使用它。它提供了对BuildContext的扩展方法,让你可以在widget树中的任何位置获取依赖实例。

以下是一个简单的示例,展示了如何使用CounterController来增加和获取当前值:

FloatingActionButton.extended(
    icon: const Icon(Icons.add),
    //Increment the value
    onPressed: context<CounterController>().increment,
    label: Text(
    //Display the value
    context<CounterController>().value.toString(),
    style: Theme.of(context).textTheme.headlineMedium,
    ),
),

更多详细信息请参阅 ioc_container 文档。

开始使用

安装插件

在你的pubspec.yaml文件的dependencies部分添加以下行:

flutter_ioc_container: <latest version>

运行flutter pub get以下载依赖项。

或者,你可以通过命令行安装该包:

flutter pub add flutter_ioc_container

基本用法

  1. 在你的widget树的根部放置一个CompositionRoot widget。这会将容器作为继承widget传播到整个widget树。
  2. 使用compose函数中的builder向容器添加单例或瞬态依赖。
  3. 通过BuildContext在整个widget树中访问依赖。

以下是基本使用的完整示例:

import 'package:flutter/material.dart';
import 'package:flutter_ioc_container/flutter_ioc_container.dart';

void main() {
  runApp(
    CompositionRoot(
      compose: (builder) => builder.addSingleton((container) => 'test'),
      child: MaterialApp(
        home: Scaffold(
          body: Builder(builder: (context) => const BasicWidget()),
        ),
      ),
    ),
  );
}

class BasicWidget extends StatelessWidget {
  const BasicWidget({super.key});

  @override
  Widget build(BuildContext context) => Text(context<String>());
}

作用域

如果你需要一组生命周期短且之后需要释放的依赖,那么widget树中的某个地方需要持有scoped容器。通过调用context.scoped()获取scoped容器。一种方法是将其放在StatefulWidget的状态中,并在状态的dispose()方法中释放内容。

这个例子在didChangeDependencies中创建了一个scoped容器。它存在于状态的生命周期内,并在小部件树处置此小部件时释放资源。

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_ioc_container/flutter_ioc_container.dart';
import 'package:ioc_container/ioc_container.dart';

class DisposableResources {
  String display = 'hello world';

  void dispose() {
    // ignore: avoid_print
    print('Disposed');
  }
}

void main() {
  runApp(
    CompositionRoot(
      compose: (builder) => builder.addServiceDefinition<DisposableResources>(
        ServiceDefinition(
          (container) => DisposableResources(),
          dispose: (service) => service.dispose(),
        ),
      ),
      child: MaterialApp(
        home: Scaffold(
          body: Builder(builder: (context) => const BasicWidget()),
        ),
      ),
    ),
  );
}

class BasicWidget extends StatefulWidget {
  const BasicWidget({super.key});

  @override
  State<BasicWidget> createState() => _BasicWidgetState();
}

class _BasicWidgetState extends State<BasicWidget> {
  late final IocContainer scopedContainer;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    scopedContainer = context.scoped();
  }

  @override
  void dispose() {
    super.dispose();
    unawaited(scopedContainer.dispose());
  }

  @override
  Widget build(BuildContext context) =>
      Text(scopedContainer<DisposableResources>().display);
}

更多关于作用域的信息,请参见这里

异步注入

如果依赖需要异步初始化,可以使用addAsync。你可以使用FutureBuilder widget在对象可用时渲染它。以下是一个示例:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_ioc_container/flutter_ioc_container.dart';

void main() {
  runApp(
    CompositionRoot(
      compose: (builder) => builder.addSingletonAsync(
        (container) async => Future<String>.delayed(
          const Duration(seconds: 5),
          () => 'Hello world!',
        ),
      ),
      child: MaterialApp(
        home: Scaffold(
          body: Builder(builder: (context) => const BasicAsyncWidget()),
        ),
      ),
    ),
  );
}

class BasicAsyncWidget extends StatefulWidget {
  const BasicAsyncWidget({super.key});

  @override
  State<BasicAsyncWidget> createState() => _BasicAsyncWidgetState();
}

class _BasicAsyncWidgetState extends State<BasicAsyncWidget> {
  late final Future<String> future;

  @override
  void didChangeDependencies() {
    // ignore: discarded_futures
    future = context.getAsync<String>();
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) => FutureBuilder(
        // ignore: discarded_futures
        future: future,
        builder: (ctx, ss) => ss.connectionState == ConnectionState.done
            ? Text(ss.data!)
            : const CircularProgressIndicator(),
      );
}

更多关于异步注入的信息,请参见这里

用测试替身替换依赖以进行测试

可以通过将configureOverrides函数传递给根widget来实现。这允许你用测试替身替换依赖。有关完整示例,请参阅小部件测试

class MyApp extends StatelessWidget {
  const MyApp({
    super.key,
    this.configureOverrides,
  });

  //This allows us to override the dependencies for testing. Take a look at
  //the widget tests
  final void Function(IocContainerBuilder builder)? configureOverrides;

  @override
  Widget build(BuildContext context) => CompositionRoot(
        configureOverrides: configureOverrides,
        compose: (builder) => builder
          //Adds a singleton CounterController to the container
          ..addSingleton(
            (container) => CounterController(),
          ),
        // [...] See the example folder of this package for a full example
      );
}

以下是如何用MockValueNotifier替换依赖的例子:

testWidgets('Basic Smoke Test', (tester) async {
    final mockValueNotifier = MockValueNotifier();

    await tester.pumpWidget(
      MyApp(
        //This is how you substitute dependencies with test doubles
        configureOverrides: (builder) => builder
            .addSingleton<CounterController>((container) => mockValueNotifier),
      ),
    );

    //Initial value
    expect(find.text('0'), findsOneWidget);

    //Tap the button
    await tester.tap(find.byIcon(Icons.add));
    await tester.pumpAndSettle();

    //Verify value
    expect(find.text('1'), findsOneWidget);
    expect(find.text('0'), findsNothing);

    //Ensure we're using the mock dependency
    expect(mockValueNotifier.hasCalls, isTrue);
  });

更多关于测试的信息,请参见这里


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

1 回复

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


当然,以下是一个关于如何在Flutter项目中使用flutter_ioc_container插件进行依赖注入的示例代码。这个插件允许你创建和管理依赖关系,从而使代码更加模块化和易于测试。

首先,你需要在pubspec.yaml文件中添加flutter_ioc_container依赖:

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

然后运行flutter pub get来获取依赖。

接下来,我们创建一个简单的例子来展示如何使用flutter_ioc_container

1. 定义服务接口和实现

首先,定义一个服务接口MyService以及它的实现MyServiceImpl

// my_service.dart
abstract class MyService {
  String getData();
}

// my_service_impl.dart
class MyServiceImpl implements MyService {
  @override
  String getData() {
    return "Hello from MyService!";
  }
}

2. 设置依赖注入容器

在你的应用程序入口文件(通常是main.dart)中,设置依赖注入容器并注册服务。

import 'package:flutter/material.dart';
import 'package:flutter_ioc_container/flutter_ioc_container.dart';
import 'my_service.dart';
import 'my_service_impl.dart';

void main() {
  // 创建并配置IoC容器
  final iocContainer = IoCContainer();
  iocContainer.registerSingleton<MyService>(MyServiceImpl());

  // 使用Builder来提供依赖
  runApp(Builder(
    builder: (context) {
      return MaterialApp(
        home: MyHomePage(iocContainer: iocContainer),
      );
    },
  ));
}

class MyHomePage extends StatelessWidget {
  final IoCContainer iocContainer;

  MyHomePage({required this.iocContainer});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter IoC Container Example'),
      ),
      body: Center(
        child: Text(
          // 从IoC容器中获取MyService实例并调用getData方法
          iocContainer.get<MyService>().getData(),
        ),
      ),
    );
  }
}

3. 使用依赖

在上面的例子中,我们在MyHomePage构造函数中接收了IoCContainer实例,并通过iocContainer.get<MyService>()来获取MyService的实现。

4. 运行应用程序

现在你可以运行你的Flutter应用程序,应该会看到一个屏幕显示文本“Hello from MyService!”。

注意事项

  • 确保你已经正确导入了所有必要的包。
  • 在实际应用中,你可能需要在多个地方使用依赖注入,因此可以将iocContainer传递给需要的组件,或者考虑使用全局状态管理工具(如Provider)来管理iocContainer的实例。

这个示例展示了如何使用flutter_ioc_container进行基本的依赖注入。根据你的需求,你可以进一步扩展这个示例,比如注册多个服务、使用不同的生命周期管理策略等。

回到顶部