Flutter命令行工具生成插件cli_gen的使用

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

Flutter命令行工具生成插件cli_gen的使用

cli_gen 是一个用于从普通的 Dart 类和函数生成命令行应用程序的工具。它通过注解的方式简化了 CLI 应用程序的开发和维护,提供了类型安全的参数解析、自动帮助文本生成等功能。以下是关于如何使用 cli_gen 的完整示例。

1. 安装依赖

首先,在 pubspec.yaml 文件中添加 cli_annotations 作为依赖项,并将 cli_genbuild_runner 作为开发依赖项:

name: dart_git
description: An implementation of the git CLI in Dart.

environment:
  sdk: ^3.0.0

dependencies:
  cli_annotations: ^0.1.0-dev.4

dev_dependencies:
  build_runner: ^2.4.8
  cli_gen: ^0.1.0-dev.4

# 可选:定义可执行文件名
executables:
  dart_git: main

2. 运行代码生成器

安装依赖后,启动 build_runner 以开始代码生成:

$ dart run build_runner watch -d

3. 定义命令运行器

创建一个 CommandRunner 类,并使用 @cliRunner 注解标注该类,继承生成的超类(通常带有 _$ 前缀)。生成的代码包含一个 CommandRunner.run() 方法,这是 CLI 应用程序的入口点,将在 main 函数中调用。

import 'package:cli_annotations/cli_annotations.dart';

part 'example.g.dart';

/// A command-line interface for version control.
@cliRunner
class GitRunner extends _$GitRunner {
  // ...
}

4. 定义命令

CommandRunner 类中,通过创建方法并使用 [@cliCommand](/user/cliCommand) 注解来定义命令。以下是一个简单的 merge 命令示例:

@cliRunner
class GitRunner extends _$GitRunner {
  /// Join two or more development histories together.
  [@cliCommand](/user/cliCommand)
  Future<void> merge({
    /// The name of the branch to be merged into the current branch.
    required String branch,

    /// Pass merge strategy specific option through to the merge strategy.
    MergeStrategy strategy = MergeStrategy.ort,

    /// Perform the merge and commit the result.
    bool? commit,
  }) async {
    print('Merging branch $branch');
    if (commit == true) {
      print('Committing merge');
    }
    await Future.delayed(const Duration(seconds: 1));
  }
}

enum MergeStrategy { ort, recursive, resolve, octopus, ours, subtree }

5. 定义子命令

随着应用程序的增长,您可能希望将命令分组。为此,可以创建一个 Subcommand 类,并使用 @cliSubcommand 注解标注该类,继承生成的超类。然后使用 @cliMount 注解将子命令连接到主 CommandRunner 类。

/// Commands for managing a stack of stashed changes.
///
/// Use this to save and restore changes in a working directory temporarily, allowing
/// you to switch contexts and manage your work in progress without committing to the
/// Git history.
@cliSubcommand
class StashSubcommand extends _$StashSubcommand {
  /// Save your local modifications to a new stash.
  [@cliCommand](/user/cliCommand)
  Future<void> push({
    required String message,
  }) async {
    print('Stashing changes with message: $message');
    await Future.delayed(const Duration(seconds: 1));
  }

  /// Apply the stash at the given [stashRef] to the working directory.
  [@cliCommand](/user/cliCommand)
  Future<void> apply({
    String stashRef = '0',
  }) async {
    print('Applying stash $stashRef');
    await Future.delayed(const Duration(seconds: 1));
  }
}

@cliRunner
class GitRunner extends _$GitRunner {
  @cliMount
  StashSubcommand get stash => StashSubcommand();
}

6. 运行应用程序

最后,创建一个 main 函数,调用 CommandRunnerrun 方法:

Future<void> main(List<String> args) => GitRunner().run(args);

7. 测试应用程序

激活可执行文件(如果在 pubspec.yaml 中定义了可执行文件)并运行应用程序:

# 激活可执行文件
$ dart pub global activate . --source=path

# 运行应用程序
$ dart_git merge --help

您应该看到类似以下的帮助文本输出:

$ dart_git merge --help
Join two or more development histories together.

Usage: git-runner merge [arguments]
--branch (mandatory)
--commit
--options

Run "git-runner help" to see global options.

特性

类型安全的参数解析

cli_gen 自动将传入的字符串参数解析为正确的 Dart 类型,并在用户输入无效值时自动提示错误。支持的类型包括 StringintdoubleboolnumUriBigIntDateTime。还可以使用集合类型 ListSetIterable

帮助文本推断

cli_gen 会根据方法声明、文档注释和默认值自动生成帮助文本。例如:

[@cliCommand](/user/cliCommand)
Future<void> myCustomCommand({
  /// A parameter that uses a doc comment
  String someDocumentedParameter,
}) async {
  // ...
}

生成的帮助文本:

$ my-custom-command --help
Save your local modifications to a new stash.

Usage: git stash my-custom-command [arguments]
-h, --help                         Print this usage information.
    --some-documented-parameter    A parameter that uses a doc comment

Run "git help" to see global options.

位置参数

cli_gen 支持位置参数,允许用户直接传递参数而无需使用命名标志。例如:

[@cliCommand](/user/cliCommand)
Future<void> push(
  String remote,      // 位置参数
  String? branch, {   // 位置参数
  bool force = false, // 命名参数
}) async {
  /* ... */
}

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

1 回复

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


当然,下面是一个关于如何使用Flutter命令行工具生成插件 cli_gen 的代码案例和相关说明。假设你已经安装了Flutter和Dart环境,并且已经配置好了你的开发环境。

1. 安装 cli_gen 工具

首先,你需要全局安装 cli_gen 工具。你可以通过Dart的Pub工具来安装它:

dart pub global activate cli_gen

2. 创建 Flutter 插件项目

使用 cli_gen 工具可以简化插件项目的创建过程。首先,导航到你希望创建插件项目的目录,然后运行以下命令:

cli_gen create --template=plugin my_flutter_plugin

这里,my_flutter_plugin 是你希望为你的插件项目指定的名称。这个命令会根据模板生成一个基本的Flutter插件项目结构。

3. 项目结构

cli_gen 创建的项目结构通常包括以下几个主要文件和目录:

my_flutter_plugin/
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android/
│   └── ... (Android specific files)
├── ios/
│   └── ... (iOS specific files)
├── lib/
│   ├── my_flutter_plugin.dart
│   └── ... (Dart source files)
├── example/
│   ├── android/
│   ├── ios/
│   ├── lib/
│   │   └── main.dart
│   └── ... (example app files)
├── pubspec.yaml
└── pubspec.lock

4. 修改插件代码

接下来,你可以开始编辑 lib/my_flutter_plugin.dart 文件,添加你的插件逻辑。例如,一个简单的插件可能包含一个方法,该方法在平台上执行一些操作并返回结果。

Dart 代码示例

lib/my_flutter_plugin.dart 文件中,你可能会看到类似以下内容:

import 'dart:typed_data';

import 'package:flutter/services.dart';

class MyFlutterPlugin {
  static const MethodChannel _channel = MethodChannel('my_flutter_plugin');

  static Future<String?> get platformVersion async {
    final String? version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

原生平台代码示例

你还需要在原生平台(iOS和Android)上实现 getPlatformVersion 方法。

iOS (Swift):

ios/Classes/MyFlutterPluginPlugin.swift 文件中:

import Flutter

public class MyFlutterPluginPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterRegistrar) {
    let channel = FlutterMethodChannel(name: "my_flutter_plugin", binaryMessenger: registrar.messenger())
    let instance = MyFlutterPluginPlugin()
    instance.setup(channel, registrar: registrar)
  }

  public func setup(_ channel: FlutterMethodChannel, registrar: FlutterRegistrar) {
    channel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      if call.method == "getPlatformVersion" {
        let version = UIDevice.current.systemVersion
        result(version)
      } else {
        result(FlutterMethodNotImplemented)
      }
    })
  }
}

Android (Kotlin):

android/src/main/kotlin/com/example/my_flutter_plugin/MyFlutterPluginPlugin.kt 文件中:

package com.example.my_flutter_plugin

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import android.os.Build
import android.os.Bundle
import androidx.annotation.NonNull

/** MyFlutterPluginPlugin */
class MyFlutterPluginPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
  /// The MethodChannel that will be used to communicate with the Flutter application.
  private lateinit var channel : MethodChannel

  override fun onAttachedToEngine(@NonNull flutterEngine: FlutterEngine) {
    channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "my_flutter_plugin")
    channel.setMethodCallHandler(this)
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success(Build.VERSION.RELEASE)
    } else {
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(@NonNull flutterEngine: FlutterEngine) {
    channel.setMethodCallHandler(null)
  }

  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    // No-op
  }

  override fun onDetachedFromActivityForConfigChanges() {
    // No-op
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
    // No-op
  }

  override fun onDetachedFromActivity() {
    // No-op
  }
}

5. 运行示例应用

example 目录下,有一个完整的Flutter应用示例,用于测试你的插件。你可以通过以下命令运行这个示例应用:

flutter run

这将启动一个Flutter模拟器或连接到的设备,并运行你的插件示例应用。

总结

以上是一个使用 cli_gen 工具创建和配置Flutter插件的完整示例。希望这个示例能帮助你快速上手并开始开发你的Flutter插件。

回到顶部