dashronym 0.1.0
dashronym: ^0.1.0 copied to clipboard
Accessible inline acronym glossary tooltips for Flutter, with rich entries, aliases, scoped configuration, and portable JSON.
dashronym #
Dashronym turns known acronyms inside Flutter text into accessible inline controls with viewport-safe definition tooltips. It supports simple maps, rich glossary entries and aliases, shared app-level configuration, existing rich-text trees, and a versioned JSON interchange format.
The package is local-first: rendering a glossary does not require an account, network connection, telemetry, or a hosted Dashronym service.
Highlights #
- Add glossary behavior to a
Textwith.dashronyms()or useDashronymTextdirectly. - Share a registry, parser configuration, theme, and tooltip builder through
DashronymScope. - Install
DashronymThemeas a FlutterThemeExtension. - Keep nested
TextSpanstyling, locale metadata, existingWidgetSpans, layout options, selection, directionality, text scaling, and recognizers on unmatched text; matched terms intentionally become glossary controls. - Use immutable
DashronymEntryvalues with expansions, longer definitions, aliases, tags, sources, and JSON-safe metadata. - Import and export deterministic, versioned
DashronymGlossaryJSON. - Resolve alias collisions with explicit reject, keep-first, or keep-last policies.
- Support touch, mouse, keyboard, screen readers, RTL layouts, compact viewports, visible keyboards, and long definitions.
- Replace the tooltip surface while retaining Dashronym's overlay, focus, dismissal, and viewport constraints.
Requirements #
Dashronym 0.1.0 requires:
- Dart
>=3.10.0 <4.0.0 - Flutter
>=3.38.1
CI validates the exact minimum and the latest supported stable Flutter release.
See the migration guide before upgrading from 0.0.x.
Installation #
Add Dashronym to your Flutter application:
dependencies:
dashronym: ^0.1.0
Then run:
flutter pub get
Quick start #
For one text widget, pass a registry directly:
import 'package:dashronym/dashronym.dart';
import 'package:flutter/material.dart';
final registry = DashronymRegistry({
'SDK': 'Software Development Kit',
'API': 'Application Programming Interface',
});
class Overview extends StatelessWidget {
const Overview({super.key});
@override
Widget build(BuildContext context) {
return const Text(
'Our SDK exposes a lightweight API.',
).dashronyms(
registry: registry,
config: const DashronymConfig(enableBareAcronyms: true),
);
}
}
Marker-wrapped terms work without enabling bare matching:
DashronymText(
'Install the (SDK) before using the (API).',
registry: registry,
)
Only terms present in the registry become interactive.
Share one glossary across a subtree #
Use DashronymScope when a feature or application uses the same glossary in
several places:
DashronymScope(
registry: registry,
config: const DashronymConfig(enableBareAcronyms: true),
child: const Article(),
)
Descendants can omit the shared values:
const Text('The SDK calls the API.').dashronyms()
const DashronymText('The API is also available from the CLI.')
Resolution precedence is:
- an explicit widget or extension argument;
- the nearest
DashronymScope; - a
DashronymThemeinThemeData.extensionsfor visual configuration; and - package defaults.
Omitting a registry without a surrounding scope produces a focused
FlutterError explaining both valid setup options.
Rich entries, aliases, and portable JSON #
Use DashronymEntry when a definition needs more structure than a single
description string:
final glossary = DashronymGlossary(
name: 'Software basics',
id: 'com.example.software-basics',
version: '2026.07.1',
locale: 'en-CA',
license: 'LicenseRef-Example',
entries: [
DashronymEntry(
acronym: 'API',
expansion: 'Application Programming Interface',
definition: 'A defined interface used by software components.',
aliases: const ['APIS'],
tags: const ['software', 'integration'],
source: 'https://example.com/editorial-method',
metadata: const {
'entryId': 'software.api',
'status': 'reviewed',
},
),
],
);
final registry = glossary.toRegistry();
registry.descriptionOf('apis');
// Application Programming Interface — A defined interface used by
// software components.
registry.entryOf('APIS')?.source;
// https://example.com/editorial-method
Decode or encode the same model at an application boundary:
final glossary = DashronymGlossary.fromJsonString(encodedJson);
final prettyJson = glossary.toJsonString(pretty: true);
The decoder is intentionally strict. Unknown fields, unsupported schema
versions, malformed timestamps, invalid metadata, and duplicate aliases fail
with descriptive errors. Put application-specific JSON values in metadata.
When the document is converted with toRegistry(), its default duplicate
policy also rejects normalized canonical/alias collisions across entries.
The authoritative version-1 contract is
schema/v1/dashronym-glossary.schema.json.
Core entry point #
Flutter-SDK projects can import the model and registry layer without importing Flutter APIs:
import 'package:dashronym/dashronym_core.dart';
That library's import graph is pure Dart, but the current dashronym package
still declares an SDK dependency on Flutter. A Dart-only project that does not
install Flutter cannot consume this package yet. Publishing a separately
versioned core package is tracked under
portable Dart runtime priorities
before non-Flutter integrations.
Matching behavior #
DashronymConfig has two matching modes:
- Marker matching is enabled by default. The default pairs recognize
(API),'API', and"API". A registered marker-wrapped term can contain punctuation or mixed case, so(C++),(.NET),(R&D), and(OAuth)are supported within the configured Unicode-scalar length bounds. - Bare matching is opt-in with
enableBareAcronyms: trueand recognizes known all-uppercase terms withinminLenandmaxLen.
Registries are case-insensitive by default, so a marker such as (api) can
resolve an API entry. Pass caseInsensitive: false when case is meaningful.
Rich registries reject normalized canonical or alias collisions by default:
final registry = DashronymRegistry.fromEntries(
glossary.entries,
duplicatePolicy: DashronymDuplicatePolicy.reject,
);
Use keepFirst or keepLast only when the import workflow has an intentional,
documented precedence rule.
Punctuation-aware bare terms, escaping, and broader Unicode-aware unmarked matching policies require explicit future APIs rather than silent changes to legacy matching. See the matching priorities.
Existing rich text #
Text.rich(...).dashronyms() recursively processes text inside nested
TextSpans:
Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Use the '),
TextSpan(
text: 'SDK',
style: Theme.of(context).textTheme.labelLarge,
),
const TextSpan(text: ' from your IDE.'),
],
),
).dashronyms(
registry: registry,
config: const DashronymConfig(enableBareAcronyms: true),
)
Existing WidgetSpans are preserved. A TextSpan with an explicit
semanticsLabel is treated as an author-controlled accessibility boundary and
is left unchanged. Use DashronymText.rich when constructing the
glossary-aware tree directly.
Theming #
Pass a theme locally:
DashronymText(
'Tap (API) for its definition.',
registry: registry,
theme: DashronymTheme(
decorationStyle: TextDecorationStyle.dashed,
cardWidth: 360,
tooltipMaxWidth: 420,
hoverShowDelay: Duration(milliseconds: 120),
),
)
Or install an app-level theme extension:
MaterialApp(
theme: ThemeData(
extensions: const [
DashronymTheme(
acronymStyle: TextStyle(color: Colors.indigo),
cardElevation: 10,
),
],
),
home: const HomePage(),
)
DashronymTheme.copyWith uses clear… flags for nullable settings (for
example, clearTooltipMaxWidth: true), and lerp participates in animated
Flutter theme transitions.
Custom tooltip content #
tooltipBuilder receives the visible term, display description, effective
theme, close callback, and the rich entry when one exists:
DashronymText(
'Review the (API).',
registry: registry,
tooltipBuilder: (context, details) {
final entry = details.entry;
return Material(
borderRadius: BorderRadius.circular(12),
child: ListTile(
title: Text(entry?.expansion ?? details.acronym),
subtitle: Text(entry?.definition ?? details.description),
trailing: IconButton(
tooltip: 'Close definition',
onPressed: details.hideTooltip,
icon: const Icon(Icons.close),
),
),
);
},
)
Dashronym constrains custom surfaces to the usable viewport and makes oversized content scrollable. The builder still owns the content's labels, reading order, and control semantics.
Accessibility and interaction contract #
Inline terms expose a button role, expanded/collapsed state, and a dynamic show/hide hint.
- Tap, click, Enter, or Space opens or toggles a definition.
- Keyboard focus opens a definition; Escape dismisses it.
- Focus can move into tooltip controls and is restored to the trigger after a close action.
- Hover can move from the trigger into the tooltip without closing it.
- Outside activation, scrolling, resizing, or orientation changes dismiss a stale surface.
- At most one Dashronym tooltip is active at a time.
- Supported platforms receive the complete definition through one capability-appropriate announcement path, so opening a tooltip does not produce duplicate or content-free announcements.
- The trigger keeps the surrounding text scale, locale, direction, baseline, style, and selection behavior. Tooltip text retains the effective scale without applying it twice to inline widgets.
- Stock and custom surfaces share safe-area, keyboard-inset, width, and height constraints. Long stock definitions remain scrollable.
Automated tests cover these behaviors, but assistive-technology checks on each target platform remain part of the release process. See CONTRIBUTING.md for the manual matrix.
Localization #
Dashronym includes English strings and falls back safely when its delegate is not installed. Applications that configure Flutter localization can add:
localizationsDelegates: const [
DashronymLocalizations.delegate,
// GlobalMaterialLocalizations.delegate,
// GlobalWidgetsLocalizations.delegate,
// GlobalCupertinoLocalizations.delegate,
],
supportedLocales: DashronymLocalizations.supportedLocales,
Project resources #
Issues and feature proposals belong in the GitHub tracker.
When a visual change is intentional, inspect and regenerate the inline goldens on macOS, the canonical golden renderer, with:
flutter test --update-goldens test/src/dashronym_inline_golden_test.dart
Dashronym is available under the BSD 3-Clause License.
