octo_ui 0.8.5
octo_ui: ^0.8.5 copied to clipboard
Cross-platform Primer-inspired Flutter UI kit. Optimised for devtools, dashboards, and dense data-heavy interfaces.
Changelog #
0.8.5 #
Added #
OctoTooltip.tooltipKey— optionalKeyforwarded to the inner MaterialTooltip. Lets callers hold aGlobalKey<TooltipState>and drive show / dismiss programmatically — useful for golden snapshots, guided tutorials, and first-run coachmarks.- Golden coverage: a new
octo_tooltip/shownscenario captures the popup at full opacity across all four theme variants. - The golden theme axis (
octoThemes) now includeslight-hcanddark-hcso every existing scenario also snapshots the high-contrast palette. Catches regressions where a component reads from the wrong token slot in addition to the WCAG-AA contrast unit tests.
Fixed #
- Dark-theme tooltip was unreadable — the popup used
neutral.emphasisPlusas its background, but in Primer's dark paletteemphasisPlusflips to a light "highest-contrast inverse surface" colour. Pairing that withfg.onEmphasis(white in dark mode) rendered white text on a near-white tooltip. The Material adapter now usesneutral.emphasisfor the tooltip background — it stays high-contrast against the canvas in every palette and pairs correctly withfg.onEmphasis. - The tooltip popup textStyle now sets an explicit
fontFamily('Roboto'with a platform-aware fallback cascade). Previously the textStyle inherited onlyfontFamilyFallbackfrom the ambient typography, but Material's_TooltipOverlayreplaces the ambientDefaultTextStylerather than merging into it — so on platforms where the fallback families don't resolve (Linux desktop, certain embedded builds, golden tests) every glyph rendered as the Ahem.notdefblock.
0.8.3 #
Added #
- Live demo on GitHub Pages — the kitchen-sink runs in-browser at
https://autocrab.github.io/flutter-octo-ui/, redeployed on every
push to
main. Built withflutter build web --release --wasmso the Skwasm renderer is used on modern Chromium / Firefox with automatic CanvasKit fallback for older browsers. - README now opens with a link to the live demo right under the badges.
0.8.2 #
Added #
- Five pub.dev topics for discovery:
design-system,ui,widgets,theme,dashboard. - Codecov badge in the README;
flutter test --coveragenow uploadscoverage/lcov.infoto codecov from CI.
Changed #
- CI gates
lib/line coverage at >= 90% — a regression that drops below the threshold fails the workflow. Current coverage sits at 92.4%, leaving headroom for the next round of features.
0.8.1 #
Changed #
- Minor fixes — README now ships pub.dev + CI badges and the install snippet drops the git-ref fallback now that the package is live.
0.8.0 #
First stable release on pub.dev. Promotes the 0.7.0-dev.0 component
surface (25 components across form / display / navigation / overlay /
data / layout categories) without API changes.
Changed #
- Repository / issue-tracker URLs in
pubspec.yamlpoint at the canonicalgithub.com/Autocrab/flutter-octo-uilocation. - Package description, library doc comment, and README reframe the
positioning as cross-platform, optimised for devtools / dashboards
/ dense data. No platform restrictions in
pubspec.yaml— the kit runs anywhere Flutter does; mobile just isn't the design target. README.mdrewritten to cover the current component catalogue, theming model, accessibility baseline, and the golden-test split betweenmatrixGoldenandcomponentMatrixGolden.NOTICEkeeps the upstream MIT / BSD-3 attributions for Octicons +flutter_octiconsand drops the standalone disclaimer paragraph.- New
.pubignoreshaves the published archive from ~15 MB to ~88 KB by excluding test goldens, example platform scaffolding, CI plumbing, and dev tooling configs. Mirrors.gitignorepatterns so the two files stay in sync.
0.7.0-dev.0 #
Changed #
- Upgraded
golden_matrix0.18.1 → 0.19.0 to pick up the newcomponentMatrixGoldenAPI. Migrated 11 small-component golden tests (button, icon_button, label, counter_label, spinner, state_label, divider, progress_bar, flash, collapsible, pagination) offmatrixGoldenso the captured PNGs are widget-sized instead of swimming inside a phone viewport — file names drop the device segment (<theme>_<locale>_<dir>_<scale>.png). Bigger / overlay / scaffold-positioned components stay onmatrixGoldenwhere the viewport context still matters. - New
octoComponentWrap(child)helper intest/goldens/_octo_matrix .dart—componentMatrixGoldendoes not expose awrapApphook, so the helper pullsOctoThemeDataout of the inherited Material theme (it sits there as aThemeExtensionviaOctoThemeData.toMaterialTheme) and installsOctoThemeabove the scenario subtree.
Added #
OctoDataTable<T>+OctoDataColumn<T>+OctoDataColumnAlignmentOctoSortDirection+OctoDataTableDensity— tabular presenter generic over the row type, built on Flutter'sTablewidget so columns size byIntrinsicColumnWidthby default — each column hugs the widest of its header and cells, no hand-tuned widths required. Override withOctoDataColumn.width(fixed pixels) orOctoDataColumn.flex(the column that should soak up leftover horizontal space — typically the title / subject column). Columns expose either atext: T → Stringaccessor or acell: (BuildContext, T) → Widgetbuilder (cell wins when both are present); columns can opt intosortableto render a tappable header that cycles asc → desc → none and reports viaonSortChanged. The table is presentation only — the parent owns the sorted list. OptionalonRowTap,zebrastriping,density(comfortable / compact), anemptyMessageempty state, and an optionalheaderwidget override per column (e.g. an icon for tight numeric columns). The rounded border + per-row dividers come fromborder.muted; the header sits oncanvas.subtle.
- Golden coverage:
octo_data_table/{default,sorted_desc,compact,empty}(light + dark) — the scenario file usesMatrixDevice.tabletLandscapeso the wide cells render at their native size instead of being crushed into the phoneSmall viewport. - Demo: a new "Data table" section in the kitchen sink wired to a controlled sort state.
0.6.0-dev.0 #
Added #
OctoTimeline+OctoTimelineItem+OctoTimelineVariant— vertical activity feed (Primer "Timeline"). Each entry pairs a 24 px variant-tinted marker disc with a title / subtitle / optionalbodywidget; a 2 pxborder.mutedrail runs through the entries to give the feed a continuous chronological spine (the rail usesClip.noneon its Stack so it extends through the per-row bottom padding into the next entry). Five marker variants — standard / accent / success / attention / danger.OctoSideNav+OctoSideNavItem— vertical sidebar navigation (Primer "SideNav"). Renders a stretched column of tappable rows bounded on the right by aborder.muteddivider. The selected row paints a 2 px accent bar flush against its leading edge over aneutral.subtlebackground; its label switches toOctoTextKind.bodyEmphasis. Tapping the already-selected row is a no-op;selectedIndex: -1highlights nothing. Per-row a11y carriesSemantics(button, selected, enabled, label); the focus ring and state layer behave the same as elsewhere.OctoTabs— content-switching tab group built on top ofOctoUnderlineNav. Pairs a list ofOctoUnderlineNavItemtabs with an equal-length list of body widgets and swaps the visible body viaAnimatedSwitcher(180 ms cross-fade by default) when the user picks a different tab. Uncontrolled viainitialIndexor controlled viaselectedIndex+onTabChanged. Motion-reduce (MediaQuery.disableAnimationsOf) drops the switch duration to zero so transitions snap. Tapping the already-active tab is a no-op.OctoPagination— paged navigator (Primer "Pagination"). 1-basedcurrentPage+pageCount, firesonPageChanged(int)when the user picks a different page (the already-selected tile silently ignores taps). Prev / Next chevrons step by one and disable at the range edges. Numbered slots are bounded bymaxVisible(default 7) and collapse with…ellipsis tokens when the gap exceeds one — the slot-computation helper is exposed asOctoPagination.computeSlotsfor callers that need to predict the rendered sequence.Semantics(button, selected, enabled, label: 'Page N')per tile; Prev / Next carry their own a11y labels.OctoStateLabel+OctoStateLabelVariant+OctoStateLabelEmphasis— Primer-style PR / issue lifecycle pill. Five variants (open,closed,merged,draft,attention) with variant-implied default icons (git_pull_request_16/issue_closed_16/git_merge_16/git_pull_request_draft_16/git_pull_request_16); passiconto override. Two emphasis tiers: high = filled.emphasisbackground (header look) / low = subtle.subtlebackground (dense list look).mergedreuses the accent palette until adone(purple) family lands.ExcludeSemanticsinside silences the inner icon + text, so screen readers read theSemantics(label:)once — passsemanticLabelto override the visible text.OctoSpinner+OctoSpinnerSize— circular indeterminate loading indicator. A 270° arc rotates continuously over a configurableduration(default 900 ms). Three size presets (16 / 24 / 40 px); default colour istheme.colors.fg.muted. Under motion-reduce (MediaQuery.disableAnimationsOf) the controller stops at a parked quarter-turn so the static shape still reads as a spinner.Semantics(label, liveRegion)announces the loading state.- Golden coverage:
octo_misc/spinners(small / medium / large, light + dark) — scenario wraps the row inMediaQuery(disableAnimations: true)so the snapshot stays deterministic underfreezeAnimations. - Demo: a new "Spinners" section in the kitchen-sink showing all three sizes paired with body-small status text.
0.5.0-dev.0 #
Added #
OctoToast+OctoToastVariant+OctoToastAction+OctoToastController— transient floating status pill (Primer "Toast"). StaticOctoToast.show(context, ...)mounts anOverlayEntryat the bottom-center, slides + fades in, schedules auto-dismiss afterduration(defaults to 4s; passDuration.zeroto make it sticky), and returns a controller whose.dismiss()removes the toast early. Four variants drive the leading-icon tint; optional action button + dismiss button.Semantics(liveRegion)announces the message to screen readers.OctoToast.showcaptures the ambientOctoTheme+Directionalityand re-injects them into the overlay subtree so the pill keeps theme access even though the rootOverlaysits above the inherited theme.OctoCollapsible— disclosure section (Primer "Accordion item"). Supports both uncontrolled (initiallyExpanded) and controlled (expanded+onExpansionChanged) modes. Header is a focusable button: Space / Enter toggle expansion viaFocusableActionDetector, state layer for hover / pressed, focus ring on keyboard focus.Semantics(button: true, expanded: ...)exposes the state to screen readers. The body height + chevron rotation animate overanimationDuration;MediaQuery.disableAnimationsOf(ADR-0008) drops the duration to zero so transitions snap under motion-reduce.OctoProgressBar+OctoProgressBarVariant+OctoProgressBarSize— linear progress indicator.value: double?drives determinate (0..1) vs. indeterminate (null) modes. Indeterminate uses anAnimationController.repeat()sliding stripe;MediaQuery .disableAnimationsOf(motion-reduce / ADR-0008) automatically swaps in a static 50%-filled track so the bar still hints "in progress" without burning frames. Four variants (accent / success / attention / danger), two sizes (small = 4 px, medium = 8 px). Determinate value is exposed toSemantics(value: '${n}%').OctoDivider+OctoDividerAxis+OctoDividerEmphasis— thin separator line between layout regions. Horizontal divider via the default constructor, vertical viaOctoDivider.vertical(). Emphasis maps ontotheme.colors.border.{subtle,muted,defaultColor};coloroverrides the resolved palette value when needed.indent/endIndentinset the painted region while the widget itself still spans the full cross-axis (mirrors Material'sDividerAPI). Wrapped inExcludeSemantics— dividers are decorative.OctoChipships a compact custom_ChipDismissButton(16×16) instead of reusingOctoIconButtonso chips with and without anxshare the same height.OctoDropdown<T>accepts an optional externalOctoMenuController, letting callers (e.g. golden scenarios) open / close the popover programmatically without exposing internal state.- Golden coverage:
octo_misc/dividers,octo_misc/progress_bars,octo_misc/collapsibles,octo_misc/toasts(light + dark) and anocto_pickers/dropdown_openscenario that snapshots the menu in its open state. - Demo: a new "Dividers" section in the kitchen-sink showing subtle / muted / strong horizontals plus a vertical inline strip.
0.4.0-dev.0 #
Added #
OctoSegmentedControl<T>+OctoSegmentedControlItem<T>— single-select group of connected buttons. Outer container usescanvas.subtle; the selected segment lifts above the rest with acanvas.defaultColorbackground and a subtle border. Items take alabeland / or anicon.Tabwalks between segments,Spaceactivates the focused segment; the already-selected segment ignores taps.Semantics(button, selected, enabled, label)per segment.OctoChip+OctoChipVariant— compact interactive pill, filled rather than outlined (the distinguishing trait againstOctoLabel). Five variants (standard/accent/success/attention/danger). OptionalonPressedmakes the chip tappable; optionalonDismissadds a trailingOctIcons.x_16close button with a'Remove $label'default a11y label.OctoDropdown<T>+OctoDropdownItem<T>— controlled single- select picker built on top ofOctoMenu. Trigger renders the selected item's label plus achevron_down_16; tapping opens a menu with all options (selected row pre-marked); picking an option firesonChangedand auto-closes the menu.placeholdershown whilevalueisnull.- Demo grows three new sections — "Segmented control", "Chips", "Dropdown" — wired to live state.
0.3.0-dev.0 #
Added #
-
OctoDialog+OctoDialog.show<T>()— themed modal dialog. Wraps Material'sDialogwith Primer chrome (canvas.overlay,border.defaultColor,radii.large,shadows.large);title/content/actionsslots.Escapeand outside-tap on the scrim both dismiss. Action buttons wire their ownNavigator.pop(ctx, value)to surface aFuture<T?>fromshow.OctoDialogTitleis sugar for the common heading text case. -
OctoSkeleton+OctoSkeletonText+OctoSkeletonAvatar— loading placeholders that pulse betweenneutral.mutedandneutral.subtle. Wrapped inExcludeSemanticsso the placeholder is invisible to screen readers. HonoursMediaQuery.disableAnimationsOf(ADR-0008) by suspending the controller — for golden tests, passfreezeAnimations: truetomatrixGolden. -
OctoAvatar— user avatar with image-then-initials fallback.imageUrl(defaultNetworkImage) ORimageProvider(asset / memory / custom);initialsfor the fallback. 5 sizes (xs/sm/md/lg/xl= 16/20/32/48/64 dp) and 2 shapes (circle/square). RequiredsemanticLabel+Semantics(image: true). -
OctoBreadcrumbs+OctoBreadcrumbItem— horizontal navigation trail. Clickable segments render asinvisible-variantOctoButtons; the final segment (onPressed: null) becomes plain text — the "current page" convention. Octiconschevron_right_16separates pairs. -
OctoSwitch— pill-shaped on/off toggle. Controlled (value+onChanged);onChanged: nulldisables. Animated thumb (theme.animation.fast+standardCurve).Spaceactivates the focused switch viaActivateIntent.Semantics(toggled, enabled, label). -
OctoCheckbox— 16×16 box with the Octiconscheck_16/dash_16glyph. Supportstristate: truefor indeterminate (cyclefalse → true → null → false); a non-tristate checkbox with anullvalue triggers an assertion.Semantics(checked, mixed, enabled, label). -
OctoRadio<T>— generic radio withvalue/groupValue/onChanged. Tapping the already-selected radio is a no-op; tapping a sibling sends its value.Semantics(inMutuallyExclusiveGroup, checked, enabled, label). -
Shared golden suite
octo_form_controlscovers Switch / Checkbox / Radio across off / on / disabled / indeterminate states. Tests 13 widget cases (4 + 5 + 4) across the three components. -
Kitchen-sink demo grows a "Form controls" section: a notifications switch, a tri-state terms checkbox, and a 3-radio priority group.
-
Octicons integration —
flutter_octicons ^1.71.0is now a direct dependency, andpackage:octo_ui/octo_ui.dartre-exports theOctIconsclass so apps can writeIcon(OctIcons.code_16)without an extra import. Sample / golden / demo code throughout the package is re-imaged with Octicons; no MaterialIcons.*glyphs remain in the visual layer. -
OctoFlashfinally ships its dismiss button. PassonDismissto render a trailing close affordance (Octiconsx_16); the optionaldismissSemanticLabel(default'Dismiss') overrides the screen reader announcement. The "deferred until octo_icons" stop-gap is retired. -
NOTICEfile at the repository root carries the MIT attribution for Octicons (© GitHub, Inc.) and the BSD-3-Clause attribution for theflutter_octiconsFlutter wrapper, plus the standard "not affiliated with GitHub" disclaimer. -
OctoUnderlineNav+OctoUnderlineNavItem— horizontal section-tab strip with an underline indicator under the selected tab (Primer "UnderlineNav"). Items take optionaliconandtrailingwidgets (counter labels fit there naturally). Controlled API: passselectedIndex+onChanged. Selected label usesOctoTextKind.bodyEmphasis; each tab is independently focusable and carriesSemantics(button: true, selected: ...). The component itself does not handle horizontal overflow — wrap in aSingleChildScrollViewwhen the tab strip is wider than the available room.
0.2.0-dev.0 #
Added #
OctoFocusRing.overlay— clip-proof named constructor that renders the ring throughOverlayPortalin the rootOverlay. Survives ancestor clips (ClipRect,ListViewitems, dialog containers); same visibility rules as the inline variant (focus +FocusHighlightMode.traditional). Requires an enclosingOverlay(provided byMaterialApp/WidgetsApp). See ADR-0006.OctoCounterLabel— compact numeric counter pill (Primer "CounterLabel"). 3 variants (standard / primary / secondary); optionalmaxDisplayedclamps oversized counts with a+suffix (OctoCounterLabel(150, maxDisplayed: 99)→ "99+"). OptionalsemanticLabelto spell out what the count counts.OctoCommandPalette+OctoCommandPaletteController— modal command palette rendered throughOverlayPortalon top of the host app. The modal contains an autofocused search field that filters items by case-insensitive substring match onlabel + description;Enteractivates the first enabled match,Escapeand outside-scrim taps dismiss. OptionalopenShortcut: ShortcutActivator?(e.g.SingleActivator(LogicalKeyboardKey.keyK, meta: true)forCmd+K) installs a global key handler that opens the palette. The modal body wraps in a transparentMaterialso the embeddedOctoTextField(which delegates to Material'sTextField) finds an ancestor outside the host route.- Arrow-key navigation in
OctoActionList— each row is a focusable node; the list wraps inShortcuts({Up: PreviousFocusIntent, Down: NextFocusIntent})+Actions(NextFocusAction, PreviousFocusAction)+FocusTraversalGroup(ReadingOrderTraversalPolicy), so arrow keys traverse rows without requiring aWidgetsApp.Enter/Space/NumpadEnteractivate the focused row. Newautofocus: boolparameter requests focus on the first row at mount —OctoMenunow passesautofocus: trueso menus are keyboard-ready on open. OctoStateLayer.focusednow paints theneutral.subtleoverlay, matchinghovered— focused rows / buttons are visible during keyboard traversal without a pointer hover.OctoMenu+OctoMenuController— popover-style menu anchored to a trigger widget. ComposesOctoActionListinside anOverlayPortaltracked to the trigger viaLayerLink+CompositedTransformFollower. Dismisses on outside tap (TapRegion),Escapekey (Shortcuts/Actions→DismissIntent), and (by default) on item selection.closeOnSelect: falsekeeps the menu open for multi-select filter patterns. Width snaps to the trigger's measured width viaIntrinsicWidth+ minimum-width constraint; override withminWidth.OctoTooltip— thin wrapper over Material'sTooltip. Visuals (padding, radius, colours, typography) come from theTooltipThemeDatainstalled bytoMaterialTheme(); behaviour (hover-after-delay, long-press, smart edge-flip, a11y announcement) is delegated.OctoActionList+OctoActionListItem— vertical list of action rows, used standalone or as the body of an overlay menu / palette. Each row has its own hover / pressed / selected / disabled state machine on Flutter's built-inWidgetState.dangervariant tints label and icon viadanger.fg. Default constructor takes an eagerList<OctoActionListItem>;OctoActionList.builderis the lazy variant for long lists (filter dropdowns, command palette). Keyboard arrow-traversal deferred to a later milestone.- Dynamic-state golden coverage —
hovered,pressed,focusedscenarios forOctoButtonandOctoIconButton. A new@visibleForTestingdebugStates: Set<WidgetState>?parameter on both components lets goldens inject hover / pressed without driving real pointer events;focusedusesautofocus: trueplus aGoldenFocusScopehelper that pinsFocusManager.highlightStrategytoalwaysTraditionalfor the duration of the scenario. Total goldens grow from 28 to 40. - High-contrast palette —
OctoColorScheme.light(variant: highContrast)and.dark(variant: highContrast)now return concrete values (Primer- aligned). The shape was reserved in 0.1.0-dev.0; only colour-blind variants (protanopia/deuteranopia/tritanopia) still throwUnimplementedError. In hi-contrast dark,fg.onEmphasisflips to a near-black because emphasis backgrounds are bright. All four palettes (light, dark, light-hc, dark-hc) pass the WCAG-AA contrast assertions. See ADR-0005.
Documentation #
- All public members under
lib/src/now carry///doc comments; lintpublic_member_api_docsis enabled at warning level and reports zero issues.
0.1.0-dev.0 #
Foundation release. API is unstable until 0.1.0.
Added #
- Design tokens under
lib/src/tokens/:OctoColorSchemewithcanvas/fg/border/neutral/accent/success/attention/dangersubschemes.light()anddark()factories withOctoColorSchemeVariantenum (standard implemented;highContrastand colour-blind variants reserved as enum slots).OctoSpacing— indexed scale + semanticgap/insetaliases (xs/sm/md/lg/xl).OctoRadius,OctoTypography,OctoShadows,OctoBreakpoints,OctoAnimation.- Every token class is
@immutablewithcopyWith/lerp/==/hashCode.
- Theme propagation under
lib/src/theme/:OctoThemeDataaggregates all token groups and implementsThemeExtension<OctoThemeData>.OctoThemeextendsInheritedTheme(theme flows intoDialog/PopupMenu/TooltipviaInheritedTheme.captureAll).OctoTheme.of/OctoTheme.maybeOf/debugCheckHasOctoTheme(context).OctoMaterialAdapter.toMaterialTheme()returns Material 3ThemeDatawith fullColorSchememapping,NoSplashfactory, and themedDialog/SnackBar/PopupMenu/Tooltipsubthemes.OctoThemeDatais also registered inThemeData.extensions.
- Foundation widgets under
lib/src/foundation/:OctoBox,OctoText(semantic kind picker),OctoIcon(size 12 / 16 / 24).OctoFocusRing— keyboard-only outline viaCustomPaint; watchesFocusManager.instance.highlightMode.OctoStateLayer— translucent overlay driven by Flutter's built-inWidgetStateset.
- Components under
lib/src/components/:OctoLabel— pill tag, 5 variants.OctoButton— 4 variants × 3 sizes, loading, leading / trailing icons, keyboard activation viaActivateIntent(Enter / Space / NumpadEnter).OctoIconButton— composition overOctoButton; requiredsemanticLabel.OctoFlash— 4 variants;liveRegion: truesemantics. No dismiss button — see omissions below.OctoTextField— outlined input over MaterialTextField; full prop set (controller,focusNode,inputFormatters,obscureText,autofillHints,keyboardType,textInputAction,min/maxLines,maxLength, helper / error text, etc.).
- Tests — 114 widget / unit + 28 golden baselines via
golden_matrix. WCAG-AA contrast is checked on every standard palette. - Demo —
example/runs on macOS and web (flutter run -d macos|chrome).
Architectural decisions #
- Monolithic single-package layout under
lib/src/{tokens,theme,foundation,components,utils}/. Multi-package / Melos split deferred until a concrete trigger. - Uses Flutter's built-in
WidgetState/WidgetStatesController. No parallelOctoWidgetStateenum. - Material is only touched at the integration boundary:
toMaterialTheme()and the editing internals ofOctoTextField. Component visuals contain no Material widgets (noIcons.*, noInkWell, no ripple). dart formatruns atpage_width: 100. Field order inside classes:static→final→ constructor → methods →copyWith/lerp→==/hashCode. Lintssort_constructors_firstandsort_unnamed_constructors_firstare explicitly disabled.
Known omissions (planned for 0.2 / 0.3) #
OctoFocusRing.overlay(clip-proof variant viaOverlayPortal).- High-contrast and colour-blind palette values (
OctoColorSchemeVariantshape is in place but onlystandardis implemented; other variants throwUnimplementedError). OctoCounterLabel,OctoActionList,OctoUnderlineNav, overlay family.OctoFlashdismiss button — blocked until a non-Material close glyph ships with the icon wrapper.- Token generator from Primer Primitives JSON.
- Widgetbook playground.
- Goldens for dynamic states (
hovered,pressed,focused,loading).