Skip to content

Commit

Permalink
Fix filled text field active indicator overflows container bounds (fl…
Browse files Browse the repository at this point in the history
…utter#146637)

## Description

This PRs fixes the active indicator vertical position for a filled text field.
Before this PR, the active indicator overflowed of container bounds, After this PR the active indicator is painted inside the container bounds.

Screenshot of filled text field with active indicator width sets to 4.0dp:

| Before | After |
|--------|--------|
| ![image](https://github.com/flutter/flutter/assets/840911/ed2878ba-130a-4410-b170-423d00a8893d) | ![image](https://github.com/flutter/flutter/assets/840911/f78d06c0-a161-4ab6-b01f-dae297939956) | 

</details> 

## Related Issue

Fixes flutter#146507

## Tests

Adds 1 tests, updates 3 tests.
  • Loading branch information
bleroux authored Apr 15, 2024
1 parent 1002ce4 commit 63634c2
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 57 deletions.
14 changes: 7 additions & 7 deletions packages/flutter/lib/src/material/input_border.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,15 @@ class UnderlineInputBorder extends InputBorder {
bottomRight: borderRadius.bottomRight.clamp(maximum: Radius.circular(rect.height / 2)),
);

// We set the strokeAlign to center, so the behavior is consistent with
// drawLine and with the historical behavior of this border.
BoxBorder.paintNonUniformBorder(canvas, rect,
textDirection: textDirection,
borderRadius: updatedBorderRadius,
bottom: borderSide.copyWith(strokeAlign: BorderSide.strokeAlignCenter),
color: borderSide.color);
textDirection: textDirection,
borderRadius: updatedBorderRadius,
bottom: borderSide.copyWith(strokeAlign: BorderSide.strokeAlignInside),
color: borderSide.color,
);
} else {
canvas.drawLine(rect.bottomLeft, rect.bottomRight, borderSide.toPaint());
final Offset alignInsideOffset = Offset(0, borderSide.width / 2);
canvas.drawLine(rect.bottomLeft - alignInsideOffset, rect.bottomRight - alignInsideOffset, borderSide.toPaint());
}
}

Expand Down
117 changes: 67 additions & 50 deletions packages/flutter/test/material/input_decorator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1537,17 +1537,70 @@ void main() {

// Fill is the border's outer path, a rounded rectangle.
expect(box, paints
..drrect(
style: PaintingStyle.fill,
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 47.5,
bottomRight: const Radius.elliptical(12.0, 11.5),
bottomLeft: const Radius.elliptical(12.0, 11.5)),
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 48.5,
bottomRight: const Radius.elliptical(12.0, 12.5),
bottomLeft: const Radius.elliptical(12.0, 12.5)),
..drrect(
style: PaintingStyle.fill,
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 47.0,
bottomRight: const Radius.elliptical(12.0, 11.0),
bottomLeft: const Radius.elliptical(12.0, 11.0)),
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 48.0,
bottomRight: const Radius.elliptical(12.0, 12.0),
bottomLeft: const Radius.elliptical(12.0, 12.0)),
));
});

testWidgets('UnderlineInputBorder clips top border to prevent anti-aliasing glitches', (WidgetTester tester) async {
const Rect canvasRect = Rect.fromLTWH(0, 0, 100, 100);
const UnderlineInputBorder border = UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
);
expect(
(Canvas canvas) => border.paint(canvas, canvasRect),
paints
..drrect(
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 100.0,
bottomRight: const Radius.elliptical(12.0, 12.0),
bottomLeft: const Radius.elliptical(12.0, 12.0)),
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 99.0,
bottomRight: const Radius.elliptical(12.0, 11.0),
bottomLeft: const Radius.elliptical(12.0, 11.0)),
),
);

const UnderlineInputBorder border2 = UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(60.0)),
);
expect(
(Canvas canvas) => border2.paint(canvas, canvasRect),
paints
..drrect(
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 100.0,
bottomRight: const Radius.elliptical(50.0, 50.0),
bottomLeft: const Radius.elliptical(50.0, 50.0)),
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 99.0,
bottomRight: const Radius.elliptical(50.0, 49.0),
bottomLeft: const Radius.elliptical(50.0, 49.0)),
),
reason: 'clamp is expected',
);
});

testWidgets('UnderlineInputBorder draws bottom border inside container bounds', (WidgetTester tester) async {
const Rect canvasRect = Rect.fromLTWH(0, 0, 100, 100);
const double borderWidth = 2.0;
const UnderlineInputBorder border = UnderlineInputBorder(
borderSide: BorderSide(width: borderWidth),
);
expect(
(Canvas canvas) => border.paint(canvas, canvasRect),
paints
..line(
p1: Offset(0, canvasRect.height - borderWidth / 2),
p2: Offset(100, canvasRect.height - borderWidth / 2),
strokeWidth: borderWidth,
),
);
});

testWidgets('InputDecorator OutlineBorder focused label with icon', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/82321
Widget buildFrame(TextDirection textDirection) {
Expand Down Expand Up @@ -5174,42 +5227,6 @@ void main() {
});
});

testWidgets('UnderlineInputBorder clips top border to prevent anti-aliasing glitches', (WidgetTester tester) async {
const Rect canvasRect = Rect.fromLTWH(0, 0, 100, 100);
const UnderlineInputBorder border = UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
);
expect(
(Canvas canvas) => border.paint(canvas, canvasRect),
paints
..drrect(
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 100.5,
bottomRight: const Radius.elliptical(12.0, 12.5),
bottomLeft: const Radius.elliptical(12.0, 12.5)),
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 99.5,
bottomRight: const Radius.elliptical(12.0, 11.5),
bottomLeft: const Radius.elliptical(12.0, 11.5)),
),
);

const UnderlineInputBorder border2 = UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(60.0)),
);
expect(
(Canvas canvas) => border2.paint(canvas, canvasRect),
paints
..drrect(
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 100.5,
bottomRight: const Radius.elliptical(50.0, 50.5),
bottomLeft: const Radius.elliptical(50.0, 50.5)),
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 100.0, 99.5,
bottomRight: const Radius.elliptical(50.0, 49.5),
bottomLeft: const Radius.elliptical(50.0, 49.5)),
),
reason: 'clamp is expected',
);
});

testWidgets('Ensure the height of labelStyle remains unchanged when TextField is focused', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/141448.
final FocusNode focusNode = FocusNode();
Expand Down Expand Up @@ -9754,12 +9771,12 @@ void main() {
expect(box, paints
..drrect(
style: PaintingStyle.fill,
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 47.5,
bottomRight: const Radius.elliptical(12.0, 11.5),
bottomLeft: const Radius.elliptical(12.0, 11.5)),
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 48.5,
bottomRight: const Radius.elliptical(12.0, 12.5),
bottomLeft: const Radius.elliptical(12.0, 12.5)),
inner: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 47.0,
bottomRight: const Radius.elliptical(12.0, 11.0),
bottomLeft: const Radius.elliptical(12.0, 11.0)),
outer: RRect.fromLTRBAndCorners(0.0, 0.0, 800.0, 48.0,
bottomRight: const Radius.elliptical(12.0, 12.0),
bottomLeft: const Radius.elliptical(12.0, 12.0)),
));
});

Expand Down

0 comments on commit 63634c2

Please sign in to comment.