Skip to content

[flutter_markdown] Added sizedImageBuilder to Markdown widget #6739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
22180cc
Added height and width to image builder
M97Chahboun May 15, 2024
c8b90a9
Updated CHANGELOG file
M97Chahboun May 15, 2024
025bf8f
format tests
M97Chahboun May 15, 2024
ce61fb5
Update pubspec.yaml
M97Chahboun May 18, 2024
a79a517
Merge branch 'main' into main
M97Chahboun May 18, 2024
d8192d1
Adds image builder height and width tests
M97Chahboun May 19, 2024
2ef0b8a
Added size to precacheImage tests
M97Chahboun May 19, 2024
af07d08
Merge branch 'main' into main
M97Chahboun May 27, 2024
efb8038
Removed unnecessary size for precacheImage test method
M97Chahboun Jun 5, 2024
a928995
Merge branch 'main' into main
M97Chahboun Sep 4, 2024
0357934
Revert custom image builder test changes
M97Chahboun Dec 4, 2024
6356ebf
Merge branch 'main' of https://github.com/flutter/packages
M97Chahboun Dec 4, 2024
a6ce01f
Added custom image builder test width and height
M97Chahboun Dec 4, 2024
e2729ce
Added golden test for image size with builder
M97Chahboun Dec 4, 2024
e56c5b4
Added custom image builder test width and height to image group test
M97Chahboun Dec 4, 2024
ade6d95
Make image configurations extensible by class parameter
M97Chahboun Dec 5, 2024
c4ddc92
Updated CHANGELOG
M97Chahboun Dec 5, 2024
2a341cb
Mark imageBuilder as Deprecated
M97Chahboun Dec 5, 2024
d20def3
Merge branch 'main' into main
M97Chahboun Dec 8, 2024
15193de
Updated MarkdownBody widget
M97Chahboun Dec 22, 2024
0ced948
Merge branch 'main' of https://github.com/flutter/packages
M97Chahboun Dec 22, 2024
b54c7bf
Merge branch 'main' into main
M97Chahboun Jan 5, 2025
b8efb0e
Merge branch 'main' of https://github.com/flutter/packages
M97Chahboun Feb 10, 2025
8a6497e
typo fix
M97Chahboun Feb 10, 2025
9f558c7
format builder.dart
M97Chahboun Feb 10, 2025
3b80f5b
format tests
M97Chahboun Feb 10, 2025
64a57a2
Merge branch 'main' into main
M97Chahboun Feb 10, 2025
df12f5a
Merge branch 'main' into main
M97Chahboun Feb 10, 2025
f6c518c
Merge branch 'flutter:main' into main
M97Chahboun Feb 11, 2025
0eea20f
Merge branch 'main' into main
M97Chahboun Feb 11, 2025
ac01be0
Merge branch 'main' into main
M97Chahboun Feb 12, 2025
672b44d
Merge branch 'main' into main
M97Chahboun Feb 13, 2025
86717f9
Solved fail test
M97Chahboun Feb 13, 2025
ef8b046
solve package version
M97Chahboun Feb 15, 2025
24b8297
updated imageBuilder & sizedImageBuilder docs
M97Chahboun Feb 18, 2025
e42ac4b
Improved _buildImage if statement
M97Chahboun Feb 18, 2025
ed8db58
Updated MarkdownSizedImageBuilder doc
M97Chahboun Feb 18, 2025
af00261
Adds assert that both aren't passed.
M97Chahboun Feb 18, 2025
99d08a9
format codes
M97Chahboun Feb 18, 2025
4f02de0
removed license comment
M97Chahboun Feb 19, 2025
c8acdbd
Improves docs
M97Chahboun Mar 8, 2025
698f74c
format code & mark imageBuilder as Deprecated
M97Chahboun Mar 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/flutter_markdown/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.7.7

* Introduces `MarkdownImageConfig` for `sizedImageBuilder` builder.

## 0.7.6+2

* Updates README to indicate that this package will be discontinued.
Expand Down
75 changes: 70 additions & 5 deletions packages/flutter_markdown/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@ class _TableElement {
final List<TableRow> rows = <TableRow>[];
}

