Watch the video (8 min)
On this page21 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. One Tool, Many Commands
  5. 2. dart create and Its Templates
  6. 3. Reading Arguments in main
  7. 4. Exit Codes with exit
  8. 5. Reading Input with stdin.readLineSync()
  9. 6. dart compile exe
  10. 7. Other Compile Targets
  11. 8. The Rest of the Toolbox
  12. 9. A Real-World Example
  13. Code Examples
  14. Common Beginner Mistakes
  15. Best Practices
  16. Mini Challenge
  17. Practice Questions
  18. Practice Answers
  19. Quick Revision
  20. Key Takeaways
  21. What's Next?

Lecture 113 — Dart CLI

Category19 — Dart Developer Tools
Lecture113 of 140
Estimated video length8 min
TargetDart 3.12.2
LevelAdvanced

So far our programs ran only through dart run and always did the same thing. The dart tool can also pass them input, report success or failure, and build them into standalone programs that run without Dart installed.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every output and error message in this lecture is real. Each one was produced by running the command shown on Dart 3.12. Folder paths are replaced with <project>, and the user name with user.


Prerequisites

Students should already understand, from Lectures 001–112:

  • dart create and dart run (Lecture 002)
  • lists, for-in loops, int.tryParse and null checks
  • dart:io and stdout.write (Lectures 101 and 106)
  • packages, pubspec.yaml and dart pub (Lectures 103–105)

Connection With Previous Lectures

Every project from Lecture 002 on started with this line, and we never used its parameter:

