Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ customize to your needs.
| [onSaved](https://api.flutter.dev/flutter/widgets/FormField/onSaved.html) | Called with the current selected item when the form is saved | FormFieldSetter<T> | No |
| [validator](https://api.flutter.dev/flutter/widgets/FormField/validator.html) | Called to validates if the input is invalid and display error text | FormFieldValidator<T> | No |
| [errorBuilder](https://api.flutter.dev/flutter/widgets/FormField/errorBuilder.html) | Called to display a custom error widget instead of the default error text | Widget Function(BuildContext, String) | No |
| [forceErrorText](https://api.flutter.dev/flutter/widgets/FormField/forceErrorText.html) | Forces the FormFieldState into an error state and displays the given text | String | No |
| [autovalidateMode](https://api.flutter.dev/flutter/widgets/AutovalidateMode.html) | Used to enable/disable auto validation | AutovalidateMode | No |

## Installation
Expand Down
1 change: 1 addition & 0 deletions packages/dropdown_button2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Upgrade minimum required Flutter SDK version to 3.32.0.
- Add `errorBuilder` support for DropdownButtonFormField2 [Flutter core].
- Add `forceErrorText` support for DropdownButtonFormField2 [Flutter core].

## 3.0.0

Expand Down
1 change: 1 addition & 0 deletions packages/dropdown_button2/lib/src/dropdown_button2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ class DropdownButtonFormField2<T> extends FormField<T> {
super.onSaved,
super.validator,
super.errorBuilder,
super.forceErrorText,
AutovalidateMode? autovalidateMode,
bool? enableFeedback,
AlignmentGeometry alignment = AlignmentDirectional.centerStart,
Expand Down
87 changes: 79 additions & 8 deletions packages/dropdown_button2/test/dropdown_button2_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void main() {
return tester.widget<InputDecorator>(findInputDecorator.first);
}

testWidgets('validator error text is displayed', (WidgetTester tester) async {
testWidgets('validator error text should be displayed when validation fails', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
const errorMessage = 'Please select a value';

Expand Down Expand Up @@ -201,7 +201,7 @@ void main() {
expect(find.text(errorMessage), findsOneWidget);
});

testWidgets('validator returning null shows no error', (WidgetTester tester) async {
testWidgets('validator should show no error when returning null', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();

await tester.pumpWidget(
Expand Down Expand Up @@ -230,7 +230,7 @@ void main() {
expect(inputDecorator.decoration.error, isNull);
});

testWidgets('autovalidateMode.always validates on first build', (
testWidgets('autovalidateMode.always should validate on first build', (
WidgetTester tester,
) async {
int validateCalled = 0;
Expand Down Expand Up @@ -258,7 +258,7 @@ void main() {
expect(find.text('Error'), findsOneWidget);
});

testWidgets('decoration errorStyle is applied to error text', (
testWidgets('decoration errorStyle should be applied to error text', (
WidgetTester tester,
) async {
const errorStyle = TextStyle(color: Colors.orange, fontSize: 20);
Expand Down Expand Up @@ -287,7 +287,7 @@ void main() {
expect(find.text('Styled error'), findsOneWidget);
});

testWidgets('decoration errorMaxLines is respected', (WidgetTester tester) async {
testWidgets('decoration errorMaxLines should be respected', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
Expand All @@ -311,7 +311,7 @@ void main() {
expect(inputDecorator.decoration.errorMaxLines, 2);
});

testWidgets('widget returned by errorBuilder is shown', (WidgetTester tester) async {
testWidgets('errorBuilder should replace default error text when provided', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
Expand All @@ -337,7 +337,7 @@ void main() {
expect(find.text('Required'), findsNothing);
});

testWidgets('errorBuilder widget is passed to InputDecorator as error', (
testWidgets('errorBuilder widget should be passed to InputDecorator as error', (
WidgetTester tester,
) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
Expand Down Expand Up @@ -378,7 +378,7 @@ void main() {
expect(find.byIcon(Icons.error), findsOneWidget);
});

testWidgets('errorBuilder is not called when there is no error', (
testWidgets('errorBuilder should not be called when there is no error', (
WidgetTester tester,
) async {
bool errorBuilderCalled = false;
Expand Down Expand Up @@ -407,6 +407,77 @@ void main() {

expect(errorBuilderCalled, isFalse);
});

testWidgets('forceErrorText should force field to display error', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButtonFormField2<int>(
valueListenable: valueListenable,
items: buildItems(),
onChanged: (_) {},
forceErrorText: 'Forced error',
),
),
),
),
);

await tester.pumpAndSettle();

expect(find.text('Forced error'), findsOneWidget);
final inputDecorator = getFormFieldInputDecorator(tester);
expect(inputDecorator.decoration.errorText, 'Forced error');
});

testWidgets('forceErrorText should make isValid return false', (WidgetTester tester) async {
final GlobalKey<FormFieldState<int>> fieldKey = GlobalKey<FormFieldState<int>>();

await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButtonFormField2<int>(
key: fieldKey,
valueListenable: valueListenable,
items: buildItems(),
onChanged: (_) {},
forceErrorText: 'Forced error',
),
),
),
),
);

expect(fieldKey.currentState!.isValid, isFalse);
expect(fieldKey.currentState!.hasError, isTrue);
});

testWidgets('forceErrorText should override InputDecoration.errorText when both are provided', (
WidgetTester tester,
) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButtonFormField2<int>(
valueListenable: valueListenable,
items: buildItems(),
onChanged: (_) {},
decoration: const InputDecoration(errorText: 'Decoration error'),
forceErrorText: 'Forced error',
),
),
),
),
);

await tester.pumpAndSettle();

expect(find.text('Forced error'), findsOneWidget);
expect(find.text('Decoration error'), findsNothing);
});
},
);
}
Loading