octo_ui 1.0.0 copy "octo_ui: ^1.0.0" to clipboard
octo_ui: ^1.0.0 copied to clipboard

Cross-platform Primer-inspired Flutter UI kit. Optimised for devtools, dashboards, and dense data-heavy interfaces.

octo_ui #

pub package CI codecov

🎮 Live demo: https://mavoryl.github.io/flutter-octo-ui/ — the kitchen sink running in your browser. Updated on every push to main.

A cross-platform Flutter UI kit inspired by Primer. Optimised for devtools, dashboards, admin panels, and dense data-heavy interfaces — but every widget shares the same theme tokens, focus model, and accessibility baseline regardless of platform, so it runs cleanly on mobile too.

Why another UI kit #

Material is mobile-first and design-opinionated; Cupertino is iOS-locked; most boutique kits hard-code colours and skip keyboard navigation. octo_ui fills the gap in between:

  • Tokens first, not components first — colours, spacing, radii, typography, shadows, breakpoints, and animation curves live in OctoThemeData. Widgets read semantic tokens (theme.colors.fg.defaultColor, theme.colors.accent.emphasis) — never hardcoded values.
  • Every interactive state is mandatory — default, hover, focus, pressed, selected, disabled, loading. Web/desktop UIs without hover/focus look like stretched mobile apps; octo_ui widgets all wire FocusableActionDetector + WidgetStatesController so they cooperate with the rest of the Flutter ecosystem.
  • A11y is shipped, not added laterSemantics flags on every interactive surface, liveRegion on flash / toast, required semanticLabel on icon-only buttons. WCAG-AA contrast is verified by automated tests across light / dark / high-contrast palettes.
  • Material adapter includedoctoTheme.toMaterialTheme() returns Material 3 ThemeData so dialogs, snackbars, popup menus, tooltips, and editing internals inherit Octo colours without you wiring them up.

Status #

Stable1.0.0. The theme API and every component API are frozen under semantic versioning: additions land in minor releases, and nothing breaks before 2.0.0.

What that covers: light and dark themes with high-contrast variants, an accessibility baseline verified by tests, 37 components with golden coverage, colour tokens generated from Primer Primitives, and per-component documentation. What it does not: the colour-blind palette variants are enum slots that still throw UnimplementedError, and lib/src/ remains private — internal paths may move without a major bump.

Component catalogue #

37 components across 6 categories, each covered by unit and widget tests.

Form & input   OctoButton · OctoIconButton · OctoTextField · OctoSwitch · OctoCheckbox · OctoRadio · OctoSegmentedControl · OctoDropdown

Display & labels   OctoLabel · OctoCounterLabel · OctoStateLabel · OctoChip · OctoAvatar · OctoAvatarStack · OctoFlash · OctoSkeleton · OctoEmptyState

Navigation   OctoUnderlineNav · OctoSideNav · OctoTabs · OctoBreadcrumbs · OctoPagination

Overlays   OctoDialog · OctoTooltip · OctoMenu · OctoPopover · OctoToast · OctoCommandPalette (⌘K-style picker) · OctoActionList

Data & feedback   OctoDataTable<T> · OctoTimeline · OctoProgressBar · OctoSpinner

Layout primitives   OctoCard · OctoCollapsible · OctoDivider · OctoFilterBar

Documentation #

Every component carries a class-level doc comment with the same sections: a one-line summary, a usage sample, its variants, its sizes, the interactive states it tracks, and what it exposes to assistive technology. The API reference is the primary documentation — there is no separate site to drift out of sync with the code.

Both halves are enforced. The samples are type-checked against the real API by a test, so a renamed parameter breaks the build instead of shipping a snippet that doesn't compile, and CI fails on any unresolved doc reference.

Installation #

dependencies:
  octo_ui: ^1.0.0

Or flutter pub add octo_ui.

Quick start #

import 'package:flutter/material.dart';
import 'package:octo_ui/octo_ui.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final octo = OctoThemeData.light();
    return OctoTheme(
      data: octo,
      child: MaterialApp(
        theme: octo.toMaterialTheme(),
        home: const Scaffold(body: Center(child: _Example())),
      ),
    );
  }
}

class _Example extends StatelessWidget {
  const _Example();

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        const OctoStateLabel(
          label: 'Open',
          variant: OctoStateLabelVariant.open,
        ),
        const SizedBox(height: 16),
        OctoButton.label(
          'Save changes',
          onPressed: () {},
          variant: OctoButtonVariant.primary,
        ),
      ],
    );
  }
}

A full kitchen sink — every component with controlled state, hover / focus / pressed showcases, dark-mode + high-contrast toggles, and a ⌘K command palette — lives in example/:

