From e7934ba64ce2b975a3a484a735ccc710a172c4da Mon Sep 17 00:00:00 2001 From: Ricardo Dalarme Date: Wed, 9 Apr 2025 22:53:18 -0300 Subject: [PATCH 1/3] feat: expose color mapper --- third_party/packages/flutter_svg/README.md | 48 +++++++ .../example/lib/readme_excerpts.dart | 40 ++++++ third_party/packages/flutter_svg/lib/svg.dart | 25 +++- .../flutter_logo.asset.color_mapper.png | Bin 0 -> 12704 bytes .../flutter_logo.memory.color_mapper.png | Bin 0 -> 12704 bytes .../flutter_logo.network.color_mapper.png | Bin 0 -> 12704 bytes .../flutter_logo.string.color_mapper.png | Bin 0 -> 12704 bytes .../flutter_svg/test/widget_svg_test.dart | 132 ++++++++++++++++++ 8 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 third_party/packages/flutter_svg/test/golden_widget/flutter_logo.asset.color_mapper.png create mode 100644 third_party/packages/flutter_svg/test/golden_widget/flutter_logo.memory.color_mapper.png create mode 100644 third_party/packages/flutter_svg/test/golden_widget/flutter_logo.network.color_mapper.png create mode 100644 third_party/packages/flutter_svg/test/golden_widget/flutter_logo.string.color_mapper.png diff --git a/third_party/packages/flutter_svg/README.md b/third_party/packages/flutter_svg/README.md index d4d205c3eb0..e25ca450bfa 100644 --- a/third_party/packages/flutter_svg/README.md +++ b/third_party/packages/flutter_svg/README.md @@ -33,6 +33,53 @@ final Widget svgIcon = SvgPicture.asset( ); ``` +For more advanced color manipulation, you can use the `colorMapper` property. +This allows you to define a custom mapping function that will be called for +every color encountered during SVG parsing, enabling you to substitute colors +based on various criteria like the color value itself, the element name, or the +attribute name. + +To use this feature, you need to create a class that extends `ColorMapper` and +override the `substitute` method. + +Here's an example of how to implement a `ColorMapper` to replace specific colors in an SVG: + + +```dart +class _MyColorMapper extends ColorMapper { + const _MyColorMapper(); + + @override + Color substitute( + String? id, + String elementName, + String attributeName, + Color color, + ) { + if (color == const Color(0xFFFF0000)) { + return Colors.blue; + } + if (color == const Color(0xFF00FF00)) { + return Colors.yellow; + } + return color; + } +} +// ··· + const String svgString = ''' + + + + +'''; + final Widget svgIcon = SvgPicture.string( + svgString, + colorMapper: const _MyColorMapper(), + ); +``` + +In this example, all red colors in the SVG will be rendered as blue, and all green colors will be rendered as yellow. You can customize the `substitute` method to implement more complex color mapping logic based on your requirements. + The default placeholder is an empty box (`LimitedBox`) - although if a `height` or `width` is specified on the `SvgPicture`, a `SizedBox` will be used instead (which ensures better layout experience). There is currently no way to show an @@ -67,6 +114,7 @@ If you'd like to render the SVG to some other canvas, you can do something like: ```dart import 'dart:ui' as ui; + // ··· const String rawSvg = '''...'''; final PictureInfo pictureInfo = diff --git a/third_party/packages/flutter_svg/example/lib/readme_excerpts.dart b/third_party/packages/flutter_svg/example/lib/readme_excerpts.dart index fc17e62abe3..7fd7f9132ee 100644 --- a/third_party/packages/flutter_svg/example/lib/readme_excerpts.dart +++ b/third_party/packages/flutter_svg/example/lib/readme_excerpts.dart @@ -7,6 +7,7 @@ // #docregion OutputConversion import 'dart:ui' as ui; + // #enddocregion OutputConversion import 'package:flutter/material.dart'; @@ -101,3 +102,42 @@ Future convertSvgOutput() async { // #enddocregion OutputConversion return image; } + +// #docregion ColorMapper +class _MyColorMapper extends ColorMapper { + const _MyColorMapper(); + + @override + Color substitute( + String? id, + String elementName, + String attributeName, + Color color, + ) { + if (color == const Color(0xFFFF0000)) { + return Colors.blue; + } + if (color == const Color(0xFF00FF00)) { + return Colors.yellow; + } + return color; + } +} +// #enddocregion ColorMapper + +/// Demonstrates loading an SVG asset with a color mapping. +Widget loadWithColorMapper() { + // #docregion ColorMapper + const String svgString = ''' + + + + +'''; + final Widget svgIcon = SvgPicture.string( + svgString, + colorMapper: const _MyColorMapper(), + ); + // #enddocregion ColorMapper + return svgIcon; +} diff --git a/third_party/packages/flutter_svg/lib/svg.dart b/third_party/packages/flutter_svg/lib/svg.dart index 0cb5bb071ee..b1343f47b8e 100644 --- a/third_party/packages/flutter_svg/lib/svg.dart +++ b/third_party/packages/flutter_svg/lib/svg.dart @@ -195,6 +195,7 @@ class SvgPicture extends StatelessWidget { this.clipBehavior = Clip.hardEdge, this.errorBuilder, SvgTheme? theme, + ColorMapper? colorMapper, ui.ColorFilter? colorFilter, @Deprecated('Use colorFilter instead.') ui.Color? color, @Deprecated('Use colorFilter instead.') @@ -205,6 +206,7 @@ class SvgPicture extends StatelessWidget { packageName: package, assetBundle: bundle, theme: theme, + colorMapper: colorMapper, ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); @@ -261,11 +263,13 @@ class SvgPicture extends StatelessWidget { this.errorBuilder, @Deprecated('This no longer does anything.') bool cacheColorFilter = false, SvgTheme? theme, + ColorMapper? colorMapper, http.Client? httpClient, }) : bytesLoader = SvgNetworkLoader( url, headers: headers, theme: theme, + colorMapper: colorMapper, httpClient: httpClient, ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); @@ -319,8 +323,13 @@ class SvgPicture extends StatelessWidget { this.clipBehavior = Clip.hardEdge, this.errorBuilder, SvgTheme? theme, + ColorMapper? colorMapper, @Deprecated('This no longer does anything.') bool cacheColorFilter = false, - }) : bytesLoader = SvgFileLoader(file, theme: theme), + }) : bytesLoader = SvgFileLoader( + file, + theme: theme, + colorMapper: colorMapper, + ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); /// Creates a widget that displays an SVG obtained from a [Uint8List]. @@ -369,8 +378,13 @@ class SvgPicture extends StatelessWidget { this.clipBehavior = Clip.hardEdge, this.errorBuilder, SvgTheme? theme, + ColorMapper? colorMapper, @Deprecated('This no longer does anything.') bool cacheColorFilter = false, - }) : bytesLoader = SvgBytesLoader(bytes, theme: theme), + }) : bytesLoader = SvgBytesLoader( + bytes, + theme: theme, + colorMapper: colorMapper, + ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); /// Creates a widget that displays an SVG obtained from a [String]. @@ -419,8 +433,13 @@ class SvgPicture extends StatelessWidget { this.clipBehavior = Clip.hardEdge, this.errorBuilder, SvgTheme? theme, + ColorMapper? colorMapper, @Deprecated('This no longer does anything.') bool cacheColorFilter = false, - }) : bytesLoader = SvgStringLoader(string, theme: theme), + }) : bytesLoader = SvgStringLoader( + string, + theme: theme, + colorMapper: colorMapper, + ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); static ColorFilter? _getColorFilter( diff --git a/third_party/packages/flutter_svg/test/golden_widget/flutter_logo.asset.color_mapper.png b/third_party/packages/flutter_svg/test/golden_widget/flutter_logo.asset.color_mapper.png new file mode 100644 index 0000000000000000000000000000000000000000..8af44b8b9f1085c70f75359e82e48303c70b801d GIT binary patch literal 12704 zcmcIq`6JZp_kYir`!U@X^X8M~BW8?95-K+pjcFQ&vfL?KD>p4FSt=BR>2hmq!;M=K zb)!^NQi+n~c4f&@Dsh!p#QeEs6gGv|4oC4FyX~~u z0znpJmfjVOKM{?$y^bTR7b&6^sME7`!{O{!3KbdBVO7`&j1QA3!qbZM&ZVp*a}v2^ zTBLAXkzV~PLUP|*D!)Eb;oxQ=VoaBWr%U~W2 z$*>c|Cycv42H3X*>`{O{Ou%jg*kK%&0rQt&^W`pJoLZOyCI5l1P4KUMqd@93E_Gcv zkRNgK6aoyVA~ChT(g4*DM{QdIq!h*UZX*L&2NMVT0hB4*j431BN@E}~;h2h~i;fC2 z2;yr9qTOh8-_yDbq*Iqw@nt?LP2j)CI~Ph*RVR_zdvPmwgJ_t9X?f2^h{VU?5E}ul z6PLku0CA^rg$#8%P6!G_Gz8sM~fNA61

