Skip to content
Tarn UI

Docs

Getting started with Tarn UI

Everything here is also in the README inside the zip. Tarn UI 1.0.0 needs Flutter 3.27 or newer and has no runtime dependencies beyond the Flutter SDK.

Install

Unzip the download and copy the tarn_ui folder into your repository — packages/tarn_ui is a good home. Then depend on it by path (or push it to a private git repo and depend on that):

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  tarn_ui:
    path: packages/tarn_ui   # or a private git URL

Run flutter pub get and import package:tarn_ui/tarn_ui.dart. The IBM Plex fonts ship inside the package, so there is nothing else to declare.

Theme

TarnTheme returns an ordinary ThemeData with a matching ColorScheme, so Material widgets you already use pick up Tarn’s colors. The palette itself lives in the TarnColors theme extension.

dart
MaterialApp(
  theme: TarnTheme.light(),
  darkTheme: TarnTheme.dark(),
  themeMode: ThemeMode.system,
  home: const HomePage(),
);

// Your brand color instead of Tarn teal:
TarnTheme.fromSeed(const Color(0xFF3D5AFE));
TarnTheme.fromSeed(const Color(0xFF3D5AFE), brightness: Brightness.dark);

// Full control over the palette:
final colors = TarnColors.light.copyWith(
  canvas: const Color(0xFFF7F7F5),
  sidebar: const Color(0xFF101418),
);
TarnTheme.build(colors, Brightness.light);

fromSeed adjusts the seed’s lightness for the chosen brightness, then picks dark or white text for it by luminance, so labels on buttons stay readable whatever color you pass.

App shell

TarnAppShell renders one set of destinations three ways: a bottom bar with a “More” sheet below 640 px, a rail from 640 px, and a sidebar with sections, workspace header and user card from 1100 px. The top bar shows a breadcrumb and, on wide windows, a search field.

dart
TarnAppShell(
  workspaceName: 'Acme',
  destinations: const [
    TarnDestination(id: 'overview', label: 'Overview',
        icon: Icons.space_dashboard_outlined, section: 'Workspace'),
    TarnDestination(id: 'invoices', label: 'Invoices',
        icon: Icons.receipt_long_outlined, badge: '3'),
    TarnDestination(id: 'settings', label: 'Settings',
        icon: Icons.settings_outlined, section: 'Account'),
  ],
  selectedId: current,
  onSelect: (id) => setState(() => current = id),
  user: const TarnShellUser(name: 'Mara Ellison', email: 'mara@acme.com'),
  body: pages[current]!,
);

Widgets

ActionsTarnButton (5 variants, 3 sizes, loading), TarnIconButton, TarnLink, TarnMenuButton
InputTarnTextField, TarnSearchField, TarnCodeField, TarnSelect, TarnCheckbox, TarnSwitch
SelectionTarnSegmented, TarnTabs, TarnFilterChips
DisplayTarnCard, TarnBadge, TarnMetricTile, TarnAvatar, TarnAvatarStack, TarnListTile, TarnProgressBar, TarnEmptyState
FeedbackshowTarnToast, showTarnConfirm, showTarnSheet, TarnDialog, TarnCallout, TarnSkeleton
DataTarnDataTable with sorting, selection and responsive scrolling
ChartsTarnLineChart, TarnBarChart, TarnDonutChart, TarnSparkline, TarnChartLegend
LayoutTarnAppShell, TarnPage, TarnPageHeader, TarnResponsiveGrid, TarnAdaptiveRow, TarnAuthLayout, TarnLogo
dart
TarnButton(label: 'New invoice', icon: Icons.add, onPressed: create);
TarnButton(label: 'Export', variant: TarnButtonVariant.outline, onPressed: export);
TarnBadge(label: 'Overdue', tone: TarnTone.negative, dot: true);
TarnMetricTile(label: 'Collected', value: r'$48,210', delta: 12.4);

showTarnToast(context, 'Reminder sent', tone: TarnTone.positive);
final ok = await showTarnConfirm(context,
    title: 'Void invoice?', message: 'This can’t be undone.',
    confirmLabel: 'Void', destructive: true);

Data tables

Columns describe how to render and sort a row. Header taps toggle sorting; with selectable on, a header checkbox selects every row. When the table is narrower than its columns’ minimum widths it scrolls horizontally instead of squeezing text.

dart
TarnDataTable<Customer>(
  rows: customers,
  selectable: true,
  selected: selected,
  onSelectionChanged: (s) => setState(() => selected = s),
  columns: [
    TarnColumn(label: 'Customer', flex: 2,
        cell: (context, c) => Text(c.name), sortKey: (c) => c.name),
    TarnColumn(label: 'Balance', numeric: true,
        cell: (context, c) => Text(TarnFormat.money(c.balance)),
        sortKey: (c) => c.balance),
  ],
);

Charts

Charts are painted with CustomPainter and read colors from the theme. Axis maximums are rounded to readable steps, the line chart shows a tooltip on hover or drag, and bars can be grouped or stacked. Give each chart a semanticLabel for screen readers.

dart
TarnLineChart(
  labels: const ['Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov'],
  series: const [
    TarnSeries(name: 'Billed', values: [31, 34, 33, 38, 41, 44]),
    TarnSeries(name: 'Collected', values: [27, 30, 31, 33, 37, 40]),
  ],
  valueFormat: (v) => TarnFormat.money(v * 1000),
  semanticLabel: 'Billed and collected, last six months',
);

Screens

Nine full screens are exported: sign in, sign up, verify code, overview, customers, invoices, inbox, reports and settings. They run on demo data so you can see every state; each exposes callbacks for the parts you’ll connect, and is a readable starting point to copy into your app and adapt.

dart
TarnSignInScreen(
  productName: 'Acme',
  onSignIn: (email, password) async {
    await auth.signIn(email, password); // throw to show the message in the form
  },
  onForgotPassword: () => context.go('/reset'),
);

Tokens

  • context.tarn — the active TarnColors (canvas, surface, ink, accent, tones, sidebar, chart series…)
  • TarnType — display, title, heading, body, small, caption, label and mono styles, with tabular figures for numbers
  • TarnSpace.x1 … x10 — a 4-point spacing scale; TarnRadius.sm / md / lg / pill
  • TarnBreakpoints.compact (640) and .expanded (1100)
  • TarnFormat — money, number, compact (48.2k), dates

Showcase & tests

The example/ app shows every screen inside the shell, plus a gallery of all widgets. Run it with cd example && flutter run -d chrome (or any device). Deep links like ?screen=invoices&theme=dark open a screen directly on web.

flutter test in the package root runs the widget tests, including a matrix that renders every screen at phone, tablet and desktop sizes in both themes and fails on any overflow. Keep it running as you customise and you’ll know when a change breaks a layout.

Something unclear? Get in touch.