cd example
flutter run -d macos   # or -d chrome, -d linux, -d windows, -d ios, -d android

Theming #

OctoTheme is both an InheritedTheme (propagates through Dialog / PopupMenu / Tooltip via InheritedTheme.captureAll) and a ThemeExtension<OctoThemeData> (so Theme.of(context).extension<OctoThemeData>() works in Material descendants).

final theme = OctoTheme.of(context);
final paragraphColor = theme.colors.fg.defaultColor;
final cardPadding   = theme.spacing.gap.md;
final radius        = BorderRadius.all(Radius.circular(theme.radii.medium));

Three built-in variants: OctoThemeData.light(), .dark(), and OctoColorSchemeVariant.highContrast for either brightness — WCAG-AA verified by tests.

Where the colours come from #

The four palettes and the breakpoint scale are generated from a pinned @primer/primitives snapshot, not typed by hand, so every value traces back to an upstream token and a tarball digest. The snapshot is committed — builds never reach the network — and CI fails if the generated file and the snapshot disagree.

OctoRadius, OctoTypography, OctoShadows and OctoAnimation stay hand-written: Primer states those as CSS shorthand, or on a step scale that doesn't line up with Flutter's — borderRadius-small is 3 px upstream against 4 px here, and there is no 10 px type step for labelSmall at all.

Architecture #

lib/
  octo_ui.dart                       # public barrel — only export surface
  src/
    tokens/                          # OctoColorScheme, OctoSpacing, OctoRadius,
                                     # OctoTypography, OctoShadows, OctoBreakpoints,
                                     # OctoAnimation
    theme/                           # OctoThemeData, OctoTheme, toMaterialTheme()
    foundation/                      # OctoBox, OctoText, OctoIcon,
                                     # OctoFocusRing, OctoStateLayer
    components/<name>/               # one folder per component, internals private
    tokens/generated/                # emitted from the Primer snapshot — never edited

The public API is reachable only through package:octo_ui/octo_ui.dart. Internal paths under lib/src/ are private and may move without a breaking-change bump.

The package re-exports OctIcons from flutter_octicons so callers can paint Octicons without an extra dependency.

Responsive layout #

Breakpoints are theme tokens (xs 320 · sm 544 · md 768 · lg 1012 · xl 1280 · xxl 1400, matching Primer viewports), and two APIs read them:

// Window size class — for decisions that follow the viewport.
if (context.isAtLeast(OctoBreakpoint.lg)) showSidebar();

// Available-space size class — for content that sits beside a sidebar,
// inside a split view, or in any box narrower than the window.
OctoResponsiveBuilder(
  builder: (context, breakpoint) => GridView.count(
    crossAxisCount: breakpoint.isAtLeast(OctoBreakpoint.lg) ? 4 : 2,
    children: tiles,
  ),
)

Thresholds come from OctoThemeData.breakpoints, so copyWith re-tunes every call site at once.

Accessibility #

  • Every interactive widget exposes Semantics(button|toggled|selected|enabled|expanded, label) matching its current state.
  • Keyboard activation (Enter / Space / NumpadEnter) is wired via FocusableActionDetector; arrow-key navigation is supported on OctoActionList, OctoCommandPalette, and OctoMenu.
  • The focus ring (OctoFocusRing) only paints in keyboard mode — FocusManager.highlightMode watches for real keyboard events.
  • Live regions on OctoFlash and OctoToast announce status changes to screen readers.
  • Motion is respected: components with infinite animations (OctoSpinner, OctoSkeleton, OctoProgressBar indeterminate) honour MediaQuery.disableAnimationsOf and fall back to a static frame.
  • Colour-pair contrast (foreground on canvas, foreground-on-emphasis on every status colour) is enforced by automated WCAG-AA tests across light, dark, light-hc, and dark-hc palettes.

Testing #

flutter analyze
flutter test

CI runs dart format + flutter analyze + flutter test on every push.

License #

MIT. See NOTICE for third-party attributions — Octicons are © GitHub, Inc. (MIT) and ship through flutter_octicons (BSD-3-Clause).

3
likes
160
points
143
downloads
screenshot

Documentation

API reference

Publisher

verified publishermavoryl.com

Weekly Downloads

Cross-platform Primer-inspired Flutter UI kit. Optimised for devtools, dashboards, and dense data-heavy interfaces.

Repository (GitHub)
View/report issues

Topics

#design-system #ui #widget #theme #dashboard

License

MIT (license)

Dependencies

flutter, flutter_octicons, meta

More

Packages that depend on octo_ui