Flutter依赖注入与服务定位插件bilocator的使用

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

Flutter依赖注入与服务定位插件bilocator的使用

bilocator logo

bilocator 是一个 Flutter 混合定位器,它管理全局注册表中的模型(类似于 GetIt)和小部件树中的模型(类似于 Provider、InheritedWidget)。bilocatormvvm+ 状态管理包所使用,因此经过了实战测试。

Bilocator 目标

  • 在全局注册表中定位单个服务。
  • 在小部件树中定位继承的模型。
  • 将单个服务和继承模型的生命周期绑定到小部件。
  • 支持懒加载。
  • 支持模型同时在注册表和小部件树中可定位。
  • 可单独使用或与其他状态管理包(如 RxDart、Provider、GetIt 等)一起使用。
  • 具有可扩展性和高性能,适用于独立开发者和生产应用。

单个服务

单个服务是指那些需要从小部件树的任何地方定位的单个实例。

要将单个服务添加到注册表中,可以给 Bilocator 小部件提供一个构建器,并将其添加到小部件树中:

Bilocator<MyService>(
  builder: () => MyService(),
  child: MyWidget(),
);

如何定位单个服务

可以通过类型从任何地方定位单个服务实例:

final myService = Bilocator.get<MyService>();

如果需要多个相同类型的单个服务实例,可以指定一个唯一的名称:

Bilocator<MyService>(
  builder: () => MyService(),
  name: 'some unique name',
  child: MyWidget(),
);

然后通过类型和名称获取服务:

final myService = Bilocator.get<MyService>(name: 'some unique name');

当需要在一个小部件中管理多个服务时,可以使用 Bilocators

Bilocators(
  delegates: [
    BilocatorDelegate<MyService>(builder: () => MyService()),
    BilocatorDelegate<MyOtherService>(builder: () => MyOtherService()),
  ],
  child: MyWidget(),
);

对于少数需要直接管理注册和注销服务的情况,可以使用静态的 registerunregister 函数:

Bilocator.register<MyService>(builder: () => MyService());

继承模型

继承模型位于小部件树中(类似于 Provider、InheritedWidget)。与单个服务不同,你可以根据需要添加任意数量的相同类型的继承模型。

默认情况下,模型存储在注册表中(location: Location.registry)。要将模型添加到小部件树中,将 location 设置为 Location.tree

Bilocator<MyModel>(
  builder: () => MyModel(),
  location: Location.tree,
  child: MyWidget(),
);

Bilocator 小部件在从小部件树中移除时会注销其服务和模型。如果这些服务和模型是 ChangeNotifiersBilocator 小部件可以选择调用 ChangeNotifiersdispose 方法。

定位继承模型

Bilocator 实现了观察者模式作为混入,可以添加到你的模型和小部件中:

class MyModel with Observer {
  int counter;
}

使用 Observer 的模型和小部件可以 listenTo 继承模型和单个服务。要监听单个服务:

final text = listenTo<MyWidgetViewModel>(listener: myListener).text;

要监听小部件树上的继承模型,添加 context 参数:

final text = listenTo<MyWidgetViewModel>(context: context, listener: myListener).text;

为了方便起见,Observer 还添加了一个 get 函数,不需要前缀 Bilocator 类名。使用 Observer 的模型和小部件可以获取单个服务:

final text = get<MyModel>().text;

并获取继承模型:

final text = get<MyModel>(context: context).text;

使用 .of

.of 函数(如 Theme.of、Provider.of)被认为会在应用中引入不必要的依赖项(从而导致不必要的构建)。因此,建议使用 listenTo。但是,如果你正在从使用 of 的其他库迁移(或只是喜欢使用 of),Bilocator 在其 BuildContext 扩展中包含了 of

final text = context.of<MyModel>().text;

或者,你可以使用 BuildContext 扩展来获取模型而不添加依赖项:

final text = context.get<MyModel>().text;

注册继承模型作为单个服务

要使小部件树上的继承模型对其他分支的小部件可见,可以将继承模型注册为单个服务:

register<MyModel>(context);

注册后,模型将从任何地方可用。当不再需要在注册表中时,简单地注销它:

unregister<MyModel>(context);

示例

以下是一个使用 bilocator 的完整示例。该示例中有三个注册的服务:

  1. ColorNotifier 每隔 N 秒更改一次颜色并调用 notifyListeners
  2. FortyTwoService 持有一个等于 42 的数字。
  3. RandomService 生成一个随机数。

第一个服务使用 Bilocator 添加到小部件树中。其余服务使用 Bilocators 添加。

import 'dart:async';
import 'dart:math';

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

void main() => runApp(myApp());

Widget myApp() => MaterialApp(
      home: Bilocators(
        delegates: [
          BilocatorDelegate<RandomService>(builder: () => RandomService()),
          BilocatorDelegate<ColorNotifier>(builder: () => ColorNotifier()),
        ],
        child: Bilocator(
          builder: () => Counter(),
          location: Location.tree,
          child: const Page(),
        ),
      ),
    );

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

  @override
  State<Page> createState() => _PageState();
}

class _PageState extends State<Page> with Observer {
  @override
  void initState() {
    super.initState();
    get<ColorNotifier>().addListener(() => setState(() {}));
  }

  @override
  void dispose() {
    get<ColorNotifier>().removeListener(() => setState(() {}));
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              context.of<Counter>().count.toString(),
              style: TextStyle(
                fontSize: 64,
                color: listenTo<ColorNotifier>(listener: () => setState(() {})).color.value,
              ),
            ),
            OutlinedButton(
              onPressed: () => context.of<Counter>().count = get<RandomService>().number,
              child: const Text('Set Random'),
            ),
            OutlinedButton(
              onPressed: () => context.of<Counter>().count = 0,
              child: const Text('Clear'),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.of<Counter>().count++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

class Counter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  set count(int value) {
    _count = value;
    notifyListeners();
  }
}

class ColorNotifier extends ChangeNotifier {
  int _counter = 0;
  late final color = ValueNotifier<Color>(Colors.black)..addListener(notifyListeners);

  ColorNotifier() {
    _timer = Timer.periodic(const Duration(seconds: 5), (_) {
      color.value = [Colors.orange, Colors.purple, Colors.cyan][++_counter % 3];
    });
  }

  late Timer _timer;

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

class RandomService {
  int get number => Random().nextInt(100);
}

那就是全部!

如果有任何关于 bilocator 的问题或建议,请随时联系我。


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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用bilocator进行依赖注入和服务定位的详细代码示例。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加bilocator的依赖:

dependencies:
  flutter:
    sdk: flutter
  bilocator: ^4.0.0  # 请确保使用最新版本

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

2. 配置Bilocator

在你的项目根目录下创建一个新的文件,比如dependency_injection.dart,用于配置Bilocator。

import 'package:bilocator/bilocator.dart';

// 假设我们有一个简单的服务类
class MyService {
  void performAction() {
    print("Performing action in MyService");
  }
}

// 配置Bilocator
final getIt = GetIt.instance;

void setupLocator() {
  // 注册服务
  getIt.registerSingleton<MyService>(MyService());
}

3. 初始化Bilocator

在你的应用入口文件(通常是main.dart)中,初始化Bilocator。

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

void main() {
  // 初始化Bilocator
  setupLocator();

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

4. 使用依赖注入

现在,你可以在任何需要的地方通过getIt来获取你的服务实例。

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

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Bilocator Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 获取MyService实例并调用方法
            final myService = getIt<MyService>();
            myService.performAction();
          },
          child: Text('Perform Action'),
        ),
      ),
    );
  }
}

完整代码结构

  • pubspec.yaml:添加依赖
  • dependency_injection.dart:配置Bilocator
  • main.dart:应用入口,初始化Bilocator
  • home_screen.dart(可选,如果分离文件):包含HomeScreen

注意事项

  1. 单例模式:在上面的例子中,MyService是通过registerSingleton方法注册的,这意味着在整个应用中MyService的实例是唯一的。
  2. 懒加载bilocator(或get_it)支持懒加载,即只有在第一次获取实例时才会创建对象。
  3. 清理资源:如果你的服务持有资源(如数据库连接),你可能需要在应用关闭时清理这些资源。可以通过监听应用生命周期事件来实现。

通过这种方式,你可以轻松地在Flutter应用中使用依赖注入和服务定位,从而提高代码的可测试性和模块化程度。

回到顶部