From cd44ecc77836d9fea95c4e91d71421171bfb6df3 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 21 Nov 2025 11:04:45 -0500 Subject: [PATCH 1/3] [video_player_avfoundation] Create a Dart player instance Refactors the Dart internals to create a pleyer instance class on the Dart side, and intermediates the event stream with an instance-managed stream controller that would allow for alternate event sources (e.g., Dart code). This prepares for future moves of more logic to Dart, and parallels Android Dart code changes made in https://github.com/flutter/packages/pull/9771 Part of https://github.com/flutter/flutter/issues/172763 --- .../video_player_avfoundation/CHANGELOG.md | 4 + .../FVPVideoPlayerPlugin.m | 2 +- .../lib/src/avfoundation_video_player.dart | 162 +++++++++++------- .../video_player_avfoundation/pubspec.yaml | 2 +- .../test/avfoundation_video_player_test.dart | 82 +++++---- 5 files changed, 151 insertions(+), 101 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 9ddb15d3a51..7f509fcc462 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.8 + +* Refactors Dart internals for maintainability. + ## 2.8.7 * Updates to Pigeon 26. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m index d1914d9f7c1..dbc130acc50 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m @@ -116,7 +116,7 @@ - (int64_t)configurePlayer:(FVPVideoPlayer *)player // Set up the event channel. FVPEventBridge *eventBridge = [[FVPEventBridge alloc] initWithMessenger:messenger - channelName:[NSString stringWithFormat:@"flutter.io/videoPlayer/videoEvents%@", + channelName:[NSString stringWithFormat:@"flutter.dev/videoPlayer/videoEvents%@", channelSuffix]]; player.eventListener = eventBridge; diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index 4c1719578f6..0bb2593bd35 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -22,23 +22,16 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { AVFoundationVideoPlayer({ @visibleForTesting AVFoundationVideoPlayerApi? pluginApi, @visibleForTesting - VideoPlayerInstanceApi Function(int playerId)? playerProvider, + VideoPlayerInstanceApi Function(int playerId)? playerApiProvider, }) : _api = pluginApi ?? AVFoundationVideoPlayerApi(), - _playerProvider = playerProvider ?? _productionApiProvider; + _playerApiProvider = playerApiProvider ?? _productionApiProvider; final AVFoundationVideoPlayerApi _api; // A method to create VideoPlayerInstanceApi instances, which can be // overridden for testing. - final VideoPlayerInstanceApi Function(int mapId) _playerProvider; + final VideoPlayerInstanceApi Function(int mapId) _playerApiProvider; - /// A map that associates player ID with a view state. - /// This is used to determine which view type to use when building a view. - @visibleForTesting - final Map playerViewStates = - {}; - - final Map _players = - {}; + final Map _players = {}; /// Registers this class as the default instance of [VideoPlayerPlatform]. static void registerWith() { @@ -52,9 +45,8 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future dispose(int playerId) async { - final VideoPlayerInstanceApi? player = _players.remove(playerId); + final _PlayerInstance? player = _players.remove(playerId); await player?.dispose(); - playerViewStates.remove(playerId); } @override @@ -118,8 +110,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { playerId = await _api.createForPlatformView(pigeonCreationOptions); state = const VideoPlayerPlatformViewState(); } - playerViewStates[playerId] = state; - ensureApiInitialized(playerId); + ensurePlayerInitialized(playerId, state); return playerId; } @@ -127,9 +118,16 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { /// Returns the API instance for [playerId], creating it if it doesn't already /// exist. @visibleForTesting - VideoPlayerInstanceApi ensureApiInitialized(int playerId) { - return _players.putIfAbsent(playerId, () { - return _playerProvider(playerId); + void ensurePlayerInitialized(int playerId, VideoPlayerViewState viewState) { + _players.putIfAbsent(playerId, () { + return _PlayerInstance( + _playerApiProvider(playerId), + viewState, + eventChannel: EventChannel( + // This must match the channel name used in FVPVideoPlayerPlugin.m. + 'flutter.dev/videoPlayer/videoEvents$playerId', + ), + ); }); } @@ -162,48 +160,17 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future seekTo(int playerId, Duration position) { - return _playerWith(id: playerId).seekTo(position.inMilliseconds); + return _playerWith(id: playerId).seekTo(position); } @override Future getPosition(int playerId) async { - final int position = await _playerWith(id: playerId).getPosition(); - return Duration(milliseconds: position); + return _playerWith(id: playerId).getPosition(); } @override Stream videoEventsFor(int playerId) { - return _eventChannelFor(playerId).receiveBroadcastStream().map(( - dynamic event, - ) { - final Map map = event as Map; - return switch (map['event']) { - 'initialized' => VideoEvent( - eventType: VideoEventType.initialized, - duration: Duration(milliseconds: map['duration'] as int), - size: Size( - (map['width'] as num?)?.toDouble() ?? 0.0, - (map['height'] as num?)?.toDouble() ?? 0.0, - ), - ), - 'completed' => VideoEvent(eventType: VideoEventType.completed), - 'bufferingUpdate' => VideoEvent( - buffered: (map['values'] as List) - .map(_toDurationRange) - .toList(), - eventType: VideoEventType.bufferingUpdate, - ), - 'bufferingStart' => VideoEvent( - eventType: VideoEventType.bufferingStart, - ), - 'bufferingEnd' => VideoEvent(eventType: VideoEventType.bufferingEnd), - 'isPlayingStateUpdate' => VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: map['isPlaying'] as bool, - ), - _ => VideoEvent(eventType: VideoEventType.unknown), - }; - }); + return _playerWith(id: playerId).videoEvents; } @override @@ -219,16 +186,13 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Widget buildViewWithOptions(VideoViewOptions options) { final int playerId = options.playerId; - final VideoPlayerViewState? viewState = playerViewStates[playerId]; + final VideoPlayerViewState viewState = _playerWith(id: playerId).viewState; return switch (viewState) { VideoPlayerTextureViewState(:final int textureId) => Texture( textureId: textureId, ), VideoPlayerPlatformViewState() => _buildPlatformView(playerId), - null => throw Exception( - 'Could not find corresponding view type for playerId: $playerId', - ), }; } @@ -246,13 +210,89 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { ); } - EventChannel _eventChannelFor(int playerId) { - return EventChannel('flutter.io/videoPlayer/videoEvents$playerId'); + _PlayerInstance _playerWith({required int id}) { + final _PlayerInstance? player = _players[id]; + return player ?? (throw StateError('No active player with ID $id.')); } +} - VideoPlayerInstanceApi _playerWith({required int id}) { - final VideoPlayerInstanceApi? player = _players[id]; - return player ?? (throw StateError('No active player with ID $id.')); +/// An instance of a video player, corresponding to a single player ID in +/// [AVFoundationVideoPlayer]. +class _PlayerInstance { + _PlayerInstance( + this._api, + this.viewState, { + required EventChannel eventChannel, + }) : _eventChannel = eventChannel; + + final VideoPlayerInstanceApi _api; + final VideoPlayerViewState viewState; + final EventChannel _eventChannel; + final StreamController _eventStreamController = + StreamController(); + StreamSubscription? _eventSubscription; + + Future play() => _api.play(); + + Future pause() => _api.pause(); + + Future setLooping(bool looping) => _api.setLooping(looping); + + Future setVolume(double volume) => _api.setVolume(volume); + + Future setPlaybackSpeed(double speed) => _api.setPlaybackSpeed(speed); + + Future seekTo(Duration position) { + return _api.seekTo(position.inMilliseconds); + } + + Future getPosition() async { + return Duration(milliseconds: await _api.getPosition()); + } + + Stream get videoEvents { + _eventSubscription ??= _eventChannel.receiveBroadcastStream().listen( + _onStreamEvent, + onError: (Object e) { + _eventStreamController.addError(e); + }, + ); + + return _eventStreamController.stream; + } + + Future dispose() async { + await _eventSubscription?.cancel(); + await _api.dispose(); + } + + void _onStreamEvent(dynamic event) { + final Map map = event as Map; + // The strings here must all match the strings in FVPEventBridge.m. + _eventStreamController.add(switch (map['event']) { + 'initialized' => VideoEvent( + eventType: VideoEventType.initialized, + duration: Duration(milliseconds: map['duration'] as int), + size: Size( + (map['width'] as num?)?.toDouble() ?? 0.0, + (map['height'] as num?)?.toDouble() ?? 0.0, + ), + ), + 'completed' => VideoEvent(eventType: VideoEventType.completed), + 'bufferingUpdate' => VideoEvent( + buffered: (map['values'] as List) + .map(_toDurationRange) + .toList(), + eventType: VideoEventType.bufferingUpdate, + ), + 'bufferingStart' => VideoEvent(eventType: VideoEventType.bufferingStart), + 'bufferingEnd' => VideoEvent(eventType: VideoEventType.bufferingEnd), + 'isPlayingStateUpdate' => VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: map['isPlaying'] as bool, + ), + _ => VideoEvent(eventType: VideoEventType.unknown), + }); } DurationRange _toDurationRange(dynamic value) { diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index e9f7eba7ae9..9c326136d92 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS and macOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.8.7 +version: 2.8.8 environment: sdk: ^3.9.0 diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 11cac97a7dd..9bdba2a903c 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -24,15 +25,20 @@ void main() { MockAVFoundationVideoPlayerApi, MockVideoPlayerInstanceApi, ) - setUpMockPlayer({required int playerId}) { + setUpMockPlayer({required int playerId, int? textureId}) { final MockAVFoundationVideoPlayerApi pluginApi = MockAVFoundationVideoPlayerApi(); final MockVideoPlayerInstanceApi instanceApi = MockVideoPlayerInstanceApi(); final AVFoundationVideoPlayer player = AVFoundationVideoPlayer( pluginApi: pluginApi, - playerProvider: (_) => instanceApi, + playerApiProvider: (_) => instanceApi, + ); + player.ensurePlayerInitialized( + playerId, + textureId == null + ? const VideoPlayerPlatformViewState() + : VideoPlayerTextureViewState(textureId: textureId), ); - player.ensureApiInitialized(playerId); return (player, pluginApi, instanceApi); } @@ -49,6 +55,7 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); await player.init(); @@ -62,11 +69,11 @@ void main() { MockVideoPlayerInstanceApi playerApi, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); await player.dispose(1); verify(playerApi.dispose()); - expect(player.playerViewStates, isEmpty); }); test('create with asset', () async { @@ -76,12 +83,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const int newPlayerId = 2; - const int textureId = 100; when(api.createForTextureView(any)).thenAnswer( - (_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); const String asset = 'someAsset'; @@ -105,8 +111,8 @@ void main() { expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -119,6 +125,7 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const String asset = 'someAsset'; @@ -145,12 +152,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const int newPlayerId = 2; - const int textureId = 100; when(api.createForTextureView(any)).thenAnswer( - (_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); const String uri = 'https://example.com'; @@ -171,8 +177,8 @@ void main() { expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -183,10 +189,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); when( api.createForTextureView(any), - ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 102)); const Map headers = { 'Authorization': 'Bearer token', @@ -213,12 +220,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const int newPlayerId = 2; - const int textureId = 100; when(api.createForTextureView(any)).thenAnswer( - (_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); const String fileUri = 'file:///foo/bar'; @@ -233,8 +239,8 @@ void main() { expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -245,12 +251,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const int newPlayerId = 2; - const int textureId = 100; when(api.createForTextureView(any)).thenAnswer( - (_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); const String asset = 'someAsset'; @@ -276,8 +281,8 @@ void main() { expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -288,12 +293,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const int newPlayerId = 2; - const int textureId = 100; when(api.createForTextureView(any)).thenAnswer( - (_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); const String uri = 'https://example.com'; @@ -317,8 +321,8 @@ void main() { expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -329,10 +333,11 @@ void main() { _, ) = setUpMockPlayer( playerId: 1, + textureId: 101, ); const int newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( - (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); const Map headers = { @@ -389,8 +394,8 @@ void main() { expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -417,8 +422,8 @@ void main() { expect(playerId, newPlayerId); expect( - player.playerViewStates[newPlayerId], - const VideoPlayerPlatformViewState(), + player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), + isA(), ); }); @@ -552,14 +557,15 @@ void main() { }); test('videoEventsFor', () async { + const int playerId = 1; final ( AVFoundationVideoPlayer player, MockAVFoundationVideoPlayerApi api, _, ) = setUpMockPlayer( - playerId: 1, + playerId: playerId, ); - const String mockChannel = 'flutter.io/videoPlayer/videoEvents123'; + const String mockChannel = 'flutter.dev/videoPlayer/videoEvents$playerId'; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMessageHandler(mockChannel, (ByteData? message) async { final MethodCall methodCall = const StandardMethodCodec() @@ -666,7 +672,7 @@ void main() { } }); expect( - player.videoEventsFor(123), + player.videoEventsFor(playerId), emitsInOrder([ VideoEvent( eventType: VideoEventType.initialized, From 0383fd49dee9aeb7a6d1d0f966a7b20d2a00bbd5 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 21 Nov 2025 13:11:40 -0500 Subject: [PATCH 2/3] Gemini review --- .../lib/src/avfoundation_video_player.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index 0bb2593bd35..b4de6a0775e 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -229,7 +229,7 @@ class _PlayerInstance { final VideoPlayerViewState viewState; final EventChannel _eventChannel; final StreamController _eventStreamController = - StreamController(); + StreamController.broadcast(); StreamSubscription? _eventSubscription; Future play() => _api.play(); @@ -263,6 +263,7 @@ class _PlayerInstance { Future dispose() async { await _eventSubscription?.cancel(); + unawaited(_eventStreamController.close()); await _api.dispose(); } From 66da28510f2ae61a10e35a903309ba3e3be4425f Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 26 Nov 2025 09:59:54 -0500 Subject: [PATCH 3/3] Apply formatting from main --- .../integration_test/video_player_test.dart | 12 +-- .../example/lib/mini_controller.dart | 14 +-- .../lib/src/avfoundation_video_player.dart | 13 ++- .../test/avfoundation_video_player_test.dart | 93 ++++++++----------- 4 files changed, 59 insertions(+), 73 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart index 206fcf7ce07..31321932c1b 100644 --- a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -103,7 +103,7 @@ void main() { // Verify that we stopped playing after the pause. // TODO(stuartmorgan): Investigate why this has a slight discrepency, and // fix it if possible. Is AVPlayer's pause method internally async? - const Duration allowableDelta = Duration(milliseconds: 10); + const allowableDelta = Duration(milliseconds: 10); expect( await controller.position, lessThan(pausedPosition + allowableDelta), @@ -119,7 +119,7 @@ void main() { // Write it to a file to use as a source. final String filename = _videoAssetKey.split('/').last; - final File file = File('$tempDir/$filename'); + final file = File('$tempDir/$filename'); await file.writeAsBytes(bytes.buffer.asInt8List()); controller = MiniController.file(file); @@ -148,8 +148,8 @@ void main() { (WidgetTester tester) async { await controller.initialize(); - final Completer started = Completer(); - final Completer ended = Completer(); + final started = Completer(); + final ended = Completer(); controller.addListener(() { if (!started.isCompleted && controller.value.isBuffering) { started.complete(); @@ -180,7 +180,7 @@ void main() { ); testWidgets('live stream duration != 0', (WidgetTester tester) async { - final MiniController livestreamController = MiniController.network( + final livestreamController = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await livestreamController.initialize(); @@ -199,7 +199,7 @@ void main() { ) async { // Some m3u8 files contain rotation data that may incorrectly invert the aspect ratio. // More info [here](https://github.com/flutter/flutter/issues/109116). - final MiniController livestreamController = MiniController.network( + final livestreamController = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/rotated_nail_manifest.m3u8', ); await livestreamController.initialize(); diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 8640d16c703..bfc6b1dd496 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -245,7 +245,7 @@ class MiniController extends ValueNotifier { ); } - final VideoCreationOptions creationOptions = VideoCreationOptions( + final creationOptions = VideoCreationOptions( dataSource: dataSourceDescription, viewType: viewType, ); @@ -254,7 +254,7 @@ class MiniController extends ValueNotifier { (await _platform.createWithOptions(creationOptions)) ?? kUninitializedPlayerId; _creatingCompleter!.complete(null); - final Completer initializingCompleter = Completer(); + final initializingCompleter = Completer(); void eventListener(VideoEvent event) { switch (event.eventType) { @@ -284,7 +284,7 @@ class MiniController extends ValueNotifier { } void errorListener(Object obj) { - final PlatformException e = obj as PlatformException; + final e = obj as PlatformException; value = VideoPlayerValue.erroneous(e.message!); _timer?.cancel(); if (!initializingCompleter.isCompleted) { @@ -450,7 +450,7 @@ class _VideoScrubberState extends State<_VideoScrubber> { @override Widget build(BuildContext context) { void seekToRelativePosition(Offset globalPosition) { - final RenderBox box = context.findRenderObject()! as RenderBox; + final box = context.findRenderObject()! as RenderBox; final Offset tapPos = box.globalToLocal(globalPosition); final double relative = tapPos.dx / box.size.width; final Duration position = controller.value.duration * relative; @@ -510,9 +510,9 @@ class _VideoProgressIndicatorState extends State { @override Widget build(BuildContext context) { - const Color playedColor = Color.fromRGBO(255, 0, 0, 0.7); - const Color bufferedColor = Color.fromRGBO(50, 50, 200, 0.2); - const Color backgroundColor = Color.fromRGBO(200, 200, 200, 0.5); + const playedColor = Color.fromRGBO(255, 0, 0, 0.7); + const bufferedColor = Color.fromRGBO(50, 50, 200, 0.2); + const backgroundColor = Color.fromRGBO(200, 200, 200, 0.5); final Widget progressIndicator; if (controller.value.isInitialized) { diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index b4de6a0775e..834b36ed6b0 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -92,7 +92,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { if (uri == null) { throw ArgumentError('Unable to construct a video asset from $options'); } - final CreationOptions pigeonCreationOptions = CreationOptions( + final pigeonCreationOptions = CreationOptions( uri: uri, httpHeaders: dataSource.httpHeaders, ); @@ -197,8 +197,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } Widget _buildPlatformView(int playerId) { - final PlatformVideoViewCreationParams creationParams = - PlatformVideoViewCreationParams(playerId: playerId); + final creationParams = PlatformVideoViewCreationParams(playerId: playerId); return IgnorePointer( // IgnorePointer so that GestureDetector can be used above the platform view. @@ -268,7 +267,7 @@ class _PlayerInstance { } void _onStreamEvent(dynamic event) { - final Map map = event as Map; + final map = event as Map; // The strings here must all match the strings in FVPEventBridge.m. _eventStreamController.add(switch (map['event']) { 'initialized' => VideoEvent( @@ -297,9 +296,9 @@ class _PlayerInstance { } DurationRange _toDurationRange(dynamic value) { - final List pair = value as List; - final int startMilliseconds = pair[0] as int; - final int durationMilliseconds = pair[1] as int; + final pair = value as List; + final startMilliseconds = pair[0] as int; + final durationMilliseconds = pair[1] as int; return DurationRange( Duration(milliseconds: startMilliseconds), Duration(milliseconds: startMilliseconds + durationMilliseconds), diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 9bdba2a903c..c16d5bcb08e 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -26,10 +26,9 @@ void main() { MockVideoPlayerInstanceApi, ) setUpMockPlayer({required int playerId, int? textureId}) { - final MockAVFoundationVideoPlayerApi pluginApi = - MockAVFoundationVideoPlayerApi(); - final MockVideoPlayerInstanceApi instanceApi = MockVideoPlayerInstanceApi(); - final AVFoundationVideoPlayer player = AVFoundationVideoPlayer( + final pluginApi = MockAVFoundationVideoPlayerApi(); + final instanceApi = MockVideoPlayerInstanceApi(); + final player = AVFoundationVideoPlayer( pluginApi: pluginApi, playerApiProvider: (_) => instanceApi, ); @@ -85,14 +84,14 @@ void main() { playerId: 1, textureId: 101, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); - const String asset = 'someAsset'; - const String package = 'somePackage'; - const String assetUrl = 'file:///some/asset/path'; + const asset = 'someAsset'; + const package = 'somePackage'; + const assetUrl = 'file:///some/asset/path'; when(api.getAssetUrl(asset, package)).thenAnswer((_) async => assetUrl); final int? playerId = await player.create( @@ -106,8 +105,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect( @@ -128,8 +126,8 @@ void main() { textureId: 101, ); - const String asset = 'someAsset'; - const String package = 'somePackage'; + const asset = 'someAsset'; + const package = 'somePackage'; when(api.getAssetUrl(asset, package)).thenAnswer((_) async => null); expect( @@ -154,12 +152,12 @@ void main() { playerId: 1, textureId: 101, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); - const String uri = 'https://example.com'; + const uri = 'https://example.com'; final int? playerId = await player.create( DataSource( sourceType: DataSourceType.network, @@ -171,8 +169,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, uri); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); @@ -195,9 +192,7 @@ void main() { api.createForTextureView(any), ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 102)); - const Map headers = { - 'Authorization': 'Bearer token', - }; + const headers = {'Authorization': 'Bearer token'}; await player.create( DataSource( sourceType: DataSourceType.network, @@ -208,8 +203,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.httpHeaders, headers); }); @@ -222,20 +216,19 @@ void main() { playerId: 1, textureId: 101, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); - const String fileUri = 'file:///foo/bar'; + const fileUri = 'file:///foo/bar'; final int? playerId = await player.create( DataSource(sourceType: DataSourceType.file, uri: fileUri), ); final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect( @@ -253,14 +246,14 @@ void main() { playerId: 1, textureId: 101, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); - const String asset = 'someAsset'; - const String package = 'somePackage'; - const String assetUrl = 'file:///some/asset/path'; + const asset = 'someAsset'; + const package = 'somePackage'; + const assetUrl = 'file:///some/asset/path'; when(api.getAssetUrl(asset, package)).thenAnswer((_) async => assetUrl); final int? playerId = await player.createWithOptions( VideoCreationOptions( @@ -276,8 +269,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect( @@ -295,12 +287,12 @@ void main() { playerId: 1, textureId: 101, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); - const String uri = 'https://example.com'; + const uri = 'https://example.com'; final int? playerId = await player.createWithOptions( VideoCreationOptions( dataSource: DataSource( @@ -315,8 +307,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, uri); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); @@ -335,14 +326,12 @@ void main() { playerId: 1, textureId: 101, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 102), ); - const Map headers = { - 'Authorization': 'Bearer token', - }; + const headers = {'Authorization': 'Bearer token'}; final int? playerId = await player.createWithOptions( VideoCreationOptions( dataSource: DataSource( @@ -357,8 +346,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.httpHeaders, headers); expect(playerId, newPlayerId); }); @@ -371,14 +359,14 @@ void main() { ) = setUpMockPlayer( playerId: 1, ); - const int newPlayerId = 2; - const int textureId = 100; + const newPlayerId = 2; + const textureId = 100; when(api.createForTextureView(any)).thenAnswer( (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: textureId), ); - const String fileUri = 'file:///foo/bar'; + const fileUri = 'file:///foo/bar'; final int? playerId = await player.createWithOptions( VideoCreationOptions( dataSource: DataSource(sourceType: DataSourceType.file, uri: fileUri), @@ -389,8 +377,7 @@ void main() { final VerificationResult verification = verify( api.createForTextureView(captureAny), ); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; + final creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect( @@ -407,7 +394,7 @@ void main() { ) = setUpMockPlayer( playerId: 1, ); - const int newPlayerId = 2; + const newPlayerId = 2; when(api.createForPlatformView(any)).thenAnswer((_) async => newPlayerId); final int? playerId = await player.createWithOptions( @@ -502,7 +489,7 @@ void main() { ) = setUpMockPlayer( playerId: 1, ); - const double volume = 0.7; + const volume = 0.7; await player.setVolume(1, volume); verify(playerApi.setVolume(volume)); @@ -516,7 +503,7 @@ void main() { ) = setUpMockPlayer( playerId: 1, ); - const double speed = 1.5; + const speed = 1.5; await player.setPlaybackSpeed(1, speed); verify(playerApi.setPlaybackSpeed(speed)); @@ -530,7 +517,7 @@ void main() { ) = setUpMockPlayer( playerId: 1, ); - const int positionMilliseconds = 12345; + const positionMilliseconds = 12345; await player.seekTo( 1, const Duration(milliseconds: positionMilliseconds), @@ -547,7 +534,7 @@ void main() { ) = setUpMockPlayer( playerId: 1, ); - const int positionMilliseconds = 12345; + const positionMilliseconds = 12345; when( playerApi.getPosition(), ).thenAnswer((_) async => positionMilliseconds); @@ -557,7 +544,7 @@ void main() { }); test('videoEventsFor', () async { - const int playerId = 1; + const playerId = 1; final ( AVFoundationVideoPlayer player, MockAVFoundationVideoPlayerApi api, @@ -565,7 +552,7 @@ void main() { ) = setUpMockPlayer( playerId: playerId, ); - const String mockChannel = 'flutter.dev/videoPlayer/videoEvents$playerId'; + const mockChannel = 'flutter.dev/videoPlayer/videoEvents$playerId'; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMessageHandler(mockChannel, (ByteData? message) async { final MethodCall methodCall = const StandardMethodCodec()