Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ analyzer:
- "**/*.g.dart"
- "**/*.mocks.dart" # Mockito @GenerateMocks

formatter:
page_width: 100

linter:
rules:
# This list is derived from the list of all available lints located at
Expand Down
143 changes: 50 additions & 93 deletions packages/dropdown_button2/lib/src/dropdown_button2.dart

Large diffs are not rendered by default.

63 changes: 21 additions & 42 deletions packages/dropdown_button2/lib/src/dropdown_menu.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
part of 'dropdown_button2.dart';

SearchMatchFn<T> _defaultSearchMatchFn<T>() =>
(DropdownItem<T> item, String searchValue) =>
item.value.toString().toLowerCase().contains(searchValue.toLowerCase());
SearchMatchFn<T> _defaultSearchMatchFn<T>() => (DropdownItem<T> item, String searchValue) =>
item.value.toString().toLowerCase().contains(searchValue.toLowerCase());

class _MenuLimits {
const _MenuLimits(this.top, this.bottom, this.height, this.scrollOffset);
Expand Down Expand Up @@ -49,8 +48,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {

DropdownSearchData<T>? get searchData => widget.route.searchData;

_DropdownItemButton<T> dropdownItemButton(int index) =>
_DropdownItemButton<T>(
_DropdownItemButton<T> dropdownItemButton(int index) => _DropdownItemButton<T>(
route: widget.route,
scrollController: widget.scrollController,
textDirection: widget.textDirection,
Expand Down Expand Up @@ -83,8 +81,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
final searchController = searchData?.searchController;
if (searchController == null) {
_children = <Widget>[
for (int index = 0; index < items.length; ++index)
dropdownItemButton(index),
for (int index = 0; index < items.length; ++index) dropdownItemButton(index),
];
} else {
_searchMatchFn = searchData?.searchMatchFn ?? _defaultSearchMatchFn();
Expand All @@ -103,8 +100,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
final String currentSearch = searchData!.searchController!.text;
return <Widget>[
for (int index = 0; index < items.length; ++index)
if (_searchMatchFn(items[index], currentSearch))
dropdownItemButton(index),
if (_searchMatchFn(items[index], currentSearch)) dropdownItemButton(index),
];
}

Expand All @@ -125,13 +121,11 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {

ScrollbarThemeData? get _scrollbarTheme => dropdownStyle.scrollbarTheme;

bool get _iOSThumbVisibility =>
_scrollbarTheme?.thumbVisibility?.resolve(_states) ?? true;
bool get _iOSThumbVisibility => _scrollbarTheme?.thumbVisibility?.resolve(_states) ?? true;

bool get _hasIntrinsicHeight =>
widget.route.items.any((item) => item.intrinsicHeight) ||
(widget.route.dropdownSeparator != null &&
widget.route.dropdownSeparator!.intrinsicHeight);
(widget.route.dropdownSeparator != null && widget.route.dropdownSeparator!.intrinsicHeight);

@override
Widget build(BuildContext context) {
Expand All @@ -144,8 +138,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
// When the menu is dismissed we just fade the entire thing out
// in the first 0.25s.
assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final _DropdownRoute<T> route = widget.route;

final separator = widget.route.dropdownSeparator;
Expand Down Expand Up @@ -189,24 +182,20 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
thumbVisibility:
// ignore: avoid_bool_literals_in_conditional_expressions
_isIOS ? _iOSThumbVisibility : true,
thickness: _isIOS
? _scrollbarTheme?.thickness?.resolve(_states)
: null,
thickness: _isIOS ? _scrollbarTheme?.thickness?.resolve(_states) : null,
radius: _isIOS ? _scrollbarTheme?.radius : null,
child: ListView.custom(
// Ensure this always inherits the PrimaryScrollController
primary: true,
shrinkWrap: true,
padding:
dropdownStyle.padding ?? kMaterialListPadding,
padding: dropdownStyle.padding ?? kMaterialListPadding,
itemExtentBuilder: _hasIntrinsicHeight
? null
: (index, dimensions) {
final childrenLength = separator == null
? _children.length
: SeparatedSliverChildBuilderDelegate
.computeActualChildCount(
_children.length);
.computeActualChildCount(_children.length);
// TODO(Ahmed): Remove this when https://github.com/flutter/flutter/pull/142428
// is supported by the min version of the package [Flutter>=3.22.0].
if (index >= childrenLength) {
Expand All @@ -225,13 +214,9 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
)
: SeparatedSliverChildBuilderDelegate(
itemCount: _children.length,
itemBuilder: (context, index) =>
_children[index],
separatorBuilder: (context, index) =>
SizedBox(
height: separator.intrinsicHeight
? null
: separator.height,
itemBuilder: (context, index) => _children[index],
separatorBuilder: (context, index) => SizedBox(
height: separator.intrinsicHeight ? null : separator.height,
child: separator,
),
),
Expand Down Expand Up @@ -265,13 +250,10 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
label: localizations.popupMenuLabel,
child: ClipRRect(
//Prevent scrollbar, ripple effect & items from going beyond border boundaries when scrolling.
clipBehavior: dropdownStyle.decoration?.borderRadius != null
? Clip.antiAlias
: Clip.none,
borderRadius:
dropdownStyle.decoration?.borderRadius ?? BorderRadius.zero,
child: dropdownStyle.dropdownBuilder?.call(context, dropdownMenu) ??
dropdownMenu,
clipBehavior:
dropdownStyle.decoration?.borderRadius != null ? Clip.antiAlias : Clip.none,
borderRadius: dropdownStyle.decoration?.borderRadius ?? BorderRadius.zero,
child: dropdownStyle.dropdownBuilder?.call(context, dropdownMenu) ?? dropdownMenu,
),
),
),
Expand All @@ -290,8 +272,7 @@ class _DropdownMenuPainter extends CustomPainter {
}) : _painter = dropdownDecoration
?.copyWith(
color: dropdownDecoration.color ?? color,
boxShadow: dropdownDecoration.boxShadow ??
kElevationToShadow[elevation],
boxShadow: dropdownDecoration.boxShadow ?? kElevationToShadow[elevation],
)
.createBoxPainter(() {}) ??
BoxDecoration(
Expand Down Expand Up @@ -323,13 +304,11 @@ class _DropdownMenuPainter extends CustomPainter {
);

final Tween<double> bottom = Tween<double>(
begin: clampDouble(top.begin! + itemHeight,
math.min(itemHeight, size.height), size.height),
begin: clampDouble(top.begin! + itemHeight, math.min(itemHeight, size.height), size.height),
end: size.height,
);

final Rect rect = Rect.fromLTRB(
0.0, top.evaluate(resize), size.width, bottom.evaluate(resize));
final Rect rect = Rect.fromLTRB(0.0, top.evaluate(resize), size.width, bottom.evaluate(resize));

_painter.paint(canvas, rect.topLeft, ImageConfiguration(size: rect.size));
}
Expand Down
31 changes: 11 additions & 20 deletions packages/dropdown_button2/lib/src/dropdown_menu_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@ class _DropdownItemButtonState<T> extends State<_DropdownItemButton<T>> {
void _setOpacityAnimation() {
final double menuCurveEnd = widget.route.dropdownStyle.openInterval.end;
final double unit = 0.5 / (widget.route.items.length + 1.5);
final double start =
clampDouble(menuCurveEnd + (widget.itemIndex + 1) * unit, 0.0, 1.0);
final double start = clampDouble(menuCurveEnd + (widget.itemIndex + 1) * unit, 0.0, 1.0);
final double end = clampDouble(start + 1.5 * unit, 0.0, 1.0);
_opacityAnimation = CurvedAnimation(
parent: widget.route.animation!,
Expand All @@ -190,8 +189,7 @@ class _DropdownItemButtonState<T> extends State<_DropdownItemButton<T>> {
}

void _handleFocusChange(bool focused) {
final bool inTraditionalMode =
switch (FocusManager.instance.highlightMode) {
final bool inTraditionalMode = switch (FocusManager.instance.highlightMode) {
FocusHighlightMode.touch => false,
FocusHighlightMode.traditional => true,
};
Expand Down Expand Up @@ -225,29 +223,23 @@ class _DropdownItemButtonState<T> extends State<_DropdownItemButton<T>> {
}
}

static const Map<ShortcutActivator, Intent> _webShortcuts =
<ShortcutActivator, Intent>{
static const Map<ShortcutActivator, Intent> _webShortcuts = <ShortcutActivator, Intent>{
// On the web, up/down don't change focus, *except* in a <select>
// element, which is what a dropdown emulates.
SingleActivator(LogicalKeyboardKey.arrowDown):
DirectionalFocusIntent(TraversalDirection.down),
SingleActivator(LogicalKeyboardKey.arrowUp):
DirectionalFocusIntent(TraversalDirection.up),
SingleActivator(LogicalKeyboardKey.arrowDown): DirectionalFocusIntent(TraversalDirection.down),
SingleActivator(LogicalKeyboardKey.arrowUp): DirectionalFocusIntent(TraversalDirection.up),
};

MenuItemStyleData get _menuItemStyle => widget.route.menuItemStyle;
EdgeInsets? get _inputDecorationPadding =>
widget.route.inputDecorationPadding;
bool get _useDecorationHPadding =>
_menuItemStyle.useDecorationHorizontalPadding;
EdgeInsets? get _inputDecorationPadding => widget.route.inputDecorationPadding;
bool get _useDecorationHPadding => _menuItemStyle.useDecorationHorizontalPadding;

@override
Widget build(BuildContext context) {
final DropdownItem<T> dropdownItem = widget.route.items[widget.itemIndex];

final menuItemPadding =
_menuItemStyle.padding?.resolve(widget.textDirection) ??
_kMenuItemPadding;
_menuItemStyle.padding?.resolve(widget.textDirection) ?? _kMenuItemPadding;

Widget child = Padding(
padding: menuItemPadding.copyWith(
Expand All @@ -259,8 +251,8 @@ class _DropdownItemButtonState<T> extends State<_DropdownItemButton<T>> {
// An [InkWell] is added to the item only if it is enabled
// isNoSelectedItem to avoid first item highlight when no item selected
if (dropdownItem.enabled) {
final bool isSelectedItem = !widget.route.isNoSelectedItem &&
widget.itemIndex == widget.route.selectedIndex;
final bool isSelectedItem =
!widget.route.isNoSelectedItem && widget.itemIndex == widget.route.selectedIndex;
child = InkWell(
autofocus: isSelectedItem,
enableFeedback: widget.enableFeedback,
Expand All @@ -269,8 +261,7 @@ class _DropdownItemButtonState<T> extends State<_DropdownItemButton<T>> {
borderRadius: _menuItemStyle.borderRadius,
overlayColor: _menuItemStyle.overlayColor,
child: isSelectedItem
? _menuItemStyle.selectedMenuItemBuilder?.call(context, child) ??
child
? _menuItemStyle.selectedMenuItemBuilder?.call(context, child) ?? child
: child,
);
}
Expand Down
32 changes: 12 additions & 20 deletions packages/dropdown_button2/lib/src/dropdown_route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
//This will ensure menu is drawn in the actual available height.
final padding = MediaQuery.paddingOf(context);
final viewInsets = MediaQuery.viewInsetsOf(context);
final BoxConstraints actualConstraints = constraints.copyWith(
maxHeight: constraints.maxHeight - viewInsets.bottom);
final BoxConstraints actualConstraints =
constraints.copyWith(maxHeight: constraints.maxHeight - viewInsets.bottom);
final EdgeInsets mediaQueryPadding =
dropdownStyle.useSafeArea ? padding : EdgeInsets.zero;
return ValueListenableBuilder<Rect?>(
Expand Down Expand Up @@ -136,8 +136,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {

if (items.isNotEmpty && index > 0) {
if (searchData?.searchController?.text case final searchText?) {
final searchMatchFn =
searchData?.searchMatchFn ?? _defaultSearchMatchFn();
final searchMatchFn = searchData?.searchMatchFn ?? _defaultSearchMatchFn();
final selectedItemExist = searchMatchFn(items[index], searchText);
if (selectedItemExist) {
offset += _getSearchItemsHeight(index, searchText);
Expand Down Expand Up @@ -166,16 +165,14 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
EdgeInsets mediaQueryPadding,
int index,
) {
double maxHeight =
getMenuAvailableHeight(availableHeight, mediaQueryPadding);
double maxHeight = getMenuAvailableHeight(availableHeight, mediaQueryPadding);
// If a preferred MaxHeight is set by the user, use it instead of the available maxHeight.
final double? preferredMaxHeight = dropdownStyle.maxHeight;
if (preferredMaxHeight != null) {
maxHeight = math.min(maxHeight, preferredMaxHeight);
}

double actualMenuHeight =
dropdownStyle.padding?.vertical ?? kMaterialListPadding.vertical;
double actualMenuHeight = dropdownStyle.padding?.vertical ?? kMaterialListPadding.vertical;
final double innerWidgetHeight = searchData?.searchBarWidgetHeight ?? 0.0;
actualMenuHeight += innerWidgetHeight;
if (items.isNotEmpty) {
Expand Down Expand Up @@ -296,8 +293,7 @@ class _DropdownRoutePageState<T> extends State<_DropdownRoutePage<T>> {
widget.mediaQueryPadding,
widget.selectedIndex,
);
_scrollController =
ScrollController(initialScrollOffset: menuLimits.scrollOffset);
_scrollController = ScrollController(initialScrollOffset: menuLimits.scrollOffset);
}

@override
Expand Down Expand Up @@ -362,17 +358,15 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {

@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
double maxHeight =
route.getMenuAvailableHeight(availableHeight, mediaQueryPadding);
double maxHeight = route.getMenuAvailableHeight(availableHeight, mediaQueryPadding);
final double? preferredMaxHeight = route.dropdownStyle.maxHeight;
if (preferredMaxHeight != null && preferredMaxHeight <= maxHeight) {
maxHeight = preferredMaxHeight;
}
// The width of a menu should be at most the view width. This ensures that
// the menu does not extend past the left and right edges of the screen.
final double? menuWidth = route.dropdownStyle.width;
final double width =
math.min(constraints.maxWidth, menuWidth ?? buttonRect.width);
final double width = math.min(constraints.maxWidth, menuWidth ?? buttonRect.width);
return BoxConstraints(
minWidth: width,
maxWidth: width,
Expand Down Expand Up @@ -438,8 +432,7 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {

@override
bool shouldRelayout(_DropdownMenuRouteLayout<T> oldDelegate) {
return buttonRect != oldDelegate.buttonRect ||
textDirection != oldDelegate.textDirection;
return buttonRect != oldDelegate.buttonRect || textDirection != oldDelegate.textDirection;
}
}

Expand Down Expand Up @@ -534,8 +527,8 @@ class _DropdownBarrierPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
if (barrierColor != null && buttonRect != null) {
final Rect rect = Rect.fromLTRB(
-buttonRect!.left, -buttonRect!.top, pageSize.width, pageSize.height);
final Rect rect =
Rect.fromLTRB(-buttonRect!.left, -buttonRect!.top, pageSize.width, pageSize.height);
canvas.saveLayer(rect, Paint());
canvas.drawRect(rect, Paint()..color = barrierColor!);
canvas.drawRect(buttonRect!, Paint()..blendMode = BlendMode.clear);
Expand All @@ -545,7 +538,6 @@ class _DropdownBarrierPainter extends CustomPainter {

@override
bool shouldRepaint(_DropdownBarrierPainter oldPainter) {
return oldPainter.buttonRect != buttonRect ||
oldPainter.barrierColor != barrierColor;
return oldPainter.buttonRect != buttonRect || oldPainter.barrierColor != barrierColor;
}
}
6 changes: 2 additions & 4 deletions packages/dropdown_button2/lib/src/dropdown_style_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ class MenuItemStyleData {
padding: padding ?? this.padding,
borderRadius: borderRadius ?? this.borderRadius,
overlayColor: overlayColor ?? this.overlayColor,
selectedMenuItemBuilder:
selectedMenuItemBuilder ?? this.selectedMenuItemBuilder,
selectedMenuItemBuilder: selectedMenuItemBuilder ?? this.selectedMenuItemBuilder,
);
}
}
Expand Down Expand Up @@ -285,8 +284,7 @@ class DropdownSearchData<T> {
return DropdownSearchData<T>(
searchController: searchController ?? this.searchController,
searchBarWidget: searchBarWidget ?? this.searchBarWidget,
searchBarWidgetHeight:
searchBarWidgetHeight ?? this.searchBarWidgetHeight,
searchBarWidgetHeight: searchBarWidgetHeight ?? this.searchBarWidgetHeight,
noResultsWidget: noResultsWidget ?? this.noResultsWidget,
searchMatchFn: searchMatchFn ?? this.searchMatchFn,
);
Expand Down
Loading
Loading