r2+gfwSu}0&WkHn~ND3Y}^`wGJEUA|%WM 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

r2+gfwSu}0&WkHn~ND3Y}^`wGJEUA|%WM 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

r2+gfwSu}0&WkHn~ND3Y}^`wGJEUA|%WM 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

r2+gfwSu}0&WkHn~ND3Y}^`wGJEUA|%WM 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 _checkWidgetAndGolden(Key key, String filename) async { await expectLater(widgetFinder, matchesGoldenFile('golden_widget/$filename')); } +class _TestColorMapper extends ColorMapper { + const _TestColorMapper(); + + /// Substitutes specific colors for testing the SVG rendering. + @override + Color substitute( + String? id, String elementName, String attributeName, Color color) { + if (color == const Color(0xFF42A5F5)) { + return const Color(0xFF00FF00); // Green + } + if (color == const Color(0xFF0D47A1)) { + return const Color(0xFFFF0000); // Red + } + if (color == const Color(0xFF616161)) { + return const Color(0xFF0000FF); // Blue + } + if (color == const Color(0xFF000000)) { + return const Color(0xFFFFFF00); // Yellow + } + return color; + } +} + void main() { final MediaQueryData mediaQueryData = MediaQueryData.fromView(PlatformDispatcher.instance.implicitView!); @@ -116,6 +139,28 @@ void main() { await _checkWidgetAndGolden(key, 'flutter_logo.string.png'); }); + testWidgets('SvgPicture.string with colorMapper', + (WidgetTester tester) async { + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: RepaintBoundary( + key: key, + child: SvgPicture.string( + svgStr, + width: 100.0, + height: 100.0, + colorMapper: const _TestColorMapper(), + ), + ), + ), + ); + + await tester.pumpAndSettle(); + await _checkWidgetAndGolden(key, 'flutter_logo.string.color_mapper.png'); + }); + testWidgets('SvgPicture natural size', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( @@ -250,6 +295,26 @@ void main() { await _checkWidgetAndGolden(key, 'flutter_logo.memory.png'); }); + testWidgets('SvgPicture.memory with colorMapper', + (WidgetTester tester) async { + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: RepaintBoundary( + key: key, + child: SvgPicture.memory( + svgBytes, + colorMapper: const _TestColorMapper(), + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await _checkWidgetAndGolden(key, 'flutter_logo.memory.color_mapper.png'); + }); + testWidgets('SvgPicture.asset', (WidgetTester tester) async { final FakeAssetBundle fakeAsset = FakeAssetBundle(); final GlobalKey key = GlobalKey(); @@ -269,6 +334,26 @@ void main() { await _checkWidgetAndGolden(key, 'flutter_logo.asset.png'); }); + testWidgets('SvgPicture.asset with colorMapper', (WidgetTester tester) async { + final FakeAssetBundle fakeAsset = FakeAssetBundle(); + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: RepaintBoundary( + key: key, + child: SvgPicture.asset( + 'test.svg', + bundle: fakeAsset, + colorMapper: const _TestColorMapper(), + ), + ), + ), + ); + await tester.pumpAndSettle(); + await _checkWidgetAndGolden(key, 'flutter_logo.asset.color_mapper.png'); + }); + testWidgets('SvgPicture.asset DefaultAssetBundle', (WidgetTester tester) async { final FakeAssetBundle fakeAsset = FakeAssetBundle(); @@ -295,6 +380,33 @@ void main() { await _checkWidgetAndGolden(key, 'flutter_logo.asset.png'); }); + testWidgets('SvgPicture.asset DefaultAssetBundle with colorMapper', + (WidgetTester tester) async { + final FakeAssetBundle fakeAsset = FakeAssetBundle(); + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: mediaQueryData, + child: DefaultAssetBundle( + bundle: fakeAsset, + child: RepaintBoundary( + key: key, + child: SvgPicture.asset( + 'test.svg', + semanticsLabel: 'Test SVG', + colorMapper: const _TestColorMapper(), + ), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + await _checkWidgetAndGolden(key, 'flutter_logo.asset.color_mapper.png'); + }); + testWidgets('SvgPicture.network', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( @@ -313,6 +425,26 @@ void main() { await _checkWidgetAndGolden(key, 'flutter_logo.network.png'); }); + testWidgets('SvgPicture.network with colorMapper', + (WidgetTester tester) async { + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: RepaintBoundary( + key: key, + child: SvgPicture.network( + 'test.svg', + httpClient: FakeHttpClient(), + colorMapper: const _TestColorMapper(), + ), + ), + ), + ); + await tester.pumpAndSettle(); + await _checkWidgetAndGolden(key, 'flutter_logo.network.color_mapper.png'); + }); + testWidgets('SvgPicture.network with headers', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); final FakeHttpClient client = FakeHttpClient(); From 6e3ac75a2a3bd8e40a1b7b576770a7931a05969f Mon Sep 17 00:00:00 2001 From: Ricardo Dalarme Date: Thu, 10 Apr 2025 00:29:18 -0300 Subject: [PATCH 2/3] chore(flutter_svg): bump package version to 2.1.0 --- third_party/packages/flutter_svg/CHANGELOG.md | 4 ++++ third_party/packages/flutter_svg/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/third_party/packages/flutter_svg/CHANGELOG.md b/third_party/packages/flutter_svg/CHANGELOG.md index 0f9dd9ee901..3e58b61b30f 100644 --- a/third_party/packages/flutter_svg/CHANGELOG.md +++ b/third_party/packages/flutter_svg/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Exposes `colorMapper` in `SvgPicture` constructors. + ## 2.0.17 * Implement errorBuilder callback diff --git a/third_party/packages/flutter_svg/pubspec.yaml b/third_party/packages/flutter_svg/pubspec.yaml index ca33c7e36d1..b72740d0f68 100644 --- a/third_party/packages/flutter_svg/pubspec.yaml +++ b/third_party/packages/flutter_svg/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_svg description: An SVG rendering and widget library for Flutter, which allows painting and displaying Scalable Vector Graphics 1.1 files. repository: https://github.com/flutter/packages/tree/main/third_party/packages/flutter_svg issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_svg%22 -version: 2.0.17 +version: 2.1.0 environment: sdk: ^3.4.0 From 51865276d223be73e1cd5e5e7ca4bb4d2536e8df Mon Sep 17 00:00:00 2001 From: Ricardo Dalarme Date: Thu, 10 Apr 2025 01:53:22 -0300 Subject: [PATCH 3/3] chore: update authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index be6c57cfb16..b60279e3f60 100644 --- a/AUTHORS +++ b/AUTHORS @@ -78,3 +78,4 @@ Taskulu LDA Alexander Rabin LinXunFeng Hashir Shoaib +Ricardo Dalarme