[DropdownMenu] add helperText & errorText to DropdownMenu Widget (#123775)
[DropdownMenu] add helperText & errorText to DropdownMenu Widget
This commit is contained in:
@@ -127,6 +127,8 @@ class DropdownMenu<T> extends StatefulWidget {
|
||||
this.trailingIcon,
|
||||
this.label,
|
||||
this.hintText,
|
||||
this.helperText,
|
||||
this.errorText,
|
||||
this.selectedTrailingIcon,
|
||||
this.enableFilter = false,
|
||||
this.enableSearch = true,
|
||||
@@ -183,6 +185,31 @@ class DropdownMenu<T> extends StatefulWidget {
|
||||
/// Defaults to null;
|
||||
final String? hintText;
|
||||
|
||||
/// Text that provides context about the [DropdownMenu]'s value, such
|
||||
/// as how the value will be used.
|
||||
///
|
||||
/// If non-null, the text is displayed below the input field, in
|
||||
/// the same location as [errorText]. If a non-null [errorText] value is
|
||||
/// specified then the helper text is not shown.
|
||||
///
|
||||
/// Defaults to null;
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [InputDecoration.helperText], which is the text that provides context about the [InputDecorator.child]'s value.
|
||||
final String? helperText;
|
||||
|
||||
/// Text that appears below the input field and the border to show the error message.
|
||||
///
|
||||
/// If non-null, the border's color animates to red and the [helperText] is not shown.
|
||||
///
|
||||
/// Defaults to null;
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [InputDecoration.errorText], which is the text that appears below the [InputDecorator.child] and the border.
|
||||
final String? errorText;
|
||||
|
||||
/// An optional icon at the end of the text field to indicate that the text
|
||||
/// field is pressed.
|
||||
///
|
||||
@@ -579,6 +606,8 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||
enabled: widget.enabled,
|
||||
label: widget.label,
|
||||
hintText: widget.hintText,
|
||||
helperText: widget.helperText,
|
||||
errorText: widget.errorText,
|
||||
prefixIcon: widget.leadingIcon != null ? Container(
|
||||
key: _leadingKey,
|
||||
child: widget.leadingIcon
|
||||
|
||||
@@ -1110,6 +1110,123 @@ void main() {
|
||||
expect(textInput1.width, 200);
|
||||
expect(menu1.width, 200);
|
||||
});
|
||||
|
||||
testWidgets('Semantics does not include hint when input is not empty', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
const String hintText = 'I am hintText';
|
||||
TestMenu? selectedValue;
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) => MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
requestFocusOnTap: true,
|
||||
dropdownMenuEntries: menuChildren,
|
||||
hintText: hintText,
|
||||
onSelected: (TestMenu? value) {
|
||||
setState(() {
|
||||
selectedValue = value;
|
||||
});
|
||||
},
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final SemanticsNode node = tester.getSemantics(find.text(hintText));
|
||||
|
||||
expect(selectedValue?.label, null);
|
||||
expect(node.label, hintText);
|
||||
expect(node.value, '');
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(MenuItemButton, 'Item 3').last);
|
||||
await tester.pumpAndSettle();
|
||||
expect(selectedValue?.label, 'Item 3');
|
||||
expect(node.label, '');
|
||||
expect(node.value, 'Item 3');
|
||||
});
|
||||
|
||||
testWidgets('helperText is not visible when errorText is not null', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
const String helperText = 'I am helperText';
|
||||
const String errorText = 'I am errorText';
|
||||
|
||||
Widget buildFrame(bool hasError) {
|
||||
return MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
helperText: helperText,
|
||||
errorText: hasError ? errorText : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildFrame(false));
|
||||
expect(find.text(helperText), findsOneWidget);
|
||||
expect(find.text(errorText), findsNothing);
|
||||
|
||||
await tester.pumpWidget(buildFrame(true));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(helperText), findsNothing);
|
||||
expect(find.text(errorText), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('DropdownMenu can respect helperText when helperText is not null', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
const String helperText = 'I am helperText';
|
||||
|
||||
Widget buildFrame() {
|
||||
return MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
helperText: helperText,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildFrame());
|
||||
expect(find.text(helperText), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('DropdownMenu can respect errorText when errorText is not null', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
const String errorText = 'I am errorText';
|
||||
|
||||
Widget buildFrame() {
|
||||
return MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
errorText: errorText,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildFrame());
|
||||
expect(find.text(errorText), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
enum TestMenu {
|
||||
|
||||
Reference in New Issue
Block a user