Skip to content

Commit bcf23ed

Browse files
committed
feat(vector_graphics): add imageBuilder property to VectorGraphic
Adds an optional imageBuilder callback that wraps the loaded vector graphic widget, enabling post-load decoration such as borders or gesture detectors without affecting placeholder or error states. Resolves flutter/flutter#182635
1 parent 12b43a1 commit bcf23ed

5 files changed

Lines changed: 88 additions & 2 deletions

File tree

packages/vector_graphics/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
## NEXT
1+
## 1.1.20
22

33
* Updates minimum supported SDK version to Flutter 3.35/Dart 3.9.
4+
* Adds `imageBuilder` property to `VectorGraphic` for wrapping the loaded vector graphic widget.
45

56
## 1.1.19
67

packages/vector_graphics/lib/src/vector_graphics.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ enum RenderingStrategy {
4747
typedef VectorGraphicsErrorWidget =
4848
Widget Function(BuildContext context, Object error, StackTrace stackTrace);
4949

50+
/// The signature that [VectorGraphic.imageBuilder] uses to wrap the
51+
/// successfully loaded vector graphic widget.
52+
typedef VectorGraphicsImageWidget =
53+
Widget Function(BuildContext context, Widget child);
54+
5055
/// A vector graphic/flutter_svg compatibility shim.
5156
VectorGraphic createCompatVectorGraphic({
5257
Key? key,
@@ -61,6 +66,7 @@ VectorGraphic createCompatVectorGraphic({
6166
Duration? transitionDuration,
6267
WidgetBuilder? placeholderBuilder,
6368
VectorGraphicsErrorWidget? errorBuilder,
69+
VectorGraphicsImageWidget? imageBuilder,
6470
ColorFilter? colorFilter,
6571
Animation<double>? opacity,
6672
RenderingStrategy strategy = RenderingStrategy.picture,
@@ -80,6 +86,7 @@ VectorGraphic createCompatVectorGraphic({
8086
transitionDuration: transitionDuration,
8187
placeholderBuilder: placeholderBuilder,
8288
errorBuilder: errorBuilder,
89+
imageBuilder: imageBuilder,
8390
colorFilter: colorFilter,
8491
opacity: opacity,
8592
strategy: strategy,
@@ -120,6 +127,7 @@ class VectorGraphic extends StatefulWidget {
120127
this.transitionDuration,
121128
this.placeholderBuilder,
122129
this.errorBuilder,
130+
this.imageBuilder,
123131
this.colorFilter,
124132
this.opacity,
125133
this.clipViewbox = true,
@@ -140,6 +148,7 @@ class VectorGraphic extends StatefulWidget {
140148
this.transitionDuration,
141149
this.placeholderBuilder,
142150
this.errorBuilder,
151+
this.imageBuilder,
143152
this.colorFilter,
144153
this.opacity,
145154
this.strategy = RenderingStrategy.picture,
@@ -219,6 +228,13 @@ class VectorGraphic extends StatefulWidget {
219228
/// A callback that fires if some exception happens during data acquisition or decoding.
220229
final VectorGraphicsErrorWidget? errorBuilder;
221230

231+
/// A builder that wraps the successfully loaded vector graphic widget.
232+
///
233+
/// This builder is only called when the vector graphic has been successfully
234+
/// loaded and is ready to be painted. It can be used to apply decorations
235+
/// such as borders or shadows only when the image is available.
236+
final VectorGraphicsImageWidget? imageBuilder;
237+
222238
/// Set transition duration while switching from placeholder to url image
223239
final Duration? transitionDuration;
224240

@@ -517,6 +533,10 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
517533
child: SizedBox.fromSize(size: pictureInfo.size, child: child),
518534
),
519535
);
536+
537+
if (widget.imageBuilder != null) {
538+
child = widget.imageBuilder!(context, child);
539+
}
520540
} else if (_error != null && widget.errorBuilder != null) {
521541
child = widget.errorBuilder!(
522542
context,

packages/vector_graphics/lib/vector_graphics_compat.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ export 'src/vector_graphics.dart'
1111
RenderingStrategy,
1212
VectorGraphic,
1313
VectorGraphicUtilities,
14+
VectorGraphicsImageWidget,
1415
createCompatVectorGraphic,
1516
vg;

packages/vector_graphics/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: vector_graphics
22
description: A vector graphics rendering package for Flutter using a binary encoding.
33
repository: https://github.com/flutter/packages/tree/main/packages/vector_graphics
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+vector_graphics%22
5-
version: 1.1.19
5+
version: 1.1.20
66

77
environment:
88
sdk: ^3.9.0

packages/vector_graphics/test/vector_graphics_test.dart

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,70 @@ void main() {
690690
expect(matrix.row1.y, 1);
691691
});
692692

693+
testWidgets('imageBuilder wraps the loaded vector graphic', (
694+
WidgetTester tester,
695+
) async {
696+
final buffer = VectorGraphicsBuffer();
697+
codec.writeSize(buffer, 100, 200);
698+
final ByteData data = buffer.done();
699+
700+
await tester.pumpWidget(
701+
Directionality(
702+
textDirection: TextDirection.ltr,
703+
child: VectorGraphic(
704+
loader: TestBytesLoader(data),
705+
imageBuilder: (BuildContext context, Widget child) {
706+
return Container(
707+
key: const ValueKey<String>('image-builder'),
708+
child: child,
709+
);
710+
},
711+
),
712+
),
713+
);
714+
await tester.pumpAndSettle();
715+
716+
expect(find.byKey(const ValueKey<String>('image-builder')), findsOneWidget);
717+
});
718+
719+
testWidgets('imageBuilder is not called during placeholder state', (
720+
WidgetTester tester,
721+
) async {
722+
final completer = Completer<ByteData>();
723+
724+
await tester.pumpWidget(
725+
Directionality(
726+
textDirection: TextDirection.ltr,
727+
child: VectorGraphic(
728+
loader: DelayedBytesLoader(completer.future),
729+
imageBuilder: (BuildContext context, Widget child) {
730+
return Container(
731+
key: const ValueKey<String>('image-builder'),
732+
child: child,
733+
);
734+
},
735+
placeholderBuilder: (BuildContext context) {
736+
return Container(key: const ValueKey<String>('placeholder'));
737+
},
738+
),
739+
),
740+
);
741+
742+
// During loading: placeholder visible, imageBuilder not called
743+
expect(find.byKey(const ValueKey<String>('placeholder')), findsOneWidget);
744+
expect(find.byKey(const ValueKey<String>('image-builder')), findsNothing);
745+
746+
// Complete loading
747+
final buffer = VectorGraphicsBuffer();
748+
codec.writeSize(buffer, 100, 200);
749+
completer.complete(buffer.done());
750+
await tester.pumpAndSettle();
751+
752+
// After loading: imageBuilder visible, placeholder gone
753+
expect(find.byKey(const ValueKey<String>('image-builder')), findsOneWidget);
754+
expect(find.byKey(const ValueKey<String>('placeholder')), findsNothing);
755+
});
756+
693757
testWidgets('VectorGraphicsWidget can handle errors from bytes loader', (
694758
WidgetTester tester,
695759
) async {

0 commit comments

Comments
 (0)