Files
paperless-mobile/lib/core/widgets/highlighted_text.dart

128 lines
3.5 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
class HighlightedText extends StatelessWidget {
final String text;
final List<String> highlights;
final Color? color;
final TextStyle? style;
final bool caseSensitive;
final TextAlign textAlign;
final TextDirection? textDirection;
final TextOverflow overflow;
final double textScaleFactor;
final int? maxLines;
final StrutStyle? strutStyle;
final TextWidthBasis textWidthBasis;
final TextHeightBehavior? textHeightBehavior;
const HighlightedText({
super.key,
required this.text,
required this.highlights,
this.style,
this.color = Colors.yellowAccent,
this.caseSensitive = true,
this.textAlign = TextAlign.start,
this.textDirection = TextDirection.ltr,
this.overflow = TextOverflow.clip,
this.textScaleFactor = 1.0,
this.maxLines,
this.strutStyle,
this.textWidthBasis = TextWidthBasis.parent,
this.textHeightBehavior,
});
@override
Widget build(BuildContext context) {
if (text.isEmpty || highlights.isEmpty || highlights.contains('')) {
return SelectableText.rich(
_normalSpan(text, context),
key: key,
textAlign: textAlign,
textDirection: textDirection,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
style: TextStyle(overflow: overflow),
);
}
return SelectableText.rich(
TextSpan(children: _buildChildren(context)),
key: key,
textAlign: textAlign,
textDirection: textDirection,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
style: TextStyle(overflow: overflow),
);
}
List<TextSpan> _buildChildren(BuildContext context) {
List<TextSpan> _spans = [];
int _start = 0;
String _text = caseSensitive ? text : text.toLowerCase();
List<String> _highlights = caseSensitive
? highlights
: highlights.map((e) => e.toLowerCase()).toList();
while (true) {
Map<int, String> _highlightsMap = {}; //key (index), value (highlight).
for (final h in _highlights) {
final idx = _text.indexOf(h, _start);
if (idx >= 0) {
_highlightsMap.putIfAbsent(_text.indexOf(h, _start), () => h);
}
}
if (_highlightsMap.isNotEmpty) {
int _currentIndex = _highlightsMap.keys.reduce(min);
String _currentHighlight = text.substring(
_currentIndex,
_currentIndex + _highlightsMap[_currentIndex]!.length,
);
if (_currentIndex == _start) {
_spans.add(_highlightSpan(_currentHighlight));
_start += _currentHighlight.length;
} else {
_spans
.add(_normalSpan(text.substring(_start, _currentIndex), context));
_spans.add(_highlightSpan(_currentHighlight));
_start = _currentIndex + _currentHighlight.length;
}
} else {
_spans.add(_normalSpan(text.substring(_start, text.length), context));
break;
}
}
return _spans;
}
TextSpan _highlightSpan(String value) {
return TextSpan(
text: value,
style: style?.copyWith(
backgroundColor: color,
),
);
}
TextSpan _normalSpan(String value, BuildContext context) {
return TextSpan(
text: value,
style: style ?? Theme.of(context).textTheme.bodyMedium,
);
}
}