Flutter命令行工具插件commandy的使用

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

Flutter命令行工具插件commandy的使用

Commandy

Commandy Logo

Commandy 是一个为 Flutter 设计的包,旨在简化异步命令的执行和管理,同时提供一种清晰且一致的方式来处理成功、失败和状态变化。它帮助你抽象出异步操作的复杂性,使你的代码更加模块化、可测试和可维护。

关键特性 🚀

  • 统一的结果处理: 使用 Result 模型,其中包含 Success<T>FailureResult<T> 变体来明确表示操作结果。这鼓励采用函数式错误处理方法,并使代码更容易理解。

  • 异步命令Command<T, Params> 封装了异步操作,通过 ValueNotifier 通知执行状态变化(isExecuting),并通过 ValueNotifier 传递结果 Result<T>。这种模式使得将异步逻辑集成到 ViewModels 或 UseCases 中变得简单。

  • 基于流的命令StreamCommand<T, Params> 处理返回 Stream<Result<T>> 的操作,自动管理订阅并提供更新通过 ValueNotifier。这非常适合持续更新的数据源,如实时查询或传感器数据。

  • 用于 UI 的命令监听器CommandListener 小部件易于集成到 Flutter UI 代码中。它监听多个命令并在结果变化时触发回调,允许 UI 层立即响应新数据或错误。

  • 无参数简化: 提供了一种方便的方式来管理不需要参数的命令,避免了空值或不必要的复杂性。

  • 状态管理就绪: 虽然 Commandy 主要关注于命令,但它也可以作为轻量级的状态管理解决方案使用。通过在命令中封装业务逻辑和状态更改,你获得了一个干净且可测试的方法来管理应用程序状态。


安装 💻

pubspec.yaml 文件中添加 Commandy:

dependencies:
  commandy: ^0.1.0

然后,获取包:

flutter pub get

开始使用

  1. 导入库
import 'package:commandy/commandy.dart';
  1. 创建一个命令,该命令执行你的异步逻辑,例如从仓库中获取数据。

  2. 将其绑定到 UI,使用 CommandListener 小部件或通过调用 execute 并在 ViewModel 中监听结果变化。

通过将应用程序的异步操作结构化为返回结果的命令,你可以保持清晰的关注点分离,并确保逻辑和 UI 保持松耦合。


核心概念

结果及其变体

Result<T> 是一个密封类,代表操作的结果。它有两个主要变体:

  • Success<T>:表示操作成功完成。持有结果数据。
  • FailureResult<T>:表示操作失败。持有包含错误消息、可选异常和堆栈跟踪的 Failure 对象。

NoParams 类 🛠️

NoParams 是一个轻量级类,表示命令没有参数。它有助于即使没有参数的情况下也保持 Command<T, Params> 接口的一致性。

示例用法:

final incrementCommand = Command<int, NoParams>((_) async {
  // 增量逻辑
  await Future.delayed(const Duration(seconds: 1));
  return Success(42);
});

// 执行命令
await incrementCommand.execute(const NoParams());

这避免了传递 null,并确保代码库中的清晰性和一致性。


命令

封装异步操作并通过 ValueNotifier 更新其状态:

  • isExecuting:一个 ValueNotifier<bool>,指示命令是否仍在运行。
  • result:一个 ValueNotifier<Result<T>?>,保存最后一个结果。

使用 Command 进行一次性操作,如登录请求或数据获取。


StreamCommand

与流一起工作并管理持续更新。调用 start(params) 开始监听,调用 stop() 取消订阅。


CommandListener

通过反应命令结果的变化简化 UI 集成。与其手动监听 ValueNotifier,不如使用 CommandListener 来高效地处理更新。


高级功能 ⚡

ViewModelSelector 🧩

ViewModelSelector 是一个强大的工具,根据 ChangeNotifier 基础 ViewModel 的特定属性选择性地重建 Flutter 小部件。它通过确保只有依赖选定值的小部件被重建来优化 UI 更新,从而避免在 ViewModel 中其他属性更改时进行不必要的小部件重建。

关键特性:

  • 选择性重建: 使用 selector 函数从 ViewModel 中提取特定属性,并仅在该属性更改时触发重建。

  • 与 ChangeNotifier 集成: 无缝工作于现有的 ViewModels,这些 ViewModels 扩展 ChangeNotifier

  • 高效和模块化: 允许更细粒度的 UI 更新控制,而无需外部库或工具。

示例用法:

1. 创建一个 ViewModel

class MyViewModel extends ChangeNotifier {
  String _name = 'John Doe';
  int _counter = 0;

  String get name => _name;
  int get counter => _counter;

  void updateName(String newName) {
    _name = newName;
    notifyListeners();
  }

  void incrementCounter() {
    _counter++;
    notifyListeners();
  }
}

2. 在你的小部件中使用 ViewModelSelector

final myViewModel = MyViewModel();

ViewModelSelector<MyViewModel, String>(
  viewModel: myViewModel,
  selector: (vm) => vm.name, // 仅观察 `name` 属性
  builder: (context, name) {
    return Text('Name: $name');
  },
);

ViewModelSelector<MyViewModel, int>(
  viewModel: myViewModel,
  selector: (vm) => vm.counter, // 仅观察 `counter` 属性
  builder: (context, counter) {
    return Text('Counter: $counter');
  },
);

收益:

  • 确保 UI 效率,通过仅重建受影响的小部件来响应状态更改。
  • 与现有的 ChangeNotifier 架构简单且干净地集成。
  • 当处理复杂的 ViewModels 时,非常适合模块化 UI 更新。

示例 🎯

计数器示例

final incrementCommand = Command<int, NoParams>((_) async {
  // 增量逻辑
  await Future.delayed(Duration(seconds: 1));
  return Success(42);
});

计时器示例

final timerCommand = StreamCommand<int, NoParams>((_) {
  return Stream.periodic(Duration(seconds: 1), (x) => 30 - x - 1)
      .take(30)
      .map((value) => Success(value));
});

timerCommand.start(const NoParams());

完整实现

查看 example 文件夹中的完整实现,包括计数器和计时器示例的 UI 集成和详细逻辑。


额外资源 📚

了解更多关于命令模式及其实际应用:


错误处理 🚦

将所有错误转换为 FailureResult 实例:

final safeCommand = Command<int, int>((param) async {
  try {
    if (param < 0) throw Exception('Negative param');
    return Success(param * 2);
  } catch (e, s) {
    return FailureResult<int>(
      Failure(message: 'Error processing param', exception: e, stackTrace: s),
    );
  }
});

更多关于Flutter命令行工具插件commandy的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter命令行工具插件commandy的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用commandy命令行工具插件的一个基本示例。commandy 是一个用于创建命令行工具的 Dart 包,尽管它不是专门为 Flutter 设计的,但你可以在 Flutter 项目中利用它来构建命令行工具。

首先,确保你的 Flutter 环境已经设置好,然后创建一个新的 Flutter 项目(如果你还没有的话):

flutter create my_flutter_app
cd my_flutter_app

接下来,在你的 pubspec.yaml 文件中添加 commandy 依赖:

dependencies:
  flutter:
    sdk: flutter
  commandy: ^x.y.z  # 请替换为最新版本号

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

由于 commandy 主要用于创建命令行工具,你通常不会直接在 Flutter 的 UI 代码中使用它。相反,你会创建一个单独的 Dart 脚本,这个脚本使用 commandy 来定义命令行接口。

在项目的根目录下创建一个新的 Dart 文件,例如 cli_tool.dart

import 'package:commandy/commandy.dart';

void main(List<String> args) {
  // 创建一个命令行应用
  var app = Commandy()
    ..addCommand(
      Command('greet', 'Greets a person.')
        ..addOption('name', 'The name of the person to greet.', defaultsTo: 'World'),
      (context) {
        var name = context['name'];
        print('Hello, $name!');
      },
    )
    ..addCommand(
      Command('add', 'Adds two numbers.')
        ..addOption('a', 'The first number.', argParser: (p) => p..addOption('value', numArg: true, required: true))
        ..addOption('b', 'The second number.', argParser: (p) => p..addOption('value', numArg: true, required: true)),
      (context) {
        var a = double.parse(context['a']['value']!);
        var b = double.parse(context['b']['value']!);
        print('The sum is: ${a + b}');
      },
    );

  // 运行应用
  app.run(args);
}

这个示例定义了两个命令:greetaddgreet 命令接受一个可选的 name 参数,并打印一个问候语。add 命令接受两个必需的数值参数 ab,并打印它们的和。

现在,你可以通过命令行运行这个 Dart 脚本。确保你已经激活了 Dart 环境,然后运行:

dart cli_tool.dart greet --name=Flutter

你应该会看到输出:

Hello, Flutter!

同样,你可以运行加法命令:

dart cli_tool.dart add --a-value=3 --b-value=5

输出将会是:

The sum is: 8.0

通过这种方式,你可以在 Flutter 项目中利用 commandy 来构建命令行工具,尽管这些工具与 Flutter 的 UI 部分是分离的。你可以在项目的根目录下维护这些命令行工具脚本,并通过 Dart 命令行界面来运行它们。

回到顶部