Skip to content

Commit 8fbf8cb

Browse files
committed
Fix menu limits when using searchable dropdown with separators
1 parent 73f173b commit 8fbf8cb

File tree

4 files changed

+39
-59
lines changed

4 files changed

+39
-59
lines changed

packages/dropdown_button2/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## UNRELEASED
2+
3+
- Fix menu limits when using searchable dropdown with separators, closes #214.
4+
15
## 3.0.0-beta.20
26

37
- Remove an assert from updateSelectedIndex method.

packages/dropdown_button2/lib/src/dropdown_menu.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,11 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
209209
if (index >= childrenLength) {
210210
return 100;
211211
}
212-
return separator != null && index.isOdd
213-
? separator.height
214-
: route.itemHeights[index];
212+
return separator == null
213+
? route.items[index].height
214+
: index.isOdd
215+
? separator.height
216+
: route.items[index ~/ 2].height;
215217
},
216218
childrenDelegate: separator == null
217219
? SliverChildBuilderDelegate(

packages/dropdown_button2/lib/src/dropdown_route.dart

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
1919
required this.menuItemStyle,
2020
required this.searchData,
2121
this.dropdownSeparator,
22-
}) : itemHeights = addSeparatorsHeights(
23-
itemHeights: items.map((item) => item.height).toList(),
24-
separatorHeight: dropdownSeparator?.height,
25-
),
26-
barrierColor = barrierCoversButton ? barrierColor : null,
22+
}) : barrierColor = barrierCoversButton ? barrierColor : null,
2723
_altBarrierColor = barrierColor;
2824

2925
final List<DropdownItem<T>> items;
@@ -40,7 +36,6 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
4036
final DropdownSearchData<T>? searchData;
4137
final DropdownSeparator<T>? dropdownSeparator;
4238

43-
final List<double> itemHeights;
4439
ScrollController? scrollController;
4540

4641
@override
@@ -114,17 +109,32 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
114109
}
115110
}
116111

112+
double _itemHeightWithSeparator(int itemIndex) {
113+
final itemHeight = items[itemIndex].height;
114+
final separatorHeight = dropdownSeparator?.height ?? 0;
115+
return itemIndex != 0 ? itemHeight + separatorHeight : itemHeight;
116+
}
117+
118+
double _calculateHeightUntilIndex(
119+
int index, {
120+
bool Function(DropdownItem<T> item)? itemPredicate,
121+
}) {
122+
var itemsHeight = 0.0;
123+
for (int i = 0; i < index; i++) {
124+
if (itemPredicate == null || itemPredicate(items[i])) {
125+
itemsHeight += _itemHeightWithSeparator(i);
126+
}
127+
}
128+
return itemsHeight;
129+
}
130+
117131
double getItemOffset(int index) {
118132
final double paddingTop = dropdownStyle.padding != null
119133
? dropdownStyle.padding!.resolve(null).top
120134
: kMaterialListPadding.top;
121135
double offset = paddingTop;
122136

123137
if (items.isNotEmpty && index > 0) {
124-
assert(
125-
items.length + (dropdownSeparator != null ? items.length - 1 : 0) ==
126-
itemHeights.length,
127-
);
128138
if (searchData?.searchController?.text case final searchText?) {
129139
final searchMatchFn =
130140
searchData?.searchMatchFn ?? _defaultSearchMatchFn();
@@ -133,24 +143,19 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
133143
offset += _getSearchItemsHeight(index, searchText);
134144
}
135145
} else {
136-
for (int i = 0; i < index; i++) {
137-
offset += itemHeights[i];
138-
}
146+
offset += _calculateHeightUntilIndex(index);
139147
}
140148
}
141149

142150
return offset;
143151
}
144152

145153
double _getSearchItemsHeight(int index, String searchText) {
146-
var itemsHeight = 0.0;
147154
final searchMatchFn = searchData?.searchMatchFn ?? _defaultSearchMatchFn();
148-
for (int i = 0; i < index; i++) {
149-
if (searchMatchFn(items[i], searchText)) {
150-
itemsHeight += itemHeights[i];
151-
}
152-
}
153-
return itemsHeight;
155+
return _calculateHeightUntilIndex(
156+
index,
157+
itemPredicate: (item) => searchMatchFn(item, searchText),
158+
);
154159
}
155160

156161
// Returns the vertical extent of the menu and the initial scrollOffset
@@ -177,7 +182,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
177182
final searchText = searchData?.searchController?.text;
178183
actualMenuHeight += searchText != null
179184
? _getSearchItemsHeight(items.length, searchText)
180-
: itemHeights.reduce((double total, double height) => total + height);
185+
: _calculateHeightUntilIndex(items.length);
181186
}
182187

183188
// Use actualMenuHeight if it's less than maxHeight.
@@ -215,13 +220,11 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
215220
final double actualMenuNetHeight = actualMenuHeight - innerWidgetHeight;
216221
// The offset should be zero if the selected item is in view at the beginning
217222
// of the menu. Otherwise, the scroll offset should center the item if possible.
218-
final actualIndex = dropdownSeparator?.height != null ? index * 2 : index;
219-
final double selectedItemOffset = getItemOffset(actualIndex);
223+
final double selectedItemOffset = getItemOffset(index);
220224
scrollOffset = math.max(
221-
0.0,
222-
selectedItemOffset -
223-
(menuNetHeight / 2) +
224-
(itemHeights[actualIndex] / 2));
225+
0.0,
226+
selectedItemOffset - (menuNetHeight / 2) + (items[index].height / 2),
227+
);
225228
// If the selected item's scroll offset is greater than the maximum scroll offset,
226229
// set it instead to the maximum allowed scroll offset.
227230
final double maxScrollOffset = actualMenuNetHeight - menuNetHeight;

packages/dropdown_button2/lib/src/utils.dart

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,5 @@
11
part of 'dropdown_button2.dart';
22

3-
/// Adds separators to a list of heights.
4-
///
5-
/// The [itemHeights] property is the list of heights of the items.
6-
///
7-
/// The [separatorHeight] property is the height of the separator.
8-
///
9-
/// Returns a new list of heights with separators added.
10-
List<double> addSeparatorsHeights({
11-
required List<double> itemHeights,
12-
required double? separatorHeight,
13-
}) {
14-
final List<double> heights = [];
15-
16-
bool addSeparator = false;
17-
if (separatorHeight != null) {
18-
for (final item in itemHeights) {
19-
if (addSeparator) {
20-
heights.add(separatorHeight);
21-
}
22-
heights.add(item);
23-
addSeparator = true;
24-
}
25-
} else {
26-
heights.addAll(itemHeights);
27-
}
28-
29-
return heights;
30-
}
31-
323
void _uniqueValueAssert<T>(
334
List<DropdownItem<T>>? items,
345
ValueListenable<T?>? valueListenable,

0 commit comments

Comments
 (0)