Unverified Commit bb6b72d6 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Fix `DropdownButtonFormField` input border clipping (#131481)

fixes [BUG: DropdownButtonFormField input decorator focus/hover is not clipped
](https://github.com/flutter/flutter/issues/131282)

### Description

This fixes an issue where `DropdownButtonFormField`'s input border isn't used for clipping with `InkWell`.

### Before
![Screenshot 2023-07-28 at 17 06 20](https://github.com/flutter/flutter/assets/48603081/8fe1ee1f-5cea-4297-b4f6-e672d74bb583)

### After
![Screenshot 2023-07-28 at 17 06 38](https://github.com/flutter/flutter/assets/48603081/a0091459-67dc-45ca-96b1-95d7093d475f)
parent 35a3ef7e
...@@ -15,6 +15,7 @@ import 'constants.dart'; ...@@ -15,6 +15,7 @@ import 'constants.dart';
import 'debug.dart'; import 'debug.dart';
import 'icons.dart'; import 'icons.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'input_border.dart';
import 'input_decorator.dart'; import 'input_decorator.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
...@@ -1633,6 +1634,7 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1633,6 +1634,7 @@ class DropdownButtonFormField<T> extends FormField<T> {
} }
} }
final bool isEmpty = !showSelectedItem && !isHintOrDisabledHintAvailable(); final bool isEmpty = !showSelectedItem && !isHintOrDisabledHintAvailable();
final bool hasError = effectiveDecoration.errorText != null;
// An unfocusable Focus widget so that this widget can detect if its // An unfocusable Focus widget so that this widget can detect if its
// descendants have focus or not. // descendants have focus or not.
...@@ -1640,6 +1642,33 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1640,6 +1642,33 @@ class DropdownButtonFormField<T> extends FormField<T> {
canRequestFocus: false, canRequestFocus: false,
skipTraversal: true, skipTraversal: true,
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final bool isFocused = Focus.of(context).hasFocus;
InputBorder? resolveInputBorder() {
if (hasError) {
if (isFocused) {
return effectiveDecoration.focusedErrorBorder;
}
return effectiveDecoration.errorBorder;
}
if (isFocused) {
return effectiveDecoration.focusedBorder;
}
if (effectiveDecoration.enabled) {
return effectiveDecoration.enabledBorder;
}
return effectiveDecoration.border;
}
BorderRadius? effectiveBorderRadius() {
final InputBorder? inputBorder = resolveInputBorder();
if (inputBorder is OutlineInputBorder) {
return inputBorder.borderRadius;
}
if (inputBorder is UnderlineInputBorder) {
return inputBorder.borderRadius;
}
return null;
}
return DropdownButtonHideUnderline( return DropdownButtonHideUnderline(
child: DropdownButton<T>._formField( child: DropdownButton<T>._formField(
items: items, items: items,
...@@ -1665,10 +1694,10 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1665,10 +1694,10 @@ class DropdownButtonFormField<T> extends FormField<T> {
menuMaxHeight: menuMaxHeight, menuMaxHeight: menuMaxHeight,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
alignment: alignment, alignment: alignment,
borderRadius: borderRadius, borderRadius: borderRadius ?? effectiveBorderRadius(),
inputDecoration: effectiveDecoration.copyWith(errorText: field.errorText), inputDecoration: effectiveDecoration.copyWith(errorText: field.errorText),
isEmpty: isEmpty, isEmpty: isEmpty,
isFocused: Focus.of(context).hasFocus, isFocused: isFocused,
padding: padding, padding: padding,
), ),
); );
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -1134,4 +1135,100 @@ void main() { ...@@ -1134,4 +1135,100 @@ void main() {
selectedItemBox.localToGlobal(Offset(selectedItemBox.size.width / 2.0, selectedItemBox.size.height / 2.0)), selectedItemBox.localToGlobal(Offset(selectedItemBox.size.width / 2.0, selectedItemBox.size.height / 2.0)),
); );
}); });
testWidgets('InputDecoration borders are used for clipping', (WidgetTester tester) async {
const BorderRadius errorBorderRadius = BorderRadius.all(Radius.circular(5.0));
const BorderRadius focusedErrorBorderRadius = BorderRadius.all(Radius.circular(6.0));
const BorderRadius focusedBorder = BorderRadius.all(Radius.circular(7.0));
const BorderRadius enabledBorder = BorderRadius.all(Radius.circular(9.0));
final FocusNode focusNode = FocusNode();
const String errorText = 'This is an error';
bool showError = false;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
inputDecorationTheme: const InputDecorationTheme(
errorBorder: OutlineInputBorder(
borderRadius: errorBorderRadius,
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: focusedErrorBorderRadius,
),
focusedBorder: OutlineInputBorder(
borderRadius: focusedBorder,
),
enabledBorder: OutlineInputBorder(
borderRadius: enabledBorder,
),
),
),
home: Material(
child: Center(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return DropdownButtonFormField<String>(
value: 'two',
onChanged:(String? value) {
setState(() {
if (value == 'three') {
showError = true;
} else {
showError = false;
}
});
},
decoration: InputDecoration(
errorText: showError ? errorText : null,
),
focusNode: focusNode,
items: menuItems.map<DropdownMenuItem<String>>((String item) {
return DropdownMenuItem<String>(
key: ValueKey<String>(item),
value: item,
child: Text(item, key: ValueKey<String>('${item}Text')),
);
}).toList(),
);
}
),
),
),
),
);
// Test enabled border.
InkWell inkWell = tester.widget<InkWell>(find.byType(InkWell));
expect(inkWell.borderRadius, enabledBorder);
// Test focused border.
focusNode.requestFocus();
await tester.pump();
inkWell = tester.widget<InkWell>(find.byType(InkWell));
expect(inkWell.borderRadius, focusedBorder);
// Test focused error border.
await tester.tap(find.text('two'), warnIfMissed: false);
await tester.pumpAndSettle();
await tester.tap(find.text('three').last);
await tester.pumpAndSettle();
inkWell = tester.widget<InkWell>(find.byType(InkWell));
expect(inkWell.borderRadius, focusedErrorBorderRadius);
// Test error border with no focus.
focusNode.unfocus();
await tester.pump();
// Hovering over the widget should show the error border.
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.moveTo(tester.getCenter(find.text('three').last));
await tester.pumpAndSettle();
inkWell = tester.widget<InkWell>(find.byType(InkWell));
expect(inkWell.borderRadius, errorBorderRadius);
});
} }
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