Flutter依赖注入插件flutter_ioc_container的使用
Flutter依赖注入插件flutter_ioc_container的使用
flutter_ioc_container

flutter_ioc_container 是一个用于Flutter的依赖注入库,它允许你在widget树中管理依赖关系,并可以从BuildContext访问它们。你还可以用测试替身替换依赖关系进行测试。
ioc_container 是一个Dart的依赖注入和服务定位库。你可以像使用GetIt包一样在Flutter中使用它。flutter_ioc_container是ioc_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
基本用法
- 在你的widget树的根部放置一个
CompositionRootwidget。这会将容器作为继承widget传播到整个widget树。 - 使用
compose函数中的builder向容器添加单例或瞬态依赖。 - 通过
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
更多关于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进行基本的依赖注入。根据你的需求,你可以进一步扩展这个示例,比如注册多个服务、使用不同的生命周期管理策略等。
        
      
            
            
            
