mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Implement DropdownButton.selectedItemBuilder (#40461)
* Implement DropdownButton.selectedItemBuilder
This commit is contained in:
parent
64300123fa
commit
cf7e7e4529
|
@ -28,6 +28,8 @@ const EdgeInsets _kUnalignedButtonPadding = EdgeInsets.zero;
|
||||||
const EdgeInsets _kAlignedMenuMargin = EdgeInsets.zero;
|
const EdgeInsets _kAlignedMenuMargin = EdgeInsets.zero;
|
||||||
const EdgeInsetsGeometry _kUnalignedMenuMargin = EdgeInsetsDirectional.only(start: 16.0, end: 24.0);
|
const EdgeInsetsGeometry _kUnalignedMenuMargin = EdgeInsetsDirectional.only(start: 16.0, end: 24.0);
|
||||||
|
|
||||||
|
typedef DropdownButtonBuilder = List<Widget> Function(BuildContext context);
|
||||||
|
|
||||||
class _DropdownMenuPainter extends CustomPainter {
|
class _DropdownMenuPainter extends CustomPainter {
|
||||||
_DropdownMenuPainter({
|
_DropdownMenuPainter({
|
||||||
this.color,
|
this.color,
|
||||||
|
@ -604,6 +606,7 @@ class DropdownButton<T> extends StatefulWidget {
|
||||||
DropdownButton({
|
DropdownButton({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.items,
|
@required this.items,
|
||||||
|
this.selectedItemBuilder,
|
||||||
this.value,
|
this.value,
|
||||||
this.hint,
|
this.hint,
|
||||||
this.disabledHint,
|
this.disabledHint,
|
||||||
|
@ -655,6 +658,50 @@ class DropdownButton<T> extends StatefulWidget {
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
final ValueChanged<T> onChanged;
|
final ValueChanged<T> onChanged;
|
||||||
|
|
||||||
|
/// A builder to customize the dropdown buttons corresponding to the
|
||||||
|
/// [DropdownMenuItem]s in [items].
|
||||||
|
///
|
||||||
|
/// When a [DropdownMenuItem] is selected, the widget that will be displayed
|
||||||
|
/// from the list corresponds to the [DropdownMenuItem] of the same index
|
||||||
|
/// in [items].
|
||||||
|
///
|
||||||
|
/// {@tool snippet --template=stateful_widget_scaffold}
|
||||||
|
///
|
||||||
|
/// This sample shows a `DropdownButton` with a button with [Text] that
|
||||||
|
/// corresponds to but is unique from [DropdownMenuItem].
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// final List<String> items = <String>['1','2','3'];
|
||||||
|
/// String selectedItem = '1';
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Widget build(BuildContext context) {
|
||||||
|
/// return Padding(
|
||||||
|
/// padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
/// child: DropdownButton<String>(
|
||||||
|
/// value: selectedItem,
|
||||||
|
/// onChanged: (String string) => setState(() => selectedItem = string),
|
||||||
|
/// selectedItemBuilder: (BuildContext context) {
|
||||||
|
/// return items.map((String item) {
|
||||||
|
/// return Text(item);
|
||||||
|
/// }).toList();
|
||||||
|
/// },
|
||||||
|
/// items: items.map((String item) {
|
||||||
|
/// return DropdownMenuItem<String>(
|
||||||
|
/// child: Text('Log $item'),
|
||||||
|
/// value: item,
|
||||||
|
/// );
|
||||||
|
/// }).toList(),
|
||||||
|
/// ),
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
///
|
||||||
|
/// If this callback is null, the [DropdownMenuItem] from [items]
|
||||||
|
/// that matches [value] will be displayed.
|
||||||
|
final DropdownButtonBuilder selectedItemBuilder;
|
||||||
|
|
||||||
/// The z-coordinate at which to place the menu when open.
|
/// The z-coordinate at which to place the menu when open.
|
||||||
///
|
///
|
||||||
/// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12,
|
/// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12,
|
||||||
|
@ -849,7 +896,21 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
|
||||||
|
|
||||||
// The width of the button and the menu are defined by the widest
|
// The width of the button and the menu are defined by the widest
|
||||||
// item and the width of the hint.
|
// item and the width of the hint.
|
||||||
final List<Widget> items = _enabled ? List<Widget>.from(widget.items) : <Widget>[];
|
List<Widget> items;
|
||||||
|
if (_enabled) {
|
||||||
|
items = widget.selectedItemBuilder == null
|
||||||
|
? List<Widget>.from(widget.items)
|
||||||
|
: widget.selectedItemBuilder(context).map((Widget item) {
|
||||||
|
return Container(
|
||||||
|
height: _kMenuItemHeight,
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
child: item,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
} else {
|
||||||
|
items = <Widget>[];
|
||||||
|
}
|
||||||
|
|
||||||
int hintIndex;
|
int hintIndex;
|
||||||
if (widget.hint != null || (!_enabled && widget.disabledHint != null)) {
|
if (widget.hint != null || (!_enabled && widget.disabledHint != null)) {
|
||||||
final Widget emplacedHint = _enabled
|
final Widget emplacedHint = _enabled
|
||||||
|
|
|
@ -1317,7 +1317,51 @@ void main() {
|
||||||
expect(tester.widget<DecoratedBox>(decoratedBox).decoration, defaultDecoration);
|
expect(tester.widget<DecoratedBox>(decoratedBox).decoration, defaultDecoration);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Dropdown form field with autovalidation test', (WidgetTester tester) async {
|
testWidgets('DropdownButton selectedItemBuilder builds custom buttons', (WidgetTester tester) async {
|
||||||
|
const List<String> items = <String>[
|
||||||
|
'One',
|
||||||
|
'Two',
|
||||||
|
'Three',
|
||||||
|
];
|
||||||
|
String selectedItem = items[0];
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: DropdownButton<String>(
|
||||||
|
value: selectedItem,
|
||||||
|
onChanged: (String string) => setState(() => selectedItem = string),
|
||||||
|
selectedItemBuilder: (BuildContext context) {
|
||||||
|
int index = 0;
|
||||||
|
return items.map((String string) {
|
||||||
|
index += 1;
|
||||||
|
return Text('$string as an Arabic numeral: $index');
|
||||||
|
}).toList();
|
||||||
|
},
|
||||||
|
items: items.map((String string) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
child: Text(string),
|
||||||
|
value: string,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text('One as an Arabic numeral: 1'), findsOneWidget);
|
||||||
|
await tester.tap(find.text('One as an Arabic numeral: 1'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Two'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Two as an Arabic numeral: 2'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Dropdown form field with autovalidation test', (WidgetTester tester) async {
|
||||||
String value = 'one';
|
String value = 'one';
|
||||||
int _validateCalled = 0;
|
int _validateCalled = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue