@@ -54,15 +54,15 @@ typedef LoadingBuilder<T> = Widget Function(
54
54
typedef BeforeChange <T > = Future <bool ?> Function (T ? prevItem, T ? nextItem);
55
55
typedef BeforePopupOpening <T > = Future <bool ?> Function (T ? selectedItem);
56
56
typedef BeforePopupOpeningMultiSelection <T > = Future <bool ?> Function (
57
- List <T > selItems );
57
+ List <T > selectedItem );
58
58
typedef BeforeChangeMultiSelection <T > = Future <bool ?> Function (
59
59
List <T > prevItems, List <T > nextItems);
60
60
typedef FavoriteItemsBuilder <T > = Widget Function (
61
61
BuildContext context, T item, bool isSelected);
62
62
typedef ValidationMultiSelectionBuilder <T > = Widget Function (
63
63
BuildContext context, List <T > items);
64
64
typedef PositionCallback = RelativeRect Function (
65
- RenderBox popupButtonObject , RenderBox overlay);
65
+ RenderBox dropdownBox , RenderBox overlay);
66
66
typedef OnItemAdded <T > = void Function (List <T > selectedItems, T addedItem);
67
67
typedef OnItemRemoved <T > = void Function (List <T > selectedItems, T removedItem);
68
68
typedef PopupBuilder <T > = Widget Function (
@@ -96,13 +96,15 @@ class DropdownSearch<T> extends StatefulWidget {
96
96
97
97
/// scroll props for selected item on the dropdown.
98
98
/// 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
+ ///```
106
108
final ScrollProps ? selectedItemsScrollProps;
107
109
108
110
///customize the fields the be shown
@@ -169,13 +171,10 @@ class DropdownSearch<T> extends StatefulWidget {
169
171
super .key,
170
172
T ? selectedItem,
171
173
this .mode = Mode .form,
172
- this .onSaved,
173
- this .validator,
174
174
this .autoValidateMode = AutovalidateMode .disabled,
175
175
this .onChanged,
176
176
this .items,
177
177
this .dropdownBuilder,
178
- this .decoratorProps = const DropDownDecoratorProps (),
179
178
this .suffixProps = const DropdownSuffixProps (),
180
179
this .clickProps = const ClickProps (),
181
180
this .enabled = true ,
@@ -185,8 +184,24 @@ class DropdownSearch<T> extends StatefulWidget {
185
184
this .onBeforeChange,
186
185
this .onBeforePopupOpening,
187
186
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 (),
190
205
selectedItems = _itemToList (selectedItem),
191
206
popupProps = PopupPropsMultiSelection .from (popupProps),
192
207
isMultiSelectionMode = false ,
@@ -203,7 +218,6 @@ class DropdownSearch<T> extends StatefulWidget {
203
218
this .mode = Mode .form,
204
219
this .autoValidateMode = AutovalidateMode .disabled,
205
220
this .items,
206
- this .decoratorProps = const DropDownDecoratorProps (),
207
221
this .suffixProps = const DropdownSuffixProps (),
208
222
this .clickProps = const ClickProps (),
209
223
this .enabled = true ,
@@ -213,14 +227,28 @@ class DropdownSearch<T> extends StatefulWidget {
213
227
this .selectedItems = const [],
214
228
this .popupProps = const PopupPropsMultiSelection .menu (),
215
229
this .selectedItemsScrollProps,
216
- FormFieldSetter <List <T >>? onSaved,
217
230
ValueChanged <List <T >>? onChanged,
218
231
BeforeChangeMultiSelection <T >? onBeforeChange,
219
232
BeforePopupOpeningMultiSelection <T >? onBeforePopupOpening,
220
- FormFieldValidator <List <T >>? validator,
221
233
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 (),
224
252
onChangedMultiSelection = onChanged,
225
253
onBeforePopupOpeningMultiSelection = onBeforePopupOpening,
226
254
onSavedMultiSelection = onSaved,
@@ -301,7 +329,6 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
301
329
Widget _defaultSelectedItemWidget () {
302
330
Widget defaultItemMultiSelectionMode (T item) {
303
331
return Container (
304
- height: 32 ,
305
332
padding: EdgeInsets .only (left: 8 , right: 1 ),
306
333
margin: EdgeInsets .symmetric (horizontal: 2 , vertical: 1 ),
307
334
decoration: BoxDecoration (
@@ -312,24 +339,20 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
312
339
mainAxisSize: MainAxisSize .min,
313
340
mainAxisAlignment: MainAxisAlignment .spaceBetween,
314
341
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,
321
346
),
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),
333
356
),
334
357
)
335
358
],
@@ -346,13 +369,15 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
346
369
return CustomSingleScrollView (
347
370
scrollProps: widget.selectedItemsScrollProps ?? ScrollProps (),
348
371
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
+ ),
352
377
);
353
378
}
354
379
return Text (
355
- _selectedItemAsString (getSelectedItem),
380
+ _itemAsString (getSelectedItem),
356
381
style: _getBaseTextStyle (),
357
382
textAlign: widget.decoratorProps.textAlign,
358
383
);
@@ -398,8 +423,7 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
398
423
baseStyle: _getBaseTextStyle (),
399
424
textAlign: widget.decoratorProps.textAlign,
400
425
textAlignVertical: widget.decoratorProps.textAlignVertical,
401
- isEmpty:
402
- getSelectedItem == null && widget.dropdownBuilder == null ,
426
+ isEmpty: getSelectedItem == null ,
403
427
isFocused: isFocused,
404
428
expands: widget.decoratorProps.expands,
405
429
isHovering: widget.decoratorProps.isHovering,
@@ -433,8 +457,7 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
433
457
baseStyle: _getBaseTextStyle (),
434
458
textAlign: widget.decoratorProps.textAlign,
435
459
textAlignVertical: widget.decoratorProps.textAlignVertical,
436
- isEmpty: getSelectedItems.isEmpty &&
437
- widget.dropdownBuilderMultiSelection == null ,
460
+ isEmpty: getSelectedItems.isEmpty,
438
461
expands: widget.decoratorProps.expands,
439
462
isHovering: widget.decoratorProps.isHovering,
440
463
isFocused: isFocused,
@@ -458,14 +481,14 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
458
481
}
459
482
460
483
///function that return the String value of an object
461
- String _selectedItemAsString (T ? data) {
484
+ String _itemAsString (T ? data) {
462
485
if (data == null ) {
463
486
return "" ;
464
487
} else if (widget.itemAsString != null ) {
465
488
return widget.itemAsString !(data);
466
- } else {
467
- return data.toString ();
468
489
}
490
+
491
+ return data.toString ();
469
492
}
470
493
471
494
///function that manage Trailing icons(close, dropDown)
@@ -545,7 +568,6 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
545
568
transitionBuilder: widget.popupProps.dialogProps.transitionBuilder,
546
569
pageBuilder: (context, animation, secondaryAnimation) {
547
570
return AlertDialog (
548
- key: widget.popupProps.dialogProps.key,
549
571
buttonPadding: widget.popupProps.dialogProps.buttonPadding,
550
572
actionsOverflowButtonSpacing:
551
573
widget.popupProps.dialogProps.actionsOverflowButtonSpacing,
@@ -720,6 +742,10 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
720
742
///If we close the popup , or maybe we just selected
721
743
///another widget we should clear the focus
722
744
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
+
723
749
//handle onBefore popupOpening
724
750
if (widget.onBeforePopupOpening != null ) {
725
751
if (await widget.onBeforePopupOpening !(getSelectedItem) == false ) return ;
@@ -738,8 +764,7 @@ class DropdownSearchState<T> extends State<DropdownSearch<T>> {
738
764
} else {
739
765
await _openSelectDialog ();
740
766
}
741
- //dismiss either by selecting items OR clicking outside the popup
742
- widget.popupProps.onDismissed? .call ();
767
+
743
768
_handleFocus (false );
744
769
}
745
770
0 commit comments