Unverified Commit 4d1da9af authored by Chinmoy's avatar Chinmoy Committed by GitHub

Adds borderRadius property to DropdownButton (#84298)

parent 66064815
...@@ -43,6 +43,7 @@ class _DropdownMenuPainter extends CustomPainter { ...@@ -43,6 +43,7 @@ class _DropdownMenuPainter extends CustomPainter {
this.color, this.color,
this.elevation, this.elevation,
this.selectedIndex, this.selectedIndex,
this.borderRadius,
required this.resize, required this.resize,
required this.getSelectedItemOffset, required this.getSelectedItemOffset,
}) : _painter = BoxDecoration( }) : _painter = BoxDecoration(
...@@ -50,7 +51,7 @@ class _DropdownMenuPainter extends CustomPainter { ...@@ -50,7 +51,7 @@ class _DropdownMenuPainter extends CustomPainter {
// configuration in the paint() function and you must provide some sort // configuration in the paint() function and you must provide some sort
// of onChanged callback here. // of onChanged callback here.
color: color, color: color,
borderRadius: BorderRadius.circular(2.0), borderRadius: borderRadius ?? const BorderRadius.all(Radius.circular(2.0)),
boxShadow: kElevationToShadow[elevation], boxShadow: kElevationToShadow[elevation],
).createBoxPainter(), ).createBoxPainter(),
super(repaint: resize); super(repaint: resize);
...@@ -58,6 +59,7 @@ class _DropdownMenuPainter extends CustomPainter { ...@@ -58,6 +59,7 @@ class _DropdownMenuPainter extends CustomPainter {
final Color? color; final Color? color;
final int? elevation; final int? elevation;
final int? selectedIndex; final int? selectedIndex;
final BorderRadius? borderRadius;
final Animation<double> resize; final Animation<double> resize;
final ValueGetter<double> getSelectedItemOffset; final ValueGetter<double> getSelectedItemOffset;
final BoxPainter _painter; final BoxPainter _painter;
...@@ -85,6 +87,7 @@ class _DropdownMenuPainter extends CustomPainter { ...@@ -85,6 +87,7 @@ class _DropdownMenuPainter extends CustomPainter {
return oldPainter.color != color return oldPainter.color != color
|| oldPainter.elevation != elevation || oldPainter.elevation != elevation
|| oldPainter.selectedIndex != selectedIndex || oldPainter.selectedIndex != selectedIndex
|| oldPainter.borderRadius != borderRadius
|| oldPainter.resize != resize; || oldPainter.resize != resize;
} }
} }
...@@ -94,6 +97,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget { ...@@ -94,6 +97,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget {
const _DropdownMenuItemButton({ const _DropdownMenuItemButton({
Key? key, Key? key,
this.padding, this.padding,
required this.borderRadius,
required this.route, required this.route,
required this.buttonRect, required this.buttonRect,
required this.constraints, required this.constraints,
...@@ -107,6 +111,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget { ...@@ -107,6 +111,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget {
final BoxConstraints constraints; final BoxConstraints constraints;
final int itemIndex; final int itemIndex;
final bool enableFeedback; final bool enableFeedback;
final BorderRadius borderRadius;
@override @override
_DropdownMenuItemButtonState<T> createState() => _DropdownMenuItemButtonState<T>(); _DropdownMenuItemButtonState<T> createState() => _DropdownMenuItemButtonState<T>();
...@@ -172,6 +177,16 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>> ...@@ -172,6 +177,16 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
padding: widget.padding, padding: widget.padding,
child: widget.route.items[widget.itemIndex], child: widget.route.items[widget.itemIndex],
); );
final BorderRadius itemBorderRadius;
if (widget.route.items.length == 1) {
itemBorderRadius = widget.borderRadius;
} else if (widget.itemIndex == 0) {
itemBorderRadius = BorderRadius.only(topLeft: widget.borderRadius.topLeft, topRight: widget.borderRadius.topRight);
} else if (widget.itemIndex == widget.route.items.length - 1) {
itemBorderRadius = BorderRadius.only(bottomLeft: widget.borderRadius.bottomLeft, bottomRight: widget.borderRadius.bottomRight);
} else {
itemBorderRadius = BorderRadius.zero;
}
// An [InkWell] is added to the item only if it is enabled // An [InkWell] is added to the item only if it is enabled
if (dropdownMenuItem.enabled) { if (dropdownMenuItem.enabled) {
child = InkWell( child = InkWell(
...@@ -180,6 +195,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>> ...@@ -180,6 +195,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
onTap: _handleOnTap, onTap: _handleOnTap,
onFocusChange: _handleFocusChange, onFocusChange: _handleFocusChange,
child: child, child: child,
borderRadius: itemBorderRadius,
); );
} }
child = FadeTransition(opacity: opacity, child: child); child = FadeTransition(opacity: opacity, child: child);
...@@ -202,6 +218,7 @@ class _DropdownMenu<T> extends StatefulWidget { ...@@ -202,6 +218,7 @@ class _DropdownMenu<T> extends StatefulWidget {
required this.constraints, required this.constraints,
this.dropdownColor, this.dropdownColor,
required this.enableFeedback, required this.enableFeedback,
this.borderRadius,
}) : super(key: key); }) : super(key: key);
final _DropdownRoute<T> route; final _DropdownRoute<T> route;
...@@ -210,6 +227,7 @@ class _DropdownMenu<T> extends StatefulWidget { ...@@ -210,6 +227,7 @@ class _DropdownMenu<T> extends StatefulWidget {
final BoxConstraints constraints; final BoxConstraints constraints;
final Color? dropdownColor; final Color? dropdownColor;
final bool enableFeedback; final bool enableFeedback;
final BorderRadius? borderRadius;
@override @override
_DropdownMenuState<T> createState() => _DropdownMenuState<T>(); _DropdownMenuState<T> createState() => _DropdownMenuState<T>();
...@@ -260,6 +278,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> { ...@@ -260,6 +278,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
constraints: widget.constraints, constraints: widget.constraints,
itemIndex: itemIndex, itemIndex: itemIndex,
enableFeedback: widget.enableFeedback, enableFeedback: widget.enableFeedback,
borderRadius: widget.borderRadius ?? BorderRadius.zero,
), ),
]; ];
...@@ -271,6 +290,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> { ...@@ -271,6 +290,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
elevation: route.elevation, elevation: route.elevation,
selectedIndex: route.selectedIndex, selectedIndex: route.selectedIndex,
resize: _resize, resize: _resize,
borderRadius: widget.borderRadius,
// This offset is passed as a callback, not a value, because it must // This offset is passed as a callback, not a value, because it must
// be retrieved at paint time (after layout), not at build time. // be retrieved at paint time (after layout), not at build time.
getSelectedItemOffset: () => route.getItemOffset(route.selectedIndex), getSelectedItemOffset: () => route.getItemOffset(route.selectedIndex),
...@@ -420,6 +440,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> { ...@@ -420,6 +440,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
this.dropdownColor, this.dropdownColor,
this.menuMaxHeight, this.menuMaxHeight,
required this.enableFeedback, required this.enableFeedback,
this.borderRadius,
}) : assert(style != null), }) : assert(style != null),
itemHeights = List<double>.filled(items.length, itemHeight ?? kMinInteractiveDimension); itemHeights = List<double>.filled(items.length, itemHeight ?? kMinInteractiveDimension);
...@@ -434,6 +455,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> { ...@@ -434,6 +455,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
final Color? dropdownColor; final Color? dropdownColor;
final double? menuMaxHeight; final double? menuMaxHeight;
final bool enableFeedback; final bool enableFeedback;
final BorderRadius? borderRadius;
final List<double> itemHeights; final List<double> itemHeights;
ScrollController? scrollController; ScrollController? scrollController;
...@@ -466,6 +488,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> { ...@@ -466,6 +488,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
style: style, style: style,
dropdownColor: dropdownColor, dropdownColor: dropdownColor,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
borderRadius: borderRadius,
); );
}, },
); );
...@@ -562,6 +585,7 @@ class _DropdownRoutePage<T> extends StatelessWidget { ...@@ -562,6 +585,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
this.style, this.style,
required this.dropdownColor, required this.dropdownColor,
required this.enableFeedback, required this.enableFeedback,
this.borderRadius,
}) : super(key: key); }) : super(key: key);
final _DropdownRoute<T> route; final _DropdownRoute<T> route;
...@@ -575,6 +599,7 @@ class _DropdownRoutePage<T> extends StatelessWidget { ...@@ -575,6 +599,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
final TextStyle? style; final TextStyle? style;
final Color? dropdownColor; final Color? dropdownColor;
final bool enableFeedback; final bool enableFeedback;
final BorderRadius? borderRadius;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -599,6 +624,7 @@ class _DropdownRoutePage<T> extends StatelessWidget { ...@@ -599,6 +624,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
constraints: constraints, constraints: constraints,
dropdownColor: dropdownColor, dropdownColor: dropdownColor,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
borderRadius: borderRadius,
); );
return MediaQuery.removePadding( return MediaQuery.removePadding(
...@@ -883,6 +909,7 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -883,6 +909,7 @@ class DropdownButton<T> extends StatefulWidget {
this.menuMaxHeight, this.menuMaxHeight,
this.enableFeedback, this.enableFeedback,
this.alignment = AlignmentDirectional.centerStart, this.alignment = AlignmentDirectional.centerStart,
this.borderRadius,
// When adding new arguments, consider adding similar arguments to // When adding new arguments, consider adding similar arguments to
// DropdownButtonFormField. // DropdownButtonFormField.
}) : assert(items == null || items.isEmpty || value == null || }) : assert(items == null || items.isEmpty || value == null ||
...@@ -1167,6 +1194,14 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -1167,6 +1194,14 @@ class DropdownButton<T> extends StatefulWidget {
/// relative to text direction. /// relative to text direction.
final AlignmentGeometry alignment; final AlignmentGeometry alignment;
/// Defines the corner radii of the menu's rounded rectangle shape.
///
/// The radii of the first menu item's top left and right corners are
/// defined by the corresponding properties of the [borderRadius].
/// Similarly, the radii of the last menu item's bottom and right corners
/// are defined by the corresponding properties of the [borderRadius].
final BorderRadius? borderRadius;
@override @override
State<DropdownButton<T>> createState() => _DropdownButtonState<T>(); State<DropdownButton<T>> createState() => _DropdownButtonState<T>();
} }
...@@ -1320,6 +1355,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1320,6 +1355,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
dropdownColor: widget.dropdownColor, dropdownColor: widget.dropdownColor,
menuMaxHeight: widget.menuMaxHeight, menuMaxHeight: widget.menuMaxHeight,
enableFeedback: widget.enableFeedback ?? true, enableFeedback: widget.enableFeedback ?? true,
borderRadius: widget.borderRadius,
); );
navigator.push(_dropdownRoute!).then<void>((_DropdownRouteResult<T>? newValue) { navigator.push(_dropdownRoute!).then<void>((_DropdownRouteResult<T>? newValue) {
......
...@@ -3472,4 +3472,81 @@ void main() { ...@@ -3472,4 +3472,81 @@ void main() {
expect(find.byType(RawScrollbar), findsNothing); expect(find.byType(RawScrollbar), findsNothing);
}, variant: TargetPlatformVariant.all()); }, variant: TargetPlatformVariant.all());
testWidgets('borderRadius property works properly', (WidgetTester tester) async {
const double radius = 20.0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButton<String>(
borderRadius: BorderRadius.circular(radius),
value: 'One',
items: <String>['One', 'Two', 'Three', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) { },
),
),
),
),
);
await tester.tap(find.text('One'));
await tester.pumpAndSettle();
expect(
find.ancestor(
of: find.text('One').last,
matching: find.byType(CustomPaint),
).at(2),
paints
..save()
..rrect()
..rrect()
..rrect()
..rrect(rrect: const RRect.fromLTRBXY(0.0, 0.0, 144.0, 208.0, radius, radius)),
);
final InkWell firstItem = tester.widget(find.widgetWithText(InkWell, 'One'));
final InkWell lastItem = tester.widget(find.widgetWithText(InkWell, 'Four'));
expect(firstItem.borderRadius, const BorderRadius.vertical(top: Radius.circular(radius)));
expect(lastItem.borderRadius, const BorderRadius.vertical(bottom: Radius.circular(radius)));
});
testWidgets('borderRadius is properly applied to InkWell when there is only one item', (WidgetTester tester) async {
const BorderRadius borderRadius = BorderRadius.all(Radius.circular(5.0));
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButton<String>(
borderRadius: borderRadius,
value: 'One',
items: const <DropdownMenuItem<String>>[
DropdownMenuItem<String>(
child: Text('One'), value: 'One'
),
],
onChanged: (_) { },
),
),
),
),
);
await tester.tap(find.text('One'));
await tester.pumpAndSettle();
final InkWell menuItem = tester.widget(find.widgetWithText(InkWell, 'One'));
expect(menuItem.borderRadius, borderRadius);
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment