[Dart] Create command(CLI) tool

[Dart] Create command(CLI) tool

2023-06-05 hit count image

Let's see how to create command(CLI) tool with Dart.

Outline

When you develop the app with Flutter, sometimes you need to do something with command(CLI - Command Line Interface). For example, you can update the version of pubspec.yaml file directly, but you can also make it update with the following command.

dart run bull:pub_version --version 2.5.7

In this blog post, I will introduce how to create command(CLI) tool with Dart.

Create package project

You need to create a Dart package project to create a command tool with Dart and publish it to pub.dev. If you don’t need to publish it to pub.dev, you can create a command tool in the existing project, so you can skip this part.

Then, execute the following command to create a Dart package.

# dart create --template=package [PACKAGE_NAME]
dart create --template=package cli_example

After creating the project, you can see the following folders and files.

cli_example
├── CHANGELOG.md
├── README.md
├── analysis_options.yaml
├── example
│   └── cli_example_example.dart
├── lib
│   ├── cli_example.dart
│   └── src
│       └── cli_example_base.dart
├── pubspec.lock
├── pubspec.yaml
└── test
    └── cli_example_test.dart

Install args package

The args package helps you create commands(CLI) with Dart.

Execute the following command to install the args package.

dart pub add args

If you are using Flutter project, execute the following command to install the args package.

flutter pub add args

Implement command

In order to create a command(CLI) with Dart, you need to use the Command class of the args package. Open the lib/src/cli_example_base.dart file and modify it as follows.

import 'package:args/command_runner.dart';

class Echo extends Command {
  @override
  final name = 'echo';

  @override
  final description = 'Echo option';

  Echo() {
    argParser.addOption('message', help: 'A message to echo');
  }

  @override
  void run() {
    String? message = argResults?['message'];

    // ignore: avoid_print
    print('Message: $message');
  }
}

When you inherit the Command class, you need to override three members. name is the name of the command, and description is the description of the command. And the run() function is the part that implements the command.

In the Command class, you can use the argParse and argResults variables by default. In the constructor(Echo()), the option, which is needed for the command, is added in the constructor by using addOption of the argParser.

And in the run() function which is the implementation part of the command, the value set in the option of the command is assigned through argResults?['message'], and the content is printed using print.

When implementing the command in real, you will add various options to the argParser, and implement various functions according to the options in the run() function.

Add command

The contents implemented in the lib folder can be only loaded and used in the Dart file. In other words, the Echo we’ve created cannot be used as a command yet, and can only be used in *.dart files.

In order to use the Echo command we created as a command not the *.dart file, we need to create a bin folder and create a file that runs the Echo command.

Then, create the bin/cli_example.dart file and modify it as follows.

import 'package:args/command_runner.dart';
import 'package:cli_example/cli_example.dart';

void main(List<String> arguments) {
  CommandRunner(
    "cli_example",
    "Dart CLI example",
  )
    ..addCommand(Echo())
    ..run(arguments);
}

When the user executes the command, the main function is executed and the arguments contains the options and values entered by the user.

In this file, we use the args package to register(addCommand) the Echo command we created and execute(run) it with the value (arguments) entered by the user.

This file can be executed with the following command.

dart run cli_example echo --message="test"

And then, you can get the following result.

Message: test

We’ve created the command with the Command class of the args package, you can use the following command.

dart run cli_example -h

And then, you can see the description and help message that we’ve written above.

Dart CLI example

Usage: cli_example <command> [arguments]

Global options:
-h, --help    Print this usage information.

Available commands:
  echo   Echo option

Run "cli_example help <command>" for more information about a command.

Of course, you can also print the help message for the Echo command as follows.

dart run cli_example echo -h

And then, you can see the following result.

Echo option

Usage: cli_example echo [arguments]
-h, --help       Print this usage information.
    --message    A message to echo

Create example

In order to publish the command package we created to pub.dev, we need to create an example that uses the Echo command. Open the example/cli_example_example.dart file and modify it as follows.

import 'package:args/command_runner.dart';
import 'package:cli_example/cli_example.dart';

void main() async {
  final cmd = CommandRunner(
    "cli_example",
    "Dart CLI example",
  )..addCommand(Echo());

  await cmd.run(['echo', '--message', 'test message']);
}

Actually, we don’t need to create an example because we’re creating a command tool. On the contrary, this file can confuse users, so I recommend removing it.

Write test code

In order to check if the command you created works well when we execute the command, you need to write the test code. Create the test/cli_example_test.dart file and modify it as follows.

import 'package:args/command_runner.dart';
import 'package:cli_example/cli_example.dart';
import 'package:run_with_print/run_with_print.dart';
import 'package:test/test.dart';

void main() {
  final runner = CommandRunner('test', 'test')..addCommand(Echo());

  test('Echo test message', () async {
    await runWithPrint((logs) async {
      await runner.run(['echo', '--message', 'test message']);
      expect(logs, ['Message: test message']);
    });
  });
}

The Echo command uses the print function to print the result. To check this, you can install the run_with_print package by running the following command.

dart pub add --dev run_with_print

After installing, you can check the output with print through the runWithPrint function of the run_with_print package. For more information about the run_with_print package, please refer to the following link.

Now, execute the following command to run the test code.

dart test test/cli_example_test.dart

Then, you can see the following result.

00:00 +1: All tests passed!

Test command

Now, let’s check if the command we created works well. First, upload the code that implements the command to GitHub. And then, run the following command to activate the command.

dart pub global activate --source git https://github.com/dev-yakuza/cli_example

And then, you can run the command as follows.

dart pub global run cli_example echo --message="test messsage"

Then, you can see the following result.

Message: test messsage

Build

The command can be built as a standalone executable. Run the following command to build the command as a standalone executable.

dart compile exe bin/cli_example.dart

Then, you can see the cli_example.exe file created in the bin folder. You can run the command file as follows.

./bin/cli_example.exe echo --message="test message"

The command file has the exe extension, but this command file supports not only Windows but also macOS and Linux.

Publish pub.dev

You can publish the command package you created to pub.dev and use it. For more information on publishing packages to pub.dev, please refer to the following link.

Official document references

The following is the official document that helps you create commands with Dart.

Completed

Done! We’ve seen how to create a command tool using Dart. When developing an app with Flutter or developing a project with Dart, sometimes you need to create a specific command. In this case, please refer to this post to create a command tool.

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

App promotion

You can use the applications that are created by this blog writer Deku.
Deku created the applications with Flutter.

If you have interested, please try to download them for free.

Posts