desen_ui 0.3.2
desen_ui: ^0.3.2 copied to clipboard
Web-grade UI components for Flutter, built on the widgets layer only. No Material or Cupertino dependency.
0.3.2 #
A fix and a look change for bottom sheets, nothing breaking: ^0.3.0
picks this release up.
Changed #
- A bottom sheet's detents count from the window's bottom edge, the home
indicator's space included: at
.5its top is at half the window. On a phone with a home indicator a sheet at a detent used to start that much higher.
Fixed #
- A
DsPanelbottom sheet whoseDsPanelStyle.marginhas no bottom reaches the screen's bottom edge and pads the home indicator's space inside its surface, below its content and footer: with top corners only it is a sheet attached to the bottom edge. The page used to show through a band under it. A sheet with a bottom margin, as by default, still floats above the indicator, and every sheet still sits on the on-screen keyboard. - Content in a
DsPanelsees no system insets left inMediaQuery.padding, since the panel keeps clear of them: aListViewin it no longer pads them a second time.
0.3.1 #
Additions, look changes and a fix, nothing breaking: ^0.3.0 picks this
release up.
Added #
showDsPanel(detents:, initialDetent:): heights a bottom sheet rests at, as shares of the window's height.[.5, 1]opens it at half the window and pulls it up to full; at a detent the sheet keeps its height, its content scrolls inside and the footer stays at the bottom. A drag goes to the nearest detent, a flick to the next one, and below the lowest it closes; it settles without bounce, and at once under reduced motion. Screen readers move between detents with the grabber, an adjustable control named "Resize" in every supported language.DsReorderableHandleandDsReorderableList.defaultHandles: an item can place its drag handle itself, for example before a remove button that stays the row's last control. It drags, takes keyboard focus and follows the list's style like the default handle.DsToast.iconandshowDsToast(icon:): the app's own icon in place of the status icon, in the status color with astatusand muted without one; the status is still read aloud by name.DsSpinnerStyle.arc,strokeCapandtrackColor: a shorter arc, round ends and a ring under the arc. The default look is unchanged.
Changed #
DsIcons.keyboardandkeyboardOffare redrawn: two rows of keys over a space bar, without the cable. At small sizes the old drawing read as a face.- A bottom sheet's grabber is drawn in
textSubtleat 65% opacity, as the scrollbar thumb is, instead ofchannelStrong: in dark mode it stood barely 1:1 off the sheet and could not be seen. It now stands about 2.6:1 off the sheet in light mode and 3:1 in dark mode, at both contrast levels.
Fixed #
DsTextField's screen reader node covers the whole field, where a tap focuses it, instead of only the line of text: screen readers outline the field, and tap target checks such asiOSTapTargetGuidelinemeasure it. Its clear, show-password and picker buttons keep their own nodes beside it.
0.3.0 #
Breaking: the icon set changes, and the deprecated
DsToastStyle.closeIconSize is gone. ^0.2.0 stays on 0.2.4; move to
^0.3.0 with the table below.
Breaking #
DsIconsis a fork of the free Hugeicons set (Hugeicons 4.3.5, MIT) instead of Lucide: 5388 stroke icons in place of 2225, drawn with the same 2-unit stroke and round ends. Hugeicons draws them at 1.5, so the 205 icons whose details would close up at 2 (lettering such as the file types, rows of dots, dense patterns) keep a 1.75 or 1.5 stroke;DsIconData.strokeWidthsays which. Desen names them itself, after what they show and with Lucide's naming rules; where Lucide calls the same shape something else, that name is an alias. Where the free set has a gap that matters (a plain clipboard,cloud-backup,trash-off,eject), Desen puts the icon together from Hugeicons parts. Brand logos are not included.DsToastStyle.closeIconSize, deprecated in 0.2.2, is removed.
| Before | After | Migration |
|---|---|---|
DsIcons held the 2225 Lucide icons and 94 aliases |
DsIcons holds 5388 icons; 1963 of the 2319 old names still exist, as a name or an alias |
Look up the others on the icon page, which also searches Hugeicons names and tags. Every icon has a new drawing, so check screens that rely on a particular shape |
DsToastStyle(closeIconSize: 18) |
Removed | DsToastStyle(closeStyle: DsButtonStyle(iconSize: 18)) |
0.2.4 #
Additions and a fix, nothing breaking: ^0.2.0 picks this release up.
Added #
DsIconfollows the icon theme'sweight, as the weight axis of a variable icon font does: 400 draws the icon's own stroke, other weights scale it (350 draws a 2-unit stroke at 1.75). Set it once inDsApp.builderand it reaches the icons inside components and layers;DsIcon.strokeWidthstill wins. Without a weight nothing changes.DsTopNav: the main navigation in an app's header, the wide-screen counterpart ofDsBottomNavand the horizontal one ofDsSidebar. A navigation landmark of buttons, the current one selected; one Tab stop with the arrow keys; destinations that do not fit scroll. Choosing the current destination reports it too, so an app can go back to the page's first screen or scroll it to the top.DsTopNavStyleandDsTopNavThemestyle it.
Changed #
- Icon-only buttons (
DsButton.icon) stay square under a button theme: thepaddingofDsButtonThemeData.styleand of its variants is room for text and no longer widens them. Apaddingin the button's ownstylestill applies.
0.2.3 #
The same code as 0.2.2. The archives of 0.2.0, 0.2.1 and 0.2.2 included
local files that were not meant to be published; those versions are
retracted. Update to this release: ^0.2.0 picks it up.
0.2.2 #
Additions and fixes, nothing breaking: ^0.2.0 picks this release up.
Added #
DsToastStyle.actionStyleandcloseStyle, andDsPanelStyle.closeStyle: the toast's action and close buttons and the panel's close button can be styled, like the inner buttons of other components. The app's button theme still does not reach them.
Deprecated #
DsToastStyle.closeIconSize: usecloseStyle: DsButtonStyle(iconSize: …). It still works until it is removed in 0.3.0; acloseStylethat sets its owniconSizewins over it.
Fixed #
DsTabsandDsChoiceChipson a page that is covered from its first frame (a deep link that opens a stack of pages at once) no longer fail an assertion; they bring the selected item into view once the page is laid out.
0.2.1 #
New components and fixes, nothing breaking: ^0.2.0 picks this release up.
From this release on, additions and changes of the default look come in
patch releases before 1.0, breaking changes in minor ones (see
VERSIONING.md).
Added #
DsSectionIndex: a column of section letters beside a long sorted list (contacts, stations, countries). Tap a letter or drag along the column to jump; a bubble shows the letter under the finger, letters that do not fit give way to dots, and it ticks once per section. Keyboard (arrows, Home/End, typing a letter) and screen readers (one adjustable control) step through the sections.DsLocalizations.sectionIndexnames it.DsEdgeFadeScrollViewandDsEdgeFade: a horizontal row of your own items (tags, filter chips) that scrolls when it does not fit and fades the edge that hides items, as the toolbar and text fields already do.DsEdgeFadefades a horizontal scrollable you already have.DsImage.fallback: your own stand-in (initials, a placeholder cover) shown in the image's box and corners when the picture fails, announced by the image'ssemanticLabel.DsImage.imagemay be null when there is no picture: the fallback, or the unavailable state, shows at once.DsReorderableList: a list people put in their own order (favorites, playlists). Drag an item by its grip handle, or long-press it on iOS and Android; the lifted item floats over the others, which make room. The handle moves its item with the arrow keys and Home/End, Alt+arrows move it from a focused row, and screen readers get "Move up", "Move down", "Move to the start" and "Move to the end".onReorder(from, to)countstoafter the move.DsWidgetsLocalizationsgives Flutter's reorderable lists their screen reader actions in the app's language (13 languages), not only English.DsToastInset: marks bottom chrome of your own (a mini player, a docked media bar) that toasts keep clear of while it shows. It measures the chrome after every frame, so toasts follow it as it slides in or out.
Changed #
- A
DsChoiceChipsrow too narrow for its chips fades the edge that hides chips instead of cutting one in half, and brings the selected chip into view clear of the fade.
Fixed #
- Toasts no longer cover a
DsBottomNav: they sit above it while it shows.
0.2.0 #
The first release without the alpha tag. Desen is still before 1.0:
breaking changes come in minor releases (0.3.0), never in patches, and
each is listed here with how to update (see VERSIONING.md).
Breaking #
| Before (0.1.0-alpha.2) | Now | What to do |
|---|---|---|
DsIcons held 56 icons |
DsIcons is the full Lucide set, 2225 icons, in camel case (triangle-alert is DsIcons.triangleAlert) |
Most names are the same. Look up others on the icon page, which also searches the 94 aliases (home, close, volumeUp) |
DsIcons.shift |
DsIcons.arrowBigUp |
Rename |
DsIcons.all |
Removed: an app's own icon picker should list the icons it offers | Keep your own list of DsIconData |
DsIcons.chevronLeft and chevronRight mirrored in right-to-left text |
67 icons that point along the reading direction mirror (below) | If you flipped any of them yourself, stop; to keep one fixed, use DsIcons.logOut.copyWith(matchTextDirection: false) |
DsApp.supportedLocales defaulted to all 13 Desen locales |
It defaults to locale, else English, as Flutter's app widgets do |
To follow the device's language, pass the locales your app is translated into, or supportedLocales: DsLocalizations.supportedLocales |
DsToolbar items that did not fit scrolled sideways |
They move into a "More actions" (⋯) menu (overflow: DsToolbarOverflow.menu) |
Pass overflow: DsToolbarOverflow.scroll to keep scrolling; wrap custom children in DsToolbarItem to let them collapse |
| Text fields did not check spelling | DsTextField.spellCheck is on by default on iOS and Android for plain and multi-line text (off for email, URL, password, number, name and address fields, search fields and autocompletes) |
Pass spellCheck: false to turn it off |
DsColors had no skeleton roles |
New required roles skeleton and skeletonStrong |
Only hand-built DsColors (for DsThemeData.raw) need them; generated themes have them |
showDsPanel drew a surface around whatever its builder returned |
DsPanel draws its own surface (so DsPanel.style applies) |
Return a DsPanel from the builder, or draw your own surface |
showDsToast took an actionLabel without onAction |
It asserts that they come together | Pass both, or neither |
DsPagination asserted page <= pageCount |
It shows the last page, without calling onChanged |
Nothing; remove workarounds |
The 67 mirrored icons: arrowBigLeft, arrowBigLeftDash, arrowBigRight,
arrowBigRightDash, arrowLeft, arrowLeftFromLine, arrowLeftToLine,
arrowRight, arrowRightFromLine, arrowRightToLine, chevronFirst,
chevronLast, chevronLeft, chevronRight, chevronsLeft,
chevronsRight, circleArrowLeft, circleArrowRight, circleChevronLeft,
circleChevronRight, cornerDownLeft, cornerDownRight, cornerLeftDown,
cornerLeftUp, cornerRightDown, cornerRightUp, cornerUpLeft,
cornerUpRight, forward, list, listChecks, listClock, listFilter,
listFilterPlus, listIndentDecrease, listIndentIncrease, listMinus,
listMusic, listOrdered, listPlus, listRestart, listTodo,
listTree, listVideo, listX, logIn, logOut, messageCircleReply,
messageSquareReply, redo, redo2, redoDot, reply, replyAll,
send, sendHorizontal, squareArrowLeft, squareArrowRight,
squareArrowRightEnter, squareArrowRightExit, squareChevronLeft,
squareChevronRight, tableOfContents, textQuote, undo, undo2,
undoDot.
Added #
- Icons: the full Lucide set (Lucide 1.52.0 and Lucide Lab), kept in
the package and updated only after review; unused icons add nothing to
an app's size. Icons can be solid:
DsIconData.fillandDsIcon.fillfill each closed shape, cut a mark inside out (the "!" in an alert) and leave open strokes as strokes.DsIconalso follows the icon theme'sfill, as anIconfrom a font with a fill axis does.DsIconData.copyWith. DsBottomNavdraws the selected destination's icon solid, as iOS tab bars and Android navigation bars do;DsBottomNavItemStyle(fillIcon: false)keeps it outlined.DsRangeSliderpicks a range with two thumbs (DsRangeValues), sharingDsSlider's look and theme. The thumbs never cross and can keep aminDistance. Each thumb is its own focus stop and its own slider for screen readers ("Minimum", "Maximum"), also inside aDsField.DsParagraphandDsLinkSpan: links inside running text that wrap with the sentence, with everything aDsLinkhas (hover, Tab and Enter, a focus ring around each line, link semantics). The paragraph takesmaxLines,overflowandWidgetSpans;DsLinkSpan.externalends a link with the up-right arrow.- Time picker: the columns wrap like iOS and Android wheels.
firstTimeandlastTimebound the times that can be chosen or typed, also across midnight (a night shift from 22:00 to 06:00).showSecondsandsecondStepadd a seconds column;DsTimetakes seconds. - Spell check in text fields on iOS and Android, with
DsSpellCheckSuggestionsToolbar(DsTextField.spellCheck,DsTextFieldStyle.misspelledStyle). DsToolbaroverflow menu ("More actions") andDsToolbarItem.- Layers:
routeSettingsonshowDsDialog,showDsPanelandshowDsModal;DsToastController.closedwith aDsToastClosedReason, for undo flows;DsPanel(scrollable: false)for a child that scrolls itself (aListView);showDsPanelwithDsPanelPresentation.autofollows the window while open; long content in a dialog, panel or toast scrolls from the keyboard (Page Up/Down, arrows, Home, End). - Lists and fields:
DsListRow.description;DsTextField.textDirection(an IBAN in a right-to-left app) andenableIMEPersonalizedLearning(an incognito keyboard);DsAutocomplete.labelOfandDsMultiSelect.labelOffor values not among the options;DsRadioGroup.semanticLabel;DsTextSelectionToolbar.semanticLabel. - Colors:
skeletonandskeletonStrongroles. - Strings in every language:
seconds,unavailable,timeTooEarly,timeTooLate,timeOutsideRange,rangeMinimum,rangeMaximum,moreActions,spellingSuggestions,noSpellingSuggestions,showMenu,opensExternally.
Changed #
The default look moved in these places (apps with golden tests will see them):
- Numbers in tables, counts, the stepper and the pagination set their separators at the font's own width; only the digits are tabular ("12.480,00" no longer reads like a typewriter).
- Avatar tones are a fixed set of clean hues, the same for every brand and in both modes, so a person keeps their color when the mode changes.
- Red and green brands (and the
oxbloodandforestpresets) draw the strong selection, focus outline and links in their own red or green in light mode instead of maroon or bottle green. Yellow, amber and orange brands write accent text and links in a clean tone instead of olive, mustard or khaki. - Yellow, amber and light orange brands select in gray in light mode too, as warm brands do in dark mode, with the brand in the text: a cream selection swallowed a warning badge or alert on a selected row. The warning color is unchanged.
- A light
DsAlertwhose tint barely stands off the page gets a quiet 1px edge in its status color; its icon stays centered on the title's first line with large text. - Buttons, chips, segmented controls, the select's trigger and tabs grow with large text, so their labels keep clear of the edges; at the regular size nothing moves.
- A
DsSwitchlabel is in the body weight, as a checkbox's and a radio's. - A
DsStepperbutton that cannot step stays visible, flat, with a muted glyph. - The dark-mode warning tint is a deep orange instead of a brown.
- Skeleton blocks are easier to see; dashed outlines space their dashes evenly on continuous corners.
DsAppkeeps the device's region once its language is supported, so a British device reads and writes dd/MM/y dates.
To empty a number, date or time field that holds invalid text from
outside (a "Clear" button), give it a new key or reset its form: it has
already reported null, so value: null again cannot change it. Their docs
now say so.
Fixed #
- Screen readers:
- Inside a
DsField, a segmented control, choice chips, tabs and a radio group were heard as one item, so their options could not be chosen one by one; the field's label now names the group, and its description or error is the group's hint. - A
DsContextMenuRegionadded a nameless node whose tap did nothing; the row's own node now opens the menu (long press, "Show menu"). - A bottom sheet and a toast offered a scroll action that dismissed them.
- An anchored badge was read apart from its anchor ("Notifications",
then "5"); progress was read twice on a valued
DsProgressRingand an uploadingDsFileItem. - The autocomplete's active option is said from a live region where the platform has no announcements (Android).
- An external link says it leaves the app.
- Inside a
- Keyboard and focus:
- A select, menu or popover reopened while fading out kept stale focus, so Enter could undo a typed choice.
- A context or row menu opened from the keyboard closed at once at the
window's edge (a full-width
DsTable's row menu never showed). - An open menu took Ctrl, Command and Alt shortcuts as type-ahead.
- Slider key and screen-reader steps now call
onChangeStartandonChangeEnd, Page Up and Page Down always move at least one step, and a slider disabled mid-drag ends its change. - A disabled
DsCalendarorDsPaginationstayed a Tab stop; a table focused while loading kept focus once rows arrived; a pagination focused after it narrowed focused nothing.
- Behavior:
- On touch, long-pressing a control to read its tooltip also pressed it.
DsAutocompletethrew when focus left with an option's label typed; chose a stale option on Enter while async results loaded; moved the highlight to another option when its options changed; and showedtoString()for a value not among its options.DsNumberFieldsteps stalled or skipped (step 0.5 in whole numbers, cents past a billion, Page Up and Down); aDsNumberFormatcould use the same character for both separators; a new value with a new format was ignored while the text had an issue.- Typed fields did not commit when focus left through a swapped
focusNode;FormState.reset()kept aDsTextFormField's edited text. DsPanel.stylewas mostly ignored;DsPanelthrew with aListViewchild; a bottom sheet whose content refused to close stayed dragged off screen.- An app-wide
DsButtonThemereached the buttons that are parts of other components (clear buttons, month arrows, pagination arrows, picker buttons, close buttons). - Calendars with
monthsabove 1 turned two pages for an outside day; a new value from the app did not callonMonthChanged;DsDateRangefailed for a one-day range with a UTC end west of UTC. DsBreadcrumb(items: [])threw, and a collapsed breadcrumb overflowed on touch;DsTableColumnWidth.flex(0)laid out NaN widths; a right-to-leftDsListSectiondivider took the wrong inset; menus with more than 100 entries squashed tall leadings and lost focus when they grew; aDsSelectin aRowkept its old width when its options changed in place; the edit toolbar's page chevrons pointed backwards in right-to-left text.- On the web, the browser's context menu stayed off for the whole app after a table row or context menu region was removed under the pointer.
- A time column opened scrolled had no top fade.
0.1.0-alpha.2 #
- LICENSE holds the MIT License alone, so pub.dev recognizes it. The licenses of the bundled fonts (SIL OFL 1.1) and the Lucide icon shapes (ISC) move to NOTICES, which Flutter adds to an app's license page.
- The package links to its docs site: https://derlioapp.github.io/desen_ui/
0.1.0-alpha.1 #
First public pre-release. The API may still change before 1.0; see VERSIONING.md for what an alpha promises.
Requires Dart 3.13 and Flutter 3.47 or later. Built on the widgets layer only: no Material or Cupertino dependency.
Breaking #
-
The fonts are part of
desen_ui: the separatedesen_ui_fontspackage is gone, and nothing needs to be added for Desen's typefaces.DsTypography'spackageandmonoPackagedefault to'desen_ui'(still applied only to Schibsted Grotesk and Geist Mono). The unused Schibsted Grotesk ExtraBold (800) is no longer shipped.Before After desen_ui_fontsin the app'spubspec.yamlRemove it DsTypography.fontsPackage,DsFonts.package'desen_ui'DsFonts.text,DsFonts.mono'SchibstedGrotesk','GeistMono'fontFamily: 'packages/desen_ui_fonts/…''packages/desen_ui/…'FontWeight.w800in Schibsted GroteskFontWeight.w700
Theme #
- One brand color (
DsSeed) generates every color role in OKLCH, in light and dark mode, with a contrast budget checked over every preset and random seeds: body text at least 4.5:1, control boundaries and focus rings at least 3:1 (WCAG 2.2 AA) at thestandardcontrast level. - End-user settings: light, dark or system; contrast
soft(iOS-like) orstandard; corner style (sharp, standard, soft, pill) with continuous corners; densitycompactortouch, following the platform unless set. - Typography: Schibsted Grotesk and Geist Mono bundled in the package
(SIL Open Font License 1.1, see
fonts/), the system font (San Francisco) in iOS and macOS apps, tabular figures on numbers. - Customization at every level: adjust generated tokens (
adjustColors,adjustRadii, …), component defaults for an app or a section (DsComponentThemes), per-instance styles, theme extensions, and headless behavior (DsPressable) for building your own controls.
Components #
Button, badge, count, status dot, avatar, image, card, divider, alert,
progress, spinner, skeleton, link, breadcrumb, checkbox, radio, radio card,
switch, segmented control, slider, chip, choice chips, tabs, accordion,
stepper, list, sidebar, bottom navigation, pane header, empty state,
pagination, toolbar, popover, tooltip, menu and context menu, submenu,
dialog, panel and sheet, toast, select, text field (single and
multi-line), search field, autocomplete, multi-select, number field, table,
scrollbar, calendar, date and date range picker, time picker, file upload,
text magnifier; Form integration (DsFormField, typed form fields,
DsValidators).
Behavior and accessibility #
- Keyboard support following the WAI-ARIA patterns; focus rings for keyboard users only; focus kept inside layers and returned to the control that opened them.
- Screen reader names, roles, states and announcements, tested per component.
- Large text up to 200% without clipped labels, 44px touch targets on phones, reduced motion, haptics on touch platforms.
- Strings for 13 languages (Arabic, Chinese, English, French, German, Hindi, Italian, Japanese, Korean, Portuguese, Russian, Spanish, Turkish) and 2 regional variants (Portuguese (Portugal), Traditional Chinese), with right-to-left layout.
Known limitations #
See Known limitations in the README.