假设我有一段很长的文本,想在字符5、10和1500上覆盖注释-我如何找到这些字符的位置?
我考虑过引用TextSpan组件,但是,与Flutter的其余部分不同,它们不是Widget,也不能具有GlobalKey。
答案 0 :(得分:0)
使用TextPainter和Paragraph轻松自如(感谢@pskink)。
但是,由于它们只能正确报告第一行文本的文本位置,因此它们目前都无法在网上使用。参见:https://github.com/flutter/flutter/issues/44121
此外,似乎他们可以通过“文本”小部件公开此功能。 :-/
使用TextPainter
import 'package:flutter/material.dart';
final loremIpsum =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Container(
width: 350,
color: Color.fromARGB(100, 0, 0, 0),
child: SelectText(),
),
),
),
);
}
}
class SelectText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final textPainter = TextPainter(
text: TextSpan(text: loremIpsum),
textDirection: TextDirection.ltr,
);
final width = constraints.maxWidth;
textPainter.layout(
minWidth: 20,
maxWidth: width,
);
final height = textPainter.height;
return Container(
width: width,
height: height,
color: Colors.yellow,
child: GestureDetector(
onTapDown: (details) {
print(
"Selection: ${textPainter.getPositionForOffset(details.localPosition)}");
},
child: CustomPaint(
size: Size(width, height), // Parent width, text height
painter: TextCustomPainter(textPainter),
),
));
});
}
}
class TextCustomPainter extends CustomPainter {
TextPainter textPainter;
TextCustomPainter(this.textPainter, {Listenable repaint})
: super(repaint: repaint);
@override
void paint(Canvas canvas, Size size) {
textPainter.paint(canvas, Offset(0, 0));
}
@override
bool shouldRepaint(CustomPainter old) {
return false;
}
}
带有段落
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:ui' as ui;
final loremIpsum =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Container(
width: 350,
color: Color.fromARGB(100, 0, 0, 0),
child: SelectText(),
),
),
),
);
}
}
class SelectText extends StatelessWidget {
@override
Widget build(BuildContext context) {
final TextStyle style = TextStyle(
color: Colors.black,
);
// Set width to max allowed by parent
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(
ui.ParagraphStyle(
fontSize: style.fontSize,
// There unfortunelly are some things to be copied from your common TextStyle to ParagraphStyle :C
fontFamily: style.fontFamily,
// IDK why it is like this, this is somewhat weird especially when there is `pushStyle` which can use the TextStyle...
fontStyle: style.fontStyle,
fontWeight: style.fontWeight,
textAlign: TextAlign.justify,
//maxLines: 25,
))
..pushStyle(style
.getTextStyle()) // To use multiple styles, you must make use of the builder and `pushStyle` and then `addText` (or optionally `pop`).
..addText(loremIpsum);
final width = constraints.maxWidth;
final ui.Paragraph paragraph = paragraphBuilder.build()
..layout(ui.ParagraphConstraints(width: width));
paragraph.layout(ParagraphConstraints(
width: width,
));
final height = paragraph.height;
return Container(
width: width,
height: height,
color: Colors.yellow,
child: GestureDetector(
onTapDown: (details) {
// BUG. On web- position is only correct for first line.
print(
"Selection: ${paragraph.getPositionForOffset(details.localPosition)}");
},
child: CustomPaint(
size: Size(width, height), // Parent width, text height
painter: TextCustomPainter(paragraph),
),
));
});
}
}
class TextCustomPainter extends CustomPainter {
Paragraph paragraph;
TextCustomPainter(this.paragraph, {Listenable repaint})
: super(repaint: repaint);
@override
void paint(Canvas canvas, Size size) {
canvas.drawParagraph(paragraph, const Offset(0, 0));
}
@override
bool shouldRepaint(CustomPainter old) {
return false;
}
}