Unverified Commit b878b11f authored by xubaolin's avatar xubaolin Committed by GitHub

Expose the alignment property for DropdownButton and item (#81282)

parent 532a79c8
...@@ -668,6 +668,7 @@ class _DropdownMenuItemContainer extends StatelessWidget { ...@@ -668,6 +668,7 @@ class _DropdownMenuItemContainer extends StatelessWidget {
/// The [child] argument is required. /// The [child] argument is required.
const _DropdownMenuItemContainer({ const _DropdownMenuItemContainer({
Key? key, Key? key,
this.alignment = AlignmentDirectional.centerStart,
required this.child, required this.child,
}) : assert(child != null), }) : assert(child != null),
super(key: key); super(key: key);
...@@ -677,11 +678,23 @@ class _DropdownMenuItemContainer extends StatelessWidget { ...@@ -677,11 +678,23 @@ class _DropdownMenuItemContainer extends StatelessWidget {
/// Typically a [Text] widget. /// Typically a [Text] widget.
final Widget child; final Widget child;
/// Defines how the item is positioned within the container.
///
/// This property must not be null. It defaults to [AlignmentDirectional.centerStart].
///
/// See also:
///
/// * [Alignment], a class with convenient constants typically used to
/// specify an [AlignmentGeometry].
/// * [AlignmentDirectional], like [Alignment] for specifying alignments
/// relative to text direction.
final AlignmentGeometry alignment;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
constraints: const BoxConstraints(minHeight: _kMenuItemHeight), constraints: const BoxConstraints(minHeight: _kMenuItemHeight),
alignment: AlignmentDirectional.centerStart, alignment: alignment,
child: child, child: child,
); );
} }
...@@ -700,9 +713,10 @@ class DropdownMenuItem<T> extends _DropdownMenuItemContainer { ...@@ -700,9 +713,10 @@ class DropdownMenuItem<T> extends _DropdownMenuItemContainer {
this.onTap, this.onTap,
this.value, this.value,
this.enabled = true, this.enabled = true,
AlignmentGeometry alignment = AlignmentDirectional.centerStart,
required Widget child, required Widget child,
}) : assert(child != null), }) : assert(child != null),
super(key: key, child: child); super(key: key, alignment:alignment, child: child);
/// Called when the dropdown menu item is tapped. /// Called when the dropdown menu item is tapped.
final VoidCallback? onTap; final VoidCallback? onTap;
...@@ -866,6 +880,7 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -866,6 +880,7 @@ class DropdownButton<T> extends StatefulWidget {
this.dropdownColor, this.dropdownColor,
this.menuMaxHeight, this.menuMaxHeight,
this.enableFeedback, this.enableFeedback,
this.alignment = AlignmentDirectional.centerStart,
// 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 ||
...@@ -1138,6 +1153,18 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -1138,6 +1153,18 @@ class DropdownButton<T> extends StatefulWidget {
/// * [Feedback] for providing platform-specific feedback to certain actions. /// * [Feedback] for providing platform-specific feedback to certain actions.
final bool? enableFeedback; final bool? enableFeedback;
/// Defines how the hint or the selected item is positioned within the button.
///
/// This property must not be null. It defaults to [AlignmentDirectional.centerStart].
///
/// See also:
///
/// * [Alignment], a class with convenient constants typically used to
/// specify an [AlignmentGeometry].
/// * [AlignmentDirectional], like [Alignment] for specifying alignments
/// relative to text direction.
final AlignmentGeometry alignment;
@override @override
_DropdownButtonState<T> createState() => _DropdownButtonState<T>(); _DropdownButtonState<T> createState() => _DropdownButtonState<T>();
} }
...@@ -1407,7 +1434,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1407,7 +1434,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
} else { } else {
innerItemsWidget = IndexedStack( innerItemsWidget = IndexedStack(
index: _selectedIndex ?? hintIndex, index: _selectedIndex ?? hintIndex,
alignment: AlignmentDirectional.centerStart, alignment: widget.alignment,
children: widget.isDense ? items : items.map((Widget item) { children: widget.isDense ? items : items.map((Widget item) {
return widget.itemHeight != null return widget.itemHeight != null
? SizedBox(height: widget.itemHeight, child: item) ? SizedBox(height: widget.itemHeight, child: item)
...@@ -1537,6 +1564,8 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1537,6 +1564,8 @@ class DropdownButtonFormField<T> extends FormField<T> {
bool autovalidate = false, bool autovalidate = false,
AutovalidateMode? autovalidateMode, AutovalidateMode? autovalidateMode,
double? menuMaxHeight, double? menuMaxHeight,
bool? enableFeedback,
AlignmentGeometry alignment = AlignmentDirectional.centerStart,
}) : assert(items == null || items.isEmpty || value == null || }) : assert(items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) { items.where((DropdownMenuItem<T> item) {
return item.value == value; return item.value == value;
...@@ -1606,6 +1635,8 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1606,6 +1635,8 @@ class DropdownButtonFormField<T> extends FormField<T> {
autofocus: autofocus, autofocus: autofocus,
dropdownColor: dropdownColor, dropdownColor: dropdownColor,
menuMaxHeight: menuMaxHeight, menuMaxHeight: menuMaxHeight,
enableFeedback: enableFeedback,
alignment: alignment,
), ),
), ),
); );
......
...@@ -43,6 +43,7 @@ Widget buildFormFrame({ ...@@ -43,6 +43,7 @@ Widget buildFormFrame({
List<String>? items = menuItems, List<String>? items = menuItems,
Alignment alignment = Alignment.center, Alignment alignment = Alignment.center,
TextDirection textDirection = TextDirection.ltr, TextDirection textDirection = TextDirection.ltr,
AlignmentGeometry buttonAlignment = AlignmentDirectional.centerStart,
}) { }) {
return TestApp( return TestApp(
textDirection: textDirection, textDirection: textDirection,
...@@ -72,6 +73,7 @@ Widget buildFormFrame({ ...@@ -72,6 +73,7 @@ Widget buildFormFrame({
child: Text(item, key: ValueKey<String>(item + 'Text')), child: Text(item, key: ValueKey<String>(item + 'Text')),
); );
}).toList(), }).toList(),
alignment: buttonAlignment,
), ),
), ),
), ),
...@@ -815,4 +817,20 @@ void main() { ...@@ -815,4 +817,20 @@ void main() {
expect(() => builder(), throwsAssertionError); expect(() => builder(), throwsAssertionError);
}); });
testWidgets('DropdownButtonFormField - Custom button alignment', (WidgetTester tester) async {
await tester.pumpWidget(buildFormFrame(
buttonAlignment: AlignmentDirectional.center,
items: <String>['one'],
value: 'one',
));
final RenderBox buttonBox = tester.renderObject<RenderBox>(find.byType(IndexedStack));
final RenderBox selectedItemBox = tester.renderObject(find.text('one'));
// Should be center-center aligned.
expect(
buttonBox.localToGlobal(Offset(buttonBox.size.width / 2.0, buttonBox.size.height / 2.0)),
selectedItemBox.localToGlobal(Offset(selectedItemBox.size.width / 2.0, selectedItemBox.size.height / 2.0)),
);
});
} }
...@@ -3253,6 +3253,77 @@ void main() { ...@@ -3253,6 +3253,77 @@ void main() {
expect(Focus.maybeOf(disabledItem), null, reason: 'Disabled menu item should not be able to request focus'); expect(Focus.maybeOf(disabledItem), null, reason: 'Disabled menu item should not be able to request focus');
}); });
testWidgets('alignment test', (WidgetTester tester) async {
final Key buttonKey = UniqueKey();
Widget buildFrame({AlignmentGeometry? buttonAlignment, AlignmentGeometry? menuAlignment}) {
return MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButton<String>(
key: buttonKey,
alignment: buttonAlignment ?? AlignmentDirectional.centerStart,
value: 'enabled',
onChanged: onChanged,
items: <DropdownMenuItem<String>>[
DropdownMenuItem<String>(
alignment: buttonAlignment ?? AlignmentDirectional.centerStart,
enabled: false,
child: const Text('disabled'),
),
DropdownMenuItem<String>(
alignment: buttonAlignment ?? AlignmentDirectional.centerStart,
value: 'enabled',
child: const Text('enabled'),
)
],
),
),
),
);
}
await tester.pumpWidget(buildFrame());
final RenderBox buttonBox = tester.renderObject(find.byKey(buttonKey));
RenderBox selectedItemBox = tester.renderObject(find.text('enabled'));
// Default to center-start aligned.
expect(
buttonBox.localToGlobal(Offset(0.0, buttonBox.size.height / 2.0)),
selectedItemBox.localToGlobal(Offset(0.0, selectedItemBox.size.height / 2.0)),
);
await tester.pumpWidget(buildFrame(
buttonAlignment: AlignmentDirectional.center,
menuAlignment: AlignmentDirectional.center,
));
selectedItemBox = tester.renderObject(find.text('enabled'));
// Should be center-center aligned, the icon size is 24.0 pixels.
expect(
buttonBox.localToGlobal(Offset((buttonBox.size.width -24.0) / 2.0, buttonBox.size.height / 2.0)),
selectedItemBox.localToGlobal(Offset(selectedItemBox.size.width / 2.0, selectedItemBox.size.height / 2.0)),
);
// Open dropdown.
await tester.tap(find.text('enabled').hitTestable());
await tester.pumpAndSettle();
final RenderBox selectedItemBoxInMenu = tester.renderObjectList<RenderBox>(find.text('enabled')).toList()[1];
final Finder menu = find.byWidgetPredicate((Widget widget) {
return widget.runtimeType.toString().startsWith('_DropdownMenu<');
});
final Rect menuRect = tester.getRect(menu);
final Offset center = selectedItemBoxInMenu.localToGlobal(
Offset(selectedItemBoxInMenu.size.width / 2.0, selectedItemBoxInMenu.size.height / 2.0)
);
expect(center.dx, menuRect.topCenter.dx,);
expect(
center.dy,
selectedItemBox.localToGlobal(Offset(selectedItemBox.size.width / 2.0, selectedItemBox.size.height / 2.0)).dy,
);
});
group('feedback', () { group('feedback', () {
late FeedbackTester feedback; late FeedbackTester feedback;
......
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