Skip to content

RenderEditable.getBoxesForSelection gets selection width for longest line width #180311

Closed

Description

@deva666

Steps to reproduce

Run the attached example on Flutter 3.35 or 3.38

Expected results

This is how it looks on Flutter 3.27
RenderEditable.getBoxesForSelection for Text Selection from 39 to 51 (second line) only returns coordinates of the 2nd line which gets correctly highlighted/underlined.

Image

Actual results

On Flutter version 3.35 and 3.38, same code returned coordinates for the longest line width, which highlights/underlines more than the actual TextSelection was requested.

Image

Code sample

Code sample
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'RenderEditable Bug Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'RenderEditable Bug Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey customPaintKey = GlobalKey();
  final ScrollController scrollController = ScrollController();
  final TextEditingController controller = TextEditingController();
  final List<TextSelection> errorLines = [const TextSelection(baseOffset: 39, extentOffset: 51)]; // "Highlight me" position
  ErrorLinesPainter? painter;
  @override
  void initState() {
    controller.text = '''
Some long lines of text should go here
Highlight me
Doesn't matter
''';
    painter = ErrorLinesPainter(
      customPaintKey,
      const TextStyle(),
      controller,
    );
    painter!.lineErrorPositions = errorLines;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: DumbVisitor<EditableTextState>(
        onFound: (ets) {
          painter?.setupEditableTextState(ets);
        },
        child: CustomPaint(
          key: customPaintKey,
          foregroundPainter: painter,
          child: TextField(
            controller: controller,
            maxLines: null,
            expands: true,
            readOnly: true,
            decoration: const InputDecoration(
              border: OutlineInputBorder(),
            ),
          ),
        ),
      ),
    );
  }
}

class ErrorLinesPainter extends CustomPainter {
  ErrorLinesPainter(this.customPaintKey, this.textStyle, Listenable listenable) : super(repaint: listenable);
  GlobalKey customPaintKey;
  RenderEditable? re;

  final TextStyle textStyle;

  List<TextSelection> lineErrorPositions = [];

  @override
  void paint(Canvas canvas, Size size) {
    if (re case RenderEditable re) {
      final ancestor = customPaintKey.currentContext!.findRenderObject();
      final offset = re.localToGlobal(Offset.zero, ancestor: ancestor);
      for (final e in lineErrorPositions) {
        final boxes = re.getBoxesForSelection(e);
        if (boxes.isNotEmpty) {
          final firstBox = boxes.first.toRect();
          final lastBox = boxes.last.toRect();
          canvas.drawLine(
              Offset(firstBox.left + offset.dx, firstBox.bottom + offset.dy),
              Offset(lastBox.right + offset.dx, firstBox.bottom + offset.dy),
              Paint()
                ..strokeWidth = 2
                ..style = PaintingStyle.stroke
                ..filterQuality = FilterQuality.low
                ..strokeCap = StrokeCap.round
                ..color = Colors.red.shade500);
        }
      }
    }
  }

  @override
  bool shouldRepaint(ErrorLinesPainter oldDelegate) => false;

  void setupEditableTextState(EditableTextState ets) {
    re = ets.renderEditable;
  }
}

class DumbVisitor<T> extends StatelessWidget {
  const DumbVisitor({
    super.key,
    required this.onFound,
    required this.child,
  });

  final void Function(T object) onFound;
  final Widget child;

  @override
  Widget build(BuildContext context) => child;

  @override
  StatelessElement createElement() => _DumbVisitorElement<T>(this, onFound);
}

class _DumbVisitorElement<T> extends StatelessElement {
  _DumbVisitorElement(super.widget, this.onFound);

  final void Function(T object) onFound;
  Element? oldElement;

  @override
  Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
    final element = super.updateChild(child, newWidget, newSlot);
    if (oldElement != element) {
      oldElement = element;
      element?.visitChildren(_visitor);
    }
    return element;
  }

  void _visitor(Element child) {
    if (child is StatefulElement && child.state is T) {
      onFound(child.state as T);
    } else if (child.renderObject is T) {
      onFound(child.renderObject as T);
    }
    child.visitChildren(_visitor);
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[!] Flutter (Channel [user-branch], 3.38.5, on macOS 14.7.1 23H222 darwin-arm64, locale en-GB)
    ! Flutter version 3.38.5 on channel [user-branch] at xxxx
      Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
      If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/setup.
    ! Upstream repository unknown source is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss this error.
[!] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
    ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
[✓] Network resources

! Doctor found issues in 2 categories.

Activity

  1. added
    in triagePresently being triaged by the triage team
    a: text inputEntering text in a text field or keyboard related problems
    a: typographyText rendering, possibly libtxt
    team-designOwned by Design Languages team
    and removed
    in triagePresently being triaged by the triage team
    team-designOwned by Design Languages team
    on Jan 5, 2026
  2. added
    c: regressionIt was better in the past than it is now
    P1High-priority issues at the top of the work list
    on Jan 22, 2026
  3. added
    ⏳Bot is counting down the days until it unassigns the issue
    on May 28, 2026
  4. flutter-triage-bot commented on May 28, 2026

    @flutter-triage-bot

    This issue is assigned to @Renzo-Olivares but has had no recent status updates. Please consider unassigning this issue if it is not going to be addressed in the near future. This allows people to have a clearer picture of what work is actually planned. Thanks!

  5. removed their assignment
    on Jul 30, 2026
  6. Renzo-Olivares commented on Jul 30, 2026

    @Renzo-Olivares
    Contributor

    This will be fixed by #185239

  7. removed
    ⏳Bot is counting down the days until it unassigns the issue
    on Jul 30, 2026
  8. added a commit that references this issue on Oct 9, 2026
    103d780
  9. github-actions commented on Oct 23, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  10. locked as resolved and limited conversation to collaborators on Oct 23, 2026
  11. added a commit that references this issue on Oct 29, 2026
    bc40d0b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority issues at the top of the work lista: text inputEntering text in a text field or keyboard related problemsa: typographyText rendering, possibly libtxtc: regressionIt was better in the past than it is nowteam-text-inputOwned by Text Input teamtriaged-text-inputTriaged by Text Input team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions