From 4d0b51adf1c68080c95911cd16ae9c6b2705ce6f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Sun, 18 Feb 2024 21:48:46 -0800 Subject: [PATCH 1/2] Use package:http, drop dart:html --- .gitignore | 2 +- packages/flutter_svg/CHANGELOG.md | 5 + packages/flutter_svg/lib/src/loaders.dart | 15 +- .../lib/src/utilities/_http_io.dart | 22 --- .../lib/src/utilities/_http_web.dart | 12 -- .../flutter_svg/lib/src/utilities/http.dart | 1 - packages/flutter_svg/lib/svg.dart | 4 +- packages/flutter_svg/pubspec.yaml | 9 +- .../flutter_svg/test/widget_svg_test.dart | 154 ++++++------------ 9 files changed, 73 insertions(+), 151 deletions(-) delete mode 100644 packages/flutter_svg/lib/src/utilities/_http_io.dart delete mode 100644 packages/flutter_svg/lib/src/utilities/_http_web.dart delete mode 100644 packages/flutter_svg/lib/src/utilities/http.dart diff --git a/.gitignore b/.gitignore index c6346e7f..a48af087 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,6 @@ coverage/ .project # golden failure diffs -test/failures +**/test/failures # Flutter crash logs /flutter_*.log diff --git a/packages/flutter_svg/CHANGELOG.md b/packages/flutter_svg/CHANGELOG.md index 4fce8669..ea9a4e24 100644 --- a/packages/flutter_svg/CHANGELOG.md +++ b/packages/flutter_svg/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGES +## 2.0.10 + +- Use package:http for network requests, and allow injection of the client. +- Bump vector_graphics dependency. + ## 2.0.9 - Adds back `SvgPicture(theme:)` parameter with a deprecation. Although this diff --git a/packages/flutter_svg/lib/src/loaders.dart b/packages/flutter_svg/lib/src/loaders.dart index 0c9931d0..2996cc11 100644 --- a/packages/flutter_svg/lib/src/loaders.dart +++ b/packages/flutter_svg/lib/src/loaders.dart @@ -3,6 +3,7 @@ import 'dart:convert' show utf8; import 'package:flutter/foundation.dart' hide compute; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:http/http.dart' as http; import 'package:vector_graphics/vector_graphics.dart'; import 'package:vector_graphics_compiler/vector_graphics_compiler.dart' as vg; @@ -10,7 +11,6 @@ import '../svg.dart' show svg; import 'default_theme.dart'; import 'utilities/compute.dart'; import 'utilities/file.dart'; -import 'utilities/http.dart'; /// A theme used when decoding an SVG picture. @immutable @@ -54,7 +54,7 @@ class SvgTheme { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } @@ -109,6 +109,7 @@ class _DelegateVgColorMapper extends vg.ColorMapper { /// A [BytesLoader] that parses a SVG data in an isolate and creates a /// vector_graphics binary representation. +@immutable abstract class SvgLoader extends BytesLoader { /// See class doc. const SvgLoader({ @@ -423,7 +424,8 @@ class SvgNetworkLoader extends SvgLoader { this.headers, super.theme, super.colorMapper, - }); + http.Client? httpClient, + }) : _httpClient = httpClient; /// The [Uri] encoded resource address. final String url; @@ -431,9 +433,12 @@ class SvgNetworkLoader extends SvgLoader { /// Optional HTTP headers to send as part of the request. final Map? headers; + final http.Client? _httpClient; + @override - Future prepareMessage(BuildContext? context) { - return httpGet(url, headers: headers); + Future prepareMessage(BuildContext? context) async { + final http.Client client = _httpClient ?? http.Client(); + return (await client.get(Uri.parse(url), headers: headers)).bodyBytes; } @override diff --git a/packages/flutter_svg/lib/src/utilities/_http_io.dart b/packages/flutter_svg/lib/src/utilities/_http_io.dart deleted file mode 100644 index 458a2072..00000000 --- a/packages/flutter_svg/lib/src/utilities/_http_io.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:io'; -import 'dart:typed_data'; // ignore: unnecessary_import - -import 'package:flutter/foundation.dart'; - -/// Fetches an HTTP resource from the specified [url] using the specified [headers]. -Future httpGet(String url, {Map? headers}) async { - final HttpClient httpClient = HttpClient(); - final Uri uri = Uri.base.resolve(url); - final HttpClientRequest request = await httpClient.getUrl(uri); - if (headers != null) { - headers.forEach((String key, String value) { - request.headers.add(key, value); - }); - } - final HttpClientResponse response = await request.close(); - - if (response.statusCode != HttpStatus.ok) { - throw HttpException('Could not get network asset', uri: uri); - } - return consolidateHttpClientResponseBytes(response); -} diff --git a/packages/flutter_svg/lib/src/utilities/_http_web.dart b/packages/flutter_svg/lib/src/utilities/_http_web.dart deleted file mode 100644 index fc8b2c17..00000000 --- a/packages/flutter_svg/lib/src/utilities/_http_web.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'dart:convert'; -import 'dart:html'; -import 'dart:typed_data'; - -/// Fetches an HTTP resource from the specified [url] using the specified [headers]. -Future httpGet(String url, {Map? headers}) async { - final HttpRequest request = await HttpRequest.request( - url, - requestHeaders: headers, - ); - return Uint8List.fromList(utf8.encode(request.responseText!)); -} diff --git a/packages/flutter_svg/lib/src/utilities/http.dart b/packages/flutter_svg/lib/src/utilities/http.dart deleted file mode 100644 index cdf44641..00000000 --- a/packages/flutter_svg/lib/src/utilities/http.dart +++ /dev/null @@ -1 +0,0 @@ -export '_http_io.dart' if (dart.library.html) '_http_web.dart'; diff --git a/packages/flutter_svg/lib/svg.dart b/packages/flutter_svg/lib/svg.dart index 785cfa14..b96b8076 100644 --- a/packages/flutter_svg/lib/svg.dart +++ b/packages/flutter_svg/lib/svg.dart @@ -2,6 +2,7 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'package:http/http.dart' as http; import 'package:vector_graphics/vector_graphics_compat.dart'; import 'src/cache.dart'; @@ -248,7 +249,8 @@ class SvgPicture extends StatelessWidget { this.clipBehavior = Clip.hardEdge, @deprecated bool cacheColorFilter = false, SvgTheme? theme, - }) : bytesLoader = SvgNetworkLoader(url, headers: headers, theme: theme), + http.Client? httpClient, + }) : bytesLoader = SvgNetworkLoader(url, headers: headers, theme: theme, httpClient: httpClient), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode), super(key: key); diff --git a/packages/flutter_svg/pubspec.yaml b/packages/flutter_svg/pubspec.yaml index a5d77b93..4e07c613 100644 --- a/packages/flutter_svg/pubspec.yaml +++ b/packages/flutter_svg/pubspec.yaml @@ -2,14 +2,15 @@ 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/dnfield/flutter_svg/tree/master/packages/flutter_svg issue_tracker: https://github.com/dnfield/flutter_svg/issues -version: 2.0.9 +version: 2.0.10 dependencies: flutter: sdk: flutter - vector_graphics: ^1.1.9+1 - vector_graphics_codec: ^1.1.9+1 - vector_graphics_compiler: ^1.1.9+1 + http: ^1.2.1 + vector_graphics: ^1.1.11 + vector_graphics_codec: ^1.1.11 + vector_graphics_compiler: ^1.1.11 dev_dependencies: flutter_test: diff --git a/packages/flutter_svg/test/widget_svg_test.dart b/packages/flutter_svg/test/widget_svg_test.dart index 8913907b..49dfe8ed 100644 --- a/packages/flutter_svg/test/widget_svg_test.dart +++ b/packages/flutter_svg/test/widget_svg_test.dart @@ -1,12 +1,11 @@ -import 'dart:async'; import 'dart:convert'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; class _TolerantComparator extends LocalFileComparator { _TolerantComparator(Uri testFile) : super(testFile); @@ -39,9 +38,6 @@ Future _checkWidgetAndGolden(Key key, String filename) async { } void main() { - late FakeHttpClientResponse fakeResponse; - late FakeHttpClientRequest fakeRequest; - late FakeHttpClient fakeHttpClient; final MediaQueryData mediaQueryData = MediaQueryData.fromView(PlatformDispatcher.instance.implicitView!); @@ -54,12 +50,6 @@ void main() { goldenFileComparator = newComparator; }); - setUp(() { - fakeResponse = FakeHttpClientResponse(); - fakeRequest = FakeHttpClientRequest(fakeResponse); - fakeHttpClient = FakeHttpClient(fakeRequest); - }); - testWidgets( 'SvgPicture does not use a color filtering widget when no color specified', (WidgetTester tester) async { @@ -306,40 +296,41 @@ void main() { }); testWidgets('SvgPicture.network', (WidgetTester tester) async { - await HttpOverrides.runZoned(() async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - MediaQuery( - data: mediaQueryData, - child: RepaintBoundary( - key: key, - child: SvgPicture.network( - 'test.svg', - ), + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: RepaintBoundary( + key: key, + child: SvgPicture.network( + 'test.svg', + httpClient: FakeHttpClient(), ), ), - ); - await tester.pumpAndSettle(); - await _checkWidgetAndGolden(key, 'flutter_logo.network.png'); - }, createHttpClient: (SecurityContext? c) => fakeHttpClient); + ), + ); + await tester.pumpAndSettle(); + await _checkWidgetAndGolden(key, 'flutter_logo.network.png'); }); testWidgets('SvgPicture.network with headers', (WidgetTester tester) async { - await HttpOverrides.runZoned(() async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - MediaQuery( - data: mediaQueryData, - child: RepaintBoundary( - key: key, - child: SvgPicture.network('test.svg', - headers: const {'a': 'b'}), + final GlobalKey key = GlobalKey(); + final FakeHttpClient client = FakeHttpClient(); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: RepaintBoundary( + key: key, + child: SvgPicture.network( + 'test.svg', + headers: const {'a': 'b'}, + httpClient: client, ), ), - ); - await tester.pumpAndSettle(); - expect(fakeRequest.headers['a']!.single, 'b'); - }, createHttpClient: (SecurityContext? c) => fakeHttpClient); + ), + ); + await tester.pumpAndSettle(); + expect(client.headers['a'], 'b'); }); testWidgets('SvgPicture can be created without a MediaQuery', @@ -361,19 +352,18 @@ void main() { }); testWidgets('SvgPicture.network HTTP exception', (WidgetTester tester) async { - await HttpOverrides.runZoned(() async { - expect(() async { - fakeResponse.statusCode = 400; - await tester.pumpWidget( - MediaQuery( - data: mediaQueryData, - child: SvgPicture.network( - 'notFound.svg', - ), + expect(() async { + final http.Client client = FakeHttpClient(400); + await tester.pumpWidget( + MediaQuery( + data: mediaQueryData, + child: SvgPicture.network( + 'notFound.svg', + httpClient: client, ), - ); - }, isNotNull); - }, createHttpClient: (SecurityContext? c) => fakeHttpClient); + ), + ); + }, isNotNull); }); testWidgets('SvgPicture semantics', (WidgetTester tester) async { @@ -791,65 +781,19 @@ class FakeAssetBundle extends Fake implements AssetBundle { } } -class FakeHttpClient extends Fake implements HttpClient { - FakeHttpClient(this.request); +class FakeHttpClient extends Fake implements http.Client { + FakeHttpClient([this.statusCode = 200]); - FakeHttpClientRequest request; + final int statusCode; - @override - Future getUrl(Uri url) async => request; -} - -class FakeHttpHeaders extends Fake implements HttpHeaders { - final Map values = {}; - - @override - void add(String name, Object value, {bool preserveHeaderCase = false}) { - values[name] = value.toString(); - } + final Map headers = {}; @override - List? operator [](String key) { - return [values[key]!]; - } -} - -class FakeHttpClientRequest extends Fake implements HttpClientRequest { - FakeHttpClientRequest(this.response); - - FakeHttpClientResponse response; - - @override - final HttpHeaders headers = FakeHttpHeaders(); - - @override - Future close() async => response; -} - -class FakeHttpClientResponse extends Fake implements HttpClientResponse { - @override - int statusCode = 200; - - @override - int contentLength = svgStr.length; - - @override - HttpClientResponseCompressionState get compressionState => - HttpClientResponseCompressionState.notCompressed; - - @override - StreamSubscription> listen( - void Function(List event)? onData, { - Function? onError, - void Function()? onDone, - bool? cancelOnError, - }) { - return Stream.fromIterable([svgBytes]).listen( - onData, - onDone: onDone, - onError: onError, - cancelOnError: cancelOnError, - ); + Future get(Uri url, {Map? headers}) async { + if (headers != null) { + this.headers.addAll(headers); + } + return http.Response(svgStr, statusCode); } } From 6d323d5437141f84936bc40e571e835bbb6f18c6 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 20 Feb 2024 08:52:11 -0800 Subject: [PATCH 2/2] Format --- packages/flutter_svg/lib/svg.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/flutter_svg/lib/svg.dart b/packages/flutter_svg/lib/svg.dart index b96b8076..d39e47ba 100644 --- a/packages/flutter_svg/lib/svg.dart +++ b/packages/flutter_svg/lib/svg.dart @@ -250,7 +250,12 @@ class SvgPicture extends StatelessWidget { @deprecated bool cacheColorFilter = false, SvgTheme? theme, http.Client? httpClient, - }) : bytesLoader = SvgNetworkLoader(url, headers: headers, theme: theme, httpClient: httpClient), + }) : bytesLoader = SvgNetworkLoader( + url, + headers: headers, + theme: theme, + httpClient: httpClient, + ), colorFilter = colorFilter ?? _getColorFilter(color, colorBlendMode), super(key: key);