Skip to content

Instantly share code, notes, and snippets.

@BarryDaBee
Last active October 8, 2022 15:32
Show Gist options
  • Save BarryDaBee/3f367f5705428eb27e4a4d9842f13e88 to your computer and use it in GitHub Desktop.
Save BarryDaBee/3f367f5705428eb27e4a4d9842f13e88 to your computer and use it in GitHub Desktop.
Implementation of common "Read more" feature to truncate and expand lengthy text on demand
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class ReadMore extends StatefulWidget {
final String text;
final TextStyle? textStyle;
final String readMoreText;
final TextStyle? readMoreTextStyle;
final int maxLines;
final int steps;
// final bool shouldUseSteps;
const ReadMore({
Key? key,
this.readMoreText = 'Read more',
this.readMoreTextStyle,
this.maxLines = 4,
required this.text,
this.textStyle,
this.steps = 4,
// required this.shouldUseSteps,
}) : super(key: key);
@override
State<ReadMore> createState() => _ReadMoreState();
}
class _ReadMoreState extends State<ReadMore> {
late int _maxLines = widget.maxLines;
String get _readMoreText => ' ' + widget.readMoreText;
@override
Widget build(BuildContext context) {
return _buildMessage();
}
TextSpan _bodyTextSpan() {
return TextSpan(text: widget.text, style: widget.textStyle);
}
TextSpan _readMoreTextSpan() {
return TextSpan(
text: _readMoreText,
style: widget.readMoreTextStyle,
recognizer: TapGestureRecognizer()
..onTap = () {
setState(() {
_maxLines += widget.steps;
});
},
);
}
RichText _buildMessage() {
const double maxWidth = 300;
const double minWidth = 0;
TextPainter textPainter = TextPainter(
text: _readMoreTextSpan(),
textDirection: TextDirection.ltr,
maxLines: _maxLines,
ellipsis: '',
);
textPainter.layout(minWidth: minWidth, maxWidth: maxWidth);
final linkSize = textPainter.size;
textPainter.text = _bodyTextSpan();
textPainter.layout(minWidth: minWidth, maxWidth: maxWidth);
final textSize = textPainter.size;
int? endIndex;
final pos = textPainter.getPositionForOffset(Offset(
textSize.width - linkSize.width,
textSize.height,
));
endIndex = textPainter.getOffsetBefore(pos.offset);
TextSpan textSpan;
if (textPainter.didExceedMaxLines) {
textSpan = TextSpan(
text: widget.text.substring(0, endIndex),
children: <TextSpan>[_readMoreTextSpan()],
);
} else {
textSpan = TextSpan(
text: widget.text,
);
}
return RichText(
softWrap: true,
text: textSpan,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment