Skip to content

Commit 235071c

Browse files
committed
add support math formulas
1 parent 33922ba commit 235071c

File tree

10 files changed

+84
-21
lines changed

10 files changed

+84
-21
lines changed

lib/pages/user.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:habr_app/utils/message_notifier.dart';
3+
import 'package:habr_app/widgets/link.dart';
34
import 'package:provider/provider.dart';
45
import 'package:habr_app/stores/habr_storage.dart';
56

@@ -177,7 +178,7 @@ class AuthorInfoView extends StatelessWidget {
177178
AvatarColorStore().getColor(info.alias, theme.brightness),
178179
borderWidth: 2,
179180
),
180-
Text('@' + info.alias, style: TextStyle(color: theme.primaryColor)),
181+
Text('@' + info.alias, style: TextStyle(color: linkColorFrom(context))),
181182
if (info.fullName != null && info.fullName!.isNotEmpty)
182183
Text("a.k.a. ${info.fullName}"),
183184
Text(info.speciality == null || info.speciality!.isEmpty

lib/utils/html_to_json/element_builders.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,12 @@ class Table implements Node {
157157
@override
158158
String get type => "table";
159159
}
160+
161+
class MathFormula implements Node {
162+
final String formula;
163+
164+
MathFormula(this.formula);
165+
166+
@override
167+
String get type => "formula";
168+
}

lib/utils/html_to_json/transformer.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ Node prepareHtmlBlocElement(dom.Element element) {
218218
findLanguageFromClass(element.classes.toList()),
219219
);
220220
case 'img':
221+
if (element.classes.contains('formula')) {
222+
final alt = element.attributes['alt'] ?? '';
223+
return MathFormula(alt);
224+
}
221225
final url = element.attributes['data-src'] ?? element.attributes['src'];
222226
return Image(url!);
223227
case 'blockquote':

lib/widgets/html_elements/html_elements.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export 'spoiler_block.dart';
33
export 'headline.dart';
44
export 'unordered_list.dart';
55
export 'highlight_code.dart';
6-
export 'iframe.dart';
6+
export 'iframe.dart';
7+
export 'math_formula.dart';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_math_fork/flutter_math.dart';
3+
4+
class MathFormula extends StatelessWidget {
5+
final String formula;
6+
7+
const MathFormula(this.formula, {Key? key}) : super(key: key);
8+
9+
@override
10+
Widget build(BuildContext context) {
11+
return Math.tex(
12+
formula,
13+
mathStyle: MathStyle.text,
14+
textStyle: Theme.of(context).textTheme.bodyText2,
15+
);
16+
}
17+
}

lib/widgets/html_elements/spoiler_block.dart

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:math';
22
import 'package:flutter/material.dart';
3+
import 'package:habr_app/widgets/link.dart';
34

45
class Spoiler extends StatefulWidget {
56
final String? title;
@@ -15,7 +16,7 @@ class _SpoilerState extends State<Spoiler> with TickerProviderStateMixin {
1516
late bool visible;
1617
late Animation _arrowAnimation;
1718
late AnimationController _arrowAnimationController;
18-
static const duration = Duration(milliseconds: 300);
19+
static const duration = Duration(milliseconds: 100);
1920

2021
@override
2122
initState() {
@@ -27,6 +28,12 @@ class _SpoilerState extends State<Spoiler> with TickerProviderStateMixin {
2728
Tween(begin: 0.0, end: pi / 2).animate(_arrowAnimationController);
2829
}
2930

31+
@override
32+
void dispose() {
33+
_arrowAnimationController.dispose();
34+
super.dispose();
35+
}
36+
3037
onTap() {
3138
setState(() {
3239
visible
@@ -50,14 +57,14 @@ class _SpoilerState extends State<Spoiler> with TickerProviderStateMixin {
5057
animation: _arrowAnimationController,
5158
builder: (context, child) => Transform.rotate(
5259
angle: _arrowAnimation.value, child: child),
53-
child: Icon(Icons.arrow_right, color: themeData.primaryColor),
60+
child: Icon(Icons.arrow_right, color: linkColorFrom(context)),
5461
),
5562
Expanded(
5663
child: Text(
5764
widget.title!,
5865
style: TextStyle(
59-
color: themeData.primaryColor,
60-
decorationColor: themeData.primaryColor,
66+
color: linkColorFrom(context),
67+
decorationColor: linkColorFrom(context),
6168
decoration: TextDecoration.underline,
6269
decorationStyle: TextDecorationStyle.dashed,
6370
),
@@ -66,12 +73,15 @@ class _SpoilerState extends State<Spoiler> with TickerProviderStateMixin {
6673
),
6774
onTap: onTap,
6875
),
69-
if (visible) ...[
70-
const SizedBox(
71-
height: 10,
72-
),
73-
widget.child!,
74-
],
76+
SizeTransition(
77+
sizeFactor: Tween<double>(begin: 0, end: 1)
78+
.animate(_arrowAnimationController),
79+
axisAlignment: -1.0,
80+
child: Column(children: [
81+
const SizedBox(height: 10),
82+
widget.child!,
83+
]),
84+
)
7585
],
7686
));
7787
}

lib/widgets/html_view.dart

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ Widget wrapPadding(Widget child, BuildParams params) => params.padding != null
7171
: child;
7272

7373
// may be null
74-
Widget? buildTree(view.Node element, BuildContext context, BuildParams params) {
74+
Widget? buildTree(
75+
view.Node element,
76+
BuildContext context,
77+
BuildParams params, {
78+
bool isInline = false,
79+
}) {
7580
final type = element.type;
7681
if (element is view.HeadLine) {
7782
logInfo('$type ${element.text}');
@@ -115,7 +120,11 @@ Widget? buildTree(view.Node element, BuildContext context, BuildParams params) {
115120
widget = WrappedContainer(
116121
children: [
117122
widget,
118-
Text(element.caption!, style: Theme.of(context).textTheme.subtitle2)
123+
wrapPadding(
124+
Text(element.caption!,
125+
style: Theme.of(context).textTheme.subtitle2),
126+
params,
127+
),
119128
],
120129
distance: 5,
121130
);
@@ -126,6 +135,15 @@ Widget? buildTree(view.Node element, BuildContext context, BuildParams params) {
126135
child: widget,
127136
);
128137
}
138+
} else if (element is view.MathFormula) {
139+
widget = Padding(
140+
child: MathFormula(element.formula),
141+
padding: const EdgeInsets.only(top: 10),
142+
);
143+
if (!isInline) {
144+
widget = Center(child: widget);
145+
widget = wrapPadding(widget, params);
146+
}
129147
} else if (element is view.Code) {
130148
final appSettings = Provider.of<AppSettings>(context, listen: false);
131149
widget = HighlightCode(
@@ -213,7 +231,8 @@ InlineSpan buildInline(
213231
span = InlineTextLink(
214232
title: element.text, url: element.link, context: context);
215233
} else if (element is view.BlockSpan) {
216-
span = WidgetSpan(child: buildTree(element.child, context, params)!);
234+
span = WidgetSpan(
235+
child: buildTree(element.child, context, params, isInline: true)!);
217236
}
218237

219238
return span;

lib/widgets/medium_author_preview.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:habr_app/models/author.dart';
55
import 'package:habr_app/stores/avatar_color_store.dart';
66

77
import 'author_avatar_icon.dart';
8+
import 'package:habr_app/widgets/link.dart';
89

910
class MediumAuthorPreview extends StatelessWidget {
1011
final Author author;
@@ -44,7 +45,7 @@ class MediumAuthorPreview extends StatelessWidget {
4445
TextSpan(text: '@'),
4546
TextSpan(text: author.alias),
4647
],
47-
style: TextStyle(color: theme.primaryColor),
48+
style: TextStyle(color: linkColorFrom(context)),
4849
),
4950
]),
5051
maxLines: 2,

lib/widgets/picture.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Picture extends StatelessWidget {
3030

3131
@override
3232
Widget build(BuildContext context) {
33-
final textTheme = Theme.of(context).textTheme.bodyText2;
33+
final textTheme = Theme.of(context).textTheme.bodyText2!;
3434
final habrStorage = context.watch<HabrStorage>();
3535
return Container(
3636
height: height,
@@ -46,7 +46,7 @@ class Picture extends StatelessWidget {
4646
child: Transform.scale(
4747
scale: 2,
4848
alignment: Alignment.center,
49-
child: SvgPicture.file(file, color: textTheme!.color),
49+
child: SvgPicture.file(file, color: textTheme.color),
5050
));
5151
}
5252
Widget widget = Image.file(
@@ -68,7 +68,7 @@ class Picture extends StatelessWidget {
6868
child: Transform.scale(
6969
scale: 2,
7070
alignment: Alignment.center,
71-
child: SvgPicture.network(url!, color: textTheme!.color),
71+
child: SvgPicture.network(url!, color: textTheme.color),
7272
));
7373
}
7474
Widget widget = Image.network(

pubspec.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ dependencies:
2222
share: ^2.0.1
2323
flutter_highlight: "^0.7.0"
2424
either_dart: ^0.1.3
25-
flutter_svg: "^0.22.0"
25+
flutter_svg: ^1.0.3
26+
flutter_math_fork: ^0.6.2
2627
url_launcher: "^6.0.9"
2728
hive: ^2.0.4
2829
hive_flutter: ^1.1.0
2930
flutter_slidable: 0.6.0
3031
photo_view: ^0.13.0
3132
path_provider: "^2.0.9"
3233
crypto: ">=2.1.5"
33-
provider: ^5.0.0
34+
provider: ^6.0.0
3435
itertools: ">=0.1.0"
3536

3637
dependency_overrides:

0 commit comments

Comments
 (0)