Skip to content

Commit e7f1b04

Browse files
author
Salim Lachdhaf
committed
v6.0.1
1 parent f4b890e commit e7f1b04

15 files changed

+202
-201
lines changed

CHANGELOG.md

+27-17
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
1+
## [6.0.1] - 2024.09.21
2+
* #### New Feature:
3+
* add `Semantics` to searchBox to support voiceOver/TalkBack ...
4+
5+
* #### Fix bugs:
6+
* duplicated GlobalKey issue [672](https://github.com/salim-lachdhaf/searchable_dropdown/issues/672)
7+
* multiSelection chips height issue [602](https://github.com/salim-lachdhaf/searchable_dropdown/issues/602)
8+
* hint and dropdownBuilder issue [515](https://github.com/salim-lachdhaf/searchable_dropdown/issues/515)
9+
10+
111
## [6.0.0] - 2024.09.14
212

313
* #### New features:
414
* infinite list / lazy loading
515
* add click properties to the dropdown
616
* add custom mode
717
* dropdown button can be changed depending on state (opened/closed)
8-
* add property ```cacheItems``` for popupProps
9-
* add property ```onItemLoaded```
18+
* add property `cacheItems` for popupProps
19+
* add property `onItemLoaded`
1020
* adding new properties for a lot of widget (scrollBar, modal, bottomSheet,...)
1121
* add to possibility to change position of ```clearButton``` and ```dropdownButton```
1222
using ```direction``` property
1323

1424
* #### Breaking changes :
15-
* ```AsyncItems``` and ```items``` are replaced by ```items:(filter, infiniteScrollProps)```
16-
* Add ```isDisabled``` to ```itemBuilder``` params
17-
* ```FavoriteItems``` renamed to ```SuggestedItems```
18-
* ```isFilterOnline``` renamed to ```disableFilter```
19-
* ```selectionWidget``` renamed to ```checkBoxBuilder```
20-
* ```dropdownDecoratorProps``` renamed to ```decoratorProps```
21-
* ```clearButtonProps``` and ```dropdownButtonProps``` are placed to ```suffixProps```
25+
* `AsyncItems` and `items` are replaced by `items:(filter, infiniteScrollProps)`
26+
* Add `isDisabled` to `itemBuilder` params
27+
* `FavoriteItems` renamed to `SuggestedItems`
28+
* `isFilterOnline` renamed to `disableFilter`
29+
* `selectionWidget` renamed to `checkBoxBuilder`
30+
* `dropdownDecoratorProps` renamed to `decoratorProps`
31+
* `clearButtonProps` and `dropdownButtonProps` are placed to `suffixProps`
2232
* #### Fix bugs:
2333
* Search delay issue [542](https://github.com/salim-lachdhaf/searchable_dropdown/issues/542),
2434
* Multiselection triggers didChange
@@ -60,7 +70,7 @@
6070

6171
## [5.0.2] - 2022.06.04
6272

63-
* add ```interceptCallBacks``` for popupProps
73+
* add `interceptCallBacks` for popupProps
6474

6575
## [5.0.1] - 2022.05.29
6676

@@ -74,8 +84,8 @@
7484
* replace ``IconButtonProps`` with DropdownButtonProps and ``ClearButtonProps`` for ``clearButtonProps``
7585
and dropdownButtonProps
7686
* add a full custom container for the pop `containerBuilder` to ``popup_props``
77-
* add ```isVisible``` prop to `DropdownButtonProps`
78-
* change ``validationMultiSelectionWidget`` to ``validationWidgetBuilder``
87+
* add `isVisible` prop to `DropdownButtonProps`
88+
* change `validationMultiSelectionWidget` to `validationWidgetBuilder`
7989

8090
## [4.0.1] - 2022.05.17
8191

@@ -103,11 +113,11 @@
103113
## [3.0.1] - 2022.04.15
104114

105115
* breaking changes:
106-
- remove ``hint`` and ``label`` properties, use ``dropdownSearchDecoration`` instead
107-
- remove ``showAsSuffixIcons`` property, now always are as suffixIcon
108-
- replace '``clearButtonSplashRadius``' and '``clearButtonBuilder``' into one property '``IconButtonProps``'
109-
- replace '``dropdownButtonSplashRadius``' and '``dropdownButtonBuilder``' into one property '
110-
``IconButtonProps``'
116+
- remove `hint` and `label` properties, use `dropdownSearchDecoration` instead
117+
- remove `showAsSuffixIcons` property, now always are as suffixIcon
118+
- replace '`clearButtonSplashRadius`' and '`clearButtonBuilder`' into one property '`IconButtonProps`'
119+
- replace '`dropdownButtonSplashRadius`' and '`dropdownButtonBuilder`' into one property '
120+
`IconButtonProps`'
111121
* fix issue [380](https://github.com/salim-lachdhaf/searchable_dropdown/issues/380)
112122

113123
## [2.0.1] - 2021.11.15

lib/dropdown_search.dart

+76-51
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ typedef LoadingBuilder<T> = Widget Function(
5454
typedef BeforeChange<T> = Future<bool?> Function(T? prevItem, T? nextItem);
5555
typedef BeforePopupOpening<T> = Future<bool?> Function(T? selectedItem);
5656
typedef BeforePopupOpeningMultiSelection<T> = Future<bool?> Function(
57-
List<T> selItems);
57+
List<T> selectedItem);
5858
typedef BeforeChangeMultiSelection<T> = Future<bool?> Function(
5959
List<T> prevItems, List<T> nextItems);
6060
typedef FavoriteItemsBuilder<T> = Widget Function(
6161
BuildContext context, T item, bool isSelected);
6262
typedef ValidationMultiSelectionBuilder<T> = Widget Function(
6363
BuildContext context, List<T> items);
6464
typedef PositionCallback = RelativeRect Function(
65-
RenderBox popupButtonObject, RenderBox overlay);
65+
RenderBox dropdownBox, RenderBox overlay);
6666
typedef OnItemAdded<T> = void Function(List<T> selectedItems, T addedItem);
6767
typedef OnItemRemoved<T> = void Function(List<T> selectedItems, T removedItem);
6868
typedef PopupBuilder<T> = Widget Function(
@@ -96,13 +96,15 @@ class DropdownSearch<T> extends StatefulWidget {
9696

9797
/// scroll props for selected item on the dropdown.
9898
/// example :
99-
/// SizedBox(
100-
/// height: 50,
101-
/// child: DropdownSearch<int>.multiSelection(
102-
/// items: (f, cs) => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
103-
/// selectedItemsScrollProps: ScrollProps(),
104-
/// ),
105-
/// ),
99+
/// ```dart
100+
/// SizedBox(
101+
/// height: 50,
102+
/// child: DropdownSearch<int>.multiSelection(
103+
/// items: (f, cs) => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
104+
/// selectedItemsScrollProps: ScrollProps(),
105+
/// ),
106+
/// ),
107+
///```
106108
final ScrollProps? selectedItemsScrollProps;
107109

108110
///customize the fields the be shown
@@ -169,13 +171,10 @@ class DropdownSearch<T> extends StatefulWidget {
169171
super.key,
170172
T? selectedItem,
171173
this.mode = Mode.form,
172-
this.onSaved,
173-
this.validator,
174174
this.autoValidateMode = AutovalidateMode.disabled,
175175
this.onChanged,
176176
this.items,
177177
this.dropdownBuilder,
178-
this.decoratorProps = const DropDownDecoratorProps(),
179178
this.suffixProps = const DropdownSuffixProps(),
180179
this.clickProps = const ClickProps(),
181180
this.enabled = true,
@@ -185,8 +184,24 @@ class DropdownSearch<T> extends StatefulWidget {
185184
this.onBeforeChange,
186185
this.onBeforePopupOpening,
187186
PopupProps<T> popupProps = const PopupProps.menu(),
188-
}) : assert(T == String || T == int || T == double || compareFn != null),
189-
assert(mode != Mode.custom || dropdownBuilder != null),
187+
//form properties
188+
this.onSaved,
189+
this.validator,
190+
DropDownDecoratorProps? decoratorProps,
191+
}) : assert(
192+
T == String || T == int || T == double || compareFn != null,
193+
'`compareFn` is required',
194+
),
195+
assert(
196+
mode != Mode.custom || dropdownBuilder != null,
197+
'Please implement your `dropdownBuilder`',
198+
),
199+
assert(
200+
mode != Mode.custom ||
201+
(decoratorProps == null && onSaved == null && validator == null),
202+
'Custom mode has no form properties',
203+
),
204+
decoratorProps = decoratorProps ?? const DropDownDecoratorProps(),
190205
selectedItems = _itemToList(selectedItem),
191206
popupProps = PopupPropsMultiSelection.from(popupProps),
192207
isMultiSelectionMode = false,
@@ -203,7 +218,6 @@ class DropdownSearch<T> extends StatefulWidget {
203218
this.mode = Mode.form,
204219
this.autoValidateMode = AutovalidateMode.disabled,
205220
this.items,
206-
this.decoratorProps = const DropDownDecoratorProps(),
207221
this.suffixProps = const DropdownSuffixProps(),
208222
this.clickProps = const ClickProps(),
209223
this.enabled = true,
@@ -213,14 +227,28 @@ class DropdownSearch<T> extends StatefulWidget {
213227
this.selectedItems = const [],
214228
this.popupProps = const PopupPropsMultiSelection.menu(),
215229
this.selectedItemsScrollProps,
216-
FormFieldSetter<List<T>>? onSaved,
217230
ValueChanged<List<T>>? onChanged,
218231
BeforeChangeMultiSelection<T>? onBeforeChange,
219232
BeforePopupOpeningMultiSelection<T>? onBeforePopupOpening,
220-
FormFieldValidator<List<T>>? validator,
221233
DropdownSearchBuilderMultiSelection<T>? dropdownBuilder,
222-
}) : assert(T == String || T == int || T == double || compareFn != null),
223-
assert(mode != Mode.custom || dropdownBuilder != null),
234+
//form properties
235+
FormFieldSetter<List<T>>? onSaved,
236+
FormFieldValidator<List<T>>? validator,
237+
DropDownDecoratorProps? decoratorProps,
238+
}) : assert(
239+
T == String || T == int || T == double || compareFn != null,
240+
'`compareFn` is required',
241+
),
242+
assert(
243+
mode != Mode.custom || dropdownBuilder != null,
244+
'Please implement your `dropdownBuilder`',
245+
),
246+
assert(
247+
mode != Mode.custom ||
248+
(decoratorProps == null && onSaved == null && validator == null),
249+
"Custom mode has no form properties",
250+
),
251+
decoratorProps = decoratorProps ?? const DropDownDecoratorProps(),
224252
onChangedMultiSelection = onChanged,
225253
onBeforePopupOpeningMultiSelection = onBeforePopupOpening,
226254
onSavedMultiSelection = onSaved,
@@ -301,7 +329,6 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
301329
Widget _defaultSelectedItemWidget() {
302330
Widget defaultItemMultiSelectionMode(T item) {
303331
return Container(
304-
height: 32,
305332
padding: EdgeInsets.only(left: 8, right: 1),
306333
margin: EdgeInsets.symmetric(horizontal: 2, vertical: 1),
307334
decoration: BoxDecoration(
@@ -312,24 +339,20 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
312339
mainAxisSize: MainAxisSize.min,
313340
mainAxisAlignment: MainAxisAlignment.spaceBetween,
314341
children: [
315-
Flexible(
316-
child: Text(
317-
_selectedItemAsString(item),
318-
style: Theme.of(context).textTheme.titleSmall,
319-
overflow: TextOverflow.ellipsis,
320-
),
342+
Text(
343+
_itemAsString(item),
344+
style: Theme.of(context).textTheme.titleSmall,
345+
overflow: TextOverflow.ellipsis,
321346
),
322-
MaterialButton(
323-
height: 20,
324-
shape: const CircleBorder(),
325-
padding: const EdgeInsets.all(0),
326-
minWidth: 20,
327-
onPressed: () {
328-
removeItem(item);
329-
},
330-
child: Icon(
331-
Icons.close_outlined,
332-
size: 20,
347+
Padding(padding: EdgeInsets.only(left: 8)),
348+
SizedBox(
349+
width: 32,
350+
height: 32,
351+
child: IconButton(
352+
iconSize: 20,
353+
padding: EdgeInsets.zero,
354+
icon: Icon(Icons.close_outlined, size: 20),
355+
onPressed: () => removeItem(item),
333356
),
334357
)
335358
],
@@ -346,13 +369,15 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
346369
return CustomSingleScrollView(
347370
scrollProps: widget.selectedItemsScrollProps ?? ScrollProps(),
348371
child: Wrap(
349-
children: getSelectedItems
350-
.map((e) => defaultItemMultiSelectionMode(e))
351-
.toList()),
372+
crossAxisAlignment: WrapCrossAlignment.center,
373+
children: getSelectedItems
374+
.map((e) => defaultItemMultiSelectionMode(e))
375+
.toList(),
376+
),
352377
);
353378
}
354379
return Text(
355-
_selectedItemAsString(getSelectedItem),
380+
_itemAsString(getSelectedItem),
356381
style: _getBaseTextStyle(),
357382
textAlign: widget.decoratorProps.textAlign,
358383
);
@@ -398,8 +423,7 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
398423
baseStyle: _getBaseTextStyle(),
399424
textAlign: widget.decoratorProps.textAlign,
400425
textAlignVertical: widget.decoratorProps.textAlignVertical,
401-
isEmpty:
402-
getSelectedItem == null && widget.dropdownBuilder == null,
426+
isEmpty: getSelectedItem == null,
403427
isFocused: isFocused,
404428
expands: widget.decoratorProps.expands,
405429
isHovering: widget.decoratorProps.isHovering,
@@ -433,8 +457,7 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
433457
baseStyle: _getBaseTextStyle(),
434458
textAlign: widget.decoratorProps.textAlign,
435459
textAlignVertical: widget.decoratorProps.textAlignVertical,
436-
isEmpty: getSelectedItems.isEmpty &&
437-
widget.dropdownBuilderMultiSelection == null,
460+
isEmpty: getSelectedItems.isEmpty,
438461
expands: widget.decoratorProps.expands,
439462
isHovering: widget.decoratorProps.isHovering,
440463
isFocused: isFocused,
@@ -458,14 +481,14 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
458481
}
459482

460483
///function that return the String value of an object
461-
String _selectedItemAsString(T? data) {
484+
String _itemAsString(T? data) {
462485
if (data == null) {
463486
return "";
464487
} else if (widget.itemAsString != null) {
465488
return widget.itemAsString!(data);
466-
} else {
467-
return data.toString();
468489
}
490+
491+
return data.toString();
469492
}
470493

471494
///function that manage Trailing icons(close, dropDown)
@@ -545,7 +568,6 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
545568
transitionBuilder: widget.popupProps.dialogProps.transitionBuilder,
546569
pageBuilder: (context, animation, secondaryAnimation) {
547570
return AlertDialog(
548-
key: widget.popupProps.dialogProps.key,
549571
buttonPadding: widget.popupProps.dialogProps.buttonPadding,
550572
actionsOverflowButtonSpacing:
551573
widget.popupProps.dialogProps.actionsOverflowButtonSpacing,
@@ -720,6 +742,10 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
720742
///If we close the popup , or maybe we just selected
721743
///another widget we should clear the focus
722744
Future<void> _selectSearchMode() async {
745+
//fix Duplicate GlobalKey detected in widget tree.
746+
//if the popup not yet disposed do nothing => solve issue fast click
747+
if (_popupStateKey.currentState != null) return;
748+
723749
//handle onBefore popupOpening
724750
if (widget.onBeforePopupOpening != null) {
725751
if (await widget.onBeforePopupOpening!(getSelectedItem) == false) return;
@@ -738,8 +764,7 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
738764
} else {
739765
await _openSelectDialog();
740766
}
741-
//dismiss either by selecting items OR clicking outside the popup
742-
widget.popupProps.onDismissed?.call();
767+
743768
_handleFocus(false);
744769
}
745770

lib/src/properties/clear_button_props.dart

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
33

44
class ClearButtonProps extends IconButtonProps {
55
const ClearButtonProps({
6-
super.key,
76
super.icon = const Icon(Icons.clear, size: 24),
87
super.isVisible = false,
98
super.iconSize = 24.0,

lib/src/properties/dialog_props.dart

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:flutter/material.dart';
22

33
class DialogProps {
4-
final Key? key;
54
final List<Widget>? actions;
65
final MainAxisAlignment? actionsAlignment;
76
final OverflowBarAlignment? actionsOverflowAlignment;
@@ -29,7 +28,6 @@ class DialogProps {
2928
final Color? surfaceTintColor;
3029

3130
const DialogProps({
32-
this.key,
3331
this.alignment,
3432
this.elevation,
3533
this.semanticLabel,

lib/src/properties/dropdown_props.dart

-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ class DropdownButtonProps extends IconButtonProps {
55
final Widget iconOpened;
66

77
const DropdownButtonProps({
8-
super.key,
98
this.iconOpened = const Icon(Icons.arrow_drop_up, size: 24),
109
Widget iconClosed = const Icon(Icons.arrow_drop_down, size: 24),
1110
super.isVisible = true,
@@ -313,10 +312,7 @@ class ClickProps {
313312
/// The default is 50ms.
314313
final Duration? hoverDuration;
315314

316-
final Key? key;
317-
318315
const ClickProps({
319-
this.key,
320316
this.containedInkWell = true,
321317
this.highlightShape = BoxShape.rectangle,
322318
this.onFocusChange,

lib/src/properties/icon_button_props.dart

-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
22

33
///see [IconButton] props for more details
44
class IconButtonProps {
5-
final Key? key;
65
final double iconSize;
76
final VisualDensity? visualDensity;
87
final EdgeInsetsGeometry padding;
@@ -27,7 +26,6 @@ class IconButtonProps {
2726
final Widget? selectedIcon;
2827

2928
const IconButtonProps({
30-
this.key,
3129
required this.icon,
3230
this.isVisible = false,
3331
this.iconSize = 24.0,

0 commit comments

Comments
 (0)