diff --git a/.rive_head b/.rive_head index 2551d4c..0bf90c1 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -cae6fa5ccfb6029614e6306d443a2039d1f0657c +c163c1a7fe5a8d26506c439e686716b4131f6ffe diff --git a/CHANGELOG.md b/CHANGELOG.md index e228ffe..93fb749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.11.2 + +- Add parameter to specifcy headers on RiveAnimation.network widget + ## 0.11.1 - Joysticks with custom handle sources. diff --git a/lib/src/rive_file.dart b/lib/src/rive_file.dart index d158f33..f1e60db 100644 --- a/lib/src/rive_file.dart +++ b/lib/src/rive_file.dart @@ -277,8 +277,8 @@ class RiveFile { /// your file contains images that needed to be loaded with separate network /// requests. static Future<RiveFile> network(String url, - {FileAssetResolver? assetResolver}) async { - final res = await http.get(Uri.parse(url)); + {FileAssetResolver? assetResolver, Map<String, String>? headers}) async { + final res = await http.get(Uri.parse(url), headers: headers); final bytes = ByteData.view(res.bodyBytes.buffer); return RiveFile.import(bytes, assetResolver: assetResolver); } diff --git a/lib/src/widgets/rive_animation.dart b/lib/src/widgets/rive_animation.dart index 1fc0416..7cc0477 100644 --- a/lib/src/widgets/rive_animation.dart +++ b/lib/src/widgets/rive_animation.dart @@ -55,6 +55,9 @@ class RiveAnimation extends StatefulWidget { /// Callback fired when [RiveAnimation] has initialized final OnInitCallback? onInit; + /// Headers for network requests + final Map<String, String>? headers; + /// Creates a new [RiveAnimation] from an asset bundle. /// /// *Example:* @@ -75,6 +78,7 @@ class RiveAnimation extends StatefulWidget { Key? key, }) : name = asset, file = null, + headers = null, src = _Source.asset, super(key: key); @@ -95,6 +99,7 @@ class RiveAnimation extends StatefulWidget { this.antialiasing = true, this.controllers = const [], this.onInit, + this.headers, Key? key, }) : name = url, file = null, @@ -121,6 +126,7 @@ class RiveAnimation extends StatefulWidget { Key? key, }) : name = path, file = null, + headers = null, src = _Source.file, super(key: key); @@ -145,6 +151,7 @@ class RiveAnimation extends StatefulWidget { this.onInit, Key? key, }) : name = null, + headers = null, src = _Source.direct, super(key: key); @@ -182,7 +189,7 @@ class RiveAnimationState extends State<RiveAnimation> { case _Source.asset: return RiveFile.asset(widget.name!); case _Source.network: - return RiveFile.network(widget.name!); + return RiveFile.network(widget.name!, headers: widget.headers); case _Source.file: return RiveFile.file(widget.name!); case _Source.direct: diff --git a/pubspec.yaml b/pubspec.yaml index acebc90..39856ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: rive -version: 0.11.1 +version: 0.11.2 homepage: https://rive.app description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app. repository: https://github.com/rive-app/rive-flutter diff --git a/test/rive_network_test.dart b/test/rive_network_test.dart new file mode 100644 index 0000000..fa72ad4 --- /dev/null +++ b/test/rive_network_test.dart @@ -0,0 +1,80 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:rive/rive.dart'; + +import 'mocks/mocks.dart'; +import 'src/utils.dart'; + +class MockHttpClient extends Mock implements HttpClient {} + +class MockHttpClientRequest extends Mock implements HttpClientRequest {} + +class MockHttpClientResponse extends Mock implements HttpClientResponse {} + +class MockHttpHeaders extends Mock implements HttpHeaders {} + +void main() { + late MockHttpClient mockHttpClient; + late MockHttpClientRequest request; + setUpAll(() { + registerFallbackValue(ArtboardFake()); + registerFallbackValue(Uri()); + registerFallbackValue(Stream.value(<int>[])); + // Build our app and trigger a frame. + final riveBytes = loadFile('assets/rive-flutter-test-asset.riv'); + final body = riveBytes.buffer.asUint8List(); + mockHttpClient = MockHttpClient(); + request = MockHttpClientRequest(); + + when(() => request.headers).thenReturn(MockHttpHeaders()); + + when(() => mockHttpClient.openUrl(any(), any())).thenAnswer((invocation) { + final response = MockHttpClientResponse(); + when(request.close).thenAnswer((_) => Future.value(response)); + when(() => request.addStream(any())).thenAnswer((_) async => null); + when(() => response.headers).thenReturn(MockHttpHeaders()); + when(() => response.handleError(any(), test: any(named: 'test'))) + .thenAnswer((_) => Stream.value(body)); + when(() => response.statusCode).thenReturn(200); + when(() => response.reasonPhrase).thenReturn('OK'); + when(() => response.contentLength).thenReturn(body.length); + when(() => response.isRedirect).thenReturn(false); + when(() => response.persistentConnection).thenReturn(false); + return Future.value(request); + }); + }); + + testWidgets('Using the network, calls the http client without headers', + (WidgetTester tester) async { + await HttpOverrides.runZoned(() async { + await tester.pumpWidget( + const MaterialApp( + home: RiveAnimation.network('https://some.fake.url'), + ), + ); + }, createHttpClient: (_) => mockHttpClient); + + verify(() => mockHttpClient.openUrl(any(), any())).called(1); + verifyNever(() => request.headers.set(any(), any())); + }); + + testWidgets('Using the network, calls the http client with headers', + (WidgetTester tester) async { + await HttpOverrides.runZoned(() async { + await tester.pumpWidget( + const MaterialApp( + home: RiveAnimation.network('https://some.fake.url', headers: { + 'first': 'header', + 'second': 'header', + }), + ), + ); + }, createHttpClient: (_) => mockHttpClient); + + verify(() => mockHttpClient.openUrl(any(), any())).called(1); + verify(() => request.headers.set(any(), any())).called(2); + }); +}