Showing
8 changed files
with
190 additions
and
25 deletions
| @@ -3,7 +3,6 @@ import 'dart:io'; | @@ -3,7 +3,6 @@ import 'dart:io'; | ||
| 3 | 3 | ||
| 4 | import 'package:file_picker/file_picker.dart'; | 4 | import 'package:file_picker/file_picker.dart'; |
| 5 | import 'package:flutter/material.dart'; | 5 | import 'package:flutter/material.dart'; |
| 6 | -// import 'package:flutter/services.dart'; | ||
| 7 | import 'package:tex_markdown/tex_markdown.dart'; | 6 | import 'package:tex_markdown/tex_markdown.dart'; |
| 8 | import 'package:url_launcher/url_launcher_string.dart'; | 7 | import 'package:url_launcher/url_launcher_string.dart'; |
| 9 | 8 | ||
| @@ -71,7 +70,7 @@ class _MyHomePageState extends State<MyHomePage> { | @@ -71,7 +70,7 @@ class _MyHomePageState extends State<MyHomePage> { | ||
| 71 | hello | 70 | hello |
| 72 | --- | 71 | --- |
| 73 | my name is | 72 | my name is |
| 74 | - | 73 | + |
| 75 | **bold$x^2\cfrac a{\cfrac ab}$ text** | 74 | **bold$x^2\cfrac a{\cfrac ab}$ text** |
| 76 | *Italic text$x^2\cfrac a{b}$* | 75 | *Italic text$x^2\cfrac a{b}$* |
| 77 | **hello** | 76 | **hello** |
| @@ -491,7 +491,7 @@ packages: | @@ -491,7 +491,7 @@ packages: | ||
| 491 | path: ".." | 491 | path: ".." |
| 492 | relative: true | 492 | relative: true |
| 493 | source: path | 493 | source: path |
| 494 | - version: "0.1.6" | 494 | + version: "0.1.7" |
| 495 | tex_text: | 495 | tex_text: |
| 496 | dependency: transitive | 496 | dependency: transitive |
| 497 | description: | 497 | description: |
| @@ -67,8 +67,22 @@ class RenderCustomImageError extends RenderProxyBox { | @@ -67,8 +67,22 @@ class RenderCustomImageError extends RenderProxyBox { | ||
| 67 | 67 | ||
| 68 | @override | 68 | @override |
| 69 | void performLayout() { | 69 | void performLayout() { |
| 70 | + if (constraints.hasBoundedHeight && constraints.hasBoundedWidth) { | ||
| 71 | + size = constraints.constrain(Size( | ||
| 72 | + min(constraints.maxWidth, constraints.maxHeight), | ||
| 73 | + min(constraints.maxHeight, constraints.maxWidth))); | ||
| 74 | + return; | ||
| 75 | + } | ||
| 76 | + if (constraints.hasBoundedHeight || constraints.hasBoundedWidth) { | ||
| 77 | + size = constraints.constrain(Size( | ||
| 78 | + min(constraints.maxHeight, constraints.maxWidth), | ||
| 79 | + min(constraints.maxHeight, constraints.maxWidth), | ||
| 80 | + )); | ||
| 81 | + return; | ||
| 82 | + } | ||
| 70 | size = constraints.constrain( | 83 | size = constraints.constrain( |
| 71 | - Size(min(constraints.maxWidth, 80), min(constraints.maxHeight, 80))); | 84 | + const Size(80, 80), |
| 85 | + ); | ||
| 72 | } | 86 | } |
| 73 | 87 | ||
| 74 | @override | 88 | @override |
| @@ -101,3 +115,132 @@ class RenderCustomImageError extends RenderProxyBox { | @@ -101,3 +115,132 @@ class RenderCustomImageError extends RenderProxyBox { | ||
| 101 | ); | 115 | ); |
| 102 | } | 116 | } |
| 103 | } | 117 | } |
| 118 | + | ||
| 119 | +class CustomImageLoading extends LeafRenderObjectWidget { | ||
| 120 | + const CustomImageLoading({ | ||
| 121 | + super.key, | ||
| 122 | + this.iconColor, | ||
| 123 | + this.backgroundColor, | ||
| 124 | + this.outlineColor, | ||
| 125 | + this.progress = 1, | ||
| 126 | + }); | ||
| 127 | + final Color? iconColor; | ||
| 128 | + final Color? backgroundColor; | ||
| 129 | + final Color? outlineColor; | ||
| 130 | + final double progress; | ||
| 131 | + | ||
| 132 | + @override | ||
| 133 | + RenderObject createRenderObject(BuildContext context) { | ||
| 134 | + return RenderCustomImageLoading( | ||
| 135 | + iconColor ?? Theme.of(context).colorScheme.onSurfaceVariant, | ||
| 136 | + backgroundColor ?? Theme.of(context).colorScheme.surfaceVariant, | ||
| 137 | + outlineColor ?? Theme.of(context).colorScheme.outline, | ||
| 138 | + progress, | ||
| 139 | + ); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @override | ||
| 143 | + void updateRenderObject( | ||
| 144 | + BuildContext context, covariant RenderCustomImageLoading renderObject) { | ||
| 145 | + renderObject._backgroundColor = | ||
| 146 | + backgroundColor ?? Theme.of(context).colorScheme.surfaceVariant; | ||
| 147 | + renderObject._iconColor = | ||
| 148 | + iconColor ?? Theme.of(context).colorScheme.onSurfaceVariant; | ||
| 149 | + renderObject._outlineColor = | ||
| 150 | + outlineColor ?? Theme.of(context).colorScheme.outline; | ||
| 151 | + renderObject.progress = progress; | ||
| 152 | + } | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +class RenderCustomImageLoading extends RenderProxyBox { | ||
| 156 | + RenderCustomImageLoading(this._iconColor, this._backgroundColor, | ||
| 157 | + this._outlineColor, this._progress); | ||
| 158 | + Color _iconColor; | ||
| 159 | + Color _outlineColor; | ||
| 160 | + Color _backgroundColor; | ||
| 161 | + double _progress; | ||
| 162 | + set iconColor(Color value) { | ||
| 163 | + if (value == _iconColor) { | ||
| 164 | + return; | ||
| 165 | + } | ||
| 166 | + _iconColor = value; | ||
| 167 | + markNeedsPaint(); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + set backgroundColor(Color value) { | ||
| 171 | + if (value == _backgroundColor) { | ||
| 172 | + return; | ||
| 173 | + } | ||
| 174 | + _backgroundColor = value; | ||
| 175 | + markNeedsPaint(); | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + set outlineColor(Color value) { | ||
| 179 | + if (value == _outlineColor) { | ||
| 180 | + return; | ||
| 181 | + } | ||
| 182 | + _outlineColor = value; | ||
| 183 | + markNeedsPaint(); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + set progress(double value) { | ||
| 187 | + if (value == _progress) { | ||
| 188 | + return; | ||
| 189 | + } | ||
| 190 | + _progress = value; | ||
| 191 | + markNeedsPaint(); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + @override | ||
| 195 | + void performLayout() { | ||
| 196 | + if (constraints.hasBoundedHeight && constraints.hasBoundedWidth) { | ||
| 197 | + size = constraints.constrain(Size( | ||
| 198 | + min(constraints.maxWidth, constraints.maxHeight), | ||
| 199 | + min(constraints.maxHeight, constraints.maxWidth))); | ||
| 200 | + return; | ||
| 201 | + } | ||
| 202 | + if (constraints.hasBoundedHeight || constraints.hasBoundedWidth) { | ||
| 203 | + size = constraints.constrain(Size( | ||
| 204 | + min(constraints.maxHeight, constraints.maxWidth), | ||
| 205 | + min(constraints.maxHeight, constraints.maxWidth), | ||
| 206 | + )); | ||
| 207 | + return; | ||
| 208 | + } | ||
| 209 | + size = constraints.constrain( | ||
| 210 | + const Size(80, 80), | ||
| 211 | + ); | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + @override | ||
| 215 | + void paint(PaintingContext context, Offset offset) { | ||
| 216 | + context.canvas.drawRect( | ||
| 217 | + offset & size, | ||
| 218 | + Paint()..color = _backgroundColor, | ||
| 219 | + ); | ||
| 220 | + context.canvas.drawRect( | ||
| 221 | + offset & size, | ||
| 222 | + Paint() | ||
| 223 | + ..style = PaintingStyle.stroke | ||
| 224 | + ..color = _outlineColor, | ||
| 225 | + ); | ||
| 226 | + const icon = Icons.image; | ||
| 227 | + TextPainter textPainter = TextPainter(textDirection: TextDirection.rtl); | ||
| 228 | + textPainter.text = TextSpan( | ||
| 229 | + text: String.fromCharCode(icon.codePoint), | ||
| 230 | + style: TextStyle( | ||
| 231 | + fontSize: min(min(size.width, size.height), 35), | ||
| 232 | + fontFamily: icon.fontFamily, | ||
| 233 | + color: _iconColor), | ||
| 234 | + ); | ||
| 235 | + textPainter.layout(); | ||
| 236 | + context.canvas.drawRect( | ||
| 237 | + (offset + Offset(0, size.height - 5)) & Size(size.width * _progress, 5), | ||
| 238 | + Paint()..color = _iconColor); | ||
| 239 | + textPainter.paint( | ||
| 240 | + context.canvas, | ||
| 241 | + offset + | ||
| 242 | + Offset(size.width / 2 - textPainter.size.width / 2, | ||
| 243 | + size.height / 2 - textPainter.size.height / 2), | ||
| 244 | + ); | ||
| 245 | + } | ||
| 246 | +} |
| 1 | -import 'dart:math'; | ||
| 2 | - | ||
| 3 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 4 | import 'package:tex_markdown/custom_widgets/custom_divider.dart'; | 2 | import 'package:tex_markdown/custom_widgets/custom_divider.dart'; |
| 5 | import 'package:tex_markdown/custom_widgets/custom_error_image.dart'; | 3 | import 'package:tex_markdown/custom_widgets/custom_error_image.dart'; |
| @@ -72,19 +70,21 @@ abstract class MarkdownComponent { | @@ -72,19 +70,21 @@ abstract class MarkdownComponent { | ||
| 72 | } else { | 70 | } else { |
| 73 | if (each is BlockMd) { | 71 | if (each is BlockMd) { |
| 74 | spans.addAll([ | 72 | spans.addAll([ |
| 75 | - const TextSpan( | 73 | + TextSpan( |
| 76 | text: "\n ", | 74 | text: "\n ", |
| 77 | style: TextStyle( | 75 | style: TextStyle( |
| 78 | fontSize: 0, | 76 | fontSize: 0, |
| 79 | height: 0, | 77 | height: 0, |
| 78 | + color: style?.color, | ||
| 80 | ), | 79 | ), |
| 81 | ), | 80 | ), |
| 82 | each.span(context, element.trim(), style, onLinkTab), | 81 | each.span(context, element.trim(), style, onLinkTab), |
| 83 | - const TextSpan( | 82 | + TextSpan( |
| 84 | text: "\n ", | 83 | text: "\n ", |
| 85 | style: TextStyle( | 84 | style: TextStyle( |
| 86 | fontSize: 0, | 85 | fontSize: 0, |
| 87 | height: 0, | 86 | height: 0, |
| 87 | + color: style?.color, | ||
| 88 | ), | 88 | ), |
| 89 | ), | 89 | ), |
| 90 | ]); | 90 | ]); |
| @@ -505,15 +505,24 @@ class ImageMd extends InlineMd { | @@ -505,15 +505,24 @@ class ImageMd extends InlineMd { | ||
| 505 | child: SizedBox( | 505 | child: SizedBox( |
| 506 | width: width, | 506 | width: width, |
| 507 | height: height, | 507 | height: height, |
| 508 | - child: Image.network( | ||
| 509 | - "${match?[2]}", | 508 | + child: Image( |
| 509 | + image: NetworkImage( | ||
| 510 | + "${match?[2]}", | ||
| 511 | + ), | ||
| 512 | + loadingBuilder: (BuildContext context, Widget child, | ||
| 513 | + ImageChunkEvent? loadingProgress) { | ||
| 514 | + if (loadingProgress == null) { | ||
| 515 | + return child; | ||
| 516 | + } | ||
| 517 | + return CustomImageLoading( | ||
| 518 | + progress: loadingProgress.expectedTotalBytes != null | ||
| 519 | + ? loadingProgress.cumulativeBytesLoaded / | ||
| 520 | + loadingProgress.expectedTotalBytes! | ||
| 521 | + : 1, | ||
| 522 | + ); | ||
| 523 | + }, | ||
| 510 | fit: BoxFit.fill, | 524 | fit: BoxFit.fill, |
| 511 | errorBuilder: (context, error, stackTrace) { | 525 | errorBuilder: (context, error, stackTrace) { |
| 512 | - double size = 35; | ||
| 513 | - // if (height != null && width != null) { | ||
| 514 | - size = min(size, height ?? size); | ||
| 515 | - size = min(size, width ?? size); | ||
| 516 | - // } | ||
| 517 | return const CustomImageError(); | 526 | return const CustomImageError(); |
| 518 | }, | 527 | }, |
| 519 | ), | 528 | ), |
| @@ -53,7 +53,13 @@ $value | @@ -53,7 +53,13 @@ $value | ||
| 53 | RegExp(r"\n\n+"), | 53 | RegExp(r"\n\n+"), |
| 54 | onMatch: (p0) { | 54 | onMatch: (p0) { |
| 55 | list.add( | 55 | list.add( |
| 56 | - const TextSpan(text: "\n\n", style: TextStyle(fontSize: 16)), | 56 | + TextSpan( |
| 57 | + text: "\n\n", | ||
| 58 | + style: TextStyle( | ||
| 59 | + fontSize: 16, | ||
| 60 | + color: style?.color, | ||
| 61 | + ), | ||
| 62 | + ), | ||
| 57 | ); | 63 | ); |
| 58 | return ""; | 64 | return ""; |
| 59 | }, | 65 | }, |
| @@ -79,14 +85,11 @@ $value | @@ -79,14 +85,11 @@ $value | ||
| 79 | maxCol = each.keys.length; | 85 | maxCol = each.keys.length; |
| 80 | } | 86 | } |
| 81 | } | 87 | } |
| 82 | - // if (maxCol == 0) { | ||
| 83 | - // return Text("", style: style); | ||
| 84 | - // } | ||
| 85 | list.addAll( | 88 | list.addAll( |
| 86 | [ | 89 | [ |
| 87 | - const TextSpan( | 90 | + TextSpan( |
| 88 | text: "\n ", | 91 | text: "\n ", |
| 89 | - style: TextStyle(height: 0, fontSize: 0), | 92 | + style: TextStyle(height: 0, fontSize: 0, color: style?.color), |
| 90 | ), | 93 | ), |
| 91 | WidgetSpan( | 94 | WidgetSpan( |
| 92 | child: Table( | 95 | child: Table( |
| @@ -114,9 +117,9 @@ $value | @@ -114,9 +117,9 @@ $value | ||
| 114 | .toList(), | 117 | .toList(), |
| 115 | ), | 118 | ), |
| 116 | ), | 119 | ), |
| 117 | - const TextSpan( | 120 | + TextSpan( |
| 118 | text: "\n ", | 121 | text: "\n ", |
| 119 | - style: TextStyle(height: 0, fontSize: 0), | 122 | + style: TextStyle(height: 0, fontSize: 0, color: style?.color), |
| 120 | ), | 123 | ), |
| 121 | ], | 124 | ], |
| 122 | ); | 125 | ); |
| @@ -132,6 +135,7 @@ $value | @@ -132,6 +135,7 @@ $value | ||
| 132 | return Text.rich( | 135 | return Text.rich( |
| 133 | TextSpan( | 136 | TextSpan( |
| 134 | children: list, | 137 | children: list, |
| 138 | + style: style, | ||
| 135 | ), | 139 | ), |
| 136 | ); | 140 | ); |
| 137 | } | 141 | } |
| @@ -27,6 +27,12 @@ class TexMarkdown extends StatelessWidget { | @@ -27,6 +27,12 @@ class TexMarkdown extends StatelessWidget { | ||
| 27 | 27 | ||
| 28 | @override | 28 | @override |
| 29 | Widget build(BuildContext context) { | 29 | Widget build(BuildContext context) { |
| 30 | - return ClipRRect(child: MdWidget(data.trim())); | 30 | + return ClipRRect( |
| 31 | + child: MdWidget( | ||
| 32 | + data.trim(), | ||
| 33 | + style: style, | ||
| 34 | + onLinkTab: onLinkTab, | ||
| 35 | + followLinkColor: followLinkColor, | ||
| 36 | + )); | ||
| 31 | } | 37 | } |
| 32 | } | 38 | } |
| 1 | name: tex_markdown | 1 | name: tex_markdown |
| 2 | description: This package is used to create flutter widget that can render markdown and latex formulas. It is very simple to use and uses native flutter components. | 2 | description: This package is used to create flutter widget that can render markdown and latex formulas. It is very simple to use and uses native flutter components. |
| 3 | -version: 0.1.6 | 3 | +version: 0.1.7 |
| 4 | homepage: https://github.com/saminsohag/flutter_packages/tree/main/tex_markdown | 4 | homepage: https://github.com/saminsohag/flutter_packages/tree/main/tex_markdown |
| 5 | 5 | ||
| 6 | environment: | 6 | environment: |
-
Please register or login to post a comment