Skip to content

Commit

Permalink
feat(ib): initial beta support for Mathjax forumulas
Browse files Browse the repository at this point in the history
* This is an initial support for Mathjax forumulas which is implemented using flutter_math_fork.
* Formulas which includes {equation} for auto-numbering, {tag} for custom numbering, [ ], $$ $$ multi-line
formulas are unsupported by flutter_math_fork.
* 75% of the formulas used are working as expected and this initial support should be counted as a baby
step towards the stability of future Mathjax formulas.
* Flutter_markdown allows either all the selectable widgets in the content or none which means our
non-selectable custom Widget which renders Mathjax will break down the support for selectable texts all
over Interactive Book. If we enable selectable, Mathjax widget will not render inline like normal
paragraph texts instead it will be in a new-line behaving like a block widget with line breaks.

(I have won but at what cost Meme here)
https://i.redd.it/lwq00qbbna841.png

Signed-off-by: Manjot Sidhu <manjot.techie@gmail.com>
  • Loading branch information
manjotsidhu committed Aug 5, 2021
1 parent 7144fd8 commit 4511344
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 5 deletions.
41 changes: 41 additions & 0 deletions lib/ui/views/ib/builders/ib_mathjax_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_math_fork/flutter_math.dart';
import 'package:markdown/markdown.dart' as md;

/// IbMathjaxBuilder builds RichText widget for the Mathjax Support.
///
/// It will be inserted/merged inline only if selectable is set to false
/// which means neighbouring widgets should also be RichText so that
/// their children's are merged together otherwise it's rendered
/// as any other block widget with line breaks.
class IbMathjaxBuilder extends MarkdownElementBuilder {
IbMathjaxBuilder();

@override
Widget visitElementAfter(md.Element element, TextStyle preferredStyle) {
var _content = element.textContent.trim();

return RichText(
text: TextSpan(
children: [
TextSpan(
// Intentional Nesting due to a bug in flutter_markdown
children: [
WidgetSpan(
child: SizedBox(
child: Wrap(
spacing: 1.0,
runSpacing: 5.0,
direction: Axis.vertical,
children: <Widget>[Math.tex(_content)],
),
),
),
],
),
],
),
);
}
}
5 changes: 4 additions & 1 deletion lib/ui/views/ib/ib_page_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:mobile_app/models/ib/ib_page_data.dart';
import 'package:mobile_app/ui/views/base_view.dart';
import 'package:mobile_app/ui/views/ib/builders/ib_chapter_contents_builder.dart';
import 'package:mobile_app/ui/views/ib/builders/ib_interaction_builder.dart';
import 'package:mobile_app/ui/views/ib/builders/ib_mathjax_builder.dart';
import 'package:mobile_app/ui/views/ib/builders/ib_pop_quiz_builder.dart';
import 'package:mobile_app/ui/views/ib/builders/ib_subscript_builder.dart';
import 'package:mobile_app/ui/views/ib/builders/ib_superscript_builder.dart';
Expand All @@ -19,6 +20,7 @@ import 'package:mobile_app/ui/views/ib/syntaxes/ib_embed_syntax.dart';
import 'package:mobile_app/ui/views/ib/syntaxes/ib_filter_syntax.dart';
import 'package:mobile_app/ui/views/ib/syntaxes/ib_inline_html_syntax.dart';
import 'package:mobile_app/ui/views/ib/syntaxes/ib_liquid_syntax.dart';
import 'package:mobile_app/ui/views/ib/syntaxes/ib_mathjax_syntax.dart';
import 'package:mobile_app/ui/views/ib/syntaxes/ib_md_tag_syntax.dart';
import 'package:mobile_app/utils/url_launcher.dart';
import 'package:mobile_app/viewmodels/ib/ib_page_viewmodel.dart';
Expand Down Expand Up @@ -130,11 +132,11 @@ class _IbPageViewState extends State<IbPageView> {
final _inlineBuilders = {
'sup': IbSuperscriptBuilder(),
'sub': IbSubscriptBuilder(),
'mathjax': IbMathjaxBuilder(),
};

return MarkdownBody(
data: data.content,
selectable: true,
imageDirectory: EnvironmentConfig.IB_BASE_URL,
imageBuilder: _buildMarkdownImage,
onTapLink: _onTapLink,
Expand All @@ -159,6 +161,7 @@ class _IbPageViewState extends State<IbPageView> {
],
[
IbInlineHtmlSyntax(builders: _inlineBuilders),
IbMathjaxSyntax(),
md.EmojiSyntax(),
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes,
],
Expand Down
19 changes: 19 additions & 0 deletions lib/ui/views/ib/syntaxes/ib_mathjax_syntax.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:markdown/markdown.dart' as md;

class IbMathjaxSyntax extends md.InlineSyntax {
IbMathjaxSyntax() : super(_pattern);

/// This is not the perfect pattern for any Mathjax formula
/// It's made in accordance to what flutter_math_fork works with
/// [ ], $$ $$ and muti-line formulas are unsupported
static const String _pattern = r'\$([^$\n\r]+)\$';

@override
bool onMatch(md.InlineParser parser, Match match) {
if (match[1] != null) {
parser.addNode(md.Element.text('mathjax', match[1]));
}

return true;
}
}
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ packages:
source: git
version: "0.6.2"
flutter_math_fork:
dependency: transitive
dependency: "direct main"
description:
name: flutter_math_fork
url: "https://pub.dartlang.org"
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ dependencies:
html_unescape: ^2.0.0
hive: ^2.0.4
hive_flutter: ^1.1.0
flutter_math_fork: ^0.3.3+1

dev_dependencies:
flutter_test:
Expand Down
10 changes: 7 additions & 3 deletions test/ui_tests/ib/ib_page_view_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,13 @@ void main() {
expect(find.byType(FloatingActionButton), findsNWidgets(2));

// Finds IbContent Widgets
expect(find.text('Interactive-Book'), findsOneWidget);
expect(find.text('Learn Digital Logic Design easily.'), findsOneWidget);
expect(find.text('Audience'), findsOneWidget);
expect(find.byWidgetPredicate((widget) {
return widget is RichText &&
(widget.text.toPlainText() == 'Interactive-Book' ||
widget.text.toPlainText() ==
'Learn Digital Logic Design easily.' ||
widget.text.toPlainText() == 'Audience');
}), findsNWidgets(3));
expect(find.byType(Divider), findsNWidgets(1));
});
});
Expand Down

0 comments on commit 4511344

Please sign in to comment.