flutter_dropdown_button 4.1.0
flutter_dropdown_button: ^4.1.0 copied to clipboard
A highly customizable dropdown widget with overlay-based rendering, smart positioning, search, tooltips, and full control over appearance.
Changelog #
4.1.0 #
The second half of the embedded-field pattern 4.0.0's bare anchor opened. anchorBuilder decoupled what the anchor renders; a menu embedded inside a wider field still positioned and sized against the compact anchor's own box, so it dropped from mid-field and left-aligned to the little [All ▾] segment. positioningKey decouples what the menu positions against.
- FEAT:
positioningKey— an optionalGlobalKeyonFlutterDropdownButton(both constructors) andFlutterMultiSelectDropdown. Wrap the whole field in a widget carrying the key and pass it here; the menu then measures that box instead of the anchor — dropping below it, left-aligning to it, and defaulting to its width — while the anchor keeps drawing and toggling where it sits.menuAlignment,minMenuWidthandmaxMenuWidthmeasure against the box too - FEAT: additive and orthogonal. It is a parameter on the shared
DropdownMenuShell, not a new constructor, so it composes with text mode, custom mode, the checklist and the bare anchor alike — and with a normal chromed button, unrestricted (no assert). The placement engine is untouched: only whichRenderBoxis measured changes, and the existing width/alignment/flip logic yields the field-relative result for free - CHANGE: the box is measured on every menu build, not tracked per frame — a box that moves while the menu is open is not followed, the same contract the anchor already held. It must live in the same
Overlaycoordinate space as the anchor - TEST: placement is asserted at the overlay's
Positioned— the menu's width, left and top measure the outer field, not the compact anchor — with control tests pinning the anchor-relative default, a runtime-toggle test proving the key is honoured mutably, and the checklist composition. Discriminating power confirmed by reverting the measure-key swap (the field-relative tests go red, the controls stay green)
4.0.0 #
Breaking on two axes, plus one long-promised removal. The multi-select checklist now draws its boxes with the flutter_checkbox package instead of Flutter's built-in Checkbox (DropdownCheckboxTheme redesigned around it), and the monolithic DropdownTheme is split into three surface sub-themes. The SDK floor is unchanged (>=3.32.0): flutter_checkbox 0.3.1 needs only Flutter 3.27. This release also folds in the bare-anchor feature, which was never published on its own.
DropdownTheme → button / overlay / item sub-themes #
- BREAKING: the single
DropdownTheme(~28 fields covering the button face, the menu container and the rows at once) is removed.DropdownStyleTheme'sdropdownslot is replaced by three sibling slots —button(DropdownButtonTheme),overlay(DropdownOverlayTheme),item(DropdownItemTheme) — beside thescroll/tooltip/search/checkboxslots that were always separate. Each sub-theme resolves itself. Fields keep their meaning but drop the surface prefix (buttonHoverColor→button'shoverColor,itemPadding→item'spadding,overlayPadding→overlay'spadding,selectedItemColor→item'sselectedColor,itemBorderRadius→item'sborderRadius, and so on). Full map indocumentation/migration.md - CHANGE: the shared
borderRadius,backgroundColorandborder— one field each, formerly driving two surfaces at once — are now owned per surface.overlay.backgroundColorcolours the menu;button.backgroundColor(new) colours the button; neither reaches across. This is the coupling #75's bare mode exposed (abackgroundColorthat painted the menu, not the button), resolved by construction - CHANGE:
DropdownButtonThemeis inert in bare mode (anchorBuilder) — the caller draws the anchor, so the button box it styles does not exist. Overlay, item, scroll, search and tooltip theming still apply. The type now makes that boundary explicit - TEST: every field moves 1:1, the resolve logic is preserved verbatim, and the existing widget tests (which assert the rendered button/item
BoxDecorations) pass with the same assertions — the split is behaviour-preserving
Multi-select checkbox → flutter_checkbox #
- BREAKING:
DropdownCheckboxThemeis rebuilt aroundflutter_checkbox'sCheckboxStyle. Removed — the Material-Checkboxconcepts the new box has no equivalent for:fillColor(WidgetStateProperty),side(BorderSide),shape(OutlinedBorder),materialTapTargetSize,visualDensity. Kept:activeColor,checkColor,mouseCursor. Added:inactiveColor,borderColor,borderWidth,borderRadius,size,checkStrokeWidth,checkScale, andshapenow typedCheckboxShape(an enum:rectangle/circle).resolve()now returns aCheckboxStyle, and theResolvedCheckboxStyleclass is removed. Migration map indocumentation/migration.md - FEAT: the box is a
FlutterCheckbox, drawnonChanged: nullbut leftenabled: true— presentational (a tap falls through to the row) without being greyed. This deletes theactiveColor→fillColorworkaround 3.2.0 needed: Material forced anonChanged: nullbox into its disabled state and dropped a plainactiveColor;FlutterCheckboxdoes not, so the accent is read straight (CheckboxStyle.activeColor) - FEAT:
CheckboxShapeandCheckboxStyleare re-exported from the package barrel, soDropdownCheckboxTheme(shape: CheckboxShape.circle)needs no extra import - CHANGE: the box's semantics are still excluded and the row still carries
checked. Unlike Material,FlutterCheckbox(onChanged: null)announcesenabled: true— so the row no longer risks a dimmed announcement leaking from the box — but the box still emits its owncheckednode, which the exclusion drops to keep the row the single source of the checked state - MIGRATION:
side→borderColor+borderWidth;shape: RoundedRectangleBorder(borderRadius: …)→shape: CheckboxShape.rectangle+borderRadius;fillColor→activeColor(checked) /inactiveColor(unchecked).materialTapTargetSizeandvisualDensityhave no equivalent — the box sizes viasize - TEST: 285 tests at 100% line coverage (1102 lines) across the whole release. The box is asserted presentational-but-not-greyed (
onChangednull,enabledtrue), the accent reachesstyle.activeColordirectly, and the semantics contract (rowchecked, never disabled) is held at the semantics tree
Bare anchor (folds in the unreleased 3.3.0) #
- FEAT:
anchorBuilder— a bare anchor mode onFlutterDropdownButton(both constructors) andFlutterMultiSelectDropdown. SupplyingWidget Function(BuildContext, bool isOpen)drops the entire button box — background, border, fixed width, padding, ink and trailing icon — and hangs the same anchored overlay off the widget you return. The point is to embed the dropdown inside another field,[All ▾] │ search…, where a button's own chrome nests a box inside a box; the only clean alternative was a hand-rolledPopupMenuButtonthat threw away this package's theming, keyboard navigation and searchable menu. Only the button face becomes the caller's — the menu is untouched - FEAT: the builder is handed
isOpen, not a label.isOpenis the one thing a caller cannot read for itself, and drives an inline chevron (AnimatedRotation(turns: isOpen ? 0.5 : 0.0, …)); a label the caller already holds, in thevalueorselectedit drew the anchor from. Passing one would leak a text-mode notion intoDropdownMenuShell, which does not know what an item says — the invariant that lets one shell back both widgets. The dropped ink well's button role is restored withSemantics(button: true) - FEAT: the field lives on the shell and both public widgets read it, so bare mode composes with text mode, custom mode and the checklist alike — it is orthogonal to what an item renders as, and is a parameter rather than a
.bare()constructor for exactly that reason. A constructor would have had to pickitemBuilderorlabel, leaving the other settable-but-dead — the shape 3.0.0 spent a major version deleting - FEAT: the button-box params
anchorBuilderreplaces —width,minWidth,maxWidth,expand,trailing— assert when combined with it, rather than being silently ignored. A bare anchor is compact by design and the menu takes its width from it, so setminMenuWidth(a menu width, still allowed) to give the menu a usable width
Also removed #
- BREAKING:
CustomItemPresentation.items— deprecated in 3.1.0 (read by nothing since), removed now as its dartdoc promised. Only code building a presentation by hand is affected: drop the argument. Seedocumentation/migration.md
3.2.0 #
Additive. One new optional field; nothing removed, nothing else changed.
- FEAT:
DropdownCheckboxTheme— styles the checkbox on each row of aFlutterMultiSelectDropdown, reached through the newcheckboxslot onDropdownStyleTheme. It carries the eight fields that actually render on the row's box:activeColor,fillColor,checkColor,side,shape,materialTapTargetSize,visualDensity,mouseCursor. The box is drawnonChanged: nullwith its semantics excluded, so an interactive checkbox's focus/hover/splash fields are absent by construction rather than settable-but-dead.mouseCursoris kept because itsMouseRegionis installed regardless of interactivity — so it lets you match the box's cursor to the row's - FEAT:
activeColor— the fill of a checked box — is the common case. The box is non-interactive, which puts it in Flutter'sdisabledstate, where a rawCheckbox.activeColoris dropped before it is read. The theme resolvesactiveColorintofillColor, whichCheckboxconsults first, so it survives. SetfillColoryourself for per-state control; it wins overactiveColor - FIX:
documentation/theming.mdtaught a checkboxTheme(...)wrapped around the dropdown. It silently did nothing: the menu is drawn in the rootOverlay, out of a localTheme's subtree, soCheckboxTheme.ofat the box resolved to the app theme, not the local one (pinned with a probe — the box's resolved fill came back null). Only an app-wideCheckboxThemeDatareached it, and nowDropdownCheckboxThemereaches just this dropdown - TEST: line coverage stays at 100%, with the checked box's fill asserted against the
{disabled, selected}state it is actually in — the state a naiveactiveColortest would miss
3.1.0 #
Adds a second widget. Nothing was removed; one field is deprecated and one behaviour changed, both described below.
- FEAT:
FlutterMultiSelectDropdown<T>— a checklist. Several items may be chosen, the menu stays open while they are, andonChangedfires with a newSet<T>the moment a box is ticked. Anchored rather than modal: no scrim, no confirm button, dismissed by an outside tap. It shares every layout, theming and search parameter withFlutterDropdownButton. Rows are[checkbox] [itemLeadingBuilder] [label] [itemTrailingBuilder], and only the label gives way when space runs out. Both slots are builders, because the reason to want one is that each value looks different. TheSetyou pass in is never mutated, andTmust implement==andhashCode— aSetneeds both, wherevalue == itemneeded only the first - FEAT:
MultiSelectPresentation<T>, the thirdDropdownItemPresentation. Its rows carry a checkbox that is excluded from the semantics tree, with the checked state re-attached to the row. ACheckboxwithonChanged: nullinside the row's ink well announcesisEnabled: falseonce the tree merges, so a screen reader would have called every row of a working checklist dimmed.IgnorePointerdoes not help; it suppresses hit-testing, not semantics. Nothing about this is visible on screen - CHANGE: A
valuethat is not initemsis now drawn in custom mode, rather than replaced byhintWidget. A list refresh that drops the chosen row's data leftvaluenaming a row that no longer existed, and the button quietly reverted to its hint — no callback, no error. Text mode never did this, having noitemsto consult, so the two modes disagreed and neither behaviour was documented. The widget draws what it was handed. The menu is unchanged: it iteratesitems, and never invented a row - DEPRECATED:
CustomItemPresentation.items. It fed the audit removed above and is now read by nothing. It is no longerrequired; drop the argument. Removed in 4.0.0. Only code that constructs a presentation by hand is affected - REFACTOR:
DropdownMenuShell(internal) is the button, the overlay, and everything between — 994 of the widget's 1004 lines, none of which knew what selection is. It takesisChosen(T),onItemTap(T)andcloseOnTap; the two public widgets differ only in what they pass.value,scrollToSelectedItemanddisableWhenSingleItemare absent from the multi-select type rather than asserted against at runtime - TEST: 256 tests, up from 224, still at 100% line coverage (1037 lines). The shell extraction passed the existing suite with no test file edited, the sixth refactor in this package to meet that standard. The coverage floor earned its keep twice: it rejected a
closeOnTapflag that no caller set, and it caught a test named merges the disabled style that never reached themerge()
3.0.2 #
Documentation only. No statement in lib/ changed — every corrected line is a comment. It is released because pub.dev renders the dartdoc of the version it was published from, so a reader of 3.0.1's API page is still told the opposite of what the code does. A downstream app set a trackColor, saw no track, and was right to be confused.
- CHANGE:
DropdownScrollTheme.interactive's dartdoc saidnullmeant "display only". It means the opposite:Scrollbarresolves a nullinteractiveto!_useAndroidScrollbar, so the thumb is draggable on desktop. Passfalsewhen you mean off - CHANGE:
DropdownScrollTheme.trackColorand.trackBorderColorsaid "if null, no track is displayed", implying a colour draws one. It does not. The track is drawn bytrackVisibility: trueand by nothing else; Flutter resolves a hidden track's colour to transparent. A colour is not a request - CHANGE:
DropdownScrollTheme.trackVisibilitysaid "if true or null, shows the track". Null resolves tofalse. There is no track by default - CHANGE:
DropdownScrollTheme.thumbVisibilitysaidfalsehides the thumb.falseandnullbehave identically — the thumb fades in while the list scrolls and fades out after. Onlytruepins it. SettingtrackVisibility: trueraises this totrue, because Flutter cannot draw a track without a thumb - CHANGE:
DropdownScrollTheme.radius's "the default scrollbar radius" isRadius.circular(8)on desktop and square on Android - CHANGE: The class-level example for
DropdownScrollThemetaught atrackColorwith notrackVisibility— the combination that draws nothing — and usedwithOpacity, which is deprecated. Both fixed, here and in thegradientColorsexample. The playground'sinteractivetoggle started atfalse, shipping a demo scrollbar nobody could drag - TEST: 224 tests, up from 222, still at 100% line coverage. Both new ones are guards on the corrected docs: a null
interactiveis draggable, and a track colour alone does not ask for a track. The second fails if anyone makestrackColorimplytrackVisibility
3.0.1 #
- FIX: The menu drew two scrollbars on desktop.
MaterialScrollBehaviorwraps every scroll view in aScrollbarof its own, and this package added a second on top without suppressing it. The one underneath answered to nothingDropdownScrollThemesays. The list is now wrapped inScrollConfiguration(scrollbars: false) - FIX: The scrollbar swelled from 8 to 12 logical pixels while a pointer hovered a visible track. Flutter does that to any
Scrollbarhanded nothickness(material/scrollbar.dart:303), and this package handed itnullwhenever the caller named neitherthicknessnorthumbWidth—trackVisibility: truealone was enough. The menu now passes the thickness the bar would have rested at anyway, so it looks the same at rest and no longer swells - FIX: A dropdown given no
DropdownScrollThemeat all never applied a scrollbar of its own; the menu fell through entirely to Flutter's automatic one, unstyleable and swelling.scrollnow falls back toDropdownScrollTheme.defaultTheme, the waydropdown,tooltipandsearchalways have - CHANGE:
DropdownScrollTheme.thickness's dartdoc promised a flat8.0fallback. Flutter's default is 8 on desktop and 4 on Android; honouring that doc would have doubled the bar on every Android build. The resting thickness now comes from the ambientScrollbarThemefirst, then from Flutter's own platform default. The doc now says what the code does - CHANGE:
ResolvedScrollStyle.hasCustomWidthsis informational.thicknesscarries the answer it used to gate, and nothing in the package branches on it - TEST: 222 tests, up from 207, still at 100% line coverage. Fifteen new ones drive the scrollbar across
windows,macOS,linuxandandroid
3.0.0 #
Removes everything deprecated during the 2.x line, plus one field that was never deprecated because nobody noticed it did nothing. Nothing was renamed — every removed member already had a replacement that was doing the work. If you only ever used FlutterDropdownButton, what changes for you is that semanticsLabel starts working, a visible scrollbar track stops crashing, and the scroll fades point the right way.
The deprecations landed across 2.3.2, 2.4.0, 2.4.1 and 2.5.0. Upgrading straight from an earlier 2.x skips the versions that warned, so the list below is the warning.
- BREAKING: Removed
DropdownMixin<T>. Hold aDropdownOverlayControllerinstead of mixing one in — the twenty-three members the mixin asked you to override collapse into oneDropdownOverlaySpec. Deprecated in 2.4.0 - BREAKING: Removed
DropdownMixin.calculateMenuWidth()andDropdownMixin.calculateMenuLeftPosition(). UseDropdownPlacement.resolve(), which returns the menu's full geometry in one call. Deprecated in 2.3.2 - BREAKING: Removed
DropdownPositionResultandDropdownMixin.calculateDropdownPosition(). UseDropdownOverlayController.measurePlacement(), which returns aDropdownPlacementResult - BREAKING: Removed
DropdownTheme.animationDuration. Nothing ever read it; setting it did nothing. PassanimationDurationtoFlutterDropdownButton. If you were setting it on the theme your menus animated at 200ms and still do — move the value across only if you actually wanted the slower animation. Deprecated in 2.4.1 - BREAKING: Removed
DropdownTooltipTheme.borderColorandDropdownTooltipTheme.borderWidth. Useborder, which takes anyBoxBorder:DropdownTooltipTheme(border: Border.all(color: Colors.red, width: 2)). Deprecated in 2.5.0 - BREAKING: Raised the SDK floor to Flutter
>=3.32.0/ Dart>=3.8.0. It claimed>=3.10.0, butlib/usesWidgetStateProperty(3.22),Color.withValues(3.27) andTooltip.constraints(3.32).pubresolved against the promise, so a project on 3.10 installed the package and then failed to compile inside our source. Not a new restriction; an honest statement of an old one — and the floor was found by building against it in CI, not by reading the code - BREAKING: Removed
DropdownScrollTheme.alwaysVisible. Nothing ever read it — setting it was indistinguishable from leaving it null, while its own class dartdoc recommended it. UsethumbVisibility: true, which is what it claimed to mean and what actually works. Removed rather than deprecated: it names exactly whatthumbVisibilityalready does, so there is nothing to keep, and anyone who set it has been living with an auto-hiding scrollbar without knowing they asked otherwise - FIX:
TextDropdownConfig.semanticsLabelnow labels the dropdown, as it always claimed to. It was applied to every menu item and to nothing else — andText's semantics label replaces the announced string, so a screen reader read"Fruit picker"for the Apple row, the Banana row and the Cherry row alike, suppressing every item's name and leaving the menu unnavigable by voice. It now sits on the button, announced alongside the selected value ("Fruit picker, Banana"), and items announce their own text - FIX:
DropdownScrollTheme(trackVisibility: true)no longer crashes the menu on open. Flutter asserts that a scrollbar track is never drawn without a thumb, and the overlay passedthumbVisibility: falsealongside it — so the documented way to show a track threw'A scrollbar track cannot be drawn without a scrollbar thumb'the moment the dropdown was tapped. Present since 1.2.0. A visible track now implies a visible thumb, and the illegal pair is rejected byDropdownScrollTheme's own constructor rather than by Flutter, three frames later - FIX: The scroll-fade indicators are no longer inverted. With
showScrollGradient: trueand nogradientColors, both fades were built with the transparent colour first — so instead of the menu's background bleeding over the content at the edge it is hiding, the fade was clear at the edge and opaque next to the content, washing out the middle of the list. Callers who supplied their owngradientColorswere always drawn correctly; only the default list was in the wrong order - FIX: An unstyled
DropdownScrollThemeno longer wraps the menu in aScrollbarTheme. It replaces the ambient one rather than merging with it, so wrapping unconditionally would have silenced an app-wideScrollbarTheme - BREAKING: Removed
DropdownScrollTheme.trackWidth. It was never applied to anything: Flutter'sScrollbarandRawScrollbardraw the track at the thumb's thickness and expose no separate track width, so the value was read once — to compare itself againstthumbWidth— and then discarded. UsethumbWidth, orthickness. Removed rather than deprecated, for the same reason asalwaysVisible: the field cannot be made to work, so there is nothing for a deprecation window to buy - CHANGE:
DropdownScrollTheme.resolve()now returns the scrollbar's colours and radius as well as its measurements, inResolvedScrollStyle.scrollbarThemeand.overridesScrollbarTheme. ItscrossAxisMargin,mainAxisMargin,minThumbLengthandtrackWidthfields are gone — they live insidescrollbarThemenow — andthumbVisibility/trackVisibility/interactivebecame nullable, because writing Flutter's default into them would override an ambientScrollbarTheme - CHANGE:
lib/src/buttons/dropdown_mixin.dartis gone - FEAT: Added
DropdownSearchController<T>, which owns a dropdown's query, its text field, its focus and their lifetime. It knows nothing about the menu or the scroll position; the owner drives those.visibleItems(items, filter)derives the filtered list on every call rather than caching it — every past defect in this area was a missed cache invalidation - FEAT: Added
DropdownItemPresentation<T>, withTextItemPresentationandCustomItemPresentationbehind it. Anyone building a dropdown onDropdownOverlayControllercan reuseTextItemPresentationand get overflow handling, the tooltip and the default search filter for free, rather than reimplementing them - REFACTOR: The widget no longer branches on
isTextMode. Rendering is chosen once, in one factory; eight consult sites became zero, and four private render methods leftflutter_dropdown_button.dart(1291 → 1166 lines). A third rendering mode is now a third implementation and a third branch in that factory, not another conditional at every render site - REFACTOR: The scroll-fade indicators left the widget's
Statefor aScrollGradientOverlaywidget that owns its own notifiers and scroll listener. Their 150ms fade duration was written twice; the two must match for the top and bottom to read as one effect, and now they cannot disagree - REFACTOR: The search subsystem left the widget's
State. TheTextEditingController, theFocusNode, the query and their lifecycle were spread acrossinitState,didUpdateWidget,disposeand five render sites; they now live behindDropdownSearchController, and the widget holds one - REFACTOR: The overlay's content area is now sized by
DropdownOverlaySpec.totalChromeHeightrather than by re-adding the border, padding and search-field heights by hand.DropdownPlacementgrows the menu by that same getter, so the grow and the shrink can no longer disagree — which is what they did in 2.3.2 and again in 2.5.0 - CHANGE:
isTextModeremains public but is informational — nothing inside the widget reads it - CHANGE: The
label-or-Stringinvariant is now asserted byTextItemPresentationrather than ininitState, so it fires on the first build. Still before anything paints, still anAssertionErrorin debug builds - TEST: 140 tests, up from 107. Four guarded members that no longer exist and were deleted; ten new ones exercise the presentation seam with no widget tree. One deletion is worth naming: "a tooltip
borderWidthwith noborderColordraws nothing" is now unrepresentable rather than untested, because aBoxBordercannot carry a width without a colour - FEAT: Added
DropdownTheme.resolvedIconSizeandDropdownTheme.defaultIconSize. The arrow's size is the one part ofresolveButton()'s answer that owes nothing to the ambient palette, so a caller who needs only that number can now read it without resolving a wholeResolvedButtonStyle - PERF: Building the item presentation no longer resolves the button's style. It reached for
_buttonStyle.iconSizeto size a leading widget, which lifted the ambient palette out ofTheme.of(context)and built aBoxDecoration— and the presentation is constructed on every access, including once per keystroke while searching. Measured:resolveButton()ran twice per build, four times on open and twice per typed character; it now runs once, once, and not at all. No behaviour changes:resolveButton()is a pure function, so the extra calls returned identical answers - REFACTOR:
_buildOverlayContentbuilt the presentation twice, once through_visibleItemsand once for itself. It now builds one and passes it - REFACTOR:
TextItemPresentationbuilds its text through one helper. Its two builders passed the same eightTextDropdownConfigvalues toSmartTooltipTextseparately, which is howsemanticsLabelcame to reach the menu rows and not the button. The helper forwards a null style verbatim rather than substituting a default, because a null style is a decision - REFACTOR: Three layout decisions in the button's
Rowasked "does this button fill its width?" three times; they now share one named local. Hand-writteninsets.top + insets.bottomsums replaced withEdgeInsets.vertical - CHANGE: Added CI. Until now this repository had no automated checks at all. Two Flutter versions run on every pull request:
stable, and the minimum the pubspec declares — the second is the only one that can catch the package outgrowing its own constraint, which is how the constraint above went wrong for three minor versions - DOCS:
documentation/migration.mdgains a 2.x → 3.0.0 section.api_reference.mdno longer documentsDropdownMixin, and itscloseAll()section no longer claims the call is unanimated or that only one menu can be open process-wide — both stopped being true in 2.4.0
2.5.0 #
- FIX: A
SearchFieldTheme.dividertaller than one pixel no longer overflows the item list. The overlay reserved a hardcoded1.0for any divider, but Flutter'sDivider()— the widget almost everyone reaches for — is 16px tall, so a three-item searchable menu threwA RenderFlex overflowed by 15 pixels on the bottom - FIX: A
DropdownTooltipThemethat sets one visual property no longer blanks the rest of the tooltip's box. Flutter'sTooltiptreats a non-nulldecorationas a total replacement rather than a merge, soDropdownTooltipTheme(borderRadius: BorderRadius.circular(8))produced a transparent tooltip — white text on nothing. The same held forshadowandborderColoralone. Unset slots now fall back toTooltip's own defaults - FEAT: Added
SearchFieldTheme.dividerHeight(defaults to1.0). The overlay reserves this much space and constrains the divider to it, so the height reserved and the height drawn cannot disagree - FEAT: Added
DropdownTooltipTheme.border, aBoxBorder— the same shapeDropdownTheme.borderandSearchFieldTheme.bordertake. It wins overborderColor/borderWidth - CHANGE:
SearchFieldTheme.divideris now laid out at exactlydividerHeight. A caller passingDivider()and relying on its natural 16px must now passdividerHeight: 16to keep it; otherwise the divider draws at 1px instead of overflowing - CHANGE: A
DropdownTooltipThemethat setsbackgroundColorand nothing else now keeps Flutter's 4px tooltip corners, where it previously squared them off. SetborderRadius: BorderRadius.zerofor the old look - DEPRECATED:
DropdownTooltipTheme.borderColorandDropdownTooltipTheme.borderWidthin favour ofborder. They still work. Removed in 3.0.0 - REFACTOR:
SearchFieldTheme,DropdownScrollThemeandDropdownTooltipThemeresolve themselves, completing the workDropdownThemebegan in 2.4.1.resolve()takes a plain value — aDropdownAmbientColorspalette, aBrightness, or nothing — never aBuildContext, so every styling rule is a pure function. The last fourTheme.of(context)calls and twelve??fallback chains left the widget'sbuild() - CHANGE: Exported
ResolvedSearchFieldStyle,ResolvedScrollStyleandResolvedTooltipStyle, and addedDropdownAmbientColors.hint. Additive; existing theme fields are unchanged - TEST: 107 tests, up from 77. Thirty-five exercise theme resolution with no widget tree at all.
DropdownTooltipThemehad no test coverage of any kind before this release, which is why its bug survived
2.4.1 #
- FIX: The trailing arrow now honours the single-item auto-disable. A dropdown disabled by
disableWhenSingleItemblocked taps, switched its decoration to the disabled form and applieddisabledTextStyle, while the arrow kept its enabled colour. Visible wheneverhideIconWhenSingleItem: false. The icon askedwidget.enabled; everything else askedisEnabled - DEPRECATED:
DropdownTheme.animationDuration. Nothing has ever read it — the animation is driven byFlutterDropdownButton.animationDuration, and setting it on the theme was silently ignored. It is being removed rather than wired up: honouring it now would slow the animation for everyone who set it and has been living with the widget's 200ms. Pass the duration to the widget. Removed in 3.0.0 - REFACTOR:
DropdownThemeresolves itself.resolveButton(),resolveOverlay()andresolveItem()return styles whose slots are all filled in, and take a plainDropdownAmbientColorspalette rather than aBuildContext— so styling rules are pure functions, testable without mounting a widget. Thirteen inline??fallback chains and fourteenTheme.of(context)calls leftbuild().SearchFieldThemeandDropdownScrollThemeare not converted yet - CHANGE: Exported
DropdownAmbientColors,ResolvedButtonStyle,ResolvedOverlayStyleandResolvedItemStyle. Additive; existing theme fields are unchanged - CHANGE:
pubspec.yaml's description no longer advertises "specialized variants for different content types" — there has been one widget since 2.0.0 - TEST: 77 tests, up from 50. Fourteen exercise theme resolution with no widget tree at all
- DOCS:
CLAUDE.mdanddocumentation/rewritten around the current architecture.api_reference.mddocumented five classes deleted in 2.0.0 (BaseDropdownButton,BasicDropdownButton,TextOnlyDropdownButton,DynamicTextBaseDropdownButton,DropdownItem) and never mentionedFlutterDropdownButton. Seventeen examples intheming.mdandtext_configuration.mdcalled widgets that no longer exist; three passedtheme: DropdownTheme(...)where aDropdownStyleThemeis required
2.4.0 #
- FEAT:
FlutterDropdownButton.text()now accepts alabelcallback, so text mode renders any type — not justString. Overflow handling, the tooltip and the default search filter all work off the label, so aList<User>no longer has to drop toitemBuilderand give those up. Omittinglabelfor a non-StringTnow fails loudly at construction in debug builds instead of throwing a cast error at paint time - FEAT: Added
DropdownOverlayControllerandDropdownOverlaySpec— hold a controller instead of mixing inDropdownMixinto build your own dropdown. It manages the overlay's lifetime, the open/close animation, and the "only one menu open" rule behind nine members rather than twenty-three, and can be tested without aState. Seeexample/lib/pages/domain_type_page.dart - FEAT: Only-one-menu-open is now scoped to the enclosing
Overlayrather than to the process, so two dropdowns in two differentOverlays no longer close each other - FIX:
FlutterDropdownButton.closeAll()now acceptsanimate, asREADME.mdhas documented since 2.2.1. The parameter existed only onDropdownMixin.closeAll(); the widget's facade never forwarded it, so the documented call did not compile - FIX: A dropdown menu that is already open now reflects items that change underneath it. Previously an item list arriving asynchronously never appeared until the user closed and reopened the menu
- FIX: An open menu now grows when its item list gets longer, and flips above the button when the taller menu no longer fits below. Previously the height was fixed when the menu opened, so new items were pushed below the fold of a scrollbar that appeared for no visible reason
- DEPRECATED:
DropdownMixinis deprecated in favour ofDropdownOverlayController. It still works — it now delegates to a controller, so mixin-based and controller-based menus share one registry and both answercloseAll(). It will be removed in 3.0.0 - REFACTOR:
_FlutterDropdownButtonStateholds a controller instead of inheriting from a mixin. Fourteen one-line forwarders and thestatic _currentInstanceglobal are gone - REFACTOR: The filtered item list is derived from the items and the query on read rather than cached in a field, removing four hand-written invalidation sites
- TEST: 50 tests, up from 26 — placement geometry, search invalidation, overlay bounds and resizing, label extraction, and the controller itself (unit-tested with no widget tree)
2.3.2 #
- FIX: Fixed dropdown menu rendering off-screen when the dropdown lives inside a nested
OverlayorNavigator(side panels, shell routes, embedded views) — the button's position was resolved against the root view while the menu was placed against the enclosingOverlay's origin, shifting the menu by that Overlay's offset. Position and screen-bounds clamping now both use theOverlay's render box - FIX: Fixed
searchable: trueforcing a scrollbar on dropdowns whose items would otherwise fit — the search field's height was subtracted from the item area instead of being added to the overlay height. A 3-item searchable dropdown under default theming no longer scrolls - FIX: Fixed the search query being cleared whenever an ancestor widget rebuilt.
didUpdateWidgetcompareditemsby list identity, so any caller passing a derived list (source.map(...).toList(), or a non-const literal) reset the query on every rebuild. The filter is now recomputed from the current query, which is correct regardless of the equality semantics ofT - FIX: Fixed the dropdown overrunning
screenMarginbybuttonGap(4px) when opening downward, and reserving a double margin (16px) when shrinking to fit. The menu now keeps exactly onescreenMarginfrom the safe-area edge in both cases — a menu constrained for space is ~4px taller than before - DEPRECATED:
DropdownMixin.calculateMenuWidth()andDropdownMixin.calculateMenuLeftPosition()are deprecated in favour ofresolvePlacement(), which returns the menu's full geometry in one call. They still work and will be removed in 3.0.0 - REFACTOR: Extracted all overlay positioning geometry out of
DropdownMixininto a pure module that takes plain values and returns plain values — noBuildContext, noMediaQuery, noState.DropdownMixinremains as the adapter that reads the screen and delegates - TEST: Added the package's first test suite — 26 tests, 17 of which exercise the positioning geometry without mounting a widget
2.3.1 #
- FIX: Explicitly set
mouseCursoronInkWellwidgets to restore hover cursor behavior on web/desktop after recent Flutter versions changed the defaultMaterialStateMouseCursor.clickableresolution - FIX: Dropdown button now shows
SystemMouseCursors.clickwhen enabled andSystemMouseCursors.forbiddenwhen disabled (matches HTML<button disabled>cursor: not-allowedconvention) - FIX: Dropdown items now consistently show
SystemMouseCursors.clickon hover
2.3.0 #
- FEAT: Added
DropdownTheme.disabledBackgroundColorfor the dropdown button background when disabled - FEAT: Added
DropdownTheme.disabledBorderfor the dropdown button border when disabled - FEAT: Added
DropdownTheme.disabledButtonDecorationfor a full custom button decoration when disabled (takes precedence overdisabledBackgroundColor/disabledBorder) - FEAT: Added
TextDropdownConfig.disabledTextStylefor styling the button text (both value and hint) when disabled — merged over the basetextStyle/hintStyle - FEAT: Added "Disabled Styling" section to the example playground to live-preview the new options (toggle
enabled: falseto see the effect)
2.2.1 #
- FIX: Fixed
closeAll()not resetting trailing icon rotation and internal state — overlay was removed but_overlayEntry, animation controller, andsetStatewere not handled, leaving the icon in the open (rotated) state - FIX: Fixed
openDropdown()not closing the previously open dropdown when another dropdown is opened, which could leave orphaned overlays - FEAT: Added
animateparameter tocloseAll()— defaults totruefor animated close with icon rotation, set tofalsefor immediate removal before navigation
2.2.0 #
- FEAT: Added searchable dropdown support with real-time item filtering
- FEAT: Added
searchableparameter to enable search text field at the top of the dropdown overlay - FEAT: Added
searchFilterparameter for custom filter logic (required for custom mode, optional for text mode with default case-insensitive contains matching) - FEAT: Added
emptyBuilderparameter for customizing the empty state when search yields no results - FEAT: Added
SearchFieldThemeclass for comprehensive search field styling (text style, cursor, colors, border, padding, margin, border radius, divider, keyboard type, text input action, and more) - FEAT: Added
searchfield toDropdownStyleThemefor centralized search field theming - FEAT: Dynamic overlay height — dropdown shrinks to fit filtered results instead of keeping fixed height
- FEAT: Auto-reset search query on item selection, outside-tap dismissal, and dropdown reopen
- FEAT: Added
rebuildOverlay()method toDropdownMixinfor triggering overlay rebuilds - CHANGE:
DropdownMixinoverlay container now usesBoxConstraints(maxHeight:)instead of fixed height to support dynamic content sizing
2.1.0 #
- FEAT:
TextDropdownConfig.textAlignnow controls item alignment in the dropdown menu and button (previously hardcoded to left-align) - FEAT: Supports
TextAlign.center,TextAlign.end,TextAlign.rightfor both menu items and selected value display
2.0.0 #
- BREAKING: Unified all dropdown variants into a single
FlutterDropdownButton<T>widget - BREAKING: Removed
BasicDropdownButton,TextOnlyDropdownButton,DynamicTextBaseDropdownButton(useFlutterDropdownButtonandFlutterDropdownButton.textinstead) - BREAKING: Removed
DropdownItem<T>model class (useitemBuildercallback instead) - BREAKING: Removed
BaseDropdownButtonandBaseDropdownButtonState(no longer needed as public API) - BREAKING: Removed
TextDropdownRenderMixin(absorbed intoFlutterDropdownButton) - BREAKING: Removed deprecated
showSeparatorandseparatorparameters (useDropdownTheme.itemBorderinstead) - FEAT:
FlutterDropdownButton<T>()default constructor withitemBuilderfor custom widget rendering (replacesBasicDropdownButton) - FEAT:
FlutterDropdownButton<T>.text()named constructor for text-only dropdowns (replaces bothTextOnlyDropdownButtonandDynamicTextBaseDropdownButton) - FEAT:
widthis now optional in text mode — omit for content-based dynamic width, provide for fixed width - FEAT: All features (leading, disableWhenSingleItem, expand, etc.) available in a single widget
- FEAT:
FlutterDropdownButton.closeAll()static method for manual dropdown cleanup - MIGRATION:
BasicDropdownButton(items: [DropdownItem(value: v, child: w)])→FlutterDropdownButton(items: [v], itemBuilder: (item, isSelected) => w) - MIGRATION:
TextOnlyDropdownButton(items: items, width: 200)→FlutterDropdownButton.text(items: items, width: 200) - MIGRATION:
DynamicTextBaseDropdownButton(items: items)→FlutterDropdownButton.text(items: items, disableWhenSingleItem: true)
1.6.1 #
- FIX: Removed disabled opacity override that forced 0.6 opacity on single-item dropdowns, preserving original button styling
1.6.0 #
- BREAKING:
TextOnlyDropdownButton.widthis now required (fixed-width dropdown by design) - BREAKING: Removed
minWidth,maxWidth,expandfromTextOnlyDropdownButton(useDynamicTextBaseDropdownButtonfor content-based width) - BREAKING: Removed
widthfromDynamicTextBaseDropdownButton(content-based width by design, useminWidth/maxWidthfor constraints) - FEAT: Added
disableWhenSingleItemparameter toDynamicTextBaseDropdownButtonfor toggling single-item non-interactive mode (defaults to true) - FEAT: Added
showTrailinggetter toBaseDropdownButtonStateallowing subclasses to conditionally hide the trailing icon - FEAT:
DynamicTextBaseDropdownButtonnow auto-selects the only item when in single-item disabled mode - FIX: Fixed
DynamicTextBaseDropdownButtonsingle-item mode not blocking tap interactions (was usingwidget.enabledinstead ofisEnabled) - FIX: Fixed disabled opacity check using
widget.enabledinstead ofisEnabled, causing incorrect visual state for dynamic dropdowns - FIX: Removed duplicated
build()and_applyWidthConstraints()inDynamicTextBaseDropdownButton(was missingexpandsupport) - REFACTOR: Extracted common text rendering logic into
TextDropdownRenderMixinto eliminate duplication betweenTextOnlyDropdownButtonandDynamicTextBaseDropdownButton - REFACTOR: Replaced example showcase app with interactive Playground for live parameter configuration
1.5.5 #
- FIX: Fixed overlay removal crash when dropdown is closed during widget disposal by adding mounted check and error handling to closeDropdown()
1.5.4 #
- FIX: Fixed scroll gradient direction - top gradient now properly fades from opaque to transparent downward, bottom gradient fades from transparent to opaque downward
1.5.3 #
- FIX: Fixed ScrollbarTheme colors not being applied by correcting widget hierarchy (ScrollbarTheme must wrap Scrollbar, not vice versa)
1.5.2 #
- PERF: Improved scroll performance with many items by using ListView.builder (lazy loading) and ClampingScrollPhysics (removes bouncing effect)
- FIX: Fixed dropdown height calculation to account for safe areas (status bar, navigation bar, home indicator)
1.5.1 #
- FIX: Fixed dropdown overlay remaining visible after screen transitions by immediately removing overlay on dispose without animation
- FIX: Added safe error handling for overlay removal to prevent crashes when overlay has already been removed
- FEAT: Added DropdownMixin.closeAll() static method for manual dropdown cleanup before navigation or other actions
1.5.0 #
- BREAKING: Extracted tooltip styling from TextDropdownConfig into new TooltipTheme class for better separation of concerns
- BREAKING: Removed tooltip styling properties from TextDropdownConfig (tooltipBackgroundColor, tooltipTextColor, tooltipTextStyle, tooltipDecoration, tooltipBorderRadius, tooltipBorderColor, tooltipBorderWidth, tooltipShadow, tooltipPadding, tooltipMargin, tooltipConstraints, tooltipTextAlign)
- FEAT: Added TooltipTheme class for centralized tooltip visual styling
- FEAT: Added tooltip field to DropdownStyleTheme to include TooltipTheme alongside DropdownTheme and DropdownScrollTheme
- CHANGE: TextDropdownConfig now only controls tooltip behavior (enableTooltip, tooltipMode, durations, positioning, trigger modes)
- MIGRATION: Move tooltip styling properties from TextDropdownConfig to TooltipTheme in DropdownStyleTheme
1.4.8 #
- FEAT: Added itemBorder property to DropdownTheme for applying borders to individual dropdown items (commonly used for bottom borders between items)
- FEAT: Added excludeLastItemBorder property to DropdownTheme to exclude border from the last item (defaults to true for clean design)
- DEPRECATED: showSeparator and separator parameters are now deprecated in favor of itemBorder (will be removed in 2.0.0)
1.4.7 #
- FEAT: Added minMenuWidth parameter to set minimum dropdown menu width independently from button width
- FEAT: Added maxMenuWidth parameter to set maximum dropdown menu width independently from button width
- FEAT: Added menuAlignment parameter (left/center/right) to control menu positioning when menu is wider than button
1.4.6 #
- FEAT: Added showSeparator and separator parameters to display customizable dividers between dropdown items (defaults to Divider widget)
1.4.4 #
- FIX: Fixed hover color not visible when selectedItemColor is set by changing Container to Ink widget for proper Material effect layering
1.4.3 #
- FEAT: Added trailing parameter support to DynamicTextBaseDropdownButton (static display for single-item mode, rotation animation for multi-item mode)
1.4.2 #
- FEAT: Added buttonHoverColor, buttonSplashColor, and buttonHighlightColor to DropdownTheme for controlling dropdown button InkWell interaction colors
- FEAT: Added buttonHeight property to DropdownTheme for independent button content height control from iconSize, with automatic overflow prevention
- FEAT: Added trailing parameter to BaseDropdownButton for customizing the dropdown arrow icon with automatic rotation animation
1.4.1 #
- FEAT: Added hover cursor support - dropdown button now shows click cursor on mouse hover using InkWell
1.4.0 #
- BREAKING: Changed
leadingBuildertoleadingandselectedLeadingparameters in DynamicTextBaseDropdownButton for better performance and simpler API - BREAKING: Renamed
leadingWidgetPaddingtoleadingPaddingin DynamicTextBaseDropdownButton for consistent naming - PERF: Optimized AnimatedBuilder in DropdownMixin to prevent unnecessary rebuilds of overlay content during animations (60+ rebuilds eliminated per dropdown open/close)
- PERF: Changed leading widget API from builder function to direct widget parameters, eliminating redundant widget creation (126+ widget creations reduced to 2 per dropdown)
1.3.3 #
- FEAT: Added expand parameter to automatically wrap dropdown in Expanded widget for flex layouts, with spaceBetween alignment when expanded
1.3.2 #
- FEAT: Added smart tooltip support with overflow detection, auto-positioning, and extensive customization options (background color, border, shadow, text styling, trigger modes, and timing controls)
- FIX: Fixed text and icon vertical alignment issue by adding centerLeft alignment to text and center crossAxisAlignment to Row, and fixed mainAxisAlignment to use spaceBetween when width is fixed
1.3.1 #
- FEAT: Added leadingBuilder property to DynamicTextBaseDropdownButton for displaying custom widgets (icons, images) before text
- FEAT: Added leadingWidgetPadding property to DynamicTextBaseDropdownButton for controlling leading widget spacing
1.3.0 #
- FEAT: Added DynamicTextBaseDropdownButton widget that adapts behavior based on item count (non-interactive when single item, normal dropdown when multiple items)
- FEAT: Added hideIconWhenSingleItem property to DynamicTextBaseDropdownButton for controlling icon visibility in single-item mode
- FEAT: Added interactive example demo with real-time item add/delete functionality
- FIX: Fixed dropdown button height consistency issue by wrapping Text and Icon in SizedBox with fixed height based on iconSize
- FIX: Fixed mainAxisAlignment from spaceBetween to start to allow button width to fit content size within maxWidth constraint
1.2.3 #
- FEAT: Added scrollToSelectedItem property to automatically scroll to the currently selected item when dropdown opens (defaults to true)
- FEAT: Added scrollToSelectedDuration property for controlling scroll animation duration (null for instant jump, duration value for smooth animation)
- FEAT: Improved scrollable dropdown UX by automatically positioning selected items in view when there are many items
1.2.2 #
- FEAT: Added icon property to DropdownTheme for customizing dropdown arrow icon (supports any IconData)
- FEAT: Added iconSize property to DropdownTheme for controlling dropdown icon size
- FEAT: Added iconDisabledColor property to DropdownTheme for customizing icon color in disabled state
- FEAT: Added iconPadding property to DropdownTheme for controlling spacing between selected value and icon
- FEAT: Added overlayPadding property to DropdownTheme for controlling internal spacing of the dropdown menu container
1.2.1 #
- FIX: Fixed dropdown overlay border rendering issue by removing Material borderRadius that conflicted with Container border decoration
- FIX: Fixed dropdown icon not updating on open/close state change and added rotation animation
- REFACTOR: Reorganized theme files into theme/ subdirectory for better code organization
1.2.0 #
- FEAT: Added thumbWidth and trackWidth properties to DropdownScrollTheme for independent scrollbar thumb and track width control
- FEAT: Added iconColor property to DropdownTheme for customizing dropdown arrow icon color
- FIX: Fixed dropdown overlay content clipping issue with border radius by adding clipBehavior to Material widget
- FIX: Changed theme parameter type from Object? to DropdownStyleTheme? for better type safety
- FIX: Fixed dropdown menu height calculation to properly account for itemMargin and border thickness, preventing unnecessary scrollbars
1.1.0 #
- FEAT: Added DropdownScrollTheme for customizing scrollbar appearance
- FEAT: Added DropdownStyleTheme as main theme container for dropdown and scroll themes
- FEAT: Support for custom scrollbar colors, thickness, radius, and visibility options
- REFACTOR: Updated example app with feature-based showcase and style selector
1.0.1 #
- FEAT: Added itemMargin property to DropdownTheme for controlling spacing between dropdown items
- FEAT: Added itemBorderRadius property to DropdownTheme for individual item border radius styling
- FEAT: Added hover effect support to TextOnlyDropdownButton with InkWell integration
- FIX: Fixed hover effect positioning to respect itemMargin boundaries for consistent visual feedback
- REFACTOR: Added BaseDropdownButton abstract class to reduce code duplication between dropdown variants
- FEAT: Exported BaseDropdownButton for creating custom dropdown implementations
1.0.0 #
- FEAT: Initial release with BasicDropdownButton and TextOnlyDropdownButton widgets
- FEAT: Smart dropdown positioning - automatically opens upward when insufficient space below
- FEAT: Dynamic height adjustment to prevent screen overflow
- FEAT: OverlayEntry-based dropdown with smooth animations and outside-tap dismissal
- FEAT: Dynamic width support (width, maxWidth, minWidth parameters)
- FEAT: Shared DropdownTheme system for consistent styling across variants
- FEAT: TextDropdownConfig for precise text overflow control (ellipsis, fade, clip, visible)
- FEAT: Multi-line text support and custom text styling
- FEAT: Generic DropdownItem model supporting any widget content
- FEAT: DropdownMixin for shared functionality across dropdown variants
- FEAT: Comprehensive example app with multiple dropdown demonstrations