100 lines
3.0 KiB
Dart
100 lines
3.0 KiB
Dart
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
class ReadMoreText extends StatefulWidget {
|
|
const ReadMoreText(
|
|
this.text, {
|
|
Key? key,
|
|
this.trimLines = 2,
|
|
this.collapsedText = '... read more',
|
|
this.expandedText = ' read less',
|
|
this.textStyle,
|
|
}) : assert(text != null),
|
|
super(key: key);
|
|
|
|
final String text;
|
|
final int trimLines;
|
|
final String collapsedText;
|
|
final String expandedText;
|
|
final TextStyle? textStyle;
|
|
|
|
@override
|
|
ReadMoreTextState createState() => ReadMoreTextState();
|
|
}
|
|
|
|
class ReadMoreTextState extends State<ReadMoreText> {
|
|
bool _readMore = true;
|
|
void _onTapLink() {
|
|
setState(() => _readMore = !_readMore);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
|
|
final colorClickableText = Colors.blue;
|
|
final widgetColor = Colors.black;
|
|
TextSpan link = TextSpan(
|
|
text: _readMore ? widget.collapsedText : widget.expandedText,
|
|
style: TextStyle(
|
|
color: colorClickableText,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = _onTapLink
|
|
);
|
|
Widget result = LayoutBuilder(
|
|
builder: (BuildContext context, BoxConstraints constraints) {
|
|
assert(constraints.hasBoundedWidth);
|
|
final double maxWidth = constraints.maxWidth;
|
|
// Create a TextSpan with data
|
|
final text = TextSpan(
|
|
text: widget.text,
|
|
);
|
|
// Layout and measure link
|
|
TextPainter textPainter = TextPainter(
|
|
text: link,
|
|
textDirection: TextDirection.rtl,//better to pass this from master widget if ltr and rtl both supported
|
|
maxLines: widget.trimLines,
|
|
ellipsis: '...',
|
|
);
|
|
textPainter.layout(minWidth: constraints.minWidth, maxWidth: maxWidth);
|
|
final linkSize = textPainter.size;
|
|
// Layout and measure text
|
|
textPainter.text = text;
|
|
textPainter.layout(minWidth: constraints.minWidth, maxWidth: maxWidth);
|
|
final textSize = textPainter.size;
|
|
// Get the endIndex of data
|
|
int? endIndex;
|
|
final pos = textPainter.getPositionForOffset(Offset(
|
|
textSize.width - linkSize.width,
|
|
textSize.height,
|
|
));
|
|
endIndex = textPainter.getOffsetBefore(pos.offset);
|
|
var textSpan;
|
|
if (textPainter.didExceedMaxLines) {
|
|
textSpan = TextSpan(
|
|
text: _readMore
|
|
? widget.text.substring(0, endIndex)
|
|
: widget.text,
|
|
style: widget.textStyle ?? TextStyle(
|
|
color: widgetColor,
|
|
),
|
|
children: <TextSpan>[link],
|
|
);
|
|
} else {
|
|
textSpan = TextSpan(
|
|
text: widget.text,
|
|
style: widget.textStyle ?? TextStyle(
|
|
color: widgetColor,
|
|
),
|
|
);
|
|
}
|
|
return RichText(
|
|
softWrap: true,
|
|
overflow: TextOverflow.clip,
|
|
text: textSpan,
|
|
);
|
|
},
|
|
);
|
|
return result;
|
|
}
|
|
} |