You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
9.5 KiB
257 lines
9.5 KiB
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:sonnat/core/extensions/number_extension.dart';
|
|
import 'package:sonnat/core/html/custom_render.dart';
|
|
import 'package:sonnat/core/html/flutter_html.dart';
|
|
import 'package:sonnat/core/html/src/style/fontsize.dart';
|
|
import 'package:sonnat/core/html/src/style/length.dart';
|
|
import 'package:sonnat/core/html/src/style/lineheight.dart';
|
|
import 'package:sonnat/core/html/string_proccess.dart';
|
|
import 'package:sonnat/core/html/style.dart';
|
|
import 'package:sonnat/core/player_widgets/audio_player.dart';
|
|
import 'package:sonnat/core/player_widgets/video_player.dart';
|
|
import 'package:sonnat/core/theme/app_colors.dart';
|
|
import 'package:sonnat/core/theme/app_theme.dart';
|
|
import 'package:sonnat/core/theme/reader_theme.dart';
|
|
import 'package:sonnat/core/utils/app_utils.dart';
|
|
import 'package:sonnat/core/widgets/show_image_widget.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
class HTMLViewer extends StatelessWidget {
|
|
final String htmlContent;
|
|
final double fontSizeFactor;
|
|
final bool needToReplaceTags;
|
|
final ReaderTheme? theme;
|
|
final String? searchHighLight;
|
|
final double baseFontSize = 16.0;
|
|
final Color? textColor;
|
|
|
|
const HTMLViewer({
|
|
super.key,
|
|
required this.htmlContent,
|
|
this.fontSizeFactor = 1,
|
|
this.needToReplaceTags = false,
|
|
this.theme = ReaderTheme.light,
|
|
this.searchHighLight,
|
|
this.textColor,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
var style = AppTheme.instance.fontCreator(
|
|
17,
|
|
FontWeights.regular,
|
|
AppColors.settingSemiBlack,
|
|
FontFamilyName.segoeui,
|
|
-0.0,
|
|
1.5,
|
|
);
|
|
|
|
Widget html = Builder(
|
|
builder: (context) {
|
|
double lineHeight = Theme.of(context).textTheme.displayLarge?.height ?? 1.1;
|
|
return Html(
|
|
data: needToReplaceTags
|
|
? htmlContent.replaceTHeader().replaceQHeader().replaceQText().replaceQAnswer().replaceTextStyle()
|
|
: htmlContent.replaceTextStyle(),
|
|
onLinkTap: (url, context, attributes, element) {
|
|
if (url == null) {
|
|
return;
|
|
}
|
|
launchUrl(Uri.parse(url)).then((value) {
|
|
return null;
|
|
});
|
|
},
|
|
customRenders: {
|
|
_stringMatcher('video'): CustomRender.widget(widget: (context, buildChildren) {
|
|
return _RoundFrame(
|
|
child: VideoPlayer(
|
|
url: context.tree.element!.attributes['src'] ?? '',
|
|
),
|
|
);
|
|
}),
|
|
_stringMatcher('img'): CustomRender.widget(widget: (renderContext, buildChildren) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
if (renderContext.tree.element!.attributes['src'] == null) {
|
|
return;
|
|
}
|
|
_openImage(
|
|
imageUrl: renderContext.tree.element!.attributes['src'] ?? '',
|
|
context: context,
|
|
);
|
|
},
|
|
child: _RoundFrame(
|
|
child: CachedNetworkImage(
|
|
imageUrl: renderContext.tree.element!.attributes['src'] ?? '',
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
_stringMatcher('audio'): CustomRender.widget(widget: (context, buildChildren) {
|
|
return AudioPlayer(
|
|
url: context.tree.element!.nodes[1].attributes['src'] ?? '',
|
|
);
|
|
}),
|
|
_stringMatcher('q_header'): CustomRender.widget(widget: (context, buildChildren) {
|
|
if (context.tree.element?.hasChildNodes() ?? false) {
|
|
if (context.tree.element?.firstChild?.text != null) {
|
|
String txt = context.tree.element?.firstChild?.text ?? '';
|
|
return QHeaderTextShower(
|
|
title: txt,
|
|
searchHighLight: searchHighLight,
|
|
fontSizeFactor: fontSizeFactor,
|
|
);
|
|
}
|
|
}
|
|
return const _RoundFrame(child: SizedBox());
|
|
}),
|
|
_stringMatcher('q_text'): CustomRender.widget(widget: (context, buildChildren) {
|
|
if (context.tree.element?.hasChildNodes() ?? false) {
|
|
if (context.tree.element?.firstChild?.text != null) {
|
|
String txt = context.tree.element?.firstChild?.text ?? '';
|
|
return QTextShower(
|
|
title: txt,
|
|
searchHighLight: searchHighLight,
|
|
fontSizeFactor: fontSizeFactor,
|
|
theme: theme,
|
|
);
|
|
}
|
|
}
|
|
return const _RoundFrame(child: SizedBox());
|
|
}),
|
|
_stringMatcher('q_answer'): CustomRender.widget(widget: (context, buildChildren) {
|
|
if (context.tree.element?.hasChildNodes() ?? false) {
|
|
if (context.tree.element?.firstChild?.text != null) {
|
|
String txt = context.tree.element?.firstChild?.text ?? '';
|
|
return QAnswerShower(
|
|
title: txt,
|
|
searchHighLight: searchHighLight,
|
|
fontSizeFactor: fontSizeFactor,
|
|
theme: theme,
|
|
);
|
|
}
|
|
}
|
|
return const _RoundFrame(child: SizedBox());
|
|
}),
|
|
_stringMatcher('t_header'): CustomRender.widget(widget: (context, buildChildren) {
|
|
if (context.tree.element?.hasChildNodes() ?? false) {
|
|
if (context.tree.element?.firstChild?.text != null) {
|
|
String txt = context.tree.element?.firstChild?.text ?? '';
|
|
return THeaderTextShower(
|
|
title: txt,
|
|
searchHighLight: searchHighLight,
|
|
fontSizeFactor: fontSizeFactor,
|
|
theme: theme,
|
|
);
|
|
}
|
|
}
|
|
return const _RoundFrame(child: SizedBox());
|
|
}),
|
|
},
|
|
style: {
|
|
'p': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
fontSize: FontSize(fontSizeFactor * 1.3, Unit.rem),
|
|
textAlign: TextAlign.justify,
|
|
),
|
|
'h1': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 2.3, Unit.rem),
|
|
),
|
|
'h2': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 2.1, Unit.rem),
|
|
),
|
|
'h3': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 1.9, Unit.rem),
|
|
),
|
|
'h4': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 1.7, Unit.rem),
|
|
),
|
|
'h5': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 1.6, Unit.rem),
|
|
),
|
|
'h6': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 1.4, Unit.rem),
|
|
),
|
|
'li': Style(
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 1.3, Unit.rem),
|
|
),
|
|
'a': Style(
|
|
color: textColor,
|
|
fontWeight: FontWeight.normal,
|
|
textAlign: TextAlign.justify,
|
|
fontSize: FontSize(fontSizeFactor * 1.3, Unit.rem),
|
|
),
|
|
'ol': Style(
|
|
fontWeight: FontWeight.normal,
|
|
fontSize: FontSize(fontSizeFactor * 1.3, Unit.rem),
|
|
textAlign: TextAlign.justify,
|
|
),
|
|
'html': Style(
|
|
fontSize: FontSize(baseFontSize * fontSizeFactor),
|
|
),
|
|
'*': Style.fromTextStyle(style).copyWith(
|
|
color: textColor,
|
|
lineHeight: LineHeight.rem(lineHeight),
|
|
fontSize: FontSize(fontSizeFactor * baseFontSize),
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
),
|
|
},
|
|
tagsList: Html.tags..addAll(['flutter', 'q_header', 'q_text', 'q_answer', 't_header']),
|
|
);
|
|
},
|
|
);
|
|
|
|
return html;
|
|
}
|
|
|
|
void _openImage({required String imageUrl, required BuildContext context}) {
|
|
Navigator.push(context, MaterialPageRoute(
|
|
builder: (context) {
|
|
return ShowImageWidget(imageUrl);
|
|
},
|
|
));
|
|
}
|
|
}
|
|
|
|
CustomRenderMatcher _stringMatcher(String tag) => (context) => context.tree.element?.localName == tag;
|
|
|
|
class _RoundFrame extends StatelessWidget {
|
|
final Widget child;
|
|
final bool hasFullWidth;
|
|
|
|
const _RoundFrame({super.key, required this.child, this.hasFullWidth = true});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
width: hasFullWidth ? 1.sw : null,
|
|
margin: Utils.instance.singleMargin(top: 7, bottom: 7),
|
|
child: ClipRRect(
|
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
child: child,
|
|
),
|
|
);
|
|
}
|
|
}
|