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 {
/// The [child] argument is required.
const _DropdownMenuItemContainer({
Key? key,
this.alignment = AlignmentDirectional.centerStart,
required this.child,
}) : assert(child != null),
super(key: key);
......@@ -677,11 +678,23 @@ class _DropdownMenuItemContainer extends StatelessWidget {
/// Typically a [Text] widget.
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
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(minHeight: _kMenuItemHeight),
alignment: AlignmentDirectional.centerStart,
alignment: alignment,
child: child,
);
}
......@@ -700,9 +713,10 @@ class DropdownMenuItem<T> extends _DropdownMenuItemContainer {
this.onTap,
this.value,
this.enabled = true,
AlignmentGeometry alignment = AlignmentDirectional.centerStart,
required Widget child,
}) : assert(child != null),
super(key: key, child: child);
super(key: key, alignment:alignment, child: child);
/// Called when the dropdown menu item is tapped.
final VoidCallback? onTap;
......@@ -866,6 +880,7 @@ class DropdownButton<T> extends StatefulWidget {
this.dropdownColor,
this.menuMaxHeight,
this.enableFeedback,
this.alignment = AlignmentDirectional.centerStart,
// When adding new arguments, consider adding similar arguments to
// DropdownButtonFormField.
}) : assert(items == null || items.isEmpty || value == null ||
......@@ -1138,6 +1153,18 @@ class DropdownButton<T> extends StatefulWidget {
/// * [Feedback] for providing platform-specific feedback to certain actions.
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
_DropdownButtonState<T> createState() => _DropdownButtonState<T>();
}
......@@ -1407,7 +1434,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
} else {
innerItemsWidget = IndexedStack(
index: _selectedIndex ?? hintIndex,
alignment: AlignmentDirectional.centerStart,
alignment: widget.alignment,
children: widget.isDense ? items : items.map((Widget item) {
return widget.itemHeight != null
? SizedBox(height: widget.itemHeight, child: item)
......@@ -1537,6 +1564,8 @@ class DropdownButtonFormField<T> extends FormField<T> {
bool autovalidate = false,
AutovalidateMode? autovalidateMode,
double? menuMaxHeight,
bool? enableFeedback,
AlignmentGeometry alignment = AlignmentDirectional.centerStart,
}) : assert(items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
......@@ -1606,6 +1635,8 @@ class DropdownButtonFormField<T> extends FormField<T> {
autofocus: autofocus,
dropdownColor: dropdownColor,
menuMaxHeight: menuMaxHeight,
enableFeedback: enableFeedback,
alignment: alignment,
),
),
);
......
......@@ -43,6 +43,7 @@ Widget buildFormFrame({
List<String>? items = menuItems,
Alignment alignment = Alignment.center,
TextDirection textDirection = TextDirection.ltr,
AlignmentGeometry buttonAlignment = AlignmentDirectional.centerStart,
}) {
return TestApp(
textDirection: textDirection,
......@@ -72,6 +73,7 @@ Widget buildFormFrame({
child: Text(item, key: ValueKey<String>(item + 'Text')),
);
}).toList(),
alignment: buttonAlignment,
),
),
),
......@@ -815,4 +817,20 @@ void main() {
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() {
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', () {
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