Skip to Content

Build terminal apps in Dart.

Noir gives a terminal program a widget tree, retained state, and layout measured in cells.

Build your first appRun an example

dart pub add noir

Requires Dart 3.10 or later.

Press the button to update the count.

The retained State of the first-app tutorial
class _CounterAppState extends State<CounterApp> {
  int _count = 0;

  void _increment() => setState(() => _count++);

  @override
  Widget build(BuildContext context) => Container(
    padding: const EdgeInsets.all(1),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      spacing: 1,
      children: [
        Text('Count: $_count'),
        Button(autofocus: true, label: '+ Add one', onPressed: _increment),
      ],
    ),
  );
}
The same file after one Space pressView source

 Count: 1

  + Add one 

dart run noir:run bin/noir_demo.dartPress Space once on the focused button. Captured at 40×6 through NoirDriver (NOIR_DRIVE=1), headless.

Build with text, inputs, lists, and dialogs.

Compose the screen from public widgets instead of writing escape sequences around raw terminal output.

Layout in cells

Row, Column, Flex, Stack, Wrap, overlays, borders, and scrolling use integer terminal geometry.

Learn the layout model

Input with ownership

Focus traversal, shortcuts, editable text, pointer hit testing, and wheel input share one ordered pipeline.

Route input correctly

Controls and data

Buttons, fields, selects, tabs, sliders, lists, and data tables keep values explicit and keyboard behavior predictable.

Browse the widget catalog

Terminal documents

CodeView, DiffView, MarkdownView, rich tables, hyperlinks, and terminal images live on the same retained render path.

Find document widgets