dart
void main(List<String> arguments) {

Our marks program had Ali's marks written inside the code. To grade Sara, we edited the file. And to share the program, the other person needed the Dart SDK. A real command-line tool takes its input when it starts, tells the terminal whether it succeeded, and runs as a program of its own. That is what the dart tool gives us.


Learning Objectives

By the end of this lecture you will be able to:

  • Find any dart command's options with --help
  • Create projects from the console, cli and package templates
  • Read command-line arguments in main(List<String> args)
  • Report success or failure with exit and exitCode
  • Read a line the user types with stdin.readLineSync()
  • Build a standalone executable with dart compile exe
  • Recognize aot-snapshot and js as other compile targets
  • Name what test, format, analyze, fix, pub, doc and info do

1. One Tool, Many Commands

CLI means command-line interface: a program you use by typing commands. dart is itself a CLI, and every task is a subcommand after it — dart create, dart run, dart compile. Lecture 002 listed them with dart help.

Analogy — the dart tool is a toolbox with one handle. The word after dart picks the tool.

Every subcommand explains itself with --help:

bash
dart compile --help
output
Compile Dart to various formats.

Usage: dart compile <subcommand> [arguments]
-h, --help    Print this usage information.

Available subcommands:
  js             Compile Dart to JavaScript.
  jit-snapshot   Compile Dart to a JIT snapshot.
  kernel         Compile Dart to a kernel snapshot.
  exe            Compile Dart to a self-contained executable.
  aot-snapshot   Compile Dart to an AOT snapshot.
  wasm           Compile Dart to a WebAssembly/WasmGC module.

Run "dart help" to see global options.

--help works one level deeper too: dart compile exe --help, dart create --help.


2. dart create and Its Templates

dart create copies a template. dart create --help lists them:

output
          [cli]                  A command-line application with basic argument parsing.
          [console] (default)    A command-line application.
          [package]              A package containing shared Dart libraries.
          [server-shelf]         A server app using package:shelf.
          [web]                  A web app that uses only core Dart libraries.

Choose one with -t:

bash
dart create -t package grade_utils
output
Creating grade_utils using template package...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  example/grade_utils_example.dart
  lib/grade_utils.dart
  lib/src/grade_utils_base.dart
  test/grade_utils_test.dart
...
TemplateMain codeUse it for
consolebin/, lib/, test/a program to run (Lecture 002)
clibin/ only, plus package:argsa tool with flags such as --help
packagelib/src/, example/code other projects import (Lecture 103)

The cli starter already answers --help:

bash
dart create -t cli marks_cli
cd marks_cli
dart run bin/marks_cli.dart --help
output
Usage: dart marks_cli.dart <flags> [arguments]
-h, --help       Print this usage information.
-v, --verbose    Show additional command output.
    --version    Print the tool version.

3. Reading Arguments in main

Words typed after the file name arrive in main's list, as strings, in order:

dart
void main(List<String> args) {
  print('You passed ${args.length} arguments: $args');
}
bash
dart run bin/marks.dart Ali 80 75
dart run bin/marks.dart "Sara Khan" 90
dart run bin/marks.dart
output
You passed 3 arguments: [Ali, 80, 75]
You passed 2 arguments: [Sara Khan, 90]
You passed 0 arguments: []

Spaces separate arguments; quotes keep Sara Khan together. 80 arrives as the string '80' — convert it with int.tryParse before doing maths. Any name works for the parameter; args is the usual short one.


4. Exit Codes with exit

When a program ends, it hands the terminal a number called the exit code. 0 means success; anything else means failure. Scripts and other tools read it to decide what to do next. exit(code) from dart:io stops the program at once with that code:

dart
import 'dart:io';

void main(List<String> args) {
  if (args.isEmpty) {
    print('Please give a name.');
    exit(1);
  }
  print('Welcome, ${args.first}!');
}
bash
dart run welcome.dart
echo "exit: $?"
dart run welcome.dart Zayan
echo "exit: $?"
output
Please give a name.
exit: 1
Welcome, Zayan!
exit: 0

$? is the terminal's way of showing the last exit code (on Windows PowerShell, use $LASTEXITCODE). When a program reaches the end of main normally, the code is 0.

To record a failure but keep running, set the top-level exitCode instead; the program finishes and then reports it. Example 5 shows this.


5. Reading Input with stdin.readLineSync()

Arguments are given up front. To ask a question while running, read a line from stdin, the keyboard input:

dart
import 'dart:io';

void main() {
  stdout.write('Your name: ');
  var name = stdin.readLineSync() ?? '';
  print('Hello, $name!');
}
output
Your name: Sara
Hello, Sara!

The program pauses until the user presses Enter; here the user typed Sara. readLineSync() returns String? — null when there is no more input — so ?? '' gives a safe default.


6. dart compile exe

dart run compiles your code every time it starts (JIT, Lecture 001). dart compile exe compiles it ahead of time (AOT) into one file that runs without the Dart SDK:

bash
dart compile exe bin/marks.dart -o marks
ls -lh marks
./marks Sara 90 85 88
output
Generated: <project>/marks
-rwxr-xr-x  1 user  staff   5.5M  marks
Student: Sara
Total: 263
Average: 87.7

-o names the output file (on Windows, use marks.exe). The binary is about 5.5 MB because it carries the Dart runtime inside. On our machine, ./marks finished in 0.016 seconds; dart run bin/marks.dart took about 0.5 seconds. The executable is built for your operating system — --target-os (see dart compile exe --help) builds for another.


7. Other Compile Targets

aot-snapshot builds the same machine code without the runtime, so the file is smaller but needs dartaotruntime, which ships with the SDK:

bash
dart compile aot-snapshot bin/marks.dart
dartaotruntime bin/marks.aot Sara 90 85 88
output
Generated: <project>/bin/marks.aot
Student: Sara
Total: 263
Average: 87.7

marks.aot is 1.0 MB. js turns Dart into JavaScript for browsers and Node.js:

dart
void main() {
  var celsius = 25.0;
  var fahrenheit = celsius * 9 / 5 + 32;
  print('$celsius °C = $fahrenheit °F');
}
bash
dart compile js web/hello.dart -o hello.js
node hello.js
output
Compiled 10,324,736 input bytes (5,274,524 characters source) to 12,106 characters JavaScript in 0.21 seconds
25 °C = 77 °F

dart run prints 25.0 °C = 77.0 °F; JavaScript has one number type and drops the .0. Code that imports dart:io (files, stdin, exit) cannot run in a browser.


8. The Rest of the Toolbox

CommandWhat it doesStudied in
dart testruns the tests in test/Lecture 118
dart formatrewrites code in the standard layoutLecture 114
dart analyzefinds errors, warnings and style issues without runningLecture 114
dart fix --applyapplies the fixes analyze suggestsLecture 114
dart pubadd, get, upgrade, outdated, depsLectures 103–105
dart docbuilds HTML documentation from /// comments into doc/apiLecture 125
dart infoprints your Dart version, OS and project facts for bug reports—

A quick preview of two of them on the console starter project:

bash
dart test
dart analyze
output
00:00 +0: loading test/marks_test.dart
00:00 +0: test/marks_test.dart: calculate
00:00 +1: All tests passed!
Analyzing marks...
No issues found!

And dart info begins like this (trimmed):

output
#### General info

- Dart 3.12.1 (stable) (Tue May 26 01:02:21 2026 -0700) on "macos_arm64"
- on macos / Version 26.5.1 (Build 25F80)
- locale is en-PK

#### Project info

- sdk constraint: '^3.12.1'
- dependencies: path
- dev_dependencies: lints, test

9. A Real-World Example

A marks tool that takes a name and marks, checks them, and uses exit codes: 1 for missing input, 2 for a bad mark.

dart
import 'dart:io';

void main(List<String> args) {
  if (args.length < 2) {
    stderr.writeln('Usage: marks <name> <mark> [mark...]');
    exit(1);
  }

  var name = args.first;
  var marks = <int>[];
  for (var text in args.skip(1)) {
    var mark = int.tryParse(text);
    if (mark == null) {
      stderr.writeln('Not a number: $text');
      exit(2);
    }
    marks.add(mark);
  }

  var total = marks.reduce((a, b) => a + b);
  var average = total / marks.length;
  print('Student: $name');
  print('Total: $total');
  print('Average: ${average.toStringAsFixed(1)}');
}
bash
dart run bin/marks.dart Ali 80 75 85; echo "exit: $?"
dart run bin/marks.dart Ali; echo "exit: $?"
dart run bin/marks.dart Ali eighty; echo "exit: $?"
output
Student: Ali
Total: 240
Average: 80.0
exit: 0
Usage: marks <name> <mark> [mark...]
exit: 1
Not a number: eighty
exit: 2

stderr is the terminal's error channel: it shows on screen like stdout, but tools can keep errors apart from normal output. Compiled with dart compile exe (section 6), the same file becomes the ./marks program.


Code Examples

Run each with dart run <file> <arguments>.

Example 1 — Counting Arguments

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

dart run e1.dart Ali Sara Zayan

output
Count: 3
Arguments: [Ali, Sara, Zayan]

Example 2 — A Default When Nothing Is Given

dart
void main(List<String> args) {
  var name = args.isEmpty ? 'friend' : args.first;
  print('Hello, $name!');
}

dart run e2.dart Sara, then dart run e2.dart

output
Hello, Sara!
Hello, friend!

Example 3 — Looping Over Arguments

dart
void main(List<String> args) {
  for (var arg in args) {
    print('- $arg');
  }
}

dart run e3.dart milk bread eggs

output
- milk
- bread
- eggs

Example 4 — Failing with exit

dart
import 'dart:io';

void main(List<String> args) {
  if (args.isEmpty) {
    print('Please give a name.');
    exit(1);
  }
  print('Welcome, ${args.first}!');
}

dart run e4.dart; echo "exit: $?"

output
Please give a name.
exit: 1

Example 5 — Recording Failure with exitCode

dart
import 'dart:io';

void main(List<String> args) {
  for (var arg in args) {
    if (int.tryParse(arg) == null) {
      print('Skipping $arg');
      exitCode = 1;
    } else {
      print('Mark: $arg');
    }
  }
}

dart run e5.dart 80 abc 75; echo "exit: $?"

output
Mark: 80
Skipping abc
Mark: 75
exit: 1

Unlike exit(1), setting exitCode lets the loop finish.

Example 6 — Asking for a Name

dart
import 'dart:io';

void main() {
  stdout.write('Your name: ');
  var name = stdin.readLineSync() ?? '';
  print('Hello, $name!');
}
output
Your name: Sara
Hello, Sara!

Example 7 — Student Marks

dart
void main(List<String> args) {
  var total = 0;
  for (var arg in args) {
    total += int.parse(arg);
  }
  print('Total: $total');
}

dart run e7.dart 80 75 85

output
Total: 240

Example 8 — Shopping Total

dart
void main(List<String> args) {
  var price = double.parse(args[0]);
  var quantity = int.parse(args[1]);
  print('Total: ${(price * quantity).toStringAsFixed(2)}');
}

dart run e8.dart 250.5 3

output
Total: 751.50

Example 9 — Bank Deposit

dart
import 'dart:io';

void main() {
  var balance = 1000.0;
  stdout.write('Deposit amount: ');
  var amount = double.tryParse(stdin.readLineSync() ?? '');
  if (amount == null || amount <= 0) {
    print('Invalid amount.');
    exit(1);
  }
  balance += amount;
  print('New balance: $balance');
}

Typing 500, then running again and typing abc:

output
Deposit amount: 500
New balance: 1500.0
Deposit amount: abc
Invalid amount.

Example 10 — A Compiled Temperature Converter

dart
void main(List<String> args) {
  for (var arg in args) {
    var celsius = double.parse(arg);
    var fahrenheit = celsius * 9 / 5 + 32;
    print('$celsius °C = $fahrenheit °F');
  }
}
bash
dart compile exe e10.dart -o temp
./temp 25 37 -40
output
Generated: <project>/temp
25.0 °C = 77.0 °F
37.0 °C = 98.6 °F
-40.0 °C = -40.0 °F

Common Beginner Mistakes

1. Giving dart run arguments but no file

bash
dart run Ali 80
output
Could not find package `Ali` or file `Ali`

Inside a project, a bare dart run takes its first word as the thing to run. Name the file first: dart run bin/marks.dart Ali 80.

2. Using exit without importing dart:io

dart
void main(List<String> args) {
  if (args.isEmpty) {
    // exit(1); // ✗ no import 'dart:io'
  }
}
output
Error: Method not found: 'exit'.

3. Reading args[0] when no argument was given

Running Example 8 with no arguments crashes:

output
Unhandled exception:
RangeError (length): Invalid value: Valid value range is empty: 0

Check args.length first, as the real-world example does.

4. Using int.parse on user input

int.parse('eighty') throws FormatException: Invalid radix-10 number. For input you don't control, use int.tryParse and handle null.

5. Treating readLineSync() as String

dart
import 'dart:io';

void main() {
  // String name = stdin.readLineSync(); // ✗
}
output
Error: A value of type 'String?' can't be assigned to a variable of type 'String'.

Add ?? '' or check for null.

6. Sending a compiled program to another operating system

A file built on macOS will not run on Windows. Build on each system, or use --target-os.


Best Practices

  • Check args.length before reading arguments; print a usage line when it is wrong.
  • Exit with 0 on success and a non-zero code on failure; write error messages to stderr.
  • Prefer exitCode when the program should finish its work first.
  • Parse every argument and typed line with tryParse.
  • Use dart run while developing and dart compile exe when you share the tool.
  • When unsure of an option, ask the command: dart <command> --help.

Mini Challenge

Write cart.dart for Sara's shopping. It takes item prices as arguments and prints the number of items and the total. With no arguments, it writes a usage line to stderr and exits with code 1.

output
$ dart run cart.dart 120 80 50
Items: 3
Total: 250

Try it yourself before opening the solution.

Show solution
dart
import 'dart:io';

void main(List<String> args) {
  if (args.isEmpty) {
    stderr.writeln('Usage: cart <price> [price...]');
    exit(1);
  }
  var total = 0;
  for (var arg in args) {
    total += int.parse(arg);
  }
  print('Items: ${args.length}');
  print('Total: $total');
}
output
$ dart run cart.dart 120 80 50
Items: 3
Total: 250
$ dart run cart.dart; echo "exit: $?"
Usage: cart <price> [price...]
exit: 1

Practice Questions

Beginner

  1. Which command creates a package project called grade_utils?
  2. Write a program that prints You gave N arguments. Run it with a b c d.
  3. Which exit code means success? What does $? show?

Application

  1. Write a program that prints its arguments in reverse order, separated by spaces. Run it with Zayan Sara Ali.
  2. Write a program that adds exactly two whole numbers given as arguments. With any other count, print Give exactly two numbers. and exit with 1.
  3. Which command builds bin/grades.dart into a standalone program named grades?
  4. Inside a project, dart run Sara 90 fails. Why, and what is the fix?
  5. Write a program that asks Celsius: , reads a number and prints the Fahrenheit value. If the input is not a number, print Not a number. and exit with 1.

Challenge

  1. Write a grade tool: one mark as an argument prints Mark 72: grade B (A ≥ 80, B ≥ 60, C ≥ 40, else F). A missing, non-numeric or out-of-range mark writes Give a mark from 0 to 100. to stderr and exits with 2.
  2. Write a program that prints the highest mark from its arguments. Skip anything that is not a number, but finish with exit code 1 if anything was skipped.

Practice Answers

Question 1 — Solution

dart create -t package grade_utils

Question 2 — Solution

dart
void main(List<String> args) {
  print('You gave ${args.length} arguments.');
}
output
You gave 4 arguments.

Question 3 — Solution

0 means success. echo $? shows the exit code of the last command.

Question 4 — Solution

dart
void main(List<String> args) {
  print(args.reversed.join(' '));
}
output
Ali Sara Zayan

Question 5 — Solution

dart
import 'dart:io';

void main(List<String> args) {
  if (args.length != 2) {
    print('Give exactly two numbers.');
    exit(1);
  }
  var sum = int.parse(args[0]) + int.parse(args[1]);
  print('Sum: $sum');
}

7 5 prints Sum: 12 (exit 0); 7 alone prints Give exactly two numbers. (exit 1).

Question 6 — Solution

dart compile exe bin/grades.dart -o grades

Question 7 — Solution

dart run treats Sara as the package or file to run and reports Could not find package `Sara` or file `Sara` . Name the file first: dart run bin/grades.dart Sara 90.

Question 8 — Solution

dart
import 'dart:io';

void main() {
  stdout.write('Celsius: ');
  var celsius = double.tryParse(stdin.readLineSync() ?? '');
  if (celsius == null) {
    print('Not a number.');
    exit(1);
  }
  print('Fahrenheit: ${celsius * 9 / 5 + 32}');
}
output
Celsius: 30
Fahrenheit: 86.0

Question 9 — Solution

dart
import 'dart:io';

void main(List<String> args) {
  var mark = args.isEmpty ? null : int.tryParse(args.first);
  if (mark == null || mark < 0 || mark > 100) {
    stderr.writeln('Give a mark from 0 to 100.');
    exit(2);
  }
  var grade = switch (mark) {
    >= 80 => 'A',
    >= 60 => 'B',
    >= 40 => 'C',
    _ => 'F',
  };
  print('Mark $mark: grade $grade');
}
output
$ dart run grade.dart 72; echo "exit: $?"
Mark 72: grade B
exit: 0
$ dart run grade.dart 120; echo "exit: $?"
Give a mark from 0 to 100.
exit: 2

Question 10 — Solution

dart
import 'dart:io';

void main(List<String> args) {
  var marks = <int>[];
  for (var arg in args) {
    var mark = int.tryParse(arg);
    if (mark == null) {
      exitCode = 1;
    } else {
      marks.add(mark);
    }
  }
  if (marks.isEmpty) {
    print('No marks.');
  } else {
    var highest = marks.reduce((a, b) => a > b ? a : b);
    print('Highest: $highest');
  }
}
output
$ dart run highest.dart 80 x 95 70; echo "exit: $?"
Highest: 95
exit: 1

Quick Revision

  1. How do you see the options of dart compile exe?
  2. Which three templates did we use, and what is each for?
  3. What type are command-line arguments, and how do you get an int from one?
  4. What is the difference between exit(1) and exitCode = 1?
  5. Why does readLineSync() return String??
  6. Why is a compiled executable about 5.5 MB, and an AOT snapshot much smaller?
  7. Which commands will Lecture 114 study in depth?

Key Takeaways

  • dart is a CLI; every task is a subcommand, and --help explains each one.
  • dart create -t console|cli|package picks a starter template.
  • Arguments arrive in main(List<String> args) as strings — parse with tryParse.
  • exit(code) stops now; exitCode reports a code when main ends. 0 is success.
  • stdin.readLineSync() reads a typed line and may return null.
  • dart compile exe builds a standalone AOT program for your OS; aot-snapshot needs dartaotruntime; js targets JavaScript.
  • test, format, analyze, fix, pub, doc and info complete the toolbox.

What's Next?

Lecture 114 — Dart Formatter & Analyzer. We met dart format, dart analyze and dart fix only in passing. Next we will use them properly: how the formatter decides a layout, how to read the analyzer's errors, warnings and infos, and how dart fix repairs code for you.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.