/// Holds configuration data for an image in a Markdown document.
class MarkdownImageConfig {
/// Creates a new [MarkdownImageConfig] instance.
MarkdownImageConfig({
required this.uri,
this.title,
this.alt,
this.width,
this.height,
});

/// The URI of the image.
final Uri uri;

/// The title of the image, displayed on hover.
final String? title;

/// The alternative text for the image, displayed if the image cannot be loaded.
final String? alt;

/// The desired width of the image.
final double? width;

/// The desired height of the image.
final double? height;
}

/// A collection of widgets that should be placed adjacent to (inline with)
/// other inline elements in the same parent block.
///
Expand Down Expand Up @@ -105,7 +132,8 @@ class MarkdownBuilder implements md.NodeVisitor {
required this.selectable,
required this.styleSheet,
required this.imageDirectory,
required this.imageBuilder,
@Deprecated('Use sizedImageBuilder instead') this.imageBuilder,
required this.sizedImageBuilder,
required this.checkboxBuilder,
required this.bulletBuilder,
required this.builders,
Expand All @@ -115,7 +143,8 @@ class MarkdownBuilder implements md.NodeVisitor {
this.onSelectionChanged,
this.onTapText,
this.softLineBreak = false,
});
}) : assert(imageBuilder == null || sizedImageBuilder == null,
'Only one of imageBuilder or sizedImageBuilder may be specified.');

/// A delegate that controls how link and `pre` elements behave.
final MarkdownBuilderDelegate delegate;
Expand All @@ -131,9 +160,41 @@ class MarkdownBuilder implements md.NodeVisitor {
/// The base directory holding images referenced by Img tags with local or network file paths.
final String? imageDirectory;

/// Call when build an image widget.
/// {@template flutter_markdown.builder.MarkdownBuilder.imageBuilder}
/// Called to build an image widget.
///
/// This builder allows for custom rendering of images within the Markdown content.
/// It provides the image `Uri`, `title`, and `alt` text.
///
/// **Deprecated:** Use [sizedImageBuilder] instead, which offers more comprehensive
/// image information.
///
/// Only one of [imageBuilder] or [sizedImageBuilder] may be specified.
///
/// {@endtemplate}
@Deprecated('Use sizedImageBuilder instead')
final MarkdownImageBuilder? imageBuilder;

/// {@template flutter_markdown.builder.MarkdownBuilder.sizedImageBuilder}
/// Called to build an image widget with size information.
///
/// This builder allows for custom rendering of images within the Markdown content
/// when size information is available. It provides a [MarkdownImageConfig]
/// containing the `Uri`, `title`, `alt`, `width`, and `height` of the image.
///
/// If both [imageBuilder] and [sizedImageBuilder] are `null`, a default image builder
/// will be used.
/// when size information is available. It provides a [MarkdownImageConfig]
/// containing the `Uri`, `title`, `alt`, `width`, and `height` of the image.
///
/// If both [imageBuilder] and [sizedImageBuilder] are `null`, a default
/// image builder will be used.
///
/// Only one of [imageBuilder] or [sizedImageBuilder] may be specified.
///
/// {@endtemplate}
final MarkdownSizedImageBuilder? sizedImageBuilder;

/// Call when build a checkbox widget.
final MarkdownCheckboxBuilder? checkboxBuilder;

Expand Down Expand Up @@ -619,8 +680,12 @@ class MarkdownBuilder implements md.NodeVisitor {
}

Widget child;
if (imageBuilder != null) {
child = imageBuilder!(uri, title, alt);
if (sizedImageBuilder != null) {
final MarkdownImageConfig config = MarkdownImageConfig(
uri: uri, alt: alt, title: title, height: height, width: width);
child = sizedImageBuilder!(config);
} else if (imageBuilder != null) {
child = imageBuilder!(uri, alt, title);
} else {
child = kDefaultImageBuilder(uri, imageDirectory, width, height);
}
Expand Down
31 changes: 27 additions & 4 deletions packages/flutter_markdown/lib/src/widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ typedef MarkdownOnSelectionChangedCallback = void Function(
typedef MarkdownTapLinkCallback = void Function(
String text, String? href, String title);

/// Signature for custom image builders used by [MarkdownWidget.sizedImageBuilder].
///
/// This signature allows for custom rendering of images within the Markdown
/// content when size information is available. It takes a
/// [MarkdownImageConfig] object as a parameter, which contains information
/// about the image, including:
/// - `uri`: The URI of the image.
/// - `title`: The title of the image.
/// - `alt`: The alternative text for the image.
/// - 'height': The height of the image.
/// - 'width': The width of the image.
///
/// Used by [MarkdownWidget.sizedImageBuilder]
typedef MarkdownSizedImageBuilder = Widget Function(MarkdownImageConfig config);

/// Signature for custom image widget.
///
/// Used by [MarkdownWidget.imageBuilder]
Expand Down Expand Up @@ -220,7 +235,8 @@ abstract class MarkdownWidget extends StatefulWidget {
this.blockSyntaxes,
this.inlineSyntaxes,
this.extensionSet,
this.imageBuilder,
@Deprecated('Use sizedImageBuilder instead') this.imageBuilder,
this.sizedImageBuilder,
this.checkboxBuilder,
this.bulletBuilder,
this.builders = const <String, MarkdownElementBuilder>{},
Expand Down Expand Up @@ -277,9 +293,13 @@ abstract class MarkdownWidget extends StatefulWidget {
/// Defaults to [md.ExtensionSet.gitHubFlavored]
final md.ExtensionSet? extensionSet;

/// Call when build an image widget.
/// {@macro flutter_markdown.builder.MarkdownBuilder.imageBuilder}
@Deprecated('Use sizedImageBuilder instead')
final MarkdownImageBuilder? imageBuilder;

/// {@macro flutter_markdown.builder.MarkdownBuilder.sizedImageBuilder}
final MarkdownSizedImageBuilder? sizedImageBuilder;

/// Call when build a checkbox widget.
final MarkdownCheckboxBuilder? checkboxBuilder;

Expand Down Expand Up @@ -391,6 +411,7 @@ class _MarkdownWidgetState extends State<MarkdownWidget>
styleSheet: styleSheet,
imageDirectory: widget.imageDirectory,
imageBuilder: widget.imageBuilder,
sizedImageBuilder: widget.sizedImageBuilder,
checkboxBuilder: widget.checkboxBuilder,
bulletBuilder: widget.bulletBuilder,
builders: widget.builders,
Expand Down Expand Up @@ -467,7 +488,8 @@ class MarkdownBody extends MarkdownWidget {
super.blockSyntaxes,
super.inlineSyntaxes,
super.extensionSet,
super.imageBuilder,
@Deprecated('Use sizedImageBuilder instead.') super.imageBuilder,
super.sizedImageBuilder,
super.checkboxBuilder,
super.bulletBuilder,
super.builders,
Expand Down Expand Up @@ -522,7 +544,8 @@ class Markdown extends MarkdownWidget {
super.blockSyntaxes,
super.inlineSyntaxes,
super.extensionSet,
super.imageBuilder,
@Deprecated('Use sizedImageBuilder instead.') super.imageBuilder,
super.sizedImageBuilder,
super.checkboxBuilder,
super.bulletBuilder,
super.builders,
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_markdown/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output,
formatted with simple Markdown tags.
repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22
version: 0.7.6+2
version: 0.7.7

environment:
sdk: ^3.4.0
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions packages/flutter_markdown/test/image_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -459,5 +459,60 @@ void defineTests() {
},
skip: kIsWeb, // Goldens are platform-specific.
);

testWidgets(
'custom image builder test width and height',
(WidgetTester tester) async {
const double height = 200;
const double width = 100;
const String data = '![alt](https://img.png#${width}x$height)';
Widget builder(MarkdownImageConfig config) =>
Image.asset('assets/logo.png',
width: config.width, height: config.height);

await tester.pumpWidget(
boilerplate(
MaterialApp(
home: DefaultAssetBundle(
bundle: TestAssetBundle(),
child: Center(
child: Container(
color: Colors.white,
width: 500,
child: Markdown(
data: data,
sizedImageBuilder: builder,
),
),
),
),
),
),
);

final Iterable<Widget> widgets = tester.allWidgets;
final Image image =
widgets.firstWhere((Widget widget) => widget is Image) as Image;

expect(image.image.runtimeType, AssetImage);
expect((image.image as AssetImage).assetName, 'assets/logo.png');
expect(image.width, width);
expect(image.height, height);

await tester.runAsync(() async {
final Element element = tester.element(find.byType(Markdown));
await precacheImage(image.image, element);
});

await tester.pumpAndSettle();

await expectLater(
find.byType(Container),
matchesGoldenFile(
'assets/images/golden/image_test/custom_image_builder_test.png'));
imageCache.clear();
},
skip: kIsWeb,
);
});
}