TF-2122 Fix highlight and font search result incorrect

This commit is contained in:
dab246
2024-04-12 14:38:16 +07:00
committed by Dat H. Pham
parent 6abe3cc3f0
commit a2e6b78576
4 changed files with 93 additions and 109 deletions
@@ -1,5 +1,4 @@
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
extension StringExtension on String { extension StringExtension on String {
@@ -39,11 +38,4 @@ extension StringExtension on String {
return ''; return '';
} }
} }
String get overflow {
return characters
.replaceAll(Characters(''), Characters('\u{200B}'))
.replaceAll(Characters('-'), Characters('\u{2011}'))
.toString();
}
} }
@@ -1,66 +1,62 @@
import 'package:core/presentation/utils/style_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RichTextBuilder { class RichTextBuilder extends StatelessWidget {
final String _textOrigin; final String textOrigin;
final String _wordToStyle; final String wordToStyle;
final TextStyle _styleOrigin; final TextStyle styleOrigin;
final TextStyle _styleWord; final TextStyle styleWord;
Key? _key; const RichTextBuilder({
int? _maxLines; super.key,
TextOverflow? _overflow; required this.textOrigin,
required this.wordToStyle,
required this.styleOrigin,
required this.styleWord,
});
RichTextBuilder( @override
this._textOrigin, Widget build(BuildContext context) {
this._wordToStyle, return Text.rich(
this._styleOrigin, TextSpan(
this._styleWord, style: styleOrigin,
); children: _getSpans(
text: textOrigin,
void key(Key key) { word: wordToStyle,
_key = key; styleOrigin: styleOrigin,
styleWord: styleWord,
)
),
style: styleOrigin,
maxLines: 1,
overflow: TextOverflow.ellipsis
);
} }
void maxLines(int maxLines) { List<TextSpan> _getSpans({
_maxLines = maxLines; required String text,
} required String word,
required TextStyle styleOrigin,
void setOverflow(TextOverflow textOverflow) { required TextStyle styleWord,
_overflow = textOverflow; }) {
}
RichText build() {
return RichText(
key: _key,
maxLines: _maxLines ?? 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: _overflow ?? CommonTextStyle.defaultTextOverFlow,
text: TextSpan(
style: _styleOrigin,
children: _getSpans(_textOrigin, _wordToStyle, _styleWord)));
}
List<TextSpan> _getSpans(String text, String matchWord, TextStyle style) {
List<TextSpan> spans = []; List<TextSpan> spans = [];
int spanBoundary = 0; int spanBoundary = 0;
do { do {
// look for the next match // look for the next match
final startIndex = text.toLowerCase().indexOf(matchWord.toLowerCase(), spanBoundary); final startIndex = text.toLowerCase().indexOf(word.toLowerCase(), spanBoundary);
// if no more matches then add the rest of the string without style // if no more matches then add the rest of the string without style
if (startIndex == -1) { if (startIndex == -1) {
spans.add(TextSpan(text: text.substring(spanBoundary))); spans.add(TextSpan(text: text.substring(spanBoundary), style: styleOrigin));
return spans; return spans;
} }
// add any unStyled text before the next match // add any unStyled text before the next match
if (startIndex > spanBoundary) { if (startIndex > spanBoundary) {
spans.add(TextSpan(text: text.substring(spanBoundary, startIndex))); spans.add(TextSpan(text: text.substring(spanBoundary, startIndex), style: styleOrigin));
} }
// style the matched text // style the matched text
final endIndex = startIndex + matchWord.length; final endIndex = startIndex + word.length;
final spanText = text.substring(startIndex, endIndex); final spanText = text.substring(startIndex, endIndex);
spans.add(TextSpan(text: spanText, style: style)); spans.add(TextSpan(text: spanText, style: styleWord));
// mark the boundary to start the next search from // mark the boundary to start the next search from
spanBoundary = endIndex; spanBoundary = endIndex;
// continue until there are no more matches // continue until there are no more matches
@@ -1,7 +1,4 @@
import 'package:core/presentation/extensions/string_extension.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class TextOverflowBuilder extends StatelessWidget { class TextOverflowBuilder extends StatelessWidget {
@@ -14,22 +11,23 @@ class TextOverflowBuilder extends StatelessWidget {
final TextDirection? textDirection; final TextDirection? textDirection;
final TextOverflow? overflow; final TextOverflow? overflow;
const TextOverflowBuilder(this.data, { const TextOverflowBuilder(
super.key, this.data,
this.style, {
this.textAlign, super.key,
this.softWrap = CommonTextStyle.defaultSoftWrap, this.style,
this.maxLines = 1, this.textAlign,
this.textDirection, this.softWrap = true,
this.overflow = CommonTextStyle.defaultTextOverFlow, this.maxLines = 1,
}); this.textDirection,
this.overflow = TextOverflow.ellipsis,
}
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Text( return Text(
DirectionUtils.isDirectionRTLByLanguage(context) data,
? data
: data.overflow,
style: style, style: style,
textAlign: textAlign, textAlign: textAlign,
softWrap: softWrap, softWrap: softWrap,
@@ -1,12 +1,10 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/extensions/string_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/text/rich_text_builder.dart'; import 'package:core/presentation/views/text/rich_text_builder.dart';
import 'package:core/presentation/views/text/text_overflow_builder.dart'; import 'package:core/presentation/views/text/text_overflow_builder.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@@ -88,20 +86,20 @@ mixin BaseEmailItemTile {
) { ) {
if (isSearchEnabled(isSearchEmailRunning, query)) { if (isSearchEnabled(isSearchEmailRunning, query)) {
return RichTextBuilder( return RichTextBuilder(
DirectionUtils.isDirectionRTLByLanguage(context) textOrigin: informationSender(email, mailbox),
? informationSender(email, mailbox) wordToStyle: query?.value ?? '',
: informationSender(email, mailbox).overflow, styleOrigin: TextStyle(
query?.value ?? '', fontSize: 15,
TextStyle( color: buildTextColorForReadEmail(email),
fontSize: 15, fontWeight: buildFontForReadEmail(email)
color: buildTextColorForReadEmail(email), ),
fontWeight: buildFontForReadEmail(email)), styleWord: TextStyle(
TextStyle( fontSize: 15,
fontSize: 15, color: buildTextColorForReadEmail(email),
color: buildTextColorForReadEmail(email), backgroundColor: AppColor.bgWordSearch,
backgroundColor: AppColor.bgWordSearch, fontWeight: buildFontForReadEmail(email)
fontWeight: buildFontForReadEmail(email)) )
).build(); );
} else { } else {
return TextOverflowBuilder( return TextOverflowBuilder(
informationSender(email, mailbox), informationSender(email, mailbox),
@@ -122,20 +120,20 @@ mixin BaseEmailItemTile {
) { ) {
if (isSearchEnabled(isSearchEmailRunning, query)) { if (isSearchEnabled(isSearchEmailRunning, query)) {
return RichTextBuilder( return RichTextBuilder(
DirectionUtils.isDirectionRTLByLanguage(context) textOrigin: email.getEmailTitle(),
? email.getEmailTitle() wordToStyle: query?.value ?? '',
: email.getEmailTitle().overflow, styleOrigin: TextStyle(
query?.value ?? '', fontSize: 13,
TextStyle( color: buildTextColorForReadEmail(email),
fontSize: 13, fontWeight: buildFontForReadEmail(email)
color: buildTextColorForReadEmail(email), ),
fontWeight: buildFontForReadEmail(email)), styleWord: TextStyle(
TextStyle( fontSize: 13,
fontSize: 13, backgroundColor: AppColor.bgWordSearch,
backgroundColor: AppColor.bgWordSearch, color: buildTextColorForReadEmail(email),
color: buildTextColorForReadEmail(email), fontWeight: buildFontForReadEmail(email)
fontWeight: buildFontForReadEmail(email)) )
).build(); );
} else { } else {
return TextOverflowBuilder( return TextOverflowBuilder(
email.getEmailTitle(), email.getEmailTitle(),
@@ -155,19 +153,19 @@ mixin BaseEmailItemTile {
) { ) {
if (isSearchEnabled(isSearchEmailRunning, query)) { if (isSearchEnabled(isSearchEmailRunning, query)) {
return RichTextBuilder( return RichTextBuilder(
DirectionUtils.isDirectionRTLByLanguage(context) textOrigin: email.getPartialContent(),
? email.getPartialContent() wordToStyle: query?.value ?? '',
: email.getPartialContent().overflow, styleOrigin: const TextStyle(
query?.value ?? '', fontSize: 13,
const TextStyle( color: AppColor.colorContentEmail,
fontSize: 13, fontWeight: FontWeight.normal
color: AppColor.colorContentEmail, ),
fontWeight: FontWeight.normal), styleWord: const TextStyle(
const TextStyle( fontSize: 13,
fontSize: 13, color: AppColor.colorContentEmail,
color: AppColor.colorContentEmail, backgroundColor: AppColor.bgWordSearch
backgroundColor: AppColor.bgWordSearch) )
).build(); );
} else { } else {
return TextOverflowBuilder( return TextOverflowBuilder(
email.getPartialContent(), email.getPartialContent(),