diff --git a/third_party/packages/flutter_svg/CHANGELOG.md b/third_party/packages/flutter_svg/CHANGELOG.md index a6e42f55cbf..45b68e2f4c9 100644 --- a/third_party/packages/flutter_svg/CHANGELOG.md +++ b/third_party/packages/flutter_svg/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* Adds `SvgPicture.precompiled` for loading precompiled vector graphics + assets without manually constructing an `AssetBytesLoader`. + ## 2.2.1 * Fixes message buffer access in SvgAssetLoader. diff --git a/third_party/packages/flutter_svg/README.md b/third_party/packages/flutter_svg/README.md index 15395077e6b..3f5d1a87a33 100644 --- a/third_party/packages/flutter_svg/README.md +++ b/third_party/packages/flutter_svg/README.md @@ -150,14 +150,12 @@ clipping, masking, and overdraw. The SVG compilation is provided by dart run vector_graphics_compiler -i assets/foo.svg -o assets/foo.svg.vec ``` -The output `foo.svg.vec` can be loaded using the default constructor of -`SvgPicture`. +The output `foo.svg.vec` can be loaded using the `SvgPicture.precompiled` +constructor. ```dart -import 'package:vector_graphics/vector_graphics.dart'; -// ยทยทยท - const Widget svg = SvgPicture(AssetBytesLoader('assets/foo.svg.vec')); +final Widget svg = SvgPicture.precompiled('assets/foo.svg.vec'); ``` ### Check SVG compatibility 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 eaca7ab8788..e3823aa7e41 100644 --- a/third_party/packages/flutter_svg/example/lib/readme_excerpts.dart +++ b/third_party/packages/flutter_svg/example/lib/readme_excerpts.dart @@ -12,9 +12,6 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -// #docregion PrecompiledAsset -import 'package:vector_graphics/vector_graphics.dart'; -// #enddocregion PrecompiledAsset /// Loads an SVG asset. Widget loadAsset() { @@ -73,7 +70,7 @@ Widget loadNetworkAssetWithPlaceholder() { // to sanity-check the structure of the example code. Widget loadPrecompiledAsset() { // #docregion PrecompiledAsset - const Widget svg = SvgPicture(AssetBytesLoader('assets/foo.svg.vec')); + final Widget svg = SvgPicture.precompiled('assets/foo.svg.vec'); // #enddocregion PrecompiledAsset return svg; } diff --git a/third_party/packages/flutter_svg/example/pubspec.yaml b/third_party/packages/flutter_svg/example/pubspec.yaml index ba5d03c85f0..226183aacf8 100644 --- a/third_party/packages/flutter_svg/example/pubspec.yaml +++ b/third_party/packages/flutter_svg/example/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: sdk: flutter flutter_svg: path: ../ - vector_graphics: ^1.1.13 dev_dependencies: flutter_test: diff --git a/third_party/packages/flutter_svg/lib/svg.dart b/third_party/packages/flutter_svg/lib/svg.dart index 0e61acfe7d1..a52d7a10669 100644 --- a/third_party/packages/flutter_svg/lib/svg.dart +++ b/third_party/packages/flutter_svg/lib/svg.dart @@ -10,7 +10,7 @@ import 'src/loaders.dart'; import 'src/utilities/file.dart'; export 'package:vector_graphics/vector_graphics.dart' - show BytesLoader, PictureInfo, VectorGraphicUtilities, vg; + show AssetBytesLoader, BytesLoader, PictureInfo, VectorGraphicUtilities, vg; export 'src/cache.dart'; export 'src/default_theme.dart'; @@ -446,6 +446,117 @@ class SvgPicture extends StatelessWidget { ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); + /// Creates a widget that renders precompiled vector graphics bytes from an + /// [AssetBundle]. + /// + /// This is a convenience wrapper around `SvgPicture(AssetBytesLoader(...))`, + /// allowing callers to pass the path to a `.vec` asset without importing + /// `package:vector_graphics/vector_graphics.dart` or manually constructing an + /// [AssetBytesLoader]. + /// + /// The asset should be generated by `package:vector_graphics_compiler`. See + /// https://pub.dev/packages/flutter_svg#precompiling-and-optimizing-svgs for + /// more details on precompiling SVGs. + /// + /// The key for caching will be derived from the `assetName`, `package`, and + /// `bundle` arguments. When providing assets from another package, the + /// `package` argument must be non-null. + /// + /// Either the [width] and [height] arguments should be specified, or the + /// widget should be placed in a context that sets tight layout constraints. + /// Otherwise, the image dimensions will change as the image is loaded, which + /// will result in ugly layout changes. + /// + /// If `matchTextDirection` is set to true, the picture will be flipped + /// horizontally in [TextDirection.rtl] contexts. + /// + /// The `allowDrawingOutsideViewBox` parameter should be used with caution - + /// if set to true, it will not clip the canvas used internally to the view box, + /// meaning the picture may draw beyond the intended area and lead to undefined + /// behavior or additional memory overhead. + /// + /// A custom `placeholderBuilder` can be specified for cases where decoding or + /// acquiring data may take a noticeably long time. + /// + /// The `color` and `colorBlendMode` arguments, if specified, will be used to set a + /// [ColorFilter] on any [Paint]s created for this drawing. + /// + /// ## Assets in packages + /// + /// To create the widget with an asset from a package, the [package] argument + /// must be provided. For instance, suppose a package called `my_icons` has + /// `icons/heart.svg.vec`. + /// + /// Then to display the image, use: + /// + /// ```dart + /// SvgPicture.precompiled('icons/heart.svg.vec', package: 'my_icons') + /// ``` + /// + /// Assets used by the package itself should also be displayed using the + /// [package] argument as above. + /// + /// If the desired asset is specified in the `pubspec.yaml` of the package, it + /// is bundled automatically with the app. In particular, assets used by the + /// package itself must be specified in its `pubspec.yaml`. + /// + /// A package can also choose to have assets in its 'lib/' folder that are not + /// specified in its `pubspec.yaml`. In this case for those images to be + /// bundled, the app has to specify which ones to include. For instance a + /// package named `fancy_backgrounds` could have: + /// + /// ```none + /// lib/backgrounds/background1.svg.vec + /// lib/backgrounds/background2.svg.vec + /// lib/backgrounds/background3.svg.vec + ///``` + /// + /// To include, say the first image, the `pubspec.yaml` of the app should + /// specify it in the assets section: + /// + /// ```yaml + /// assets: + /// - packages/fancy_backgrounds/backgrounds/background1.svg.vec + /// ``` + /// + /// The `lib/` is implied, so it should not be included in the asset path. + /// + /// + /// See also: + /// + /// * , an introduction to assets in + /// Flutter. + /// + /// If [excludeFromSemantics] is true, then [semanticsLabel] will be ignored. + SvgPicture.precompiled( + String assetName, { + super.key, + this.matchTextDirection = false, + AssetBundle? bundle, + String? package, + this.width, + this.height, + this.fit = BoxFit.contain, + this.alignment = Alignment.center, + this.allowDrawingOutsideViewBox = false, + this.placeholderBuilder, + this.semanticsLabel, + this.excludeFromSemantics = false, + this.clipBehavior = Clip.hardEdge, + this.errorBuilder, + ui.ColorFilter? colorFilter, + @Deprecated('Use colorFilter instead.') ui.Color? color, + @Deprecated('Use colorFilter instead.') + ui.BlendMode colorBlendMode = ui.BlendMode.srcIn, + @Deprecated('This no longer does anything.') bool cacheColorFilter = false, + this.renderingStrategy = RenderingStrategy.picture, + }) : bytesLoader = AssetBytesLoader( + assetName, + packageName: package, + assetBundle: bundle, + ), + colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode); + static ColorFilter? _getColorFilter( ui.Color? color, ui.BlendMode colorBlendMode, diff --git a/third_party/packages/flutter_svg/pubspec.yaml b/third_party/packages/flutter_svg/pubspec.yaml index 152ee9137ea..949d6b8cb6d 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.2.1 +version: 2.3.0 environment: sdk: ^3.7.0 diff --git a/third_party/packages/flutter_svg/test/golden_widget/flutter_logo.precompiled.png b/third_party/packages/flutter_svg/test/golden_widget/flutter_logo.precompiled.png new file mode 100644 index 00000000000..1aa01eccbbe Binary files /dev/null and b/third_party/packages/flutter_svg/test/golden_widget/flutter_logo.precompiled.png differ diff --git a/third_party/packages/flutter_svg/test/widget_svg_test.dart b/third_party/packages/flutter_svg/test/widget_svg_test.dart index 9cb5e1bba97..cff446b0a5c 100644 --- a/third_party/packages/flutter_svg/test/widget_svg_test.dart +++ b/third_party/packages/flutter_svg/test/widget_svg_test.dart @@ -478,6 +478,28 @@ void main() { await _checkWidgetAndGolden(key, 'flutter_logo.asset.color_mapper.png'); }); + testWidgets('SvgPicture.precompiled', (WidgetTester tester) async { + final FakePrecompiledAssetBundle fakeAsset = FakePrecompiledAssetBundle(); + 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.precompiled('test.svg'), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + await _checkWidgetAndGolden(key, 'flutter_logo.precompiled.png'); + }); + testWidgets('SvgPicture.network', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( @@ -1098,6 +1120,13 @@ class FakeAssetBundle extends Fake implements AssetBundle { } } +class FakePrecompiledAssetBundle extends Fake implements AssetBundle { + @override + Future load(String key) async { + return Uint8List.fromList(_precompiledSvgBytes).buffer.asByteData(); + } +} + class FakeHttpClient extends Fake implements http.Client { FakeHttpClient([this.statusCode = 200]); @@ -1120,6 +1149,18 @@ const String simpleSvg = ''' '''; +/// Precompiled SVG bytes for `svgStr` (Flutter logo). +/// +/// How to regenerate: +/// 1. Save `svgStr` to `test/flutter_logo.svg`. +/// 2. Compile: `dart run vector_graphics_compiler:vector_graphics_compiler -i +/// test/flutter_logo.svg -o test/flutter_logo.svg.vec` +/// 3. Encode: `base64 -i test/flutter_logo.svg.vec` (macOS) +/// 4. Paste the output below and delete the temporary files. +final Uint8List _precompiledSvgBytes = base64Decode( + 'Yi2IAAEpAAAmQwAASkMnAADsCp9CGa0qQx3SpkKykC5DAgAAAAAAJmFhYQICAAAAzcxMPpqZWT8AJwEAAACfQs3MDkPNzPFCzcwOQwIAAAAAAACMYWFhAgIAAADNzEw+mplZPwAc9aVCzAMAAP//HKFHDf8DAQD//xz1pUL/AwIA//8c/////wMDAAAAHP////8DBAABABsBAAAEAAAAAAEBAQgAAAAAzcwWQmbmAEPNzBxBAADKQs3MyEJmZiZBMzMcQ2ZmJkEbAQEABAAAAAABAQEIAAAAMzMcQwAAvELNzMhCAAC8QgAAn0LNzOVCzczWQs3MDkMbAAIABQAAAAABAQEBCgAAAAAAAAAAn0IzsypDzczIQpqZP0MzMxxDmpk/QzMzHEOamT9DzczWQs3MDkMbAAMABQAAAAABAQEDCAAAAAAAAHilTkIC0Q5D7AqfQtPp5UIcw9ZCAtEOQ+wKn0IZrSpDGwAEAAUAAAAAAQEBAwgAAAAAAADsCp9CGa0qQxzD1kIC0Q5DTYreQpq0EkMd0qZCspAuQxsABQADAAAAAAEBBgAAAAAAAJ9CM7MqQ83M8UJmZhxDzczWQs3MDkMwHgAAAAD//x4BAAAA//8eAgABAP//HgMAAgD//x4EAAMA//8eBQAEAP//', +); + const String svgStr = '''