When using multiple WidgetSpan, getOffsetForCaret returns incorrect caret positions
- Dominant language
- Dart
- Stars
- 179k
- Forks
- 31.1k
- PR merge metrics
- PR metrics pending
Description
### Steps to reproduce
1. Copy the example below to [DartPad](https://dartpad.dev/?sample=counter).
2. The blue text shows the clicked area, while the red position marks the caret.
4. Clicking on “1” or “2” moves the caret to “a” or “b” as expected.
5. However, clicking on “4” incorrectly moves the caret to “c”, and similarly, clicking on “6” sends it to “d”.
### Expected results
When clicking on 4 and 6, the caret behavior matches that of clicking on 2, where the caret correctly jumps to the right.
### Actual results
The caret positioning is inconsistent with expectations.
### Code sample
Code sample
```dart
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: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
super.key,
required this.title,
});
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
// Caret position
Rect rect = const Rect.fromLTWH(10, 10, 30, 30);
handleTapDown(Offset globalPosition) {
var paragraph = findParagraph();
var localPosition = paragraph.globalToLocal(globalPosition);
// Focus here
var textPosition = paragraph.getPositionForOffset(localPosition);
var caretPrototype = const Rect.fromLTWH(0, 0, 1, 40);
var caretOffset = paragraph.getOffsetForCaret(textPosition, caretPrototype);
var caretPosition =
paragraph.localToGlobal(caretOffset) & caretPrototype.size;
print(textPosition);
print(caretPosition);
setState(() {
rect = caretPosition;
});
}
// ignore this
RenderParagraph findParagraph() {
var renderObj = context.findRenderObject();
late RenderParagraph paragraph;
find(RenderObject object) {
if (object is RenderParagraph) paragraph = object;
object.visitChildren(find);
}
find(renderObj!);
return paragraph;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTapDown: (details) {
handleTapDown(details.globalPosition);
},
child: Stack(
children: [
Center(
child: Text.rich(
style: const TextStyle(
fontSize: 48,
),
TextSpan(
children: [
const TextSpan(text: "A"),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Container(
width: 60,
height: 40,
decoration: BoxDecoration(border: Border.all()),
),
),
const TextSpan(text: "A"),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Container(
width: 80,
height: 40,
decoration: BoxDecoration(border: Border.all()),
),
),
const TextSpan(text: "A"),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Container(
width: 60,
height: 40,
decoration: BoxDecoration(border: Border.all()),
),
),
const TextSpan(text: "A"),
],
),
),
),
DebugRectWidget(rect: rect),
],
),
),
);
}
}
class DebugRectWidget extends StatefulWidget {
const DebugRectWidget({super.key, required this.rect});
final Rect rect;
@override
State createState() => _DebugRectWidgetState();
}
class _DebugRectWidgetState extends State {
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: RectPainter(widget.rect),
);
}
}
class RectPainter extends CustomPainter {
final Rect rect;
RectPainter(this.rect);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 1;
canvas.drawRect(rect, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
```
### Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
### Logs
Logs
```console
[Paste your logs here]
```
### Flutter Doctor output
Doctor output
```console
[✓] Flutter (Channel stable, 3.24.1, on macOS 14.6.1 23G93 darwin-arm64, locale
zh-Hans-CN)
• Flutter version 3.24.1 on channel stable at /Users/xxxx/code/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 5874a72aa4 (4 weeks ago), 2024-08-20 16:46:00 -0500
• Engine revision c9b9d5780d
• Dart version 3.5.1
• DevTools version 2.37.2
• Pub download mirror https://pub.flutter-io.cn
• Flutter download mirror https://storage.flutter-io.cn
[✗] Android toolchain - develop for Android devices
✗ Unable to locate Android SDK.
Install Android Studio from: https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android SDK components.
(or visit https://flutter.dev/to/macos-android-setup for detailed
instructions).
If the Android SDK has been installed to a custom location, please use
`flutter config --android-sdk` to update to that location.
[!] Xcode - develop for iOS and macOS (Xcode 16.0)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 16A242d
! iOS 18.0 Simulator not installed; this may be necessary for iOS and macOS
development.
To download and install the platform, open Xcode, select Xcode > Settings >
Platforms,
and click the GET button for the required platform.
For more information, please visit:
https://developer.apple.com/documentation/xcode/installing-additional-simula
tor-runtimes
• CocoaPods version 1.15.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[!] Android Studio (not installed)
• Android Studio not found; download from
https://developer.android.com/studio/index.html
(or visit https://flutter.dev/to/macos-android-setup for detailed
instructions).
[✓] VS Code (version 1.93.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.96.0
[✓] VS Code (version 1.92.0-insider)
• VS Code at /Applications/Visual Studio Code - Insiders.app/Contents
• Flutter extension version 3.92.0
[✓] Connected device (3 available)
• macOS (desktop) • macos • darwin-arm64 •
macOS 14.6.1 23G93 darwin-arm64
• Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin •
macOS 14.6.1 23G93 darwin-arm64
• Chrome (web) • chrome • web-javascript •
Google Chrome 128.0.6613.138
[✓] Network resources
• All expected network resources are available.
! Doctor found issues in 3 categories.
```
Contributor guide
Assessment
This issue has not been assessed yet.