getSentenceList method
List<TextSpan>
getSentenceList({
- required int maxShowCharactersLength,
- required TextStyle effectiveTextStyle,
- required TextStyle? defaultHighlightStyle,
- required List<
TextHighlight> allTextHighlights, - required Iterable<
TextRange> allUrlRanges, - required Map<
int, TargetTextHighlights> allIndexTargetTextHighlightsMap, - required void trackActiveTapGesture(
- TapGestureRecognizer recognizer
Implementation
List<TextSpan> getSentenceList({
required int maxShowCharactersLength,
required TextStyle effectiveTextStyle,
required TextStyle? defaultHighlightStyle,
required List<TextHighlight> allTextHighlights,
required Iterable<TextRange> allUrlRanges,
required Map<int, TargetTextHighlights> allIndexTargetTextHighlightsMap,
required void Function(TapGestureRecognizer recognizer)
trackActiveTapGesture,
}) {
final allIndexes = Set<int>.unmodifiable(
Iterable<int>.generate(maxShowCharactersLength + 1),
);
final textHighlightRangesIndexes = allTextHighlights
.expand(
(textHighlight) => textHighlight.highlightRanges.expand(
(range) => range.rangeIndexes().toSet().intersection(allIndexes),
),
)
.toSet();
final urlRanges =
allUrlRanges.where((range) => range.start < maxShowCharactersLength);
final urlHighlightIndexes = Set<int>.from(urlRanges.expand(
(range) => range.rangeIndexes(),
));
// We have to remove the index which are greater than endIndex.
// As while creating urlRanges we check if start index is greater than
// endIndex. And we do that because [_createUrlTextSpan] method takes
// highlightText and fullUrl as parameter. So to get the fullUrl we
// need to check if start index is greater than endIndex. Which means
// even if the highlight portion of the url is trimmed, we need to
// make sure that even if user click on the highlight portion,
// click should work. But by removing below we make sure that
// while displaying the trimmed version we don't show any unwanted portion.
urlHighlightIndexes.removeWhere((i) => i > maxShowCharactersLength);
final allHighlightIndexesSet =
textHighlightRangesIndexes.union(urlHighlightIndexes);
final allHighlightIndexes = allHighlightIndexesSet.toList()..sort();
final nonHighlightIndexes =
allIndexes.difference(allHighlightIndexesSet).toList()..sort();
final textSpans = <TextSpan>[];
if (maxShowCharactersLength == 0) {
return textSpans;
}
for (var i = 0; i < maxShowCharactersLength + 1; i++) {
// We have -1 as end index is also increased by 1, and when next time
// the loop is execute it will be increase by one as [i++]. So,
// we need to decrease it by 1 to normalize it.
final indexValue = allIndexes.elementAt(i == 0 ? 0 : i - 1);
if (nonHighlightIndexes.contains(indexValue)) {
final nonHighlightRange =
nonHighlightIndexes.firstTextRangeFromStartIndex(
indexValue,
validateExpectEndIndexValue: (expectEndIndexValue) {
if (allIndexes.contains(expectEndIndexValue)) {
return expectEndIndexValue;
}
return allIndexes.last;
},
);
final text = nonHighlightRange.textInside(data);
textSpans.add(
TextSpan(
text: text,
style: effectiveTextStyle,
),
);
i = nonHighlightRange.end;
continue;
}
final highlightRange = allHighlightIndexes.firstTextRangeFromStartIndex(
indexValue,
validateExpectEndIndexValue: (expectEndIndexValue) {
if (allIndexes.contains(expectEndIndexValue)) {
return expectEndIndexValue;
}
return allIndexes.last;
},
);
final highlightRangeIndexes = highlightRange.rangeIndexes().toSet();
final urlRange = allUrlRanges.firstWhereOrNull(
(urlRange) =>
highlightRangeIndexes.containsAll(urlRange.rangeIndexes()),
);
if (urlRange != null) {
final fullUrl = urlRange.textInside(data);
var url = fullUrl;
final urlRangeIndexes = urlRange.rangeIndexes().toSet();
final targetSubStringHighlightIndexes =
textHighlightRangesIndexes.intersection(urlRangeIndexes);
onTapLinkCallback() async {
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://$fullUrl';
}
onTapLink?.call(fullUrl);
if (onTapLink != null) {
} else {
try {
final launchUri = Uri.parse(url);
await launchUrl(launchUri, mode: LaunchMode.externalApplication);
} on Exception catch (e) {
throw Exception(e);
}
}
}
final urlChildren = _urlSubStringHighlightTextSpans(
urlRangeIndexes: urlRangeIndexes,
targetSubStringHighlightIndexes: targetSubStringHighlightIndexes,
effectiveTextStyle: effectiveTextStyle,
defaultHighlightStyle: defaultHighlightStyle,
overrideOnTap: (_) {
onTapLinkCallback();
},
shouldApplyHighlight: (targetHighlight) {
return targetHighlight.highlightInUrl;
},
allIndexTargetTextHighlightsMap: allIndexTargetTextHighlightsMap,
trackActiveTapGesture: trackActiveTapGesture,
);
final firstHighlightSubstring = urlChildren.isEmpty
? fullUrl
: targetSubStringHighlightIndexes.contains(urlRange.start)
? targetSubStringHighlightIndexes
.firstTextRangeFromStartIndex(urlRange.start)
.textInside(data)
: null;
textSpans.add(
_createUrlTextSpan(
firstHighlightSubstring: firstHighlightSubstring,
fullUrl: fullUrl,
children: urlChildren,
effectiveTextStyle: effectiveTextStyle,
onTapLinkCallback: onTapLinkCallback,
),
);
} else {
final highlightTextSpans = _highlightTextSpans(
highlightRange: highlightRange,
highlightRangeIndexes: highlightRangeIndexes,
effectiveTextStyle: effectiveTextStyle,
defaultHighlightStyle: defaultHighlightStyle,
allIndexTargetTextHighlightsMap: allIndexTargetTextHighlightsMap,
trackActiveTapGesture: trackActiveTapGesture,
);
textSpans.addAll(highlightTextSpans);
}
i = highlightRange.end;
}
return textSpans;
}