Flutter作用域管理插件dart_scope的使用

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

Flutter作用域管理插件dart_scope的使用

dart_scope 是一个声明式依赖注入库,它使用 Dart 语法和 Flutter 风格。以下是该插件的一些特性:

  • 完全基于 Dart 实现,不依赖于 Flutter。
  • 配置与 Dart 语言的语法对齐。
  • 配置与 Flutter 小部件的风格对齐。
  • 作用域策略与函数的作用域对齐。
  • 可以处理异步设置。

目录

快速浏览

让我们通过一些快速示例来探索 dart_scope 的用法。假设我们有以下类:

class Repository {
  // ...实现细节
}

class AppNotifier {
  AppNotifier({
    required this.repository,
  });
  final Repository repository;

  // ...实现细节

  void dispose() {}
}

使用 Scope.root(...)

使用 Scope.root(...) 创建顶级作用域并配置:

Future<void> scopeRootExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(name: 'repository', equal: (scope) => Repository()),
    Final<AppNotifier>(name: 'appNotifier', equal: (scope) => AppNotifier(
      repository: scope.get<Repository>(name: 'repository'),
    )),
  ]);

  // 解析实例
  final myRepository = rootScope.get<Repository>(name: 'repository');
  final myAppNotifier = rootScope.get<AppNotifier>(name: 'appNotifier');

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
}

使用 name

使用不同的名称创建多个实例:

Future<void> multipleNamesExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(name: 'repository1', equal: (scope) => Repository()),
    Final<Repository>(name: 'repository2', equal: (scope) => Repository()),
    Final<Repository>(name: 'repository3', equal: (scope) => Repository()),
  ]);

  final myRepository1 = rootScope.get<Repository>(name: 'repository1');
  final myRepository2 = rootScope.get<Repository>(name: 'repository2');
  final myRepository3 = rootScope.get<Repository>(name: 'repository3');

  print('myRepository1: $myRepository1');
  print('myRepository2: $myRepository2');
  print('myRepository3: $myRepository3');
}

Scope.root(...) 异步设置

如果需要异步设置(如解析 SharedPreferences),可以这样做:

Future<Repository> createRepositoryAsync() async {
  await Future<void>.delayed(Duration(seconds: 1));
  return Repository();
}

Future<void> scopeRootAsyncExample() async {
  final rootScope = await Scope.root([
    AsyncFinal<Repository>(equal: (scope) async {
      return await createRepositoryAsync();
    }),
    Final<AppNotifier>(equal: (scope) => AppNotifier(
      repository: scope.get<Repository>(),
    )),
  ]);

  final myRepository = rootScope.get<Repository>();
  final myAppNotifier = rootScope.get<AppNotifier>();

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
}

使用 scope.push(...)

使用 scope.push(...) 创建新的子作用域。子作用域继承父作用域中的获取器:

class AddTodoNotifier {}

Future<void> scopePushExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(equal: (scope) => Repository()),
    Final<AppNotifier>(equal: (scope) => AppNotifier(
      repository: scope.get<Repository>(),
    )),
  ]);

  // 创建子作用域
  final childScope = await rootScope.push([
    Final<AddTodoNotifier>(equal: (scope) => AddTodoNotifier()),
  ]);

  // 在子作用域中解析实例
  final myRepository = childScope.get<Repository>();
  final myAppNotifier = childScope.get<AppNotifier>();
  final myAddTodoNotifier = childScope.get<AddTodoNotifier>();

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
  print('myAddTodoNotifier: $myAddTodoNotifier');
}

使用 scope.has<T>(...)

使用 scope.has<T>(...) 检查是否已暴露实例:

Future<void> scopeHasExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(equal: (scope) => Repository()),
    Final<AppNotifier>(equal: (scope) => AppNotifier(
      repository: scope.get<Repository>(),
    )),
  ]);

  final childScope = await rootScope.push([
    Final<AddTodoNotifier>(equal: (scope) => AddTodoNotifier()),
  ]);

  // 检查父作用域
  print(rootScope.has<Repository>());       // true
  print(rootScope.has<AppNotifier>());      // true
  print(rootScope.has<AddTodoNotifier>());  // false

  // 检查子作用域
  print(childScope.has<Repository>());      // true
  print(childScope.has<AppNotifier>());     // true
  print(childScope.has<AddTodoNotifier>()); // true
}

使用 scope.getOrNull<T>(...)

使用 scope.getOrNull<T>(...) 安全地解析实例。此方法在实例未被暴露时返回 null

Future<void> scopeGetOrNullExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(equal: (scope) => Repository()),
    Final<AppNotifier>(equal: (scope) => AppNotifier(
      repository: scope.get<Repository>(),
    )),
  ]);

  final childScope = await rootScope.push([
    Final<AddTodoNotifier>(equal: (scope) => AddTodoNotifier()),
  ]);

  print(rootScope.getOrNull<Repository>());       // Instance of 'Repository'
  print(rootScope.getOrNull<AppNotifier>());      // Instance of 'AppNotifier'
  print(rootScope.getOrNull<AddTodoNotifier>());  // null

  print(childScope.getOrNull<Repository>());      // Instance of 'Repository'
  print(childScope.getOrNull<AppNotifier>());     // Instance of 'AppNotifier'
  print(childScope.getOrNull<AddTodoNotifier>()); // Instance of 'AddTodoNotifier'
}

使用 scope.dispose()

作为 scope.push 的相反操作,作用域也可以被销毁/弹出。我们可以注册销毁逻辑,当作用域被销毁时运行:

Future<void> scopeDisposeExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(equal: (scope) => Repository()),
    Final<AppNotifier>(
      equal: (scope) => AppNotifier(
        repository: scope.get<Repository>(),
      ),
      // 注册销毁实例逻辑
      dispose: (appNotifier) => appNotifier.dispose(),
    ),
  ]);

  // 销毁作用域将同时销毁 `appNotifier`
  rootScope.dispose();
}

(非)懒惰赋值

实例默认是懒惰赋值的,这意味着它们将在第一次访问时分配。如果我们需要立即分配,只需将 lazy 设置为 false

Future<void> nonLazyFinalExample() async {
  final rootScope = await Scope.root([
    Final<Repository>(
      equal: (scope) => Repository(),
      lazy: false // 设置为 false
    ),
    Final<AppNotifier>(
      equal: (scope) => AppNotifier(
        repository: scope.get<Repository>(),
      ),
      lazy: false // 设置为 false
    ),
  ]);

  final myRepository = rootScope.get<Repository>();
  final myAppNotifier = rootScope.get<AppNotifier>();

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
}

高级功能

我们已经涵盖了 dart_scope 的基本部分:

  • 配置与 Dart 语言的语法对齐。
  • 作用域策略与函数的作用域对齐。
  • 可以处理异步设置。

接下来,我们将探索高级功能:

  • 配置与 Flutter 小部件的风格对齐。
  • 配置可组合/可分解。

配置

我们已经使用 Scope.rootscope.push 创建了新的作用域:

class Scope {
  // Scope.root(...)
  static FutureOr<Scope> root(List<Configurable> configure);

  // scope.push(...)
  FutureOr<Scope> push(List<Configurable> configure);
  ...
}

创建 Scope 需要配置,称为 Configurable

abstract class Configurable {
  FutureOr<void> configure(ConfigurableScope scope);
}

Configurable 是一个接口,要求有一个 configure 方法。让我们通过一些示例来了解其工作原理。

内联配置

以前,我们见过这个示例:

Future<void> example() async {
  final rootScope = await Scope.root([
    Final<Repository>(equal: (scope) => Repository()),
    Final<AppNotifier>(
      equal: (scope) => AppNotifier(
        repository: scope.get<Repository>(),
      ),
      dispose: (appNotifier) => appNotifier.dispose(),
    ),
  ]);

  final myRepository = rootScope.get<Repository>();
  final myAppNotifier = rootScope.get<AppNotifier>();
}

我们可以使用内联 Configurable 达到相同的效果:

Future<void> configurableInlineExample() async {
  final rootScope = await Scope.root([
    // 内联 `Configurable`
    Configurable((scope) {
      // 构建依赖图
      late final Repository repository = Repository();
      late final AppNotifier appNotifier = AppNotifier(
        repository: repository,
      );
      // 在当前作用域中暴露实例
      scope.expose<Repository>(expose: () => repository);
      scope.expose<AppNotifier>(expose: () => appNotifier);
      // 注册销毁逻辑
      scope.addDispose(() {
        appNotifier.dispose();
      });
      // 完成
    }),
  ]);

  final myRepository = rootScope.get<Repository>();
  final myAppNotifier = rootScope.get<AppNotifier>();

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
}

内联 Configurable 使用闭包 (scope) { ... } 来以自定义方式配置当前作用域,步骤包括:

  1. 使用赋值构建依赖图 late final Repository repository = Repository();
  2. 使用 scope.expose(...) 暴露实例
  3. 使用 scope.addDispose(...) 注册销毁逻辑

此闭包仅在作用域创建期间运行一次。它用于以可定制的方式配置作用域。内联 Configurable 仅为了方便使用,如果需要扩展,可以创建实现 Configurable 接口的类。

分解配置

一般来说,高层配置可以拆分为低层 Configurable,这样更容易重用和组合。这就是 Final 的来源,以及它是如何工作的:

class MyFinal<T> implements Configurable {

  MyFinal({
    this.name,
    required this.equal,
    this.dispose,
    this.lazy = true,
  });

  final Object? name;
  final T Function(ScopeGet scope) equal;
  final void Function(T)? dispose;
  final bool lazy;

  @override
  FutureOr<void> configure(ConfigurableScope scope) {

    final T Function() getValue;
    if (lazy) {
      late final instance = equal(scope);
      getValue = () => instance;
    } else {
      final instance = equal(scope);
      getValue = () => instance;
    }

    scope.expose<T>(name: name, expose: getValue);
    
    if (dispose != null) {
      scope.addDispose(() {
        final instance = getValue();
        dispose!(instance);
      });
    }
  }
}

Configurable 类似于 Flutter 小部件,configure 方法类似于 build 方法。现在我们可以像这样使用 MyFinal

Future<void> configurableExample() async {
  final rootScope = await Scope.root([
    MyFinal<Repository>(
      name: 'repository',
      equal: (scope) => Repository(),
      lazy: false,
    ),
    MyFinal<AppNotifier>(
      name: 'appNotifier',
      equal: (scope) => AppNotifier(
        repository: scope.get<Repository>(name: 'repository'),
      ),
      lazy: false,
      dispose: (appNotifier) => appNotifier.dispose(),
    ),
  ]);

  final myRepository = rootScope.get<Repository>(name: 'repository');
  final myAppNotifier = rootScope.get<AppNotifier>(name: 'appNotifier');

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
}

组合配置

高层配置通常与低层配置组合/组合在一起:

class AppConfigurables extends ConfigurableCombine {

  const AppConfigurables({
    this.repositoryName,
    this.appNotifierName,
    this.lazy = true,
    this.dispose = true,
  });

  final Object? repositoryName;
  final Object? appNotifierName;
  final bool lazy;
  final bool dispose;

  @override
  List<Configurable> combine() {
    return [
      MyFinal<Repository>(
        name: repositoryName,
        equal: (scope) => Repository(),
        lazy: lazy,
      ),
      MyFinal<AppNotifier>(
        name: appNotifierName,
        equal: (scope) => AppNotifier(
          repository: scope.get<Repository>(name: repositoryName),
        ),
        lazy: lazy,
        dispose: dispose 
          ? (appNotifier) => appNotifier.dispose() 
          : null,
      ),
    ];
  }
}

AppConfigurables 是多个 Configurable 的组合,类似于高层 Flutter 小部件是由低层小部件组成的。然后它可以这样使用:

Future<void> configurableCombineExample() async {
  final rootScope = await Scope.root([
    AppConfigurables(),
  ]);

  final myRepository = rootScope.get<Repository>();
  final myAppNotifier = rootScope.get<AppNotifier>();

  print('myRepository: $myRepository');
  print('myAppNotifier: $myAppNotifier');
}

更多关于Flutter作用域管理插件dart_scope的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter作用域管理插件dart_scope的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是关于如何在Flutter项目中使用dart_scope插件进行作用域管理的一个简单代码示例。dart_scope插件允许你在Dart中创建和管理作用域,这对于状态管理、依赖注入等场景非常有用。

首先,你需要确保你的Flutter项目中已经添加了dart_scope依赖。你可以在你的pubspec.yaml文件中添加以下依赖:

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

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

接下来,让我们看一个如何使用dart_scope的简单示例。假设我们有一个简单的Flutter应用,它需要在不同的作用域中管理一些数据。

1. 初始化Scope

首先,我们需要定义一个Scope并在应用中初始化它。Scope可以看作是一个容器,用于存储和管理数据。

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

void main() {
  // 创建并初始化一个全局Scope
  final myScope = Scope();
  myScope.provide(key: 'counter', value: 0);

  runApp(MyApp(scope: myScope));
}

2. 创建Flutter应用

接下来,我们创建一个Flutter应用,并在其中使用我们初始化的Scope

class MyApp extends StatelessWidget {
  final Scope scope;

  MyApp({required this.scope});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Scope Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(scope: scope),
    );
  }
}

3. 使用ScopeProvider和ScopeConsumer

MyHomePage中,我们可以使用ScopeProvider来提供我们的Scope,并使用ScopeConsumer来消费作用域中的数据。

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

class MyHomePage extends StatefulWidget {
  final Scope scope;

  MyHomePage({required this.scope});

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _incrementCounter() {
    final counter = widget.scope.get<int>(key: 'counter') ?? 0;
    widget.scope.provide(key: 'counter', value: counter + 1);
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Scope Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ScopeConsumer<int>(
              scope: widget.scope,
              key: 'counter',
              builder: (context, counter) {
                return Text(
                  'You have pushed the button this many times:',
                  style: TextStyle(fontSize: 18),
                );
              },
              child: Text(
                '${counter ?? 0}',
                style: Theme.of(context).textTheme.headline4,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

在这个示例中,我们定义了一个简单的计数器应用。我们使用ScopeProvider来提供我们的Scope,并使用ScopeConsumer来消费作用域中的counter值。当按钮被点击时,我们更新作用域中的counter值,并调用setState来刷新UI。

请注意,这个示例中的ScopeConsumer是一个简化的版本,用于演示目的。在实际使用中,dart_scope插件可能提供了更具体或更灵活的API来消费作用域中的数据。

希望这个示例能帮助你理解如何在Flutter项目中使用dart_scope插件进行作用域管理。如果你有更具体的需求或问题,请查阅dart_scope的官方文档以获取更多信息。

回到顶部