ZetCode

Dart Command-Line Arguments

last modified October 18, 2026

In this article we explore command-line arguments in Dart. The entry point receives the arguments as a simple list, and the args package turns that list into named options, flags, and subcommands.

Command-Line Arguments Overview

A Dart command-line program declares its entry point as void main(List<String> args). Everything typed after the script name arrives in that list with no interpretation: options, their values, and positional arguments are all present as raw strings in order. For anything beyond the simplest tool, that raw list is tedious to handle by hand, so the args package provides ArgParser to describe and parse a proper command-line interface.

API Purpose Example
main(args) Raw argument list void main(List<String> args)
ArgParser Declare and parse a CLI final parser = ArgParser();
addFlag Boolean switch addFlag('verbose', abbr: 'v')
addOption Option that takes a value addOption('name')
addCommand Named subcommand addCommand('add')
parser.usage Generated help text print(parser.usage);

The examples in this article use the args package. Add it to a project with dart pub add args.

Reading Raw Arguments

The entry point can declare a List<String> parameter to receive the arguments. To pass arguments to a script you use dart run, placing them after the script name.

args1_raw.dart
void main(List<String> args) {
  print('count: ${args.length}');
  print(args);
}

Nothing is parsed here: the output is exactly what was typed. The list includes foo, the flag-looking --bar, and baz. The two dash prefix is just text at this level, which is why real programs hand the list to a parser.

$ dart run args1_raw.dart foo --bar baz
count: 3
[foo, --bar, baz]

Parsing Options and Flags

An ArgParser is built by declaring each flag and option, then parse interprets the arguments. Flags are switches that are either present or not; options take a value. Both may have short abbreviations and default values.

args2_parser.dart
import 'package:args/args.dart';

void main(List<String> arguments) {
  final parser = ArgParser()
    ..addFlag('verbose', abbr: 'v', negatable: false)
    ..addOption('name', abbr: 'n', defaultsTo: 'world')
    ..addOption('count', abbr: 'c', defaultsTo: '1');

  final results = parser.parse(arguments);

  final name = results['name'] as String;
  final count = int.parse(results['count'] as String);
  final verbose = results['verbose'] as bool;

  for (var i = 0; i < count; i++) {
    print('Hello, $name${verbose ? ' ($i)' : ''}');
  }
}

The verbose flag uses negatable: false, so both -v and --verbose are accepted, but --no-verbose is not. The options name and count both declare defaults, so the program runs even when they are omitted. Values come back as strings and are cast to the expected type before use.

$ dart run args2_parser.dart -v -n Alice -c 2
Hello, Alice (0)
Hello, Alice (1)

$ dart run args2_parser.dart
Hello, world

Generating Usage and Help

The parser can generate its own help text from the declared options. Each option accepts a help string, and parser.usage renders the whole interface. A common convention is a --help flag that prints it.

args3_help.dart
import 'package:args/args.dart';

void main(List<String> arguments) {
  final parser = ArgParser()
    ..addFlag('help', abbr: 'h', negatable: false)
    ..addOption('output', abbr: 'o', help: 'Write output to a file')
    ..addFlag('verbose', abbr: 'v', help: 'Enable verbose logging');

  final results = parser.parse(arguments);

  if (results['help'] as bool) {
    print('usage: tool [options]');
    print(parser.usage);
    return;
  }

  print('running');
}

When -h is passed, the program prints a usage line and then the generated listing. Notice that the flag verbose is shown as --[no-]verbose because it was not declared non-negatable, so both forms are valid. Help text is derived from the parser, so the documentation never drifts from the actual options.

$ dart run args3_help.dart -h
usage: tool [options]
-h, --help
-o, --output          Write output to a file
-v, --[no-]verbose    Enable verbose logging

Positional and Rest Arguments

Positional arguments, such as file names, are collected in results.rest, while options are read by name. Unknown options cause a FormatException; they are not collected as positional arguments. Use -- to end option parsing and treat subsequent arguments as rest arguments, even if they begin with a dash.

args4_rest.dart
import 'package:args/args.dart';

void main(List<String> arguments) {
  final parser = ArgParser()..addOption('output', abbr: 'o');
  final results = parser.parse(arguments);

  final output = results['output'] as String?;
  print('output: ${output ?? '(stdout)'}');

  for (final file in results.rest) {
    print('file: $file');
  }
}

The output option is read by name and defaults to null when absent, printed here as (stdout). The two file names are not options, so they land in rest and are listed in order. This split between named options and positional arguments is the standard shape of a CLI.

$ dart run args4_rest.dart -o out.txt a.txt b.txt
output: out.txt
file: a.txt
file: b.txt

Subcommands

Tools with several modes, such as git commit and git push, are built with subcommands. Registering a name with addCommand lets the parser select it, and the chosen command carries its own rest and options.

args5_commands.dart
import 'package:args/args.dart';

void main(List<String> arguments) {
  final parser = ArgParser()
    ..addCommand('add')
    ..addCommand('remove');

  final results = parser.parse(arguments);
  final command = results.command;

  if (command == null) {
    print('usage: tool <add|remove> [files...]');
    return;
  }

  switch (command.name) {
    case 'add':
      print('adding: ${command.rest}');
    case 'remove':
      print('removing: ${command.rest}');
  }
}

results.command is the parsed subcommand, or null when none was given. Its name selects the branch, and its own rest holds the arguments that follow the command. Because both commands only need positional arguments, no options are declared for them.

$ dart run args5_commands.dart add a b
adding: [a, b]

Handling Parse Errors

Unknown options and invalid option syntax cause parse to throw a FormatException. Value validation and conversion can also throw this exception. Wrapping parsing and validation in a try block lets the program print a friendly message instead of crashing with a stack trace.

args6_error.dart
import 'package:args/args.dart';

void main(List<String> arguments) {
  final parser = ArgParser()..addOption('port', abbr: 'p');

  try {
    final results = parser.parse(arguments);
    final value = results['port'] as String?;
    if (value == null) {
      throw FormatException('Missing required option --port.');
    }
    final port = int.parse(value);
    print('port=$port');
  } on FormatException catch (e) {
    print('invalid arguments: ${e.message}');
  }
}

The parser itself is strict: an unknown option such as --unknown raises a FormatException. The explicit null check rejects a missing port with a FormatException before numeric conversion. Converting a non-numeric value with int.parse raises one as well. All three cases are caught by the same clause, so the program reports the problem cleanly.

$ dart run args6_error.dart -p abc
invalid arguments: Invalid radix-10 number

$ dart run args6_error.dart --unknown
invalid arguments: Could not find an option named "--unknown".

$ dart run args6_error.dart
invalid arguments: Missing required option --port.

Source

Dart: Command-line apps
pub.dev: args package
Dart API: ArgParser
Dart API: List

Dart hands command-line arguments to the entry point as a plain list, and the args package turns that list into a real interface. Flags, options, defaults, generated help, positional rest arguments, and subcommands all come from a declarative ArgParser, while a FormatException guard keeps invalid input from crashing the program.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.