ANSI escape codes in Dart — stdout.supportsAnsiEscapes, chalkdart, tint
Dart's `dart:io` `stdout` already exposes the canonical capability check most other ecosystems force you to roll yourself — `stdout.supportsAnsiEscapes` returns `true` on a TTY that understands VT sequences, `false` when piped, redirected, or running on a legacy Conhost without VT enabled. Pair it with raw `stdout.write('\x1B[31m…\x1B[0m')` (Dart accepts both `\x1B` and `\u001B`) and you have a portable starting point with zero dependencies on macOS, Linux, and Windows Terminal / Conhost 1709+. For ergonomic colour APIs reach for `chalkdart` (the most popular Dart ANSI library — chained-extension API mirrored after Node's `chalk`), `tint` (compact, modern), or `cli_util` (Google's first-party CLI helpers including an ANSI module). The Flutter SDK's own `flutter` CLI uses these primitives — its progress / build-status output is a canonical reference for Dart-shaped capability gating.
Recommended libraries
- chalkdart
Most-popular Dart ANSI library — chained-extension API mirroring Node's `chalk`: `print(chalk.red.bold('error'))`. Supports TrueColor (`chalk.rgb(255,128,0)`), 256-colour, named colours, background pairs, and underline variants. Pure Dart, zero native deps.
- tint
Compact modern alternative — `String` extensions like `'error'.red().bold()`. Smaller surface area than chalkdart; pairs well with code that already uses the extension-method idiom. Auto-respects `NO_COLOR`.
- cli_util
Google's first-party Dart CLI helpers — used by `dart` and `flutter` tooling. Includes an `ansi` module with capability-aware `green`, `red`, etc., plus `Ansi(supportsAnsi)` constructor for explicit gating. The canonical Dart pattern for production CLIs.
- ansi_styles
Low-level escape-sequence map — `AnsiStyles.red('text')`, `AnsiStyles.bgBlue('text')`, plus exposed escape strings for use cases that need the raw bytes (e.g. templating into a custom render). No capability detection — pair with `stdout.supportsAnsiEscapes`.
Idiomatic patterns
import 'dart:io';
void main() {
// Dart accepts both \x1B (hex) and \u001B (Unicode) — pick one.
stdout.write('\x1B[1;31merror:\x1B[0m permission denied\n');
stdout.write('\x1B[33mwarn:\x1B[0m deprecated flag\n');
stdout.write('\x1B[32mok:\x1B[0m 142 tests passed\n');
// Truecolor (38;2;R;G;B):
stdout.write('\x1B[38;2;255;128;0morange truecolor\x1B[0m\n');
}import 'dart:io';
// dart:io's stdout already exposes the portable capability check.
// supportsAnsiEscapes returns false on a piped stdout, on a non-TTY,
// and on legacy Windows console without VT enabled.
String style(String text, String sgr) {
if (!stdout.supportsAnsiEscapes) return text;
return '\x1B[${sgr}m${text}\x1B[0m';
}
void main() {
print(style('OK', '32'));
print(style('FAIL', '1;31'));
// Bonus — also reflects redirection state independently:
if (stdout.hasTerminal) {
final cols = stdout.terminalColumns; // dynamic width for bars / tables
print('terminal width: $cols');
}
}// pubspec.yaml:
// dependencies:
// chalkdart: ^3.0.0
import 'package:chalkdart/chalk.dart';
void main() {
print(chalk.red.bold('error:') + ' permission denied');
print(chalk.yellow('warn:') + ' deprecated flag');
print(chalk.green('ok:') + ' 142 tests passed');
// Background + foreground composition:
print(chalk.white.bold.bgRed(' FATAL '));
// Truecolor via rgb / hex:
print(chalk.rgb(255, 128, 0)('orange truecolor'));
print(chalk.hex('#ff8000')('same, hex form'));
}import 'dart:io';
bool ansiCapable() {
// Honour the Unix-standard kill switch first.
if (Platform.environment['NO_COLOR'] != null) return false;
// dart:io's own portable TTY check.
if (!stdout.supportsAnsiEscapes) return false;
return true;
}
void main() {
if (ansiCapable()) {
stdout.write('\x1B[32mready\x1B[0m\n');
} else {
stdout.write('ready\n');
}
}
// Flutter caveats:
// 1) print() in a Flutter app routes to platform logs (logcat / NSLog),
// which do NOT parse ANSI — your bytes appear verbatim. Gate output
// with kReleaseMode / kDebugMode or use the developer.log API.
// 2) flutter run --machine emits JSON, never ANSI — supportsAnsiEscapes
// returns false in that mode.
// 3) On Windows, supportsAnsiEscapes already accounts for VT-enabled
// Conhost (1709+) and Windows Terminal — no SetConsoleMode needed.