在Dart和Flutter工程中,为一个组件提供对象/服务的默认方式是通过InheritedWidget。

还有Provider、Singleton、IoC等方式。

1.1 InheritedWidget

如果希望一个部件或其模型能够访问服务,则组件必须是继承的组件的子组件。然后这会导致不必要的嵌套。而且依赖性强,持续性维护差。

///创建数据类 DataWidget,然后在页面中用数据类DataWidget包含页面child部分
///1、创建 DataWidget
class DataWidget extends InheritedWidget {
  DataWidget({
    @required this.data,
    Widget child
  }) :super(child: child);
  final int data;
  static DataWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<DataWidget>();
  }
  @override
  bool updateShouldNotify(DataWidget old) {
    return old.data != data;
  }
}

///2、创建 InheritedWidgetTestRoute.dart 文件
class InheritedWidgetTestRoute extends StatefulWidget {
  @override
  _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return  Center(
      child: DataWidget( // 重点:使用DataWidget包裹
        data: count,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(bottom: 20.0),
              child: Text(DataWidget.of(context)
              .data.toString())
            ),
            RaisedButton(
              child: Text("Increment"),
              //每点击一次,将count自增,然后更新页面渲染,DataWidget的data将被更新  
              onPressed: () => setState(() => ++count),
            )
          ],
        ),
      ),
    );
  }
}
复制代码

1.2 Provider

为组件提供对象/服务,还有一种方式是使用Provider,使用比较繁琐。

///创建数据类 `CountNotifier`,然后在页面中用数据类 `CountNotifier`包裹child部分。
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 创建 Widget 持有 CountNotifier 数据
    return ChangeNotifierProvider.value(
      value: CountNotifier(),
      child: MaterialApp(
        title: 'Privoder Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: ProvidePage(title: 'Provider 测试页面'),
      ),
    );
  }
}

class ProvidePage extends StatelessWidget {
  final String title;
  ProvidePage({Key key, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 获取 CountNotifier 数据 (最简单的方式)
    final counter = Provider.of<CountNotifier>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text( '按下按钮,使数字增加:', ),
            Text(
              '${counter.count}',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.increment();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

/// 核心:继承自ChangeNotifier
/// 可以单独放在一个类文件里
class CountNotifier with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  increment() {
    _count++;
    // 核心方法,通知刷新UI,调用build方法
    notifyListeners();
  }
}
复制代码

1.3 Singleton、IoC

我们也可以通过其他的方式在App中的任意位置获取到要访问的对象,但是:

  • 如果使用Singleton,则无法轻松地将实现切换到另一个(例如用于单元测试的模拟版本)

  • 用于依赖项注入的IoC容器提供了类似的功能,但代价是启动时间慢且可读性差,因为不知道神奇注入的对象来自何处。 由于大多数IoC库都依赖反射,因此它们不能与Flutter一起使用。

1.4 GetIt

在App迭代发展中,随着代码工程的增长,在某些时候需要将App的部分逻辑放在与Widget分离的类中。使Widget不具有直接依赖关系可以使代码更好地组织并更易于测试和维护。但是现在需要一种从 UI 代码访问这些对象的方法。

作者escamoteur 借鉴.net中的Service Locator Splat概念,在Dart中开发而成。

Service Locators 的概念,它是一种将接口(抽象基类)与具体实现解耦的方法,同时允许通过接口从您的 App 中的任何地方访问具体实现。

故GetIt应运而生,从1.0到现在的7.x

二、介绍

2.1 定义

[GetIt] 官方介绍

This is a simple Service Locator for Dart and Flutter projects with some additional goodies highly inspired by Splat. It can be used instead of InheritedWidget or Provider to access objects e.g. from your UI.

Typical usage:

  • Accessing service objects like REST API clients or databases so that they easily can be mocked.
  • Accessing View/AppModels/Managers/BLoCs from Flutter Views

V7.0 has some breaking changes Check please check the release notes to see what's new.

译文:

GetIt是一个用于 Dart 和 Flutter 项目的简单服务定位器,其中包含一些受到 Splat 启发的附加功能。 它可以用来代替 InheritedWidget 或 Provider 比如从你的用户界面来访问对象。

典型用法(使用场景):

  • 访问 REST API 客户端或数据库等服务对象,以便轻松模拟它们
  • 从 Flutter 视图访问 View/AppModels/Managers/BLoCs

简而言之

GetIt是一个简单的直接服务定位器,允许将接口与具体实现分离,并从应用程序的任何地方访问具体实现。

一句话总结

GetIt是一个工具箱。

2.2 特点

  • 调用极快 (复杂度O(1))
  • 易学/易用
  • 不会像提供程序或 Redux 那样使用特殊的小部件来使您的 UI 树混乱以访问您的数据。

Redux 的设计思想很简单,就两句话。

(1)Web 应用是一个状态机,视图与状态是一一对应的。

(2)所有的状态,保存在一个对象里面。

三、使用

3.1 引入

在某包下的pubspec.yaml引入框架

dependencies:
  flutter:
    sdk: flutter
  domain_common:
    path: ../domain_common
  res_common:
    path: ../res_common
  provider: ^5.0.0
  get_it: ^7.1.3
复制代码