From a5ea2ef1d4d2d36af4feffccec41c876707c9544 Mon Sep 17 00:00:00 2001 From: Bruno Leroux Date: Fri, 5 Apr 2024 06:01:35 +0200 Subject: [PATCH] Fix cursor is not centered when cursorHeight is set (non-Apple platforms). (#145829) ## Description This PRs fixes the cursor vertical position when a custom cursor height is set on non-Apple platforms (on Apple platforms the cursor is already centered) . | Before | After | |--------|--------| | ![image](https://github.com/flutter/flutter/assets/840911/2d1b855d-d36c-4941-85be-5044ea0e9bb2) | ![image](https://github.com/flutter/flutter/assets/840911/306510c8-42ca-45c7-8c25-ddfa2e22c7f3) |
Code sample used for the screenshots (cursorHeight 18, font size 16, line height 2) ```dart import 'package:flutter/material.dart'; void main() async { runApp( const MaterialApp( title: 'Flutter Demo', home: MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const Scaffold( body: Center( child: TextField( cursorHeight: 18, style: TextStyle(fontSize: 16, height: 2), ), ), ); } } ```
## Related Issue Fixes https://github.com/flutter/flutter/issues/143480 ## Tests Adds 2 tests, updates 1. --- .../flutter/lib/src/rendering/editable.dart | 12 ++- .../widgets/editable_text_cursor_test.dart | 92 ++++++++++++++++++- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index 41a13c6f38..ae2a1e0b62 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -1793,13 +1793,12 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, final double caretX = clampDouble(caretRect.left, 0, math.max(scrollableWidth - _caretMargin, 0)); caretRect = Offset(caretX, caretRect.top) & caretRect.size; - final double caretHeight = cursorHeight; + final double fullHeight = _textPainter.getFullHeightForCaret(caretPosition, caretPrototype); switch (defaultTargetPlatform) { case TargetPlatform.iOS: case TargetPlatform.macOS: - final double fullHeight = _textPainter.getFullHeightForCaret(caretPosition, caretPrototype); - final double heightDiff = fullHeight - caretRect.height; // Center the caret vertically along the text. + final double heightDiff = fullHeight - caretRect.height; caretRect = Rect.fromLTWH( caretRect.left, caretRect.top + heightDiff / 2, @@ -1812,10 +1811,13 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, case TargetPlatform.windows: // Override the height to take the full height of the glyph at the TextPosition // when not on iOS. iOS has special handling that creates a taller caret. - // TODO(garyq): See the TODO for _computeCaretPrototype(). + // TODO(garyq): see https://github.com/flutter/flutter/issues/120836. + final double caretHeight = cursorHeight; + // Center the caret vertically along the text. + final double heightDiff = fullHeight - caretHeight; caretRect = Rect.fromLTWH( caretRect.left, - caretRect.top - _kCaretHeightOffset, + caretRect.top - _kCaretHeightOffset + heightDiff / 2, caretRect.width, caretHeight, ); diff --git a/packages/flutter/test/widgets/editable_text_cursor_test.dart b/packages/flutter/test/widgets/editable_text_cursor_test.dart index 59e3a4e62f..c4b34703e8 100644 --- a/packages/flutter/test/widgets/editable_text_cursor_test.dart +++ b/packages/flutter/test/widgets/editable_text_cursor_test.dart @@ -1236,7 +1236,7 @@ void main() { backgroundCursorColor: Colors.grey, controller: controller, focusNode: focusNode, - style: const TextStyle(), + style: const TextStyle(fontSize: 17), textAlign: TextAlign.center, keyboardType: TextInputType.text, cursorColor: cursorColor, @@ -1267,6 +1267,96 @@ void main() { skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308 ); + testWidgets( + 'Caret with a cursorHeight smaller than font size is vertically centered on non-Apple platforms', + (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/143480. + final TextEditingController controller = TextEditingController.fromValue( + const TextEditingValue(selection: TextSelection.collapsed(offset: 0)), + ); + addTearDown(controller.dispose); + + const double cursorHeight = 12.0; + const double cursorWidth = 4.0; + const double fontSize = 16.0; + + final Widget widget = EditableText( + autofocus: true, + backgroundCursorColor: Colors.grey, + controller: controller, + focusNode: focusNode, + style: const TextStyle(fontSize: fontSize), + keyboardType: TextInputType.text, + cursorColor: cursorColor, + cursorHeight: cursorHeight, + cursorWidth: cursorWidth, + ); + await tester.pumpWidget(MaterialApp(home: widget)); + + final EditableTextState editableTextState = tester.firstState(find.byWidget(widget)); + final RenderEditable renderEditable = editableTextState.renderEditable; + + // The caretRect is vertically centered. + const Rect caretRect = Rect.fromLTWH( + 0.0, + (fontSize - cursorHeight) / 2, + cursorWidth, + cursorHeight, + ); + expect( + renderEditable, + paints..rect(color: cursorColor, rect: caretRect), + ); + }, + variant: TargetPlatformVariant.all(excluding: {TargetPlatform.macOS, TargetPlatform.iOS}), + skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308 + ); + + testWidgets( + 'Caret with a cursorHeight bigger than font size is vertically centered on non-Apple platforms', + (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/143480. + final TextEditingController controller = TextEditingController.fromValue( + const TextEditingValue(selection: TextSelection.collapsed(offset: 0)), + ); + addTearDown(controller.dispose); + + const double cursorHeight = 24.0; + const double cursorWidth = 4.0; + const double fontSize = 16.0; + + final Widget widget = EditableText( + autofocus: true, + backgroundCursorColor: Colors.grey, + controller: controller, + focusNode: focusNode, + style: const TextStyle(fontSize: fontSize), + keyboardType: TextInputType.text, + cursorColor: cursorColor, + cursorHeight: cursorHeight, + cursorWidth: cursorWidth, + ); + await tester.pumpWidget(MaterialApp(home: widget)); + + final EditableTextState editableTextState = tester.firstState(find.byWidget(widget)); + final RenderEditable renderEditable = editableTextState.renderEditable; + + // The caretRect is vertically centered. + const Rect caretRect = Rect.fromLTWH( + 0.0, + (fontSize - cursorHeight) / 2, + cursorWidth, + cursorHeight, + ); + expect( + renderEditable, + paints..rect(color: cursorColor, rect: caretRect), + ); + }, + variant: TargetPlatformVariant.all(excluding: {TargetPlatform.macOS, TargetPlatform.iOS}), + skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308 + ); + testWidgets('getLocalRectForCaret reports the real caret Rect', (WidgetTester tester) async { EditableText.debugDeterministicCursor = true; addTearDown(() { EditableText.debugDeterministicCursor = false; });