mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-06 09:15:48 -06:00
128 lines
3.5 KiB
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,
|
|
);
|
|
}
|
|
}
|