diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 466a0a6f37b..d01c0ec1d9a 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. + ## 2.10.0 * Adds support for platform views as an optional way of displaying a video on Android and iOS. diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index da86c78668b..261b68ba5c5 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -77,8 +77,11 @@ class _VideoAppState extends State { @override void initState() { super.initState(); - _controller = VideoPlayerController.networkUrl(Uri.parse( - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4')) + _controller = VideoPlayerController.networkUrl( + Uri.parse( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + ), + ) ..initialize().then((_) { // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. setState(() {}); @@ -91,12 +94,13 @@ class _VideoAppState extends State { title: 'Video Demo', home: Scaffold( body: Center( - child: _controller.value.isInitialized - ? AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ) - : Container(), + child: + _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : Container(), ), floatingActionButton: FloatingActionButton( onPressed: () { @@ -120,6 +124,7 @@ class _VideoAppState extends State { super.dispose(); } } + ``` ## Usage diff --git a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart index fb5e048dbd7..4bd72991798 100644 --- a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart +++ b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart @@ -62,8 +62,10 @@ void main() { await another.pause(); // Expect that `another` played. - expect(another.value.position, - (Duration position) => position > Duration.zero); + expect( + another.value.position, + (Duration position) => position > Duration.zero, + ); await expectLater(started.future, completes); await expectLater(ended.future, completes); @@ -75,12 +77,9 @@ void main() { // TODO(tarrinneal): Remove once other test is enabled, // https://github.com/flutter/flutter/issues/164651 - testWidgets( - 'no-op', - (WidgetTester tester) async { - expect(true, true); - }, - ); + testWidgets('no-op', (WidgetTester tester) async { + expect(true, true); + }); } Widget renderVideoWidget(VideoPlayerController controller) { diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index 586254e3ac3..2c3f4cdbdd4 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -49,77 +49,71 @@ void main() { expect(controller.value.position, Duration.zero); expect(controller.value.isPlaying, false); // The WebM version has a slightly different duration than the MP4. - expect(controller.value.duration, - const Duration(seconds: 7, milliseconds: kIsWeb ? 544 : 540)); + expect( + controller.value.duration, + const Duration(seconds: 7, milliseconds: kIsWeb ? 544 : 540), + ); }); - testWidgets( - 'live stream duration != 0', - (WidgetTester tester) async { - final VideoPlayerController networkController = - VideoPlayerController.networkUrl( - Uri.parse( - 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8'), - ); - await networkController.initialize(); + testWidgets('live stream duration != 0', (WidgetTester tester) async { + final VideoPlayerController + networkController = VideoPlayerController.networkUrl( + Uri.parse( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', + ), + ); + await networkController.initialize(); - expect(networkController.value.isInitialized, true); - // Live streams should have either a positive duration or C.TIME_UNSET if the duration is unknown - // See https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#getDuration-- - expect(networkController.value.duration, - (Duration duration) => duration != Duration.zero); - }, - skip: kIsWeb, - ); + expect(networkController.value.isInitialized, true); + // Live streams should have either a positive duration or C.TIME_UNSET if the duration is unknown + // See https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#getDuration-- + expect( + networkController.value.duration, + (Duration duration) => duration != Duration.zero, + ); + }, skip: kIsWeb); - testWidgets( - 'can be played', - (WidgetTester tester) async { - await controller.initialize(); - // Mute to allow playing without DOM interaction on Web. - // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await controller.setVolume(0); + testWidgets('can be played', (WidgetTester tester) async { + await controller.initialize(); + // Mute to allow playing without DOM interaction on Web. + // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes + await controller.setVolume(0); - await controller.play(); - await tester.pumpAndSettle(_playDuration); + await controller.play(); + await tester.pumpAndSettle(_playDuration); - expect(controller.value.isPlaying, true); - expect(controller.value.position, - (Duration position) => position > Duration.zero); - }, - ); + expect(controller.value.isPlaying, true); + expect( + controller.value.position, + (Duration position) => position > Duration.zero, + ); + }); - testWidgets( - 'can seek', - (WidgetTester tester) async { - await controller.initialize(); + testWidgets('can seek', (WidgetTester tester) async { + await controller.initialize(); - await controller.seekTo(const Duration(seconds: 3)); + await controller.seekTo(const Duration(seconds: 3)); - expect(controller.value.position, const Duration(seconds: 3)); - }, - ); + expect(controller.value.position, const Duration(seconds: 3)); + }); - testWidgets( - 'can be paused', - (WidgetTester tester) async { - await controller.initialize(); - // Mute to allow playing without DOM interaction on Web. - // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await controller.setVolume(0); + testWidgets('can be paused', (WidgetTester tester) async { + await controller.initialize(); + // Mute to allow playing without DOM interaction on Web. + // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes + await controller.setVolume(0); - // Play for a second, then pause, and then wait a second. - await controller.play(); - await tester.pumpAndSettle(_playDuration); - await controller.pause(); - final Duration pausedPosition = controller.value.position; - await tester.pumpAndSettle(_playDuration); + // Play for a second, then pause, and then wait a second. + await controller.play(); + await tester.pumpAndSettle(_playDuration); + await controller.pause(); + final Duration pausedPosition = controller.value.position; + await tester.pumpAndSettle(_playDuration); - // Verify that we stopped playing after the pause. - expect(controller.value.isPlaying, false); - expect(controller.value.position, pausedPosition); - }, - ); + // Verify that we stopped playing after the pause. + expect(controller.value.isPlaying, false); + expect(controller.value.position, pausedPosition); + }); testWidgets( 'stay paused when seeking after video completed', @@ -142,7 +136,8 @@ void main() { // https://github.com/flutter/flutter/issues/141145 is fixed. if ((!kIsWeb && Platform.isAndroid) && controller.value.isPlaying) { markTestSkipped( - 'Skipping due to https://github.com/flutter/flutter/issues/141145'); + 'Skipping due to https://github.com/flutter/flutter/issues/141145', + ); return; } expect(controller.value.isPlaying, false); @@ -166,7 +161,8 @@ void main() { // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes await controller.setVolume(0); await controller.seekTo( - controller.value.duration - const Duration(milliseconds: 10)); + controller.value.duration - const Duration(milliseconds: 10), + ); await controller.play(); await tester.pumpAndSettle(_playDuration); // Android emulators in our CI have frequent flake where the video @@ -178,7 +174,8 @@ void main() { // https://github.com/flutter/flutter/issues/141145 is fixed. if ((!kIsWeb && Platform.isAndroid) && controller.value.isPlaying) { markTestSkipped( - 'Skipping due to https://github.com/flutter/flutter/issues/141145'); + 'Skipping due to https://github.com/flutter/flutter/issues/141145', + ); return; } expect(controller.value.isPlaying, false); @@ -187,8 +184,10 @@ void main() { await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(controller.value.position, - lessThanOrEqualTo(controller.value.duration)); + expect( + controller.value.position, + lessThanOrEqualTo(controller.value.duration), + ); }, // Flaky on the web, headless browsers don't like to seek to non-buffered // positions of a video (and since this isn't even injecting the video @@ -196,43 +195,50 @@ void main() { skip: kIsWeb, ); - testWidgets('test video player view with local asset', - (WidgetTester tester) async { - final Completer loaded = Completer(); - Future started() async { - await controller.initialize(); - await controller.play(); - loaded.complete(); - return true; - } - - await tester.pumpWidget(Material( - child: Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: FutureBuilder( - future: started(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data ?? false) { - return AspectRatio( - aspectRatio: controller.value.aspectRatio, - child: VideoPlayer(controller), - ); - } else { - return const Text('waiting for video to load'); - } - }, + testWidgets( + 'test video player view with local asset', + (WidgetTester tester) async { + final Completer loaded = Completer(); + Future started() async { + await controller.initialize(); + await controller.play(); + loaded.complete(); + return true; + } + + await tester.pumpWidget( + Material( + child: Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: FutureBuilder( + future: started(), + builder: ( + BuildContext context, + AsyncSnapshot snapshot, + ) { + if (snapshot.data ?? false) { + return AspectRatio( + aspectRatio: controller.value.aspectRatio, + child: VideoPlayer(controller), + ); + } else { + return const Text('waiting for video to load'); + } + }, + ), + ), ), ), - ), - )); + ); - await loaded.future; - await tester.pumpAndSettle(); - expect(controller.value.isPlaying, true); - }, - // Web does not support local assets. - skip: kIsWeb); + await loaded.future; + await tester.pumpAndSettle(); + expect(controller.value.isPlaying, true); + }, + // Web does not support local assets. + skip: kIsWeb, + ); }); group('file-based videos', () { @@ -249,8 +255,9 @@ void main() { controller = VideoPlayerController.file(file); }); - testWidgets('test video player using static file() method as constructor', - (WidgetTester tester) async { + testWidgets('test video player using static file() method as constructor', ( + WidgetTester tester, + ) async { await controller.initialize(); await controller.play(); @@ -264,7 +271,8 @@ void main() { group('network videos', () { setUp(() { controller = VideoPlayerController.networkUrl( - Uri.parse(getUrlForAssetAsNetworkSource(_videoAssetKey))); + Uri.parse(getUrlForAssetAsNetworkSource(_videoAssetKey)), + ); }); testWidgets( @@ -293,8 +301,10 @@ void main() { await controller.pause(); expect(controller.value.isPlaying, false); - expect(controller.value.position, - (Duration position) => position > Duration.zero); + expect( + controller.value.position, + (Duration position) => position > Duration.zero, + ); await expectLater(started.future, completes); await expectLater(ended.future, completes); @@ -302,8 +312,8 @@ void main() { skip: // MEDIA_ELEMENT_ERROR on web, see https://github.com/flutter/flutter/issues/169219 kIsWeb || - // Hanging on Android, see https://github.com/flutter/flutter/issues/160797 - defaultTargetPlatform == TargetPlatform.android, + // Hanging on Android, see https://github.com/flutter/flutter/issues/160797 + defaultTargetPlatform == TargetPlatform.android, ); }); diff --git a/packages/video_player/video_player/example/lib/basic.dart b/packages/video_player/video_player/example/lib/basic.dart index c2c92047f9e..67cc2593089 100644 --- a/packages/video_player/video_player/example/lib/basic.dart +++ b/packages/video_player/video_player/example/lib/basic.dart @@ -27,8 +27,11 @@ class _VideoAppState extends State { @override void initState() { super.initState(); - _controller = VideoPlayerController.networkUrl(Uri.parse( - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4')) + _controller = VideoPlayerController.networkUrl( + Uri.parse( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + ), + ) ..initialize().then((_) { // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. setState(() {}); @@ -41,12 +44,13 @@ class _VideoAppState extends State { title: 'Video Demo', home: Scaffold( body: Center( - child: _controller.value.isInitialized - ? AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ) - : Container(), + child: + _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : Container(), ), floatingActionButton: FloatingActionButton( onPressed: () { @@ -70,4 +74,5 @@ class _VideoAppState extends State { super.dispose(); } } + // #enddocregion basic-example diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 3f76f8c32e0..eb86d32ad61 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -12,11 +12,7 @@ import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp( - MaterialApp( - home: _App(), - ), - ); + runApp(MaterialApp(home: _App())); } class _App extends StatelessWidget { @@ -40,15 +36,12 @@ class _App extends StatelessWidget { ), ); }, - ) + ), ], bottom: const TabBar( isScrollable: true, tabs: [ - Tab( - icon: Icon(Icons.cloud), - text: 'Remote', - ), + Tab(icon: Icon(Icons.cloud), text: 'Remote'), Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'), Tab(icon: Icon(Icons.list), text: 'List example'), ], @@ -57,16 +50,17 @@ class _App extends StatelessWidget { body: TabBarView( children: [ _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _BumbleBeeRemoteVideo(viewType), + builder: + (VideoViewType viewType) => _BumbleBeeRemoteVideo(viewType), ), _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _ButterFlyAssetVideo(viewType), + builder: + (VideoViewType viewType) => _ButterFlyAssetVideo(viewType), ), _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _ButterFlyAssetVideoInList(viewType), + builder: + (VideoViewType viewType) => + _ButterFlyAssetVideoInList(viewType), ), ], ), @@ -76,9 +70,7 @@ class _App extends StatelessWidget { } class _ViewTypeTabBar extends StatefulWidget { - const _ViewTypeTabBar({ - required this.builder, - }); + const _ViewTypeTabBar({required this.builder}); final Widget Function(VideoViewType) builder; @@ -110,14 +102,8 @@ class _ViewTypeTabBarState extends State<_ViewTypeTabBar> controller: _tabController, isScrollable: true, tabs: const [ - Tab( - icon: Icon(Icons.texture), - text: 'Texture view', - ), - Tab( - icon: Icon(Icons.construction), - text: 'Platform view', - ), + Tab(icon: Icon(Icons.texture), text: 'Texture view'), + Tab(icon: Icon(Icons.construction), text: 'Platform view'), ], ), Expanded( @@ -151,23 +137,28 @@ class _ButterFlyAssetVideoInList extends StatelessWidget { const _ExampleCard(title: 'Item f'), const _ExampleCard(title: 'Item g'), Card( - child: Column(children: [ - Column( + child: Column( children: [ - const ListTile( - leading: Icon(Icons.cake), - title: Text('Video video'), + Column( + children: [ + const ListTile( + leading: Icon(Icons.cake), + title: Text('Video video'), + ), + Stack( + alignment: + FractionalOffset.bottomRight + + const FractionalOffset(-0.1, -0.1), + children: [ + _ButterFlyAssetVideo(viewType), + Image.asset('assets/flutter-mark-square-64.png'), + ], + ), + ], ), - Stack( - alignment: FractionalOffset.bottomRight + - const FractionalOffset(-0.1, -0.1), - children: [ - _ButterFlyAssetVideo(viewType), - Image.asset('assets/flutter-mark-square-64.png'), - ]), ], ), - ])), + ), const _ExampleCard(title: 'Item h'), const _ExampleCard(title: 'Item i'), const _ExampleCard(title: 'Item j'), @@ -260,9 +251,7 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { return SingleChildScrollView( child: Column( children: [ - Container( - padding: const EdgeInsets.only(top: 20.0), - ), + Container(padding: const EdgeInsets.only(top: 20.0)), const Text('With assets mp4'), Container( padding: const EdgeInsets.all(20), @@ -297,10 +286,12 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { late VideoPlayerController _controller; Future _loadCaptions() async { - final String fileContents = await DefaultAssetBundle.of(context) - .loadString('assets/bumble_bee_captions.vtt'); + final String fileContents = await DefaultAssetBundle.of( + context, + ).loadString('assets/bumble_bee_captions.vtt'); return WebVTTCaptionFile( - fileContents); // For vtt files, use WebVTTCaptionFile + fileContents, + ); // For vtt files, use WebVTTCaptionFile } @override @@ -308,7 +299,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { super.initState(); _controller = VideoPlayerController.networkUrl( Uri.parse( - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + ), closedCaptionFile: _loadCaptions(), videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true), viewType: widget.viewType, @@ -389,19 +381,20 @@ class _ControlsOverlay extends StatelessWidget { AnimatedSwitcher( duration: const Duration(milliseconds: 50), reverseDuration: const Duration(milliseconds: 200), - child: controller.value.isPlaying - ? const SizedBox.shrink() - : const ColoredBox( - color: Colors.black26, - child: Center( - child: Icon( - Icons.play_arrow, - color: Colors.white, - size: 100.0, - semanticLabel: 'Play', + child: + controller.value.isPlaying + ? const SizedBox.shrink() + : const ColoredBox( + color: Colors.black26, + child: Center( + child: Icon( + Icons.play_arrow, + color: Colors.white, + size: 100.0, + semanticLabel: 'Play', + ), ), ), - ), ), GestureDetector( onTap: () { @@ -422,7 +415,7 @@ class _ControlsOverlay extends StatelessWidget { PopupMenuItem( value: offsetDuration, child: Text('${offsetDuration.inMilliseconds}ms'), - ) + ), ]; }, child: Padding( @@ -448,10 +441,7 @@ class _ControlsOverlay extends StatelessWidget { itemBuilder: (BuildContext context) { return >[ for (final double speed in _examplePlaybackRates) - PopupMenuItem( - value: speed, - child: Text('${speed}x'), - ) + PopupMenuItem(value: speed, child: Text('${speed}x')), ]; }, child: Padding( @@ -484,8 +474,9 @@ class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> { void initState() { super.initState(); - _videoPlayerController = - VideoPlayerController.asset('assets/Butterfly-209.mp4'); + _videoPlayerController = VideoPlayerController.asset( + 'assets/Butterfly-209.mp4', + ); _videoPlayerController.addListener(() { if (startedPlaying && !_videoPlayerController.value.isPlaying) { Navigator.pop(context); diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index d4ed8773a5b..6c990c8b34f 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the video_player plugin. publish_to: none environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" dependencies: flutter: diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 91b44687d7d..d4459137724 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -87,10 +87,5 @@ class Caption { text == other.text; @override - int get hashCode => Object.hash( - number, - start, - end, - text, - ); + int get hashCode => Object.hash(number, start, end, text); } diff --git a/packages/video_player/video_player/lib/src/sub_rip.dart b/packages/video_player/video_player/lib/src/sub_rip.dart index 7b807cd4d5d..e46d34e3b81 100644 --- a/packages/video_player/video_player/lib/src/sub_rip.dart +++ b/packages/video_player/video_player/lib/src/sub_rip.dart @@ -13,7 +13,7 @@ class SubRipCaptionFile extends ClosedCaptionFile { /// the SubRip file format. /// * See: https://en.wikipedia.org/wiki/SubRip SubRipCaptionFile(this.fileContents) - : _captions = _parseCaptionsFromSubRipString(fileContents); + : _captions = _parseCaptionsFromSubRipString(fileContents); /// The entire body of the SubRip file. // TODO(cyanglaz): Remove this public member as it doesn't seem need to exist. @@ -34,8 +34,9 @@ List _parseCaptionsFromSubRipString(String file) { } final int captionNumber = int.parse(captionLines[0]); - final _CaptionRange captionRange = - _CaptionRange.fromSubRipString(captionLines[1]); + final _CaptionRange captionRange = _CaptionRange.fromSubRipString( + captionLines[1], + ); final String text = captionLines.sublist(2).join('\n'); @@ -63,8 +64,9 @@ class _CaptionRange { // For example: // 00:01:54,724 --> 00:01:56,760 static _CaptionRange fromSubRipString(String line) { - final RegExp format = - RegExp(_subRipTimeStamp + _subRipArrow + _subRipTimeStamp); + final RegExp format = RegExp( + _subRipTimeStamp + _subRipArrow + _subRipTimeStamp, + ); if (!format.hasMatch(line)) { return _CaptionRange(Duration.zero, Duration.zero); diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 9f6a7452721..d346227d6de 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -16,7 +16,7 @@ class WebVTTCaptionFile extends ClosedCaptionFile { /// the WebVTT file format. /// * See: https://en.wikipedia.org/wiki/WebVTT WebVTTCaptionFile(String fileContents) - : _captions = _parseCaptionsFromWebVTTString(fileContents); + : _captions = _parseCaptionsFromWebVTTString(fileContents); @override List get captions => _captions; @@ -96,8 +96,9 @@ class _CaptionRange { // For example: // 00:09.000 --> 00:11.000 static _CaptionRange? fromWebVTTString(String line) { - final RegExp format = - RegExp(_webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp); + final RegExp format = RegExp( + _webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp, + ); if (!format.hasMatch(line)) { return null; diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 17c5bcb2995..46eff91f316 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -63,14 +63,15 @@ class VideoPlayerValue { /// Returns an instance for a video that hasn't been loaded. const VideoPlayerValue.uninitialized() - : this(duration: Duration.zero, isInitialized: false); + : this(duration: Duration.zero, isInitialized: false); /// Returns an instance with the given [errorDescription]. const VideoPlayerValue.erroneous(String errorDescription) - : this( - duration: Duration.zero, - isInitialized: false, - errorDescription: errorDescription); + : this( + duration: Duration.zero, + isInitialized: false, + errorDescription: errorDescription, + ); /// This constant is just to indicate that parameter is not passed to [copyWith] /// workaround for this issue https://github.com/dart-lang/language/issues/2009 @@ -187,9 +188,10 @@ class VideoPlayerValue { volume: volume ?? this.volume, playbackSpeed: playbackSpeed ?? this.playbackSpeed, rotationCorrection: rotationCorrection ?? this.rotationCorrection, - errorDescription: errorDescription != _defaultErrorDescription - ? errorDescription - : this.errorDescription, + errorDescription: + errorDescription != _defaultErrorDescription + ? errorDescription + : this.errorDescription, isCompleted: isCompleted ?? this.isCompleted, ); } @@ -236,22 +238,22 @@ class VideoPlayerValue { @override int get hashCode => Object.hash( - duration, - position, - caption, - captionOffset, - buffered, - isPlaying, - isLooping, - isBuffering, - volume, - playbackSpeed, - errorDescription, - size, - rotationCorrection, - isInitialized, - isCompleted, - ); + duration, + position, + caption, + captionOffset, + buffered, + isPlaying, + isLooping, + isBuffering, + volume, + playbackSpeed, + errorDescription, + size, + rotationCorrection, + isInitialized, + isCompleted, + ); } /// Controls a platform video player, and provides updates when the state is @@ -280,11 +282,11 @@ class VideoPlayerController extends ValueNotifier { Future? closedCaptionFile, this.videoPlayerOptions, this.viewType = VideoViewType.textureView, - }) : _closedCaptionFileFuture = closedCaptionFile, - dataSourceType = DataSourceType.asset, - formatHint = null, - httpHeaders = const {}, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : _closedCaptionFileFuture = closedCaptionFile, + dataSourceType = DataSourceType.asset, + formatHint = null, + httpHeaders = const {}, + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a network video. /// @@ -307,10 +309,10 @@ class VideoPlayerController extends ValueNotifier { this.videoPlayerOptions, this.httpHeaders = const {}, this.viewType = VideoViewType.textureView, - }) : _closedCaptionFileFuture = closedCaptionFile, - dataSourceType = DataSourceType.network, - package = null, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : _closedCaptionFileFuture = closedCaptionFile, + dataSourceType = DataSourceType.network, + package = null, + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a network video. /// @@ -328,11 +330,11 @@ class VideoPlayerController extends ValueNotifier { this.videoPlayerOptions, this.httpHeaders = const {}, this.viewType = VideoViewType.textureView, - }) : _closedCaptionFileFuture = closedCaptionFile, - dataSource = url.toString(), - dataSourceType = DataSourceType.network, - package = null, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : _closedCaptionFileFuture = closedCaptionFile, + dataSource = url.toString(), + dataSourceType = DataSourceType.network, + package = null, + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from a file. /// @@ -344,12 +346,12 @@ class VideoPlayerController extends ValueNotifier { this.videoPlayerOptions, this.httpHeaders = const {}, this.viewType = VideoViewType.textureView, - }) : _closedCaptionFileFuture = closedCaptionFile, - dataSource = Uri.file(file.absolute.path).toString(), - dataSourceType = DataSourceType.file, - package = null, - formatHint = null, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : _closedCaptionFileFuture = closedCaptionFile, + dataSource = Uri.file(file.absolute.path).toString(), + dataSourceType = DataSourceType.file, + package = null, + formatHint = null, + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from a contentUri. /// @@ -360,15 +362,17 @@ class VideoPlayerController extends ValueNotifier { Future? closedCaptionFile, this.videoPlayerOptions, this.viewType = VideoViewType.textureView, - }) : assert(defaultTargetPlatform == TargetPlatform.android, - 'VideoPlayerController.contentUri is only supported on Android.'), - _closedCaptionFileFuture = closedCaptionFile, - dataSource = contentUri.toString(), - dataSourceType = DataSourceType.contentUri, - package = null, - formatHint = null, - httpHeaders = const {}, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : assert( + defaultTargetPlatform == TargetPlatform.android, + 'VideoPlayerController.contentUri is only supported on Android.', + ), + _closedCaptionFileFuture = closedCaptionFile, + dataSource = contentUri.toString(), + dataSourceType = DataSourceType.contentUri, + package = null, + formatHint = null, + httpHeaders = const {}, + super(const VideoPlayerValue(duration: Duration.zero)); /// The URI to the video file. This will be in different formats depending on /// the [DataSourceType] of the original video. @@ -460,13 +464,14 @@ class VideoPlayerController extends ValueNotifier { ); if (videoPlayerOptions?.mixWithOthers != null) { - await _videoPlayerPlatform - .setMixWithOthers(videoPlayerOptions!.mixWithOthers); + await _videoPlayerPlatform.setMixWithOthers( + videoPlayerOptions!.mixWithOthers, + ); } _playerId = (await _videoPlayerPlatform.createWithOptions(creationOptions)) ?? - kUninitializedPlayerId; + kUninitializedPlayerId; _creatingCompleter!.complete(null); final Completer initializingCompleter = Completer(); @@ -522,8 +527,10 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(isBuffering: false); case VideoEventType.isPlayingStateUpdate: if (event.isPlaying ?? false) { - value = - value.copyWith(isPlaying: event.isPlaying, isCompleted: false); + value = value.copyWith( + isPlaying: event.isPlaying, + isCompleted: false, + ); } else { value = value.copyWith(isPlaying: event.isPlaying); } @@ -614,19 +621,18 @@ class VideoPlayerController extends ValueNotifier { await _videoPlayerPlatform.play(_playerId); _timer?.cancel(); - _timer = Timer.periodic( - const Duration(milliseconds: 100), - (Timer timer) async { - if (_isDisposed) { - return; - } - final Duration? newPosition = await position; - if (newPosition == null) { - return; - } - _updatePosition(newPosition); - }, - ); + _timer = Timer.periodic(const Duration(milliseconds: 100), ( + Timer timer, + ) async { + if (_isDisposed) { + return; + } + final Duration? newPosition = await position; + if (newPosition == null) { + return; + } + _updatePosition(newPosition); + }); // This ensures that the correct playback speed is always applied when // playing back. This is necessary because we do not set playback speed @@ -657,10 +663,7 @@ class VideoPlayerController extends ValueNotifier { return; } - await _videoPlayerPlatform.setPlaybackSpeed( - _playerId, - value.playbackSpeed, - ); + await _videoPlayerPlatform.setPlaybackSpeed(_playerId, value.playbackSpeed); } /// The position in the current video. @@ -903,17 +906,17 @@ class _VideoPlayerState extends State { return _playerId == VideoPlayerController.kUninitializedPlayerId ? Container() : _VideoPlayerWithRotation( - rotation: widget.controller.value.rotationCorrection, - child: _videoPlayerPlatform.buildViewWithOptions( - VideoViewOptions(playerId: _playerId), - ), - ); + rotation: widget.controller.value.rotationCorrection, + child: _videoPlayerPlatform.buildViewWithOptions( + VideoViewOptions(playerId: _playerId), + ), + ); } } class _VideoPlayerWithRotation extends StatelessWidget { const _VideoPlayerWithRotation({required this.rotation, required this.child}) - : assert(rotation % 90 == 0, 'Rotation must be a multiple of 90'); + : assert(rotation % 90 == 0, 'Rotation must be a multiple of 90'); final int rotation; final Widget child; @@ -923,10 +926,7 @@ class _VideoPlayerWithRotation extends StatelessWidget { if (rotation == 0) { return child; } - return RotatedBox( - quarterTurns: rotation ~/ 90, - child: child, - ); + return RotatedBox(quarterTurns: rotation ~/ 90, child: child); } } @@ -1210,11 +1210,11 @@ class ClosedCaption extends StatelessWidget { return const SizedBox.shrink(); } - final TextStyle effectiveTextStyle = textStyle ?? - DefaultTextStyle.of(context).style.copyWith( - fontSize: 36.0, - color: Colors.white, - ); + final TextStyle effectiveTextStyle = + textStyle ?? + DefaultTextStyle.of( + context, + ).style.copyWith(fontSize: 36.0, color: Colors.white); return Align( alignment: Alignment.bottomCenter, diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 7ae423d6bdb..c8863f632ff 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -6,8 +6,8 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ version: 2.10.0 environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" flutter: plugin: diff --git a/packages/video_player/video_player/test/closed_caption_file_test.dart b/packages/video_player/video_player/test/closed_caption_file_test.dart index a20f9479dc4..b7ff481cb91 100644 --- a/packages/video_player/video_player/test/closed_caption_file_test.dart +++ b/packages/video_player/video_player/test/closed_caption_file_test.dart @@ -16,12 +16,13 @@ void main() { ); expect( - caption.toString(), - 'Caption(' - 'number: 1, ' - 'start: 0:00:01.000000, ' - 'end: 0:00:02.000000, ' - 'text: caption)'); + caption.toString(), + 'Caption(' + 'number: 1, ' + 'start: 0:00:01.000000, ' + 'end: 0:00:02.000000, ' + 'text: caption)', + ); }); }); } diff --git a/packages/video_player/video_player/test/video_player_initialization_test.dart b/packages/video_player/video_player/test/video_player_initialization_test.dart index 5ebb4bb9b52..4256067b225 100644 --- a/packages/video_player/video_player/test/video_player_initialization_test.dart +++ b/packages/video_player/video_player/test/video_player_initialization_test.dart @@ -36,9 +36,7 @@ void main() { final VideoPlayerController controller = VideoPlayerController.networkUrl( Uri.parse('https://127.0.0.1'), - videoPlayerOptions: VideoPlayerOptions( - webOptions: expected, - ), + videoPlayerOptions: VideoPlayerOptions(webOptions: expected), ); await controller.initialize(); diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 38acf159dd0..c4bd4a573bf 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -122,11 +122,13 @@ void main() { required bool shouldPlayInBackground, }) { expect(controller.value.isPlaying, true); - WidgetsBinding.instance - .handleAppLifecycleStateChanged(AppLifecycleState.paused); + WidgetsBinding.instance.handleAppLifecycleStateChanged( + AppLifecycleState.paused, + ); expect(controller.value.isPlaying, shouldPlayInBackground); - WidgetsBinding.instance - .handleAppLifecycleStateChanged(AppLifecycleState.resumed); + WidgetsBinding.instance.handleAppLifecycleStateChanged( + AppLifecycleState.resumed, + ); expect(controller.value.isPlaying, true); } @@ -152,27 +154,30 @@ void main() { controller1.playerId = 101; await tester.pumpWidget(VideoPlayer(controller1)); expect( - find.byWidgetPredicate( - (Widget widget) => widget is Texture && widget.textureId == 101, - ), - findsOneWidget); + find.byWidgetPredicate( + (Widget widget) => widget is Texture && widget.textureId == 101, + ), + findsOneWidget, + ); final FakeController controller2 = FakeController(); addTearDown(controller2.dispose); controller2.playerId = 102; await tester.pumpWidget(VideoPlayer(controller2)); expect( - find.byWidgetPredicate( - (Widget widget) => widget is Texture && widget.textureId == 102, - ), - findsOneWidget); + find.byWidgetPredicate( + (Widget widget) => widget is Texture && widget.textureId == 102, + ), + findsOneWidget, + ); }); - testWidgets('non-zero rotationCorrection value is used', - (WidgetTester tester) async { + testWidgets('non-zero rotationCorrection value is used', ( + WidgetTester tester, + ) async { final FakeController controller = FakeController.value( - const VideoPlayerValue( - duration: Duration.zero, rotationCorrection: 180)); + const VideoPlayerValue(duration: Duration.zero, rotationCorrection: 180), + ); addTearDown(controller.dispose); controller.playerId = 1; await tester.pumpWidget(VideoPlayer(controller)); @@ -182,10 +187,12 @@ void main() { expect(actualQuarterTurns, equals(2)); }); - testWidgets('no RotatedBox when rotationCorrection is zero', - (WidgetTester tester) async { - final FakeController controller = - FakeController.value(const VideoPlayerValue(duration: Duration.zero)); + testWidgets('no RotatedBox when rotationCorrection is zero', ( + WidgetTester tester, + ) async { + final FakeController controller = FakeController.value( + const VideoPlayerValue(duration: Duration.zero), + ); addTearDown(controller.dispose); controller.playerId = 1; await tester.pumpWidget(VideoPlayer(controller)); @@ -195,8 +202,9 @@ void main() { group('ClosedCaption widget', () { testWidgets('uses a default text style', (WidgetTester tester) async { const String text = 'foo'; - await tester - .pumpWidget(const MaterialApp(home: ClosedCaption(text: text))); + await tester.pumpWidget( + const MaterialApp(home: ClosedCaption(text: text)), + ); final Text textWidget = tester.widget(find.text(text)); expect(textWidget.style!.fontSize, 36.0); @@ -206,12 +214,11 @@ void main() { testWidgets('uses given text and style', (WidgetTester tester) async { const String text = 'foo'; const TextStyle textStyle = TextStyle(fontSize: 14.725); - await tester.pumpWidget(const MaterialApp( - home: ClosedCaption( - text: text, - textStyle: textStyle, + await tester.pumpWidget( + const MaterialApp( + home: ClosedCaption(text: text, textStyle: textStyle), ), - )); + ); expect(find.text(text), findsOneWidget); final Text textWidget = tester.widget(find.text(text)); @@ -228,15 +235,18 @@ void main() { expect(find.byType(Text), findsNothing); }); - testWidgets('Passes text contrast ratio guidelines', - (WidgetTester tester) async { + testWidgets('Passes text contrast ratio guidelines', ( + WidgetTester tester, + ) async { const String text = 'foo'; - await tester.pumpWidget(const MaterialApp( - home: Scaffold( - backgroundColor: Colors.white, - body: ClosedCaption(text: text), + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + backgroundColor: Colors.white, + body: ClosedCaption(text: text), + ), ), - )); + ); expect(find.text(text), findsOneWidget); await expectLater(tester, meetsGuideline(textContrastGuideline)); @@ -251,14 +261,8 @@ void main() { ); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSources[0].uri, - 'https://127.0.0.1', - ); - expect( - fakeVideoPlayerPlatform.dataSources[0].formatHint, - null, - ); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1'); + expect(fakeVideoPlayerPlatform.dataSources[0].formatHint, null); expect( fakeVideoPlayerPlatform.dataSources[0].httpHeaders, {}, @@ -272,10 +276,7 @@ void main() { ); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSources[0].uri, - 'https://127.0.0.1', - ); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1'); expect( fakeVideoPlayerPlatform.dataSources[0].formatHint, VideoFormat.dash, @@ -293,14 +294,8 @@ void main() { ); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSources[0].uri, - 'https://127.0.0.1', - ); - expect( - fakeVideoPlayerPlatform.dataSources[0].formatHint, - null, - ); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1'); + expect(fakeVideoPlayerPlatform.dataSources[0].formatHint, null); expect( fakeVideoPlayerPlatform.dataSources[0].httpHeaders, {'Authorization': 'Bearer token'}, @@ -311,14 +306,14 @@ void main() { group('initialize', () { test('started app lifecycle observing', () async { final VideoPlayerController controller = - VideoPlayerController.networkUrl( - Uri.parse('https://127.0.0.1'), - ); + VideoPlayerController.networkUrl(Uri.parse('https://127.0.0.1')); addTearDown(controller.dispose); await controller.initialize(); await controller.play(); - verifyPlayStateRespondsToLifecycle(controller, - shouldPlayInBackground: false); + verifyPlayStateRespondsToLifecycle( + controller, + shouldPlayInBackground: false, + ); }); test('asset', () async { @@ -337,14 +332,8 @@ void main() { addTearDown(controller.dispose); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSources[0].uri, - 'https://127.0.0.1', - ); - expect( - fakeVideoPlayerPlatform.dataSources[0].formatHint, - null, - ); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1'); + expect(fakeVideoPlayerPlatform.dataSources[0].formatHint, null); expect( fakeVideoPlayerPlatform.dataSources[0].httpHeaders, {}, @@ -354,16 +343,13 @@ void main() { test('network url with hint', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - Uri.parse('https://127.0.0.1'), - formatHint: VideoFormat.dash, - ); + Uri.parse('https://127.0.0.1'), + formatHint: VideoFormat.dash, + ); addTearDown(controller.dispose); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSources[0].uri, - 'https://127.0.0.1', - ); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1'); expect( fakeVideoPlayerPlatform.dataSources[0].formatHint, VideoFormat.dash, @@ -377,20 +363,14 @@ void main() { test('network url with some headers', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - Uri.parse('https://127.0.0.1'), - httpHeaders: {'Authorization': 'Bearer token'}, - ); + Uri.parse('https://127.0.0.1'), + httpHeaders: {'Authorization': 'Bearer token'}, + ); addTearDown(controller.dispose); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSources[0].uri, - 'https://127.0.0.1', - ); - expect( - fakeVideoPlayerPlatform.dataSources[0].formatHint, - null, - ); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1'); + expect(fakeVideoPlayerPlatform.dataSources[0].formatHint, null); expect( fakeVideoPlayerPlatform.dataSources[0].httpHeaders, {'Authorization': 'Bearer token'}, @@ -398,99 +378,125 @@ void main() { }); test( - 'when controller is initialized with invalid url it should throw VideoError', - () async { - final Uri invalidUrl = Uri.parse('http://testing.com/invalid_url'); - - final VideoPlayerController controller = - VideoPlayerController.networkUrl(invalidUrl); - addTearDown(controller.dispose); - - late Object error; - fakeVideoPlayerPlatform.forceInitError = true; - await controller.initialize().catchError((Object e) => error = e); - final PlatformException platformEx = error as PlatformException; - expect(platformEx.code, equals('VideoError')); - }); + 'when controller is initialized with invalid url it should throw VideoError', + () async { + final Uri invalidUrl = Uri.parse('http://testing.com/invalid_url'); + + final VideoPlayerController controller = + VideoPlayerController.networkUrl(invalidUrl); + addTearDown(controller.dispose); + + late Object error; + fakeVideoPlayerPlatform.forceInitError = true; + await controller.initialize().catchError((Object e) => error = e); + final PlatformException platformEx = error as PlatformException; + expect(platformEx.code, equals('VideoError')); + }, + ); test('file', () async { - final VideoPlayerController controller = - VideoPlayerController.file(File('a.avi')); - await controller.initialize(); - - final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!; - expect(uri.startsWith('file:///'), true, reason: 'Actual string: $uri'); - expect(uri.endsWith('/a.avi'), true, reason: 'Actual string: $uri'); - }, skip: kIsWeb /* Web does not support file assets. */); - - test('file with special characters', () async { - final VideoPlayerController controller = - VideoPlayerController.file(File('A #1 Hit.avi')); - await controller.initialize(); - - final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!; - expect(uri.startsWith('file:///'), true, reason: 'Actual string: $uri'); - expect(uri.endsWith('/A%20%231%20Hit.avi'), true, - reason: 'Actual string: $uri'); - }, skip: kIsWeb /* Web does not support file assets. */); - - test('file with headers (m3u8)', () async { final VideoPlayerController controller = VideoPlayerController.file( File('a.avi'), - httpHeaders: {'Authorization': 'Bearer token'}, ); await controller.initialize(); final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!; expect(uri.startsWith('file:///'), true, reason: 'Actual string: $uri'); expect(uri.endsWith('/a.avi'), true, reason: 'Actual string: $uri'); - - expect( - fakeVideoPlayerPlatform.dataSources[0].httpHeaders, - {'Authorization': 'Bearer token'}, - ); }, skip: kIsWeb /* Web does not support file assets. */); - test('successful initialize on controller with error clears error', - () async { - final VideoPlayerController controller = VideoPlayerController.network( - 'https://127.0.0.1', - ); - fakeVideoPlayerPlatform.forceInitError = true; - await controller.initialize().catchError((dynamic e) {}); - expect(controller.value.hasError, equals(true)); - fakeVideoPlayerPlatform.forceInitError = false; - await controller.initialize(); - expect(controller.value.hasError, equals(false)); - }); + test( + 'file with special characters', + () async { + final VideoPlayerController controller = VideoPlayerController.file( + File('A #1 Hit.avi'), + ); + await controller.initialize(); + + final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!; + expect( + uri.startsWith('file:///'), + true, + reason: 'Actual string: $uri', + ); + expect( + uri.endsWith('/A%20%231%20Hit.avi'), + true, + reason: 'Actual string: $uri', + ); + }, + skip: kIsWeb /* Web does not support file assets. */, + ); test( - 'given controller with error when initialization succeeds it should clear error', - () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); - addTearDown(controller.dispose); + 'file with headers (m3u8)', + () async { + final VideoPlayerController controller = VideoPlayerController.file( + File('a.avi'), + httpHeaders: {'Authorization': 'Bearer token'}, + ); + await controller.initialize(); + + final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!; + expect( + uri.startsWith('file:///'), + true, + reason: 'Actual string: $uri', + ); + expect(uri.endsWith('/a.avi'), true, reason: 'Actual string: $uri'); + + expect( + fakeVideoPlayerPlatform.dataSources[0].httpHeaders, + {'Authorization': 'Bearer token'}, + ); + }, + skip: kIsWeb /* Web does not support file assets. */, + ); - fakeVideoPlayerPlatform.forceInitError = true; - await controller.initialize().catchError((dynamic e) {}); - expect(controller.value.hasError, equals(true)); - fakeVideoPlayerPlatform.forceInitError = false; - await controller.initialize(); - expect(controller.value.hasError, equals(false)); - }); + test( + 'successful initialize on controller with error clears error', + () async { + final VideoPlayerController controller = + VideoPlayerController.network('https://127.0.0.1'); + fakeVideoPlayerPlatform.forceInitError = true; + await controller.initialize().catchError((dynamic e) {}); + expect(controller.value.hasError, equals(true)); + fakeVideoPlayerPlatform.forceInitError = false; + await controller.initialize(); + expect(controller.value.hasError, equals(false)); + }, + ); + + test( + 'given controller with error when initialization succeeds it should clear error', + () async { + final VideoPlayerController controller = + VideoPlayerController.networkUrl(_localhostUri); + addTearDown(controller.dispose); + + fakeVideoPlayerPlatform.forceInitError = true; + await controller.initialize().catchError((dynamic e) {}); + expect(controller.value.hasError, equals(true)); + fakeVideoPlayerPlatform.forceInitError = false; + await controller.initialize(); + expect(controller.value.hasError, equals(false)); + }, + ); }); test('contentUri', () async { - final VideoPlayerController controller = - VideoPlayerController.contentUri(Uri.parse('content://video')); + final VideoPlayerController controller = VideoPlayerController.contentUri( + Uri.parse('content://video'), + ); await controller.initialize(); expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'content://video'); }); test('dispose', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + ); addTearDown(controller.dispose); expect(controller.playerId, VideoPlayerController.kUninitializedPlayerId); @@ -504,8 +510,9 @@ void main() { }); test('calling dispose() on disposed controller does not throw', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + ); addTearDown(controller.dispose); await controller.initialize(); @@ -515,8 +522,9 @@ void main() { }); test('play', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(Uri.parse('https://127.0.0.1')); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + Uri.parse('https://127.0.0.1'), + ); addTearDown(controller.dispose); await controller.initialize(); @@ -528,15 +536,16 @@ void main() { // The two last calls will be "play" and then "setPlaybackSpeed". The // reason for this is that "play" calls "setPlaybackSpeed" internally. expect( - fakeVideoPlayerPlatform - .calls[fakeVideoPlayerPlatform.calls.length - 2], - 'play'); + fakeVideoPlayerPlatform.calls[fakeVideoPlayerPlatform.calls.length - 2], + 'play', + ); expect(fakeVideoPlayerPlatform.calls.last, 'setPlaybackSpeed'); }); test('play before initialized does not call platform', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + ); addTearDown(controller.dispose); expect(controller.value.isInitialized, isFalse); @@ -547,8 +556,9 @@ void main() { }); test('play restarts from beginning if video is at end', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + ); addTearDown(controller.dispose); await controller.initialize(); @@ -565,8 +575,9 @@ void main() { }); test('setLooping', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + ); addTearDown(controller.dispose); await controller.initialize(); @@ -577,8 +588,9 @@ void main() { }); test('pause', () async { - final VideoPlayerController controller = - VideoPlayerController.networkUrl(_localhostUri); + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + ); addTearDown(controller.dispose); await controller.initialize(); @@ -692,19 +704,24 @@ void main() { }); group('scrubbing', () { - testWidgets('restarts on release if already playing', - (WidgetTester tester) async { + testWidgets('restarts on release if already playing', ( + WidgetTester tester, + ) async { final VideoPlayerController controller = VideoPlayerController.networkUrl(_localhostUri); await controller.initialize(); - final VideoProgressIndicator progressWidget = - VideoProgressIndicator(controller, allowScrubbing: true); + final VideoProgressIndicator progressWidget = VideoProgressIndicator( + controller, + allowScrubbing: true, + ); - await tester.pumpWidget(Directionality( - textDirection: TextDirection.ltr, - child: progressWidget, - )); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: progressWidget, + ), + ); await controller.play(); expect(controller.value.isPlaying, isTrue); @@ -720,19 +737,24 @@ void main() { await tester.runAsync(controller.dispose); }); - testWidgets('does not restart when dragging to end', - (WidgetTester tester) async { + testWidgets('does not restart when dragging to end', ( + WidgetTester tester, + ) async { final VideoPlayerController controller = VideoPlayerController.networkUrl(_localhostUri); await controller.initialize(); - final VideoProgressIndicator progressWidget = - VideoProgressIndicator(controller, allowScrubbing: true); + final VideoProgressIndicator progressWidget = VideoProgressIndicator( + controller, + allowScrubbing: true, + ); - await tester.pumpWidget(Directionality( - textDirection: TextDirection.ltr, - child: progressWidget, - )); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: progressWidget, + ), + ); await controller.play(); expect(controller.value.isPlaying, isTrue); @@ -751,9 +773,9 @@ void main() { test('works when position updates', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - _localhostUri, - closedCaptionFile: _loadClosedCaption(), - ); + _localhostUri, + closedCaptionFile: _loadClosedCaption(), + ); await controller.initialize(); await controller.play(); @@ -772,8 +794,9 @@ void main() { // Simulate continuous playback by incrementing in 50ms steps. for (int ms = 0; ms <= totalDurationMs; ms += 50) { - fakeVideoPlayerPlatform._positions[controller.playerId] = - Duration(milliseconds: ms); + fakeVideoPlayerPlatform._positions[controller.playerId] = Duration( + milliseconds: ms, + ); await Future.delayed(updateInterval); } @@ -790,9 +813,9 @@ void main() { test('works when seeking', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - _localhostUri, - closedCaptionFile: _loadClosedCaption(), - ); + _localhostUri, + closedCaptionFile: _loadClosedCaption(), + ); addTearDown(controller.dispose); await controller.initialize(); @@ -824,9 +847,9 @@ void main() { test('works when seeking with captionOffset positive', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - _localhostUri, - closedCaptionFile: _loadClosedCaption(), - ); + _localhostUri, + closedCaptionFile: _loadClosedCaption(), + ); addTearDown(controller.dispose); await controller.initialize(); @@ -862,9 +885,9 @@ void main() { test('works when seeking with captionOffset negative', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - _localhostUri, - closedCaptionFile: _loadClosedCaption(), - ); + _localhostUri, + closedCaptionFile: _loadClosedCaption(), + ); addTearDown(controller.dispose); await controller.initialize(); @@ -902,9 +925,7 @@ void main() { test('setClosedCaptionFile loads caption file', () async { final VideoPlayerController controller = - VideoPlayerController.networkUrl( - _localhostUri, - ); + VideoPlayerController.networkUrl(_localhostUri); addTearDown(controller.dispose); await controller.initialize(); @@ -920,9 +941,9 @@ void main() { test('setClosedCaptionFile removes/changes caption file', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( - _localhostUri, - closedCaptionFile: _loadClosedCaption(), - ); + _localhostUri, + closedCaptionFile: _loadClosedCaption(), + ); addTearDown(controller.dispose); await controller.initialize(); @@ -939,9 +960,7 @@ void main() { group('Platform callbacks', () { testWidgets('playing completed', (WidgetTester tester) async { final VideoPlayerController controller = - VideoPlayerController.networkUrl( - _localhostUri, - ); + VideoPlayerController.networkUrl(_localhostUri); await controller.initialize(); const Duration nonzeroDuration = Duration(milliseconds: 100); @@ -952,8 +971,9 @@ void main() { final StreamController fakeVideoEventStream = fakeVideoPlayerPlatform.streams[controller.playerId]!; - fakeVideoEventStream - .add(VideoEvent(eventType: VideoEventType.completed)); + fakeVideoEventStream.add( + VideoEvent(eventType: VideoEventType.completed), + ); await tester.pumpAndSettle(); expect(controller.value.isPlaying, isFalse); @@ -970,17 +990,21 @@ void main() { final StreamController fakeVideoEventStream = fakeVideoPlayerPlatform.streams[controller.playerId]!; - fakeVideoEventStream.add(VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: true, - )); + fakeVideoEventStream.add( + VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: true, + ), + ); await tester.pumpAndSettle(); expect(controller.value.isPlaying, isTrue); - fakeVideoEventStream.add(VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: false, - )); + fakeVideoEventStream.add( + VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: false, + ), + ); await tester.pumpAndSettle(); expect(controller.value.isPlaying, isFalse); await tester.runAsync(controller.dispose); @@ -988,9 +1012,7 @@ void main() { testWidgets('buffering status', (WidgetTester tester) async { final VideoPlayerController controller = - VideoPlayerController.networkUrl( - _localhostUri, - ); + VideoPlayerController.networkUrl(_localhostUri); await controller.initialize(); expect(controller.value.isBuffering, false); @@ -998,26 +1020,31 @@ void main() { final StreamController fakeVideoEventStream = fakeVideoPlayerPlatform.streams[controller.playerId]!; - fakeVideoEventStream - .add(VideoEvent(eventType: VideoEventType.bufferingStart)); + fakeVideoEventStream.add( + VideoEvent(eventType: VideoEventType.bufferingStart), + ); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); const Duration bufferStart = Duration.zero; const Duration bufferEnd = Duration(milliseconds: 500); - fakeVideoEventStream.add(VideoEvent( + fakeVideoEventStream.add( + VideoEvent( eventType: VideoEventType.bufferingUpdate, - buffered: [ - DurationRange(bufferStart, bufferEnd), - ])); + buffered: [DurationRange(bufferStart, bufferEnd)], + ), + ); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); expect(controller.value.buffered.length, 1); - expect(controller.value.buffered[0].toString(), - DurationRange(bufferStart, bufferEnd).toString()); + expect( + controller.value.buffered[0].toString(), + DurationRange(bufferStart, bufferEnd).toString(), + ); - fakeVideoEventStream - .add(VideoEvent(eventType: VideoEventType.bufferingEnd)); + fakeVideoEventStream.add( + VideoEvent(eventType: VideoEventType.bufferingEnd), + ); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isFalse); await tester.runAsync(controller.dispose); @@ -1048,8 +1075,9 @@ void main() { await controller.play(); for (int i = 0; i < 3; i++) { await Future.delayed(updatesInterval); - fakeVideoPlayerPlatform._positions[controller.playerId] = - Duration(milliseconds: i * updatesInterval.inMilliseconds); + fakeVideoPlayerPlatform._positions[controller.playerId] = Duration( + milliseconds: i * updatesInterval.inMilliseconds, + ); } // Wait for at least 3 position updates @@ -1131,10 +1159,14 @@ void main() { const Size size = Size(400, 300); const Duration position = Duration(seconds: 1); const Caption caption = Caption( - text: 'foo', number: 0, start: Duration.zero, end: Duration.zero); + text: 'foo', + number: 0, + start: Duration.zero, + end: Duration.zero, + ); const Duration captionOffset = Duration(milliseconds: 250); final List buffered = [ - DurationRange(Duration.zero, const Duration(seconds: 4)) + DurationRange(Duration.zero, const Duration(seconds: 4)), ]; const bool isInitialized = true; const bool isPlaying = true; @@ -1159,21 +1191,22 @@ void main() { ); expect( - value.toString(), - 'VideoPlayerValue(duration: 0:00:05.000000, ' - 'size: Size(400.0, 300.0), ' - 'position: 0:00:01.000000, ' - 'caption: Caption(number: 0, start: 0:00:00.000000, end: 0:00:00.000000, text: foo), ' - 'captionOffset: 0:00:00.250000, ' - 'buffered: [DurationRange(start: 0:00:00.000000, end: 0:00:04.000000)], ' - 'isInitialized: true, ' - 'isPlaying: true, ' - 'isLooping: true, ' - 'isBuffering: true, ' - 'volume: 0.5, ' - 'playbackSpeed: 1.5, ' - 'errorDescription: null, ' - 'isCompleted: false),'); + value.toString(), + 'VideoPlayerValue(duration: 0:00:05.000000, ' + 'size: Size(400.0, 300.0), ' + 'position: 0:00:01.000000, ' + 'caption: Caption(number: 0, start: 0:00:00.000000, end: 0:00:00.000000, text: foo), ' + 'captionOffset: 0:00:00.250000, ' + 'buffered: [DurationRange(start: 0:00:00.000000, end: 0:00:04.000000)], ' + 'isInitialized: true, ' + 'isPlaying: true, ' + 'isLooping: true, ' + 'isBuffering: true, ' + 'volume: 0.5, ' + 'playbackSpeed: 1.5, ' + 'errorDescription: null, ' + 'isCompleted: false),', + ); }); group('copyWith()', () { @@ -1191,15 +1224,17 @@ void main() { }); test('errorDescription is changed when copy with another error', () { const VideoPlayerValue original = VideoPlayerValue.erroneous('error'); - final VideoPlayerValue copy = - original.copyWith(errorDescription: 'new error'); + final VideoPlayerValue copy = original.copyWith( + errorDescription: 'new error', + ); expect(copy.errorDescription, 'new error'); }); test('errorDescription is changed when copy with error', () { const VideoPlayerValue original = VideoPlayerValue.uninitialized(); - final VideoPlayerValue copy = - original.copyWith(errorDescription: 'new error'); + final VideoPlayerValue copy = original.copyWith( + errorDescription: 'new error', + ); expect(copy.errorDescription, 'new error'); }); @@ -1267,9 +1302,7 @@ void main() { test('true allowBackgroundPlayback continues playback', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( _localhostUri, - videoPlayerOptions: VideoPlayerOptions( - allowBackgroundPlayback: true, - ), + videoPlayerOptions: VideoPlayerOptions(allowBackgroundPlayback: true), ); addTearDown(controller.dispose); @@ -1303,9 +1336,10 @@ void main() { const Color backgroundColor = Color.fromRGBO(255, 255, 0, 0.25); const VideoProgressColors colors = VideoProgressColors( - playedColor: playedColor, - bufferedColor: bufferedColor, - backgroundColor: backgroundColor); + playedColor: playedColor, + bufferedColor: bufferedColor, + backgroundColor: backgroundColor, + ); expect(colors.playedColor, playedColor); expect(colors.bufferedColor, bufferedColor); @@ -1364,9 +1398,12 @@ void main() { if (controller.value.isCompleted) { isCompletedTest(); if (!hasLooped) { - fakeVideoEventStream.add(VideoEvent( + fakeVideoEventStream.add( + VideoEvent( eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: true)); + isPlaying: true, + ), + ); hasLooped = !hasLooped; } } else { @@ -1391,8 +1428,9 @@ void main() { final void Function() isCompletedTest = expectAsync0(() {}); - controller.value = - controller.value.copyWith(duration: const Duration(seconds: 10)); + controller.value = controller.value.copyWith( + duration: const Duration(seconds: 10), + ); controller.addListener(() async { if (currentIsCompleted != controller.value.isCompleted) { @@ -1430,13 +1468,20 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { final StreamController stream = StreamController(); streams[nextPlayerId] = stream; if (forceInitError) { - stream.addError(PlatformException( - code: 'VideoError', message: 'Video player had error XYZ')); + stream.addError( + PlatformException( + code: 'VideoError', + message: 'Video player had error XYZ', + ), + ); } else { - stream.add(VideoEvent( + stream.add( + VideoEvent( eventType: VideoEventType.initialized, size: const Size(100, 100), - duration: const Duration(seconds: 1))); + duration: const Duration(seconds: 1), + ), + ); } dataSources.add(dataSource); return nextPlayerId++; @@ -1448,13 +1493,20 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { final StreamController stream = StreamController(); streams[nextPlayerId] = stream; if (forceInitError) { - stream.addError(PlatformException( - code: 'VideoError', message: 'Video player had error XYZ')); + stream.addError( + PlatformException( + code: 'VideoError', + message: 'Video player had error XYZ', + ), + ); } else { - stream.add(VideoEvent( + stream.add( + VideoEvent( eventType: VideoEventType.initialized, size: const Size(100, 100), - duration: const Duration(seconds: 1))); + duration: const Duration(seconds: 1), + ), + ); } dataSources.add(options.dataSource); viewTypes.add(options.viewType); @@ -1526,7 +1578,9 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { @override Future setWebOptions( - int playerId, VideoPlayerWebOptions options) async { + int playerId, + VideoPlayerWebOptions options, + ) async { if (!kIsWeb) { throw UnimplementedError('setWebOptions() is only available in the web.'); } diff --git a/packages/video_player/video_player/test/web_vtt_test.dart b/packages/video_player/video_player/test/web_vtt_test.dart index 8a356387942..ebf80de49df 100644 --- a/packages/video_player/video_player/test/web_vtt_test.dart +++ b/packages/video_player/video_player/test/web_vtt_test.dart @@ -15,8 +15,10 @@ void main() { expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].start, const Duration(seconds: 1)); - expect(parsedFile.captions[0].end, - const Duration(seconds: 2, milliseconds: 500)); + expect( + parsedFile.captions[0].end, + const Duration(seconds: 2, milliseconds: 500), + ); expect(parsedFile.captions[0].text, 'We are in New York City'); }); @@ -25,37 +27,54 @@ void main() { expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].number, 2); - expect(parsedFile.captions[0].start, - const Duration(seconds: 2, milliseconds: 800)); - expect(parsedFile.captions[0].end, - const Duration(seconds: 3, milliseconds: 283)); - expect(parsedFile.captions[0].text, - '— It will perforate your stomach.\n— You could die.'); + expect( + parsedFile.captions[0].start, + const Duration(seconds: 2, milliseconds: 800), + ); + expect( + parsedFile.captions[0].end, + const Duration(seconds: 3, milliseconds: 283), + ); + expect( + parsedFile.captions[0].text, + '— It will perforate your stomach.\n— You could die.', + ); }); test('with Multiline without identifier', () { - parsedFile = - WebVTTCaptionFile(_valid_vtt_with_multiline_without_identifier); + parsedFile = WebVTTCaptionFile( + _valid_vtt_with_multiline_without_identifier, + ); expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].number, 1); - expect(parsedFile.captions[0].start, - const Duration(seconds: 2, milliseconds: 800)); - expect(parsedFile.captions[0].end, - const Duration(seconds: 3, milliseconds: 283)); - expect(parsedFile.captions[0].text, - '— It will perforate your stomach.\n— You could die.'); + expect( + parsedFile.captions[0].start, + const Duration(seconds: 2, milliseconds: 800), + ); + expect( + parsedFile.captions[0].end, + const Duration(seconds: 3, milliseconds: 283), + ); + expect( + parsedFile.captions[0].text, + '— It will perforate your stomach.\n— You could die.', + ); }); test('with styles tags', () { parsedFile = WebVTTCaptionFile(_valid_vtt_with_styles); expect(parsedFile.captions.length, 3); - expect(parsedFile.captions[0].start, - const Duration(seconds: 5, milliseconds: 200)); + expect( + parsedFile.captions[0].start, + const Duration(seconds: 5, milliseconds: 200), + ); expect(parsedFile.captions[0].end, const Duration(seconds: 6)); - expect(parsedFile.captions[0].text, - "You know I'm so excited my glasses are falling off here."); + expect( + parsedFile.captions[0].text, + "You know I'm so excited my glasses are falling off here.", + ); }); test('with subtitling features', () { diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index df6b448cfe3..d358aae1f1c 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. + ## 2.8.4 * Simplifies native code. 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 d8a73b09d24..ce483c03c2a 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 @@ -47,8 +47,9 @@ void main() { controller = MiniController.asset(_videoAssetKey); }); - testWidgets('registers expected implementation', - (WidgetTester tester) async { + testWidgets('registers expected implementation', ( + WidgetTester tester, + ) async { AVFoundationVideoPlayer.registerWith(); expect(VideoPlayerPlatform.instance, isA()); }); @@ -58,8 +59,10 @@ void main() { expect(controller.value.isInitialized, true); expect(await controller.position, Duration.zero); - expect(controller.value.duration, - const Duration(seconds: 7, milliseconds: 540)); + expect( + controller.value.duration, + const Duration(seconds: 7, milliseconds: 540), + ); }); testWidgets('can be played', (WidgetTester tester) async { @@ -71,27 +74,21 @@ void main() { expect(await controller.position, greaterThan(Duration.zero)); }); - testWidgets( - 'can seek', - (WidgetTester tester) async { - await controller.initialize(); + testWidgets('can seek', (WidgetTester tester) async { + await controller.initialize(); - await controller.seekTo(const Duration(seconds: 3)); + await controller.seekTo(const Duration(seconds: 3)); - expect(controller.value.position, const Duration(seconds: 3)); - }, - ); + expect(controller.value.position, const Duration(seconds: 3)); + }); - testWidgets( - 'can seek to end', - (WidgetTester tester) async { - await controller.initialize(); + testWidgets('can seek to end', (WidgetTester tester) async { + await controller.initialize(); - await controller.seekTo(controller.value.duration); + await controller.seekTo(controller.value.duration); - expect(controller.value.duration, controller.value.position); - }, - ); + expect(controller.value.duration, controller.value.position); + }); testWidgets('can be paused', (WidgetTester tester) async { await controller.initialize(); @@ -108,7 +105,9 @@ void main() { // fix it if possible. Is AVPlayer's pause method internally async? const Duration allowableDelta = Duration(milliseconds: 10); expect( - await controller.position, lessThan(pausedPosition + allowableDelta)); + await controller.position, + lessThan(pausedPosition + allowableDelta), + ); }); }); @@ -126,8 +125,9 @@ void main() { controller = MiniController.file(file); }); - testWidgets('test video player using static file() method as constructor', - (WidgetTester tester) async { + testWidgets('test video player using static file() method as constructor', ( + WidgetTester tester, + ) async { await controller.initialize(); await controller.play(); @@ -143,38 +143,41 @@ void main() { controller = MiniController.network(videoUrl); }); - testWidgets('reports buffering status', (WidgetTester tester) async { - await controller.initialize(); - - final Completer started = Completer(); - final Completer ended = Completer(); - controller.addListener(() { - if (!started.isCompleted && controller.value.isBuffering) { - started.complete(); - } - if (started.isCompleted && - !controller.value.isBuffering && - !ended.isCompleted) { - ended.complete(); - } - }); - - await controller.play(); - await controller.seekTo(const Duration(seconds: 5)); - await tester.pumpAndSettle(_playDuration); - await controller.pause(); - - // TODO(stuartmorgan): Switch to _controller.position once seekTo is - // fixed on the native side to wait for completion, so this is testing - // the native code rather than the MiniController position cache. - expect(controller.value.position, greaterThan(Duration.zero)); + testWidgets( + 'reports buffering status', + (WidgetTester tester) async { + await controller.initialize(); - await expectLater(started.future, completes); - await expectLater(ended.future, completes); - }, - // TODO(stuartmorgan): Skipped on iOS without explanation in main - // package. Needs investigation. - skip: true); + final Completer started = Completer(); + final Completer ended = Completer(); + controller.addListener(() { + if (!started.isCompleted && controller.value.isBuffering) { + started.complete(); + } + if (started.isCompleted && + !controller.value.isBuffering && + !ended.isCompleted) { + ended.complete(); + } + }); + + await controller.play(); + await controller.seekTo(const Duration(seconds: 5)); + await tester.pumpAndSettle(_playDuration); + await controller.pause(); + + // TODO(stuartmorgan): Switch to _controller.position once seekTo is + // fixed on the native side to wait for completion, so this is testing + // the native code rather than the MiniController position cache. + expect(controller.value.position, greaterThan(Duration.zero)); + + await expectLater(started.future, completes); + await expectLater(ended.future, completes); + }, + // TODO(stuartmorgan): Skipped on iOS without explanation in main + // package. Needs investigation. + skip: true, + ); testWidgets('live stream duration != 0', (WidgetTester tester) async { final MiniController livestreamController = MiniController.network( @@ -185,12 +188,15 @@ void main() { expect(livestreamController.value.isInitialized, true); // Live streams should have either a positive duration or C.TIME_UNSET if the duration is unknown // See https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#getDuration-- - expect(livestreamController.value.duration, - (Duration duration) => duration != Duration.zero); + expect( + livestreamController.value.duration, + (Duration duration) => duration != Duration.zero, + ); }); - testWidgets('rotated m3u8 has correct aspect ratio', - (WidgetTester tester) async { + testWidgets('rotated m3u8 has correct aspect ratio', ( + WidgetTester tester, + ) 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( @@ -199,8 +205,10 @@ void main() { await livestreamController.initialize(); expect(livestreamController.value.isInitialized, true); - expect(livestreamController.value.size.width, - lessThan(livestreamController.value.size.height)); + expect( + livestreamController.value.size.width, + lessThan(livestreamController.value.size.height), + ); }); }); } diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index c3b61debf30..5e5766ce40a 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -10,11 +10,7 @@ import 'package:video_player_platform_interface/video_player_platform_interface. import 'mini_controller.dart'; void main() { - runApp( - MaterialApp( - home: _App(), - ), - ); + runApp(MaterialApp(home: _App())); } class _App extends StatelessWidget { @@ -29,14 +25,8 @@ class _App extends StatelessWidget { bottom: const TabBar( isScrollable: true, tabs: [ - Tab( - icon: Icon(Icons.cloud), - text: 'Remote mp4', - ), - Tab( - icon: Icon(Icons.favorite), - text: 'Remote enc m3u8', - ), + Tab(icon: Icon(Icons.cloud), text: 'Remote mp4'), + Tab(icon: Icon(Icons.favorite), text: 'Remote enc m3u8'), Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset mp4'), ], ), @@ -44,16 +34,17 @@ class _App extends StatelessWidget { body: TabBarView( children: [ _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _BumbleBeeRemoteVideo(viewType), + builder: + (VideoViewType viewType) => _BumbleBeeRemoteVideo(viewType), ), _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _BumbleBeeEncryptedLiveStream(viewType), + builder: + (VideoViewType viewType) => + _BumbleBeeEncryptedLiveStream(viewType), ), _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _ButterFlyAssetVideo(viewType), + builder: + (VideoViewType viewType) => _ButterFlyAssetVideo(viewType), ), ], ), @@ -63,9 +54,7 @@ class _App extends StatelessWidget { } class _ViewTypeTabBar extends StatefulWidget { - const _ViewTypeTabBar({ - required this.builder, - }); + const _ViewTypeTabBar({required this.builder}); final Widget Function(VideoViewType) builder; @@ -97,14 +86,8 @@ class _ViewTypeTabBarState extends State<_ViewTypeTabBar> controller: _tabController, isScrollable: true, tabs: const [ - Tab( - icon: Icon(Icons.texture), - text: 'Texture view', - ), - Tab( - icon: Icon(Icons.construction), - text: 'Platform view', - ), + Tab(icon: Icon(Icons.texture), text: 'Texture view'), + Tab(icon: Icon(Icons.construction), text: 'Platform view'), ], ), Expanded( @@ -161,9 +144,7 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { return SingleChildScrollView( child: Column( children: [ - Container( - padding: const EdgeInsets.only(top: 20.0), - ), + Container(padding: const EdgeInsets.only(top: 20.0)), const Text('With assets mp4'), Container( padding: const EdgeInsets.all(20), @@ -289,12 +270,13 @@ class _BumbleBeeEncryptedLiveStreamState const Text('With remote encrypted m3u8'), Container( padding: const EdgeInsets.all(20), - child: _controller.value.isInitialized - ? AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ) - : const Text('loading...'), + child: + _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : const Text('loading...'), ), ], ), @@ -325,19 +307,20 @@ class _ControlsOverlay extends StatelessWidget { AnimatedSwitcher( duration: const Duration(milliseconds: 50), reverseDuration: const Duration(milliseconds: 200), - child: controller.value.isPlaying - ? const SizedBox.shrink() - : const ColoredBox( - color: Colors.black26, - child: Center( - child: Icon( - Icons.play_arrow, - color: Colors.white, - size: 100.0, - semanticLabel: 'Play', + child: + controller.value.isPlaying + ? const SizedBox.shrink() + : const ColoredBox( + color: Colors.black26, + child: Center( + child: Icon( + Icons.play_arrow, + color: Colors.white, + size: 100.0, + semanticLabel: 'Play', + ), ), ), - ), ), GestureDetector( onTap: () { @@ -355,10 +338,7 @@ class _ControlsOverlay extends StatelessWidget { itemBuilder: (BuildContext context) { return >[ for (final double speed in _examplePlaybackRates) - PopupMenuItem( - value: speed, - child: Text('${speed}x'), - ) + PopupMenuItem(value: speed, child: Text('${speed}x')), ]; }, child: Padding( 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 7d406ecbd1a..f3cafdd0bc5 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 @@ -43,14 +43,15 @@ class VideoPlayerValue { /// Returns an instance for a video that hasn't been loaded. const VideoPlayerValue.uninitialized() - : this(duration: Duration.zero, isInitialized: false); + : this(duration: Duration.zero, isInitialized: false); /// Returns an instance with the given [errorDescription]. const VideoPlayerValue.erroneous(String errorDescription) - : this( - duration: Duration.zero, - isInitialized: false, - errorDescription: errorDescription); + : this( + duration: Duration.zero, + isInitialized: false, + errorDescription: errorDescription, + ); /// The total duration of the video. /// @@ -147,16 +148,16 @@ class VideoPlayerValue { @override int get hashCode => Object.hash( - duration, - position, - buffered, - isPlaying, - isBuffering, - playbackSpeed, - errorDescription, - size, - isInitialized, - ); + duration, + position, + buffered, + isPlaying, + isBuffering, + playbackSpeed, + errorDescription, + size, + isInitialized, + ); } /// A very minimal version of `VideoPlayerController` for running the example @@ -171,26 +172,24 @@ class MiniController extends ValueNotifier { this.dataSource, { this.package, this.viewType = VideoViewType.textureView, - }) : dataSourceType = DataSourceType.asset, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : dataSourceType = DataSourceType.asset, + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from /// the network. MiniController.network( this.dataSource, { this.viewType = VideoViewType.textureView, - }) : dataSourceType = DataSourceType.network, - package = null, - super(const VideoPlayerValue(duration: Duration.zero)); + }) : dataSourceType = DataSourceType.network, + package = null, + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from a file. - MiniController.file( - File file, { - this.viewType = VideoViewType.textureView, - }) : dataSource = Uri.file(file.absolute.path).toString(), - dataSourceType = DataSourceType.file, - package = null, - super(const VideoPlayerValue(duration: Duration.zero)); + MiniController.file(File file, {this.viewType = VideoViewType.textureView}) + : dataSource = Uri.file(file.absolute.path).toString(), + dataSourceType = DataSourceType.file, + package = null, + super(const VideoPlayerValue(duration: Duration.zero)); /// The URI to the video file. This will be in different formats depending on /// the [DataSourceType] of the original video. @@ -254,7 +253,8 @@ class MiniController extends ValueNotifier { viewType: viewType, ); - _playerId = (await _platform.createWithOptions(creationOptions)) ?? + _playerId = + (await _platform.createWithOptions(creationOptions)) ?? kUninitializedPlayerId; _creatingCompleter!.complete(null); final Completer initializingCompleter = Completer(); @@ -329,16 +329,15 @@ class MiniController extends ValueNotifier { if (value.isPlaying) { await _platform.play(_playerId); - _timer = Timer.periodic( - const Duration(milliseconds: 500), - (Timer timer) async { - final Duration? newPosition = await position; - if (newPosition == null) { - return; - } - _updatePosition(newPosition); - }, - ); + _timer = Timer.periodic(const Duration(milliseconds: 500), ( + Timer timer, + ) async { + final Duration? newPosition = await position; + if (newPosition == null) { + return; + } + _updatePosition(newPosition); + }); await _applyPlaybackSpeed(); } else { await _platform.pause(_playerId); @@ -347,10 +346,7 @@ class MiniController extends ValueNotifier { Future _applyPlaybackSpeed() async { if (value.isPlaying) { - await _platform.setPlaybackSpeed( - _playerId, - value.playbackSpeed, - ); + await _platform.setPlaybackSpeed(_playerId, value.playbackSpeed); } } @@ -437,17 +433,12 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _playerId == MiniController.kUninitializedPlayerId ? Container() - : _platform.buildViewWithOptions( - VideoViewOptions(playerId: _playerId), - ); + : _platform.buildViewWithOptions(VideoViewOptions(playerId: _playerId)); } } class _VideoScrubber extends StatefulWidget { - const _VideoScrubber({ - required this.child, - required this.controller, - }); + const _VideoScrubber({required this.child, required this.controller}); final Widget child; final MiniController controller; diff --git a/packages/video_player/video_player_avfoundation/example/pubspec.yaml b/packages/video_player/video_player_avfoundation/example/pubspec.yaml index 7514e578bb3..8d52a355e9d 100644 --- a/packages/video_player/video_player_avfoundation/example/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the video_player plugin. publish_to: none environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" dependencies: flutter: 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 aaccb8a2eb8..a9d3184b63e 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 @@ -23,8 +23,8 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @visibleForTesting AVFoundationVideoPlayerApi? pluginApi, @visibleForTesting VideoPlayerInstanceApi Function(int playerId)? playerProvider, - }) : _api = pluginApi ?? AVFoundationVideoPlayerApi(), - _playerProvider = playerProvider ?? _productionApiProvider; + }) : _api = pluginApi ?? AVFoundationVideoPlayerApi(), + _playerProvider = playerProvider ?? _productionApiProvider; final AVFoundationVideoPlayerApi _api; // A method to create VideoPlayerInstanceApi instances, which can be @@ -83,17 +83,14 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { '"asset" must be non-null for an asset data source', ); } - uri = await _api.getAssetUrl( - asset, - dataSource.package, - ); + uri = await _api.getAssetUrl(asset, dataSource.package); if (uri == null) { // Throw a platform exception for compatibility with the previous // implementation, which threw on the native side. throw PlatformException( - code: 'video_player', - message: - 'Asset $asset not found in package ${dataSource.package}.'); + code: 'video_player', + message: 'Asset $asset not found in package ${dataSource.package}.', + ); } case DataSourceType.network: case DataSourceType.file: @@ -112,8 +109,9 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { final VideoPlayerViewState state; switch (viewType) { case VideoViewType.textureView: - final TexturePlayerIds ids = - await _api.createForTextureView(pigeonCreationOptions); + final TexturePlayerIds ids = await _api.createForTextureView( + pigeonCreationOptions, + ); playerId = ids.playerId; state = VideoPlayerTextureViewState(textureId: ids.textureId); case VideoViewType.platformView: @@ -175,35 +173,35 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Stream videoEventsFor(int playerId) { - return _eventChannelFor(playerId) - .receiveBroadcastStream() - .map((dynamic event) { + 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, + 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), + 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, - ), + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: map['isPlaying'] as bool, + ), _ => VideoEvent(eventType: VideoEventType.unknown), }; }); @@ -216,9 +214,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Widget buildView(int playerId) { - return buildViewWithOptions( - VideoViewOptions(playerId: playerId), - ); + return buildViewWithOptions(VideoViewOptions(playerId: playerId)); } @override @@ -227,10 +223,12 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { final VideoPlayerViewState? viewState = playerViewStates[playerId]; return switch (viewState) { - VideoPlayerTextureViewState(:final int textureId) => - Texture(textureId: textureId), + VideoPlayerTextureViewState(:final int textureId) => Texture( + textureId: textureId, + ), VideoPlayerPlatformViewState() => _buildPlatformView(playerId), - null => throw Exception( + null => + throw Exception( 'Could not find corresponding view type for playerId: $playerId', ), }; @@ -281,9 +279,7 @@ sealed class VideoPlayerViewState { @visibleForTesting final class VideoPlayerTextureViewState extends VideoPlayerViewState { /// Creates a new instance of [VideoPlayerTextureViewState]. - const VideoPlayerTextureViewState({ - required this.textureId, - }); + const VideoPlayerTextureViewState({required this.textureId}); /// The ID of the texture used by the video player. final int textureId; diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index 8cc8bf343ba..5fe36c52683 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -21,30 +21,29 @@ PlatformException _createConnectionError(String channelName) { bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + a.indexed.every( + ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), + ); } if (a is Map && b is Map) { return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } /// Information passed to the platform view creation. class PlatformVideoViewCreationParams { - PlatformVideoViewCreationParams({ - required this.playerId, - }); + PlatformVideoViewCreationParams({required this.playerId}); int playerId; List _toList() { - return [ - playerId, - ]; + return [playerId]; } Object encode() { @@ -53,9 +52,7 @@ class PlatformVideoViewCreationParams { static PlatformVideoViewCreationParams decode(Object result) { result as List; - return PlatformVideoViewCreationParams( - playerId: result[0]! as int, - ); + return PlatformVideoViewCreationParams(playerId: result[0]! as int); } @override @@ -77,20 +74,14 @@ class PlatformVideoViewCreationParams { } class CreationOptions { - CreationOptions({ - required this.uri, - required this.httpHeaders, - }); + CreationOptions({required this.uri, required this.httpHeaders}); String uri; Map httpHeaders; List _toList() { - return [ - uri, - httpHeaders, - ]; + return [uri, httpHeaders]; } Object encode() { @@ -124,20 +115,14 @@ class CreationOptions { } class TexturePlayerIds { - TexturePlayerIds({ - required this.playerId, - required this.textureId, - }); + TexturePlayerIds({required this.playerId, required this.textureId}); int playerId; int textureId; List _toList() { - return [ - playerId, - textureId, - ]; + return [playerId, textureId]; } Object encode() { @@ -209,11 +194,12 @@ class AVFoundationVideoPlayerApi { /// Constructor for [AVFoundationVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AVFoundationVideoPlayerApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + AVFoundationVideoPlayerApi({ + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -225,10 +211,10 @@ class AVFoundationVideoPlayerApi { 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -250,12 +236,13 @@ class AVFoundationVideoPlayerApi { 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.createForPlatformView$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [params], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([params]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -277,17 +264,19 @@ class AVFoundationVideoPlayerApi { } Future createForTextureView( - CreationOptions creationOptions) async { + CreationOptions creationOptions, + ) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.createForTextureView$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [creationOptions], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([creationOptions]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -313,12 +302,13 @@ class AVFoundationVideoPlayerApi { 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [mixWithOthers], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([mixWithOthers]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -339,12 +329,13 @@ class AVFoundationVideoPlayerApi { 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getAssetUrl$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [asset, package], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([asset, package]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -365,11 +356,12 @@ class VideoPlayerInstanceApi { /// Constructor for [VideoPlayerInstanceApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - VideoPlayerInstanceApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + VideoPlayerInstanceApi({ + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -381,12 +373,13 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.setLooping$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [looping], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([looping]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -407,12 +400,13 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.setVolume$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [volume], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([volume]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -433,12 +427,13 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [speed], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([speed]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -459,10 +454,10 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.play$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -484,10 +479,10 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.getPosition$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -514,12 +509,13 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.seekTo$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [position], ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([position]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -540,10 +536,10 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.pause$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -565,10 +561,10 @@ class VideoPlayerInstanceApi { 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.dispose$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index 550a282dbb0..0fb40d59e80 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -4,33 +4,29 @@ import 'package:pigeon/pigeon.dart'; -@ConfigurePigeon(PigeonOptions( - dartOut: 'lib/src/messages.g.dart', - objcHeaderOut: - 'darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h', - objcSourceOut: - 'darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m', - objcOptions: ObjcOptions( - prefix: 'FVP', - headerIncludePath: './include/video_player_avfoundation/messages.g.h', +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + objcHeaderOut: + 'darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h', + objcSourceOut: + 'darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m', + objcOptions: ObjcOptions( + prefix: 'FVP', + headerIncludePath: './include/video_player_avfoundation/messages.g.h', + ), + copyrightHeader: 'pigeons/copyright.txt', ), - copyrightHeader: 'pigeons/copyright.txt', -)) - +) /// Information passed to the platform view creation. class PlatformVideoViewCreationParams { - const PlatformVideoViewCreationParams({ - required this.playerId, - }); + const PlatformVideoViewCreationParams({required this.playerId}); final int playerId; } class CreationOptions { - CreationOptions({ - required this.uri, - required this.httpHeaders, - }); + CreationOptions({required this.uri, required this.httpHeaders}); String uri; Map httpHeaders; diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index cdc4f9cc814..8675038ba86 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -5,8 +5,8 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ version: 2.8.4 environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" flutter: plugin: 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 9aa6c8f2f43..d01a8547473 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 @@ -22,8 +22,9 @@ void main() { ( AVFoundationVideoPlayer, MockAVFoundationVideoPlayerApi, - MockVideoPlayerInstanceApi - ) setUpMockPlayer({required int playerId}) { + MockVideoPlayerInstanceApi, + ) + setUpMockPlayer({required int playerId}) { final MockAVFoundationVideoPlayerApi pluginApi = MockAVFoundationVideoPlayerApi(); final MockVideoPlayerInstanceApi instanceApi = MockVideoPlayerInstanceApi(); @@ -72,15 +73,15 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; const int textureId = 100; - when(api.createForTextureView(any)).thenAnswer((_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); + when(api.createForTextureView(any)).thenAnswer( + (_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + ); const String asset = 'someAsset'; const String package = 'somePackage'; const String assetUrl = 'file:///some/asset/path'; - when( - api.getAssetUrl(asset, package), - ).thenAnswer((_) async => assetUrl); + when(api.getAssetUrl(asset, package)).thenAnswer((_) async => assetUrl); final int? playerId = await player.create( DataSource( @@ -90,31 +91,33 @@ void main() { ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId)); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerTextureViewState(textureId: textureId), + ); }); - test('create with asset throws PlatformException for missing asset', - () async { - final ( - AVFoundationVideoPlayer player, - MockAVFoundationVideoPlayerApi api, - _, - ) = setUpMockPlayer(playerId: 1); + test( + 'create with asset throws PlatformException for missing asset', + () async { + final ( + AVFoundationVideoPlayer player, + MockAVFoundationVideoPlayerApi api, + _, + ) = setUpMockPlayer(playerId: 1); - const String asset = 'someAsset'; - const String package = 'somePackage'; - when( - api.getAssetUrl(asset, package), - ).thenAnswer((_) async => null); + const String asset = 'someAsset'; + const String package = 'somePackage'; + when(api.getAssetUrl(asset, package)).thenAnswer((_) async => null); - expect( + expect( player.create( DataSource( sourceType: DataSourceType.asset, @@ -122,8 +125,10 @@ void main() { package: package, ), ), - throwsA(isA())); - }); + throwsA(isA()), + ); + }, + ); test('create with network', () async { final ( @@ -133,8 +138,10 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; const int textureId = 100; - when(api.createForTextureView(any)).thenAnswer((_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); + when(api.createForTextureView(any)).thenAnswer( + (_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + ); const String uri = 'https://example.com'; final int? playerId = await player.create( @@ -145,15 +152,18 @@ void main() { ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, uri); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId)); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerTextureViewState(textureId: textureId), + ); }); test('create with network passes headers', () async { @@ -162,8 +172,9 @@ void main() { MockAVFoundationVideoPlayerApi api, _, ) = setUpMockPlayer(playerId: 1); - when(api.createForTextureView(any)).thenAnswer( - (_) async => TexturePlayerIds(playerId: 2, textureId: 100)); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const Map headers = { 'Authorization': 'Bearer token', @@ -175,8 +186,9 @@ void main() { httpHeaders: headers, ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.httpHeaders, headers); @@ -190,21 +202,26 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; const int textureId = 100; - when(api.createForTextureView(any)).thenAnswer((_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); + when(api.createForTextureView(any)).thenAnswer( + (_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + ); const String fileUri = 'file:///foo/bar'; final int? playerId = await player.create( DataSource(sourceType: DataSourceType.file, uri: fileUri), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId)); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerTextureViewState(textureId: textureId), + ); }); test('createWithOptions with asset', () async { @@ -215,15 +232,15 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; const int textureId = 100; - when(api.createForTextureView(any)).thenAnswer((_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); + when(api.createForTextureView(any)).thenAnswer( + (_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + ); const String asset = 'someAsset'; const String package = 'somePackage'; const String assetUrl = 'file:///some/asset/path'; - when( - api.getAssetUrl(asset, package), - ).thenAnswer((_) async => assetUrl); + when(api.getAssetUrl(asset, package)).thenAnswer((_) async => assetUrl); final int? playerId = await player.createWithOptions( VideoCreationOptions( dataSource: DataSource( @@ -235,14 +252,17 @@ void main() { ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId)); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerTextureViewState(textureId: textureId), + ); }); test('createWithOptions with network', () async { @@ -253,8 +273,10 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; const int textureId = 100; - when(api.createForTextureView(any)).thenAnswer((_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); + when(api.createForTextureView(any)).thenAnswer( + (_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + ); const String uri = 'https://example.com'; final int? playerId = await player.createWithOptions( @@ -268,15 +290,18 @@ void main() { ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, uri); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId)); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerTextureViewState(textureId: textureId), + ); }); test('createWithOptions with network passes headers', () async { @@ -287,7 +312,8 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; when(api.createForTextureView(any)).thenAnswer( - (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100)); + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const Map headers = { 'Authorization': 'Bearer token', @@ -303,8 +329,9 @@ void main() { ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.httpHeaders, headers); @@ -319,8 +346,10 @@ void main() { ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; const int textureId = 100; - when(api.createForTextureView(any)).thenAnswer((_) async => - TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); + when(api.createForTextureView(any)).thenAnswer( + (_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId), + ); const String fileUri = 'file:///foo/bar'; final int? playerId = await player.createWithOptions( @@ -330,14 +359,17 @@ void main() { ), ); - final VerificationResult verification = - verify(api.createForTextureView(captureAny)); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: textureId)); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerTextureViewState(textureId: textureId), + ); }); test('createWithOptions with platform view', () async { @@ -360,8 +392,10 @@ void main() { ); expect(playerId, newPlayerId); - expect(player.playerViewStates[newPlayerId], - const VideoPlayerPlatformViewState()); + expect( + player.playerViewStates[newPlayerId], + const VideoPlayerPlatformViewState(), + ); }); test('setLooping', () async { @@ -483,131 +517,141 @@ void main() { ) = setUpMockPlayer(playerId: 1); const String mockChannel = 'flutter.io/videoPlayer/videoEvents123'; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMessageHandler( - mockChannel, - (ByteData? message) async { - final MethodCall methodCall = - const StandardMethodCodec().decodeMethodCall(message); - if (methodCall.method == 'listen') { - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + .setMockMessageHandler(mockChannel, (ByteData? message) async { + final MethodCall methodCall = const StandardMethodCodec() + .decodeMethodCall(message); + if (methodCall.method == 'listen') { + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ - 'event': 'initialized', - 'duration': 98765, - 'width': 1920, - 'height': 1080, - }), - (ByteData? data) {}); - - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + }), + (ByteData? data) {}, + ); + + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'completed', - }), - (ByteData? data) {}); - - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + const StandardMethodCodec().encodeSuccessEnvelope( + {'event': 'completed'}, + ), + (ByteData? data) {}, + ); + + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'bufferingUpdate', - 'values': >[ - [0, 1234], - [1235, 4000], - ], - }), - (ByteData? data) {}); - - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + const StandardMethodCodec().encodeSuccessEnvelope( + { + 'event': 'bufferingUpdate', + 'values': >[ + [0, 1234], + [1235, 4000], + ], + }, + ), + (ByteData? data) {}, + ); + + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'bufferingStart', - }), - (ByteData? data) {}); - - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + const StandardMethodCodec().encodeSuccessEnvelope( + {'event': 'bufferingStart'}, + ), + (ByteData? data) {}, + ); + + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'bufferingEnd', - }), - (ByteData? data) {}); - - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + const StandardMethodCodec().encodeSuccessEnvelope( + {'event': 'bufferingEnd'}, + ), + (ByteData? data) {}, + ); + + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'isPlayingStateUpdate', - 'isPlaying': true, - }), - (ByteData? data) {}); - - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( + const StandardMethodCodec().encodeSuccessEnvelope( + { + 'event': 'isPlayingStateUpdate', + 'isPlaying': true, + }, + ), + (ByteData? data) {}, + ); + + await TestDefaultBinaryMessengerBinding + .instance + .defaultBinaryMessenger + .handlePlatformMessage( mockChannel, - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'isPlayingStateUpdate', - 'isPlaying': false, - }), - (ByteData? data) {}); - - return const StandardMethodCodec().encodeSuccessEnvelope(null); - } else if (methodCall.method == 'cancel') { - return const StandardMethodCodec().encodeSuccessEnvelope(null); - } else { - fail('Expected listen or cancel'); - } - }, - ); + const StandardMethodCodec().encodeSuccessEnvelope( + { + 'event': 'isPlayingStateUpdate', + 'isPlaying': false, + }, + ), + (ByteData? data) {}, + ); + + return const StandardMethodCodec().encodeSuccessEnvelope(null); + } else if (methodCall.method == 'cancel') { + return const StandardMethodCodec().encodeSuccessEnvelope(null); + } else { + fail('Expected listen or cancel'); + } + }); expect( - player.videoEventsFor(123), - emitsInOrder([ - VideoEvent( - eventType: VideoEventType.initialized, - duration: const Duration(milliseconds: 98765), - size: const Size(1920, 1080), - ), - VideoEvent(eventType: VideoEventType.completed), - VideoEvent( - eventType: VideoEventType.bufferingUpdate, - buffered: [ - DurationRange( - Duration.zero, - const Duration(milliseconds: 1234), - ), - DurationRange( - const Duration(milliseconds: 1235), - const Duration(milliseconds: 1235 + 4000), - ), - ]), - VideoEvent(eventType: VideoEventType.bufferingStart), - VideoEvent(eventType: VideoEventType.bufferingEnd), - VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: true, - ), - VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: false, - ), - ])); + player.videoEventsFor(123), + emitsInOrder([ + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + ), + VideoEvent(eventType: VideoEventType.completed), + VideoEvent( + eventType: VideoEventType.bufferingUpdate, + buffered: [ + DurationRange(Duration.zero, const Duration(milliseconds: 1234)), + DurationRange( + const Duration(milliseconds: 1235), + const Duration(milliseconds: 1235 + 4000), + ), + ], + ), + VideoEvent(eventType: VideoEventType.bufferingStart), + VideoEvent(eventType: VideoEventType.bufferingEnd), + VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: true, + ), + VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: false, + ), + ]), + ); }); }); } diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart index dfa4a76d4ae..8caf6ad8dc4 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart @@ -25,13 +25,8 @@ import 'package:video_player_avfoundation/src/messages.g.dart' as _i2; class _FakeTexturePlayerIds_0 extends _i1.SmartFake implements _i2.TexturePlayerIds { - _FakeTexturePlayerIds_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeTexturePlayerIds_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } /// A class which mocks [AVFoundationVideoPlayerApi]. @@ -40,91 +35,76 @@ class _FakeTexturePlayerIds_0 extends _i1.SmartFake class MockAVFoundationVideoPlayerApi extends _i1.Mock implements _i2.AVFoundationVideoPlayerApi { @override - String get pigeonVar_messageChannelSuffix => (super.noSuchMethod( - Invocation.getter(#pigeonVar_messageChannelSuffix), - returnValue: _i3.dummyValue( - this, - Invocation.getter(#pigeonVar_messageChannelSuffix), - ), - returnValueForMissingStub: _i3.dummyValue( - this, - Invocation.getter(#pigeonVar_messageChannelSuffix), - ), - ) as String); + String get pigeonVar_messageChannelSuffix => + (super.noSuchMethod( + Invocation.getter(#pigeonVar_messageChannelSuffix), + returnValue: _i3.dummyValue( + this, + Invocation.getter(#pigeonVar_messageChannelSuffix), + ), + returnValueForMissingStub: _i3.dummyValue( + this, + Invocation.getter(#pigeonVar_messageChannelSuffix), + ), + ) + as String); @override - _i4.Future initialize() => (super.noSuchMethod( - Invocation.method( - #initialize, - [], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future initialize() => + (super.noSuchMethod( + Invocation.method(#initialize, []), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override _i4.Future createForPlatformView(_i2.CreationOptions? params) => (super.noSuchMethod( - Invocation.method( - #createForPlatformView, - [params], - ), - returnValue: _i4.Future.value(0), - returnValueForMissingStub: _i4.Future.value(0), - ) as _i4.Future); + Invocation.method(#createForPlatformView, [params]), + returnValue: _i4.Future.value(0), + returnValueForMissingStub: _i4.Future.value(0), + ) + as _i4.Future); @override _i4.Future<_i2.TexturePlayerIds> createForTextureView( - _i2.CreationOptions? creationOptions) => + _i2.CreationOptions? creationOptions, + ) => (super.noSuchMethod( - Invocation.method( - #createForTextureView, - [creationOptions], - ), - returnValue: - _i4.Future<_i2.TexturePlayerIds>.value(_FakeTexturePlayerIds_0( - this, - Invocation.method( - #createForTextureView, - [creationOptions], - ), - )), - returnValueForMissingStub: - _i4.Future<_i2.TexturePlayerIds>.value(_FakeTexturePlayerIds_0( - this, - Invocation.method( - #createForTextureView, - [creationOptions], - ), - )), - ) as _i4.Future<_i2.TexturePlayerIds>); + Invocation.method(#createForTextureView, [creationOptions]), + returnValue: _i4.Future<_i2.TexturePlayerIds>.value( + _FakeTexturePlayerIds_0( + this, + Invocation.method(#createForTextureView, [creationOptions]), + ), + ), + returnValueForMissingStub: _i4.Future<_i2.TexturePlayerIds>.value( + _FakeTexturePlayerIds_0( + this, + Invocation.method(#createForTextureView, [creationOptions]), + ), + ), + ) + as _i4.Future<_i2.TexturePlayerIds>); @override - _i4.Future setMixWithOthers(bool? mixWithOthers) => (super.noSuchMethod( - Invocation.method( - #setMixWithOthers, - [mixWithOthers], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future setMixWithOthers(bool? mixWithOthers) => + (super.noSuchMethod( + Invocation.method(#setMixWithOthers, [mixWithOthers]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future getAssetUrl( - String? asset, - String? package, - ) => + _i4.Future getAssetUrl(String? asset, String? package) => (super.noSuchMethod( - Invocation.method( - #getAssetUrl, - [ - asset, - package, - ], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + Invocation.method(#getAssetUrl, [asset, package]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); } /// A class which mocks [VideoPlayerInstanceApi]. @@ -133,95 +113,89 @@ class MockAVFoundationVideoPlayerApi extends _i1.Mock class MockVideoPlayerInstanceApi extends _i1.Mock implements _i2.VideoPlayerInstanceApi { @override - String get pigeonVar_messageChannelSuffix => (super.noSuchMethod( - Invocation.getter(#pigeonVar_messageChannelSuffix), - returnValue: _i3.dummyValue( - this, - Invocation.getter(#pigeonVar_messageChannelSuffix), - ), - returnValueForMissingStub: _i3.dummyValue( - this, - Invocation.getter(#pigeonVar_messageChannelSuffix), - ), - ) as String); + String get pigeonVar_messageChannelSuffix => + (super.noSuchMethod( + Invocation.getter(#pigeonVar_messageChannelSuffix), + returnValue: _i3.dummyValue( + this, + Invocation.getter(#pigeonVar_messageChannelSuffix), + ), + returnValueForMissingStub: _i3.dummyValue( + this, + Invocation.getter(#pigeonVar_messageChannelSuffix), + ), + ) + as String); @override - _i4.Future setLooping(bool? looping) => (super.noSuchMethod( - Invocation.method( - #setLooping, - [looping], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future setLooping(bool? looping) => + (super.noSuchMethod( + Invocation.method(#setLooping, [looping]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future setVolume(double? volume) => (super.noSuchMethod( - Invocation.method( - #setVolume, - [volume], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future setVolume(double? volume) => + (super.noSuchMethod( + Invocation.method(#setVolume, [volume]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future setPlaybackSpeed(double? speed) => (super.noSuchMethod( - Invocation.method( - #setPlaybackSpeed, - [speed], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future setPlaybackSpeed(double? speed) => + (super.noSuchMethod( + Invocation.method(#setPlaybackSpeed, [speed]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future play() => (super.noSuchMethod( - Invocation.method( - #play, - [], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future play() => + (super.noSuchMethod( + Invocation.method(#play, []), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future getPosition() => (super.noSuchMethod( - Invocation.method( - #getPosition, - [], - ), - returnValue: _i4.Future.value(0), - returnValueForMissingStub: _i4.Future.value(0), - ) as _i4.Future); + _i4.Future getPosition() => + (super.noSuchMethod( + Invocation.method(#getPosition, []), + returnValue: _i4.Future.value(0), + returnValueForMissingStub: _i4.Future.value(0), + ) + as _i4.Future); @override - _i4.Future seekTo(int? position) => (super.noSuchMethod( - Invocation.method( - #seekTo, - [position], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future seekTo(int? position) => + (super.noSuchMethod( + Invocation.method(#seekTo, [position]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future pause() => (super.noSuchMethod( - Invocation.method( - #pause, - [], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future pause() => + (super.noSuchMethod( + Invocation.method(#pause, []), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); @override - _i4.Future dispose() => (super.noSuchMethod( - Invocation.method( - #dispose, - [], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + _i4.Future dispose() => + (super.noSuchMethod( + Invocation.method(#dispose, []), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) + as _i4.Future); } diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index bc285f9a1ba..b6f5a93013f 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. + ## 6.4.0 * Adds HTML5 video poster support as a VideoPlayerWebOptions. diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index fe4b9210b7f..3b562d1ff43 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -286,13 +286,13 @@ class VideoEvent { @override int get hashCode => Object.hash( - eventType, - duration, - size, - rotationCorrection, - buffered, - isPlaying, - ); + eventType, + duration, + size, + rotationCorrection, + buffered, + isPlaying, + ); } /// Type of the event. @@ -458,11 +458,11 @@ class VideoPlayerWebOptionsControls { /// Disables control options. Default behavior. const VideoPlayerWebOptionsControls.disabled() - : enabled = false, - allowDownload = false, - allowFullscreen = false, - allowPlaybackRate = false, - allowPictureInPicture = false; + : enabled = false, + allowDownload = false, + allowFullscreen = false, + allowPlaybackRate = false, + allowPictureInPicture = false; /// Whether native controls are enabled final bool enabled; @@ -508,9 +508,7 @@ class VideoPlayerWebOptionsControls { @immutable class VideoViewOptions { /// Constructs an instance of [VideoViewOptions]. - const VideoViewOptions({ - required this.playerId, - }); + const VideoViewOptions({required this.playerId}); /// The identifier of the video player. final int playerId; diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index b3ae08338ba..647225dd5bb 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -7,8 +7,8 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ version: 6.4.0 environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" dependencies: flutter: diff --git a/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart index 8091cd58051..4b9a90a472e 100644 --- a/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart +++ b/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart @@ -6,18 +6,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; void main() { - test( - 'VideoPlayerOptions allowBackgroundPlayback defaults to false', - () { - final VideoPlayerOptions options = VideoPlayerOptions(); - expect(options.allowBackgroundPlayback, false); - }, - ); - test( - 'VideoPlayerOptions mixWithOthers defaults to false', - () { - final VideoPlayerOptions options = VideoPlayerOptions(); - expect(options.mixWithOthers, false); - }, - ); + test('VideoPlayerOptions allowBackgroundPlayback defaults to false', () { + final VideoPlayerOptions options = VideoPlayerOptions(); + expect(options.allowBackgroundPlayback, false); + }); + test('VideoPlayerOptions mixWithOthers defaults to false', () { + final VideoPlayerOptions options = VideoPlayerOptions(); + expect(options.mixWithOthers, false); + }); } diff --git a/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart index 7b643de83e3..ec0fd5d131b 100644 --- a/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart +++ b/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart @@ -15,10 +15,7 @@ void main() { test('default implementation setWebOptions throws unimplemented', () async { await expectLater( - () => initialInstance.setWebOptions( - 1, - const VideoPlayerWebOptions(), - ), + () => initialInstance.setWebOptions(1, const VideoPlayerWebOptions()), throwsUnimplementedError, ); }); diff --git a/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart index 1d6ea15f091..c8d1ce4c8fa 100644 --- a/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart +++ b/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart @@ -14,12 +14,9 @@ void main() { controls = const VideoPlayerWebOptionsControls.disabled(); }); - test( - 'expect enabled isFalse', - () { - expect(controls.enabled, isFalse); - }, - ); + test('expect enabled isFalse', () { + expect(controls.enabled, isFalse); + }); }); group('when enabled', () { @@ -28,23 +25,17 @@ void main() { controls = const VideoPlayerWebOptionsControls.enabled(); }); - test( - 'expect enabled isTrue', - () { - expect(controls.enabled, isTrue); - expect(controls.allowDownload, isTrue); - expect(controls.allowFullscreen, isTrue); - expect(controls.allowPlaybackRate, isTrue); - expect(controls.allowPictureInPicture, isTrue); - }, - ); + test('expect enabled isTrue', () { + expect(controls.enabled, isTrue); + expect(controls.allowDownload, isTrue); + expect(controls.allowFullscreen, isTrue); + expect(controls.allowPlaybackRate, isTrue); + expect(controls.allowPictureInPicture, isTrue); + }); - test( - 'expect controlsList isEmpty', - () { - expect(controls.controlsList, isEmpty); - }, - ); + test('expect controlsList isEmpty', () { + expect(controls.controlsList, isEmpty); + }); }); group('and some options are disallowed', () { @@ -56,26 +47,20 @@ void main() { ); }); - test( - 'expect enabled isTrue', - () { - expect(controls.enabled, isTrue); - expect(controls.allowDownload, isFalse); - expect(controls.allowFullscreen, isFalse); - expect(controls.allowPlaybackRate, isFalse); - expect(controls.allowPictureInPicture, isTrue); - }, - ); + test('expect enabled isTrue', () { + expect(controls.enabled, isTrue); + expect(controls.allowDownload, isFalse); + expect(controls.allowFullscreen, isFalse); + expect(controls.allowPlaybackRate, isFalse); + expect(controls.allowPictureInPicture, isTrue); + }); - test( - 'expect controlsList is correct', - () { - expect( - controls.controlsList, - 'nodownload nofullscreen noplaybackrate', - ); - }, - ); + test('expect controlsList is correct', () { + expect( + controls.controlsList, + 'nodownload nofullscreen noplaybackrate', + ); + }); }); }); }); diff --git a/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart index 00b9e41cf28..939b5187473 100644 --- a/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart +++ b/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart @@ -14,39 +14,27 @@ void main() { }, ); - test( - 'VideoPlayerOptions allowContextMenu defaults to true', - () { - const VideoPlayerWebOptions options = VideoPlayerWebOptions(); - expect(options.allowContextMenu, isTrue); - }, - ); + test('VideoPlayerOptions allowContextMenu defaults to true', () { + const VideoPlayerWebOptions options = VideoPlayerWebOptions(); + expect(options.allowContextMenu, isTrue); + }); - test( - 'VideoPlayerOptions allowRemotePlayback defaults to true', - () { - const VideoPlayerWebOptions options = VideoPlayerWebOptions(); - expect(options.allowRemotePlayback, isTrue); - }, - ); + test('VideoPlayerOptions allowRemotePlayback defaults to true', () { + const VideoPlayerWebOptions options = VideoPlayerWebOptions(); + expect(options.allowRemotePlayback, isTrue); + }); group('VideoPlayerOptions poster', () { - test( - 'defaults to null', - () { - const VideoPlayerWebOptions options = VideoPlayerWebOptions(); - expect(options.poster, null); - }, - ); + test('defaults to null', () { + const VideoPlayerWebOptions options = VideoPlayerWebOptions(); + expect(options.poster, null); + }); - test( - 'with a value', - () { - final VideoPlayerWebOptions options = VideoPlayerWebOptions( - poster: Uri.parse('https://example.com/poster.jpg'), - ); - expect(options.poster, Uri.parse('https://example.com/poster.jpg')); - }, - ); + test('with a value', () { + final VideoPlayerWebOptions options = VideoPlayerWebOptions( + poster: Uri.parse('https://example.com/poster.jpg'), + ); + expect(options.poster, Uri.parse('https://example.com/poster.jpg')); + }); }); } diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index e3f30516df2..83c26791acd 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. + ## 2.4.0 * Adds HTML5 video poster support as a VideoPlayerWebOptions. diff --git a/packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart b/packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart index c0d63984383..d85ad72ce07 100644 --- a/packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart @@ -10,8 +10,9 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('convertNumVideoDurationToPluginDuration', () { - testWidgets('Finite value converts to milliseconds', - (WidgetTester _) async { + testWidgets('Finite value converts to milliseconds', ( + WidgetTester _, + ) async { final Duration? result = convertNumVideoDurationToPluginDuration(1.5); final Duration? zero = convertNumVideoDurationToPluginDuration(0.0001); @@ -20,22 +21,27 @@ void main() { expect(zero, equals(Duration.zero)); }); - testWidgets('Finite value rounds 3rd decimal value', - (WidgetTester _) async { - final Duration? result = - convertNumVideoDurationToPluginDuration(1.567899089087); - final Duration? another = - convertNumVideoDurationToPluginDuration(1.567199089087); + testWidgets('Finite value rounds 3rd decimal value', ( + WidgetTester _, + ) async { + final Duration? result = convertNumVideoDurationToPluginDuration( + 1.567899089087, + ); + final Duration? another = convertNumVideoDurationToPluginDuration( + 1.567199089087, + ); expect(result, isNotNull); expect(result!.inMilliseconds, equals(1568)); expect(another!.inMilliseconds, equals(1567)); }); - testWidgets('Infinite value returns magic constant', - (WidgetTester _) async { - final Duration? result = - convertNumVideoDurationToPluginDuration(double.infinity); + testWidgets('Infinite value returns magic constant', ( + WidgetTester _, + ) async { + final Duration? result = convertNumVideoDurationToPluginDuration( + double.infinity, + ); expect(result, isNotNull); expect(result, equals(jsCompatibleTimeUnset)); @@ -43,8 +49,9 @@ void main() { }); testWidgets('NaN value returns null', (WidgetTester _) async { - final Duration? result = - convertNumVideoDurationToPluginDuration(double.nan); + final Duration? result = convertNumVideoDurationToPluginDuration( + double.nan, + ); expect(result, isNull); }); diff --git a/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart b/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart index f59ba6cbb4a..f2c2fffb82f 100644 --- a/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart +++ b/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart @@ -28,13 +28,19 @@ extension NonStandardGettersOnMediaElement on web.HTMLMediaElement { extension type DomObject._(JSAny _) { @JS('defineProperty') external static void _defineProperty( - JSAny? object, JSString property, Descriptor value); + JSAny? object, + JSString property, + Descriptor value, + ); /// `Object.defineProperty`. /// /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty static void defineProperty( - JSObject object, String property, Descriptor descriptor) { + JSObject object, + String property, + Descriptor descriptor, + ) { return _defineProperty(object, property.toJS, descriptor); } } @@ -44,24 +50,14 @@ extension type DomObject._(JSAny _) { /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description extension type Descriptor._(JSObject _) implements JSObject { /// Builds a "data descriptor". - factory Descriptor.data({ - bool? writable, - JSAny? value, - }) => - Descriptor._data( - writable: writable?.toJS, - value: value.jsify(), - ); + factory Descriptor.data({bool? writable, JSAny? value}) => + Descriptor._data(writable: writable?.toJS, value: value.jsify()); /// Builds an "accessor descriptor". factory Descriptor.accessor({ void Function(JSAny? value)? set, JSAny? Function()? get, - }) => - Descriptor._accessor( - set: set?.toJS, - get: get?.toJS, - ); + }) => Descriptor._accessor(set: set?.toJS, get: get?.toJS); external factory Descriptor._accessor({ // JSBoolean configurable, diff --git a/packages/video_player/video_player_web/example/integration_test/utils.dart b/packages/video_player/video_player_web/example/integration_test/utils.dart index ba15c39c57d..72ee0ed6f89 100644 --- a/packages/video_player/video_player_web/example/integration_test/utils.dart +++ b/packages/video_player/video_player_web/example/integration_test/utils.dart @@ -27,22 +27,20 @@ void setInfinityDuration(web.HTMLVideoElement element) { DomObject.defineProperty( element, 'duration', - Descriptor.data( - writable: true, - value: double.infinity.toJS, - ), + Descriptor.data(writable: true, value: double.infinity.toJS), ); } /// Makes the `currentTime` setter throw an exception if used. void makeSetCurrentTimeThrow(web.HTMLVideoElement element) { DomObject.defineProperty( - element, - 'currentTime', - Descriptor.accessor( - set: (JSAny? value) { - throw Exception('Unexpected call to currentTime with value: $value'); - }, - get: () => 100.toJS, - )); + element, + 'currentTime', + Descriptor.accessor( + set: (JSAny? value) { + throw Exception('Unexpected call to currentTime with value: $value'); + }, + get: () => 100.toJS, + ), + ); } diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart index 8d49aa1f86f..78c85c99d1b 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart @@ -24,17 +24,19 @@ void main() { setUp(() { // Never set "src" on the video, so this test doesn't hit the network! - video = web.HTMLVideoElement() - ..controls = true - ..playsInline = false; + video = + web.HTMLVideoElement() + ..controls = true + ..playsInline = false; }); testWidgets('initialize() calls load', (WidgetTester _) async { bool loadCalled = false; - video['load'] = () { - loadCalled = true; - }.toJS; + video['load'] = + () { + loadCalled = true; + }.toJS; VideoPlayer(videoElement: video).initialize(); @@ -44,13 +46,22 @@ void main() { testWidgets('fixes critical video element config', (WidgetTester _) async { VideoPlayer(videoElement: video).initialize(); - expect(video.controls, isFalse, - reason: 'Video is controlled through code'); - expect(video.autoplay, isFalse, - reason: 'autoplay attribute on HTMLVideoElement MUST be false'); + expect( + video.controls, + isFalse, + reason: 'Video is controlled through code', + ); + expect( + video.autoplay, + isFalse, + reason: 'autoplay attribute on HTMLVideoElement MUST be false', + ); // see: https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML - expect(video.getAttribute('autoplay'), isNull, - reason: 'autoplay attribute on video tag must NOT be set'); + expect( + video.getAttribute('autoplay'), + isNull, + reason: 'autoplay attribute on video tag must NOT be set', + ); expect(video.playsInline, true, reason: 'Needed by safari iOS'); }); @@ -61,31 +72,50 @@ void main() { expect(video.muted, isTrue, reason: 'muted attribute should be true'); // If the volume is set to zero, pressing unmute // button may not restore the audio as expected. - expect(video.volume, greaterThan(0), - reason: 'Volume should not be set to zero when muted'); + expect( + video.volume, + greaterThan(0), + reason: 'Volume should not be set to zero when muted', + ); player.setVolume(0.5); expect(video.volume, 0.5, reason: 'Volume should be set to 0.5'); expect(video.muted, isFalse, reason: 'Muted attribute should be false'); - expect(() { - player.setVolume(-0.0001); - }, throwsAssertionError, reason: 'Volume cannot be < 0'); - - expect(() { - player.setVolume(1.0001); - }, throwsAssertionError, reason: 'Volume cannot be > 1'); + expect( + () { + player.setVolume(-0.0001); + }, + throwsAssertionError, + reason: 'Volume cannot be < 0', + ); + + expect( + () { + player.setVolume(1.0001); + }, + throwsAssertionError, + reason: 'Volume cannot be > 1', + ); }); testWidgets('setPlaybackSpeed', (WidgetTester tester) async { final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); - expect(() { - player.setPlaybackSpeed(-1); - }, throwsAssertionError, reason: 'Playback speed cannot be < 0'); - - expect(() { - player.setPlaybackSpeed(0); - }, throwsAssertionError, reason: 'Playback speed cannot be == 0'); + expect( + () { + player.setPlaybackSpeed(-1); + }, + throwsAssertionError, + reason: 'Playback speed cannot be < 0', + ); + + expect( + () { + player.setPlaybackSpeed(0); + }, + throwsAssertionError, + reason: 'Playback speed cannot be == 0', + ); }); group('seekTo', () { @@ -93,21 +123,30 @@ void main() { final VideoPlayer player = VideoPlayer(videoElement: video) ..initialize(); - expect(() { - player.seekTo(const Duration(seconds: -1)); - }, throwsAssertionError, reason: 'Cannot seek into negative numbers'); + expect( + () { + player.seekTo(const Duration(seconds: -1)); + }, + throwsAssertionError, + reason: 'Cannot seek into negative numbers', + ); }); - testWidgets('setting currentTime to its current value - noop', - (WidgetTester tester) async { + testWidgets('setting currentTime to its current value - noop', ( + WidgetTester tester, + ) async { makeSetCurrentTimeThrow(video); final VideoPlayer player = VideoPlayer(videoElement: video) ..initialize(); - expect(() { - // Self-test... - video.currentTime = 123; - }, throwsException, reason: 'Setting currentTime must throw!'); + expect( + () { + // Self-test... + video.currentTime = 123; + }, + throwsException, + reason: 'Setting currentTime must throw!', + ); expect(() { // Should not set currentTime (and throw) when seekTo current time. @@ -131,9 +170,10 @@ void main() { setUp(() { streamController = StreamController(); - player = - VideoPlayer(videoElement: video, eventController: streamController) - ..initialize(); + player = VideoPlayer( + videoElement: video, + eventController: streamController, + )..initialize(); // This stream will automatically close after 100 ms without seeing any events timedStream = streamController.stream.timeout( @@ -149,15 +189,21 @@ void main() { player.dispose(); }); - testWidgets('buffering dispatches only when it changes', - (WidgetTester tester) async { + testWidgets('buffering dispatches only when it changes', ( + WidgetTester tester, + ) async { // Take all the "buffering" events that we see during the next few seconds - final Future> stream = timedStream - .where( - (VideoEvent event) => bufferingEvents.contains(event.eventType)) - .map((VideoEvent event) => - event.eventType == VideoEventType.bufferingStart) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + bufferingEvents.contains(event.eventType), + ) + .map( + (VideoEvent event) => + event.eventType == VideoEventType.bufferingStart, + ) + .toList(); // Simulate some events coming from the player... player.setBuffering(true); @@ -176,15 +222,21 @@ void main() { expect(events, [true, false, true, false, true, false]); }); - testWidgets('canplay event does not change buffering state', - (WidgetTester tester) async { + testWidgets('canplay event does not change buffering state', ( + WidgetTester tester, + ) async { // Take all the "buffering" events that we see during the next few seconds - final Future> stream = timedStream - .where( - (VideoEvent event) => bufferingEvents.contains(event.eventType)) - .map((VideoEvent event) => - event.eventType == VideoEventType.bufferingStart) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + bufferingEvents.contains(event.eventType), + ) + .map( + (VideoEvent event) => + event.eventType == VideoEventType.bufferingStart, + ) + .toList(); player.setBuffering(true); @@ -197,15 +249,21 @@ void main() { expect(events, [true]); }); - testWidgets('canplaythrough event does change buffering state', - (WidgetTester tester) async { + testWidgets('canplaythrough event does change buffering state', ( + WidgetTester tester, + ) async { // Take all the "buffering" events that we see during the next few seconds - final Future> stream = timedStream - .where( - (VideoEvent event) => bufferingEvents.contains(event.eventType)) - .map((VideoEvent event) => - event.eventType == VideoEventType.bufferingStart) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + bufferingEvents.contains(event.eventType), + ) + .map( + (VideoEvent event) => + event.eventType == VideoEventType.bufferingStart, + ) + .toList(); player.setBuffering(true); @@ -218,18 +276,22 @@ void main() { expect(events, [true, false]); }); - testWidgets('initialized dispatches only once', - (WidgetTester tester) async { + testWidgets('initialized dispatches only once', ( + WidgetTester tester, + ) async { // Dispatch some bogus "canplay" events from the video object video.dispatchEvent(web.Event('canplay')); video.dispatchEvent(web.Event('canplay')); video.dispatchEvent(web.Event('canplay')); // Take all the "initialized" events that we see during the next few seconds - final Future> stream = timedStream - .where((VideoEvent event) => - event.eventType == VideoEventType.initialized) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + event.eventType == VideoEventType.initialized, + ) + .toList(); video.dispatchEvent(web.Event('canplay')); video.dispatchEvent(web.Event('canplay')); @@ -241,30 +303,38 @@ void main() { expect(events[0].eventType, VideoEventType.initialized); }); - testWidgets('loadedmetadata does not dispatch initialized', - (WidgetTester tester) async { + testWidgets('loadedmetadata does not dispatch initialized', ( + WidgetTester tester, + ) async { video.dispatchEvent(web.Event('loadedmetadata')); video.dispatchEvent(web.Event('loadedmetadata')); - final Future> stream = timedStream - .where((VideoEvent event) => - event.eventType == VideoEventType.initialized) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + event.eventType == VideoEventType.initialized, + ) + .toList(); final List events = await stream; expect(events, isEmpty); }); - testWidgets('loadeddata does not dispatch initialized', - (WidgetTester tester) async { + testWidgets('loadeddata does not dispatch initialized', ( + WidgetTester tester, + ) async { video.dispatchEvent(web.Event('loadeddata')); video.dispatchEvent(web.Event('loadeddata')); - final Future> stream = timedStream - .where((VideoEvent event) => - event.eventType == VideoEventType.initialized) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + event.eventType == VideoEventType.initialized, + ) + .toList(); final List events = await stream; @@ -276,10 +346,13 @@ void main() { setInfinityDuration(video); expect(video.duration.isInfinite, isTrue); - final Future> stream = timedStream - .where((VideoEvent event) => - event.eventType == VideoEventType.initialized) - .toList(); + final Future> stream = + timedStream + .where( + (VideoEvent event) => + event.eventType == VideoEventType.initialized, + ) + .toList(); video.dispatchEvent(web.Event('canplay')); @@ -300,8 +373,9 @@ void main() { }); group('VideoPlayerWebOptionsControls', () { - testWidgets('when disabled expect no controls', - (WidgetTester tester) async { + testWidgets('when disabled expect no controls', ( + WidgetTester tester, + ) async { await player.setOptions( const VideoPlayerWebOptions( // ignore: avoid_redundant_argument_values @@ -331,8 +405,9 @@ void main() { expect(video.disablePictureInPicture, isFalse); }); - testWidgets('and no download expect correct controls', - (WidgetTester tester) async { + testWidgets('and no download expect correct controls', ( + WidgetTester tester, + ) async { await player.setOptions( const VideoPlayerWebOptions( controls: VideoPlayerWebOptionsControls.enabled( @@ -350,8 +425,9 @@ void main() { expect(video.disablePictureInPicture, isFalse); }); - testWidgets('and no fullscreen expect correct controls', - (WidgetTester tester) async { + testWidgets('and no fullscreen expect correct controls', ( + WidgetTester tester, + ) async { await player.setOptions( const VideoPlayerWebOptions( controls: VideoPlayerWebOptionsControls.enabled( @@ -369,8 +445,9 @@ void main() { expect(video.disablePictureInPicture, isFalse); }); - testWidgets('and no playback rate expect correct controls', - (WidgetTester tester) async { + testWidgets('and no playback rate expect correct controls', ( + WidgetTester tester, + ) async { await player.setOptions( const VideoPlayerWebOptions( controls: VideoPlayerWebOptionsControls.enabled( @@ -388,8 +465,9 @@ void main() { expect(video.disablePictureInPicture, isFalse); }); - testWidgets('and no picture in picture expect correct controls', - (WidgetTester tester) async { + testWidgets('and no picture in picture expect correct controls', ( + WidgetTester tester, + ) async { await player.setOptions( const VideoPlayerWebOptions( controls: VideoPlayerWebOptionsControls.enabled( @@ -410,8 +488,9 @@ void main() { }); group('allowRemotePlayback', () { - testWidgets('when enabled expect no attribute', - (WidgetTester tester) async { + testWidgets('when enabled expect no attribute', ( + WidgetTester tester, + ) async { await player.setOptions( const VideoPlayerWebOptions( // ignore: avoid_redundant_argument_values @@ -422,12 +501,11 @@ void main() { expect(video.disableRemotePlayback, isFalse); }); - testWidgets('when disabled expect attribute', - (WidgetTester tester) async { + testWidgets('when disabled expect attribute', ( + WidgetTester tester, + ) async { await player.setOptions( - const VideoPlayerWebOptions( - allowRemotePlayback: false, - ), + const VideoPlayerWebOptions(allowRemotePlayback: false), ); expect(video.disableRemotePlayback, isTrue); @@ -435,71 +513,58 @@ void main() { }); group('poster', () { - testWidgets('when null expect no poster attribute', - (WidgetTester tester) async { - await player.setOptions( - const VideoPlayerWebOptions(), - ); + testWidgets('when null expect no poster attribute', ( + WidgetTester tester, + ) async { + await player.setOptions(const VideoPlayerWebOptions()); expect(video.poster, isEmpty); expect(video.getAttribute('poster'), isNull); }); - testWidgets('when provided expect poster attribute set', - (WidgetTester tester) async { + testWidgets('when provided expect poster attribute set', ( + WidgetTester tester, + ) async { final Uri posterUri = Uri.parse('https://example.com/poster.jpg'); - await player.setOptions( - VideoPlayerWebOptions( - poster: posterUri, - ), - ); + await player.setOptions(VideoPlayerWebOptions(poster: posterUri)); expect(video.poster, posterUri.toString()); expect(video.getAttribute('poster'), posterUri.toString()); }); - testWidgets('when set to null after having value expect poster removed', - (WidgetTester tester) async { - final Uri posterUri = Uri.parse('https://example.com/poster.jpg'); + testWidgets( + 'when set to null after having value expect poster removed', + (WidgetTester tester) async { + final Uri posterUri = Uri.parse('https://example.com/poster.jpg'); - await player.setOptions( - VideoPlayerWebOptions( - poster: posterUri, - ), - ); + await player.setOptions(VideoPlayerWebOptions(poster: posterUri)); - expect(video.poster, posterUri.toString()); + expect(video.poster, posterUri.toString()); - await player.setOptions( - const VideoPlayerWebOptions(), - ); + await player.setOptions(const VideoPlayerWebOptions()); - expect(video.poster, isEmpty); - expect(video.getAttribute('poster'), isNull); - }); + expect(video.poster, isEmpty); + expect(video.getAttribute('poster'), isNull); + }, + ); - testWidgets('when updated expect poster attribute updated', - (WidgetTester tester) async { - final Uri initialPoster = - Uri.parse('https://example.com/poster1.jpg'); - final Uri updatedPoster = - Uri.parse('https://example.com/poster2.jpg'); + testWidgets('when updated expect poster attribute updated', ( + WidgetTester tester, + ) async { + final Uri initialPoster = Uri.parse( + 'https://example.com/poster1.jpg', + ); + final Uri updatedPoster = Uri.parse( + 'https://example.com/poster2.jpg', + ); // Set initial poster - await player.setOptions( - VideoPlayerWebOptions( - poster: initialPoster, - ), - ); + await player.setOptions(VideoPlayerWebOptions(poster: initialPoster)); expect(video.poster, initialPoster.toString()); // Update poster - await player.setOptions( - VideoPlayerWebOptions( - poster: updatedPoster, - ), - ); + await player.setOptions(VideoPlayerWebOptions(poster: updatedPoster)); expect(video.poster, updatedPoster.toString()); expect(video.getAttribute('poster'), updatedPoster.toString()); diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index e5687f263eb..9a2cd8c8e85 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -25,12 +25,15 @@ void main() { setUp(() { VideoPlayerPlatform.instance = VideoPlayerPlugin(); playerId = VideoPlayerPlatform.instance - .createWithOptions(VideoCreationOptions( + .createWithOptions( + VideoCreationOptions( dataSource: DataSource( sourceType: DataSourceType.network, uri: getUrlForAssetAsNetworkSource(_videoAssetKey), ), - viewType: VideoViewType.platformView)) + viewType: VideoViewType.platformView, + ), + ) .then((int? playerId) => playerId!); }); @@ -40,47 +43,63 @@ void main() { testWidgets('can create from network', (WidgetTester tester) async { expect( - VideoPlayerPlatform.instance.createWithOptions(VideoCreationOptions( - dataSource: DataSource( - sourceType: DataSourceType.network, - uri: getUrlForAssetAsNetworkSource(_videoAssetKey), - ), - viewType: VideoViewType.platformView)), - completion(isNonZero)); + VideoPlayerPlatform.instance.createWithOptions( + VideoCreationOptions( + dataSource: DataSource( + sourceType: DataSourceType.network, + uri: getUrlForAssetAsNetworkSource(_videoAssetKey), + ), + viewType: VideoViewType.platformView, + ), + ), + completion(isNonZero), + ); }); testWidgets('can create from asset', (WidgetTester tester) async { expect( - VideoPlayerPlatform.instance.createWithOptions(VideoCreationOptions( - dataSource: DataSource( - sourceType: DataSourceType.asset, - asset: 'videos/bee.mp4', - package: 'bee_vids', - ), - viewType: VideoViewType.platformView)), - completion(isNonZero)); + VideoPlayerPlatform.instance.createWithOptions( + VideoCreationOptions( + dataSource: DataSource( + sourceType: DataSourceType.asset, + asset: 'videos/bee.mp4', + package: 'bee_vids', + ), + viewType: VideoViewType.platformView, + ), + ), + completion(isNonZero), + ); }); testWidgets('cannot create from file', (WidgetTester tester) async { expect( - VideoPlayerPlatform.instance.createWithOptions(VideoCreationOptions( - dataSource: DataSource( - sourceType: DataSourceType.file, - uri: '/videos/bee.mp4', - ), - viewType: VideoViewType.platformView)), - throwsUnimplementedError); + VideoPlayerPlatform.instance.createWithOptions( + VideoCreationOptions( + dataSource: DataSource( + sourceType: DataSourceType.file, + uri: '/videos/bee.mp4', + ), + viewType: VideoViewType.platformView, + ), + ), + throwsUnimplementedError, + ); }); testWidgets('cannot create from content URI', (WidgetTester tester) async { expect( - VideoPlayerPlatform.instance.createWithOptions(VideoCreationOptions( - dataSource: DataSource( - sourceType: DataSourceType.contentUri, - uri: 'content://video', - ), - viewType: VideoViewType.platformView)), - throwsUnimplementedError); + VideoPlayerPlatform.instance.createWithOptions( + VideoCreationOptions( + dataSource: DataSource( + sourceType: DataSourceType.contentUri, + uri: 'content://video', + ), + viewType: VideoViewType.platformView, + ), + ), + throwsUnimplementedError, + ); }); testWidgets('can dispose', (WidgetTester tester) async { @@ -100,19 +119,24 @@ void main() { expect(VideoPlayerPlatform.instance.play(await playerId), completes); }); - testWidgets('throws PlatformException when playing bad media', - (WidgetTester tester) async { - final int videoPlayerId = (await VideoPlayerPlatform.instance - .createWithOptions(VideoCreationOptions( + testWidgets('throws PlatformException when playing bad media', ( + WidgetTester tester, + ) async { + final int videoPlayerId = + (await VideoPlayerPlatform.instance.createWithOptions( + VideoCreationOptions( dataSource: DataSource( sourceType: DataSourceType.network, - uri: - getUrlForAssetAsNetworkSource('assets/__non_existent.webm'), + uri: getUrlForAssetAsNetworkSource( + 'assets/__non_existent.webm', + ), ), - viewType: VideoViewType.platformView)))!; + viewType: VideoViewType.platformView, + ), + ))!; - final Stream eventStream = - VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); + final Stream eventStream = VideoPlayerPlatform.instance + .videoEventsFor(videoPlayerId); // Mute video to allow autoplay (See https://goo.gl/xX8pDD) await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); @@ -152,20 +176,26 @@ void main() { }); testWidgets('can get position', (WidgetTester tester) async { - expect(VideoPlayerPlatform.instance.getPosition(await playerId), - completion(isInstanceOf())); + expect( + VideoPlayerPlatform.instance.getPosition(await playerId), + completion(isInstanceOf()), + ); }); testWidgets('can get video event stream', (WidgetTester tester) async { - expect(VideoPlayerPlatform.instance.videoEventsFor(await playerId), - isInstanceOf>()); + expect( + VideoPlayerPlatform.instance.videoEventsFor(await playerId), + isInstanceOf>(), + ); }); testWidgets('can build view', (WidgetTester tester) async { expect( - VideoPlayerPlatform.instance - .buildViewWithOptions(VideoViewOptions(playerId: await playerId)), - isInstanceOf()); + VideoPlayerPlatform.instance.buildViewWithOptions( + VideoViewOptions(playerId: await playerId), + ), + isInstanceOf(), + ); }); testWidgets('ignores setting mixWithOthers', (WidgetTester tester) async { @@ -174,68 +204,80 @@ void main() { }); testWidgets( - 'double call to play will emit a single isPlayingStateUpdate event', - (WidgetTester tester) async { - final int videoPlayerId = await playerId; - final Stream eventStream = - VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); - - final Future> stream = eventStream.timeout( - const Duration(seconds: 2), - onTimeout: (EventSink sink) { - sink.close(); - }, - ).toList(); - - await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); - await VideoPlayerPlatform.instance.play(videoPlayerId); - await VideoPlayerPlatform.instance.play(videoPlayerId); - - // Let the video play, until we stop seeing events for two seconds - final List events = await stream; - - await VideoPlayerPlatform.instance.pause(videoPlayerId); - - expect( - events.where((VideoEvent e) => - e.eventType == VideoEventType.isPlayingStateUpdate), + 'double call to play will emit a single isPlayingStateUpdate event', + (WidgetTester tester) async { + final int videoPlayerId = await playerId; + final Stream eventStream = VideoPlayerPlatform.instance + .videoEventsFor(videoPlayerId); + + final Future> stream = + eventStream + .timeout( + const Duration(seconds: 2), + onTimeout: (EventSink sink) { + sink.close(); + }, + ) + .toList(); + + await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); + await VideoPlayerPlatform.instance.play(videoPlayerId); + await VideoPlayerPlatform.instance.play(videoPlayerId); + + // Let the video play, until we stop seeing events for two seconds + final List events = await stream; + + await VideoPlayerPlatform.instance.pause(videoPlayerId); + + expect( + events.where( + (VideoEvent e) => + e.eventType == VideoEventType.isPlayingStateUpdate, + ), equals([ VideoEvent( eventType: VideoEventType.isPlayingStateUpdate, isPlaying: true, - ) - ])); - }, - // MEDIA_ELEMENT_ERROR, see https://github.com/flutter/flutter/issues/169219 - skip: true); - - testWidgets('video playback lifecycle', (WidgetTester tester) async { - final int videoPlayerId = await playerId; - final Stream eventStream = - VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); - - final Future> stream = eventStream.timeout( - const Duration(seconds: 2), - onTimeout: (EventSink sink) { - sink.close(); - }, - ).toList(); - - await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); - await VideoPlayerPlatform.instance.play(videoPlayerId); + ), + ]), + ); + }, + // MEDIA_ELEMENT_ERROR, see https://github.com/flutter/flutter/issues/169219 + skip: true, + ); - // Let the video play, until we stop seeing events for two seconds - final List events = await stream; - - await VideoPlayerPlatform.instance.pause(videoPlayerId); - - // The expected list of event types should look like this: - // 1. isPlayingStateUpdate (videoElement.onPlaying) - // 2. bufferingStart, - // 3. bufferingUpdate (videoElement.onWaiting), - // 4. initialized (videoElement.onCanPlay), - // 5. bufferingEnd (videoElement.onCanPlayThrough), - expect( + testWidgets( + 'video playback lifecycle', + (WidgetTester tester) async { + final int videoPlayerId = await playerId; + final Stream eventStream = VideoPlayerPlatform.instance + .videoEventsFor(videoPlayerId); + + final Future> stream = + eventStream + .timeout( + const Duration(seconds: 2), + onTimeout: (EventSink sink) { + sink.close(); + }, + ) + .toList(); + + await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); + await VideoPlayerPlatform.instance.play(videoPlayerId); + + // Let the video play, until we stop seeing events for two seconds + final List events = await stream; + + await VideoPlayerPlatform.instance.pause(videoPlayerId); + + // The expected list of event types should look like this: + // 1. isPlayingStateUpdate (videoElement.onPlaying) + // 2. bufferingStart, + // 3. bufferingUpdate (videoElement.onWaiting), + // 4. initialized (videoElement.onCanPlay), + // 5. bufferingEnd (videoElement.onCanPlayThrough), + expect( events.map((VideoEvent e) => e.eventType), equals([ VideoEventType.isPlayingStateUpdate, @@ -243,10 +285,12 @@ void main() { VideoEventType.bufferingUpdate, VideoEventType.initialized, VideoEventType.bufferingEnd, - ])); - }, - // MEDIA_ELEMENT_ERROR, see https://github.com/flutter/flutter/issues/169219 - skip: true); + ]), + ); + }, + // MEDIA_ELEMENT_ERROR, see https://github.com/flutter/flutter/issues/169219 + skip: true, + ); testWidgets('can set web options', (WidgetTester tester) async { expect( diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index 553a22c8ecb..e3bce694990 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -2,8 +2,8 @@ name: video_player_for_web_integration_tests publish_to: none environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" dependencies: flutter: diff --git a/packages/video_player/video_player_web/lib/src/duration_utils.dart b/packages/video_player/video_player_web/lib/src/duration_utils.dart index 030d6b04098..7562e49271d 100644 --- a/packages/video_player/video_player_web/lib/src/duration_utils.dart +++ b/packages/video_player/video_player_web/lib/src/duration_utils.dart @@ -23,9 +23,7 @@ const Duration jsCompatibleTimeUnset = Duration( /// If the `videoDuration` is `NaN`, this will return null. Duration? convertNumVideoDurationToPluginDuration(num duration) { if (duration.isFinite) { - return Duration( - milliseconds: (duration * 1000).round(), - ); + return Duration(milliseconds: (duration * 1000).round()); } else if (duration.isInfinite) { return jsCompatibleTimeUnset; } diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart index 90c10c6a1fb..3791fe5395a 100644 --- a/packages/video_player/video_player_web/lib/src/video_player.dart +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -42,8 +42,8 @@ class VideoPlayer { VideoPlayer({ required web.HTMLVideoElement videoElement, @visibleForTesting StreamController? eventController, - }) : _videoElement = videoElement, - _eventController = eventController ?? StreamController(); + }) : _videoElement = videoElement, + _eventController = eventController ?? StreamController(); final StreamController _eventController; final web.HTMLVideoElement _videoElement; @@ -68,9 +68,7 @@ class VideoPlayer { /// `src` attribute is set). /// /// The `src` parameter is nullable for testing purposes. - void initialize({ - String? src, - }) { + void initialize({String? src}) { _videoElement ..autoplay = false ..controls = false @@ -98,25 +96,31 @@ class VideoPlayer { // We need to look at the HTMLMediaElement.error. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error final web.MediaError error = _videoElement.error!; - _eventController.addError(PlatformException( - code: _kErrorValueToErrorName[error.code]!, - message: error.message != '' ? error.message : _kDefaultErrorMessage, - details: _kErrorValueToErrorDescription[error.code], - )); + _eventController.addError( + PlatformException( + code: _kErrorValueToErrorName[error.code]!, + message: error.message != '' ? error.message : _kDefaultErrorMessage, + details: _kErrorValueToErrorDescription[error.code], + ), + ); }); _videoElement.onPlay.listen((dynamic _) { - _eventController.add(VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: true, - )); + _eventController.add( + VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: true, + ), + ); }); _videoElement.onPause.listen((dynamic _) { - _eventController.add(VideoEvent( - eventType: VideoEventType.isPlayingStateUpdate, - isPlaying: false, - )); + _eventController.add( + VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: false, + ), + ); }); _videoElement.onEnded.listen((dynamic _) { @@ -150,10 +154,9 @@ class VideoPlayer { // The rejection handler is called with a DOMException. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play final web.DOMException exception = e as web.DOMException; - _eventController.addError(PlatformException( - code: exception.name, - message: exception.message, - )); + _eventController.addError( + PlatformException(code: exception.name, message: exception.message), + ); return null; }, test: (Object e) => e is web.DOMException); } @@ -306,15 +309,17 @@ class VideoPlayer { // Sends an [VideoEventType.initialized] [VideoEvent] with info about the wrapped video. void _sendInitialized() { - final Duration? duration = - convertNumVideoDurationToPluginDuration(_videoElement.duration); + final Duration? duration = convertNumVideoDurationToPluginDuration( + _videoElement.duration, + ); - final Size? size = _videoElement.videoHeight.isFinite - ? Size( - _videoElement.videoWidth.toDouble(), - _videoElement.videoHeight.toDouble(), - ) - : null; + final Size? size = + _videoElement.videoHeight.isFinite + ? Size( + _videoElement.videoWidth.toDouble(), + _videoElement.videoHeight.toDouble(), + ) + : null; _eventController.add( VideoEvent( @@ -333,30 +338,37 @@ class VideoPlayer { void setBuffering(bool buffering) { if (_isBuffering != buffering) { _isBuffering = buffering; - _eventController.add(VideoEvent( - eventType: _isBuffering - ? VideoEventType.bufferingStart - : VideoEventType.bufferingEnd, - )); + _eventController.add( + VideoEvent( + eventType: + _isBuffering + ? VideoEventType.bufferingStart + : VideoEventType.bufferingEnd, + ), + ); } } // Broadcasts the [web.HTMLVideoElement.buffered] status through the [events] stream. void _sendBufferingRangesUpdate() { - _eventController.add(VideoEvent( - buffered: _toDurationRange(_videoElement.buffered), - eventType: VideoEventType.bufferingUpdate, - )); + _eventController.add( + VideoEvent( + buffered: _toDurationRange(_videoElement.buffered), + eventType: VideoEventType.bufferingUpdate, + ), + ); } // Converts from [html.TimeRanges] to our own List. List _toDurationRange(web.TimeRanges buffered) { final List durationRange = []; for (int i = 0; i < buffered.length; i++) { - durationRange.add(DurationRange( - Duration(milliseconds: (buffered.start(i) * 1000).round()), - Duration(milliseconds: (buffered.end(i) * 1000).round()), - )); + durationRange.add( + DurationRange( + Duration(milliseconds: (buffered.start(i) * 1000).round()), + Duration(milliseconds: (buffered.end(i) * 1000).round()), + ), + ); } return durationRange; } diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart index d65eabacfcc..5fdc71a8db5 100644 --- a/packages/video_player/video_player_web/lib/video_player_web.dart +++ b/packages/video_player/video_player_web/lib/video_player_web.dart @@ -77,27 +77,34 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { assetUrl = ui_web.assetManager.getAssetUrl(assetUrl); uri = assetUrl; case DataSourceType.file: - return Future.error(UnimplementedError( - 'web implementation of video_player cannot play local files')); + return Future.error( + UnimplementedError( + 'web implementation of video_player cannot play local files', + ), + ); case DataSourceType.contentUri: - return Future.error(UnimplementedError( - 'web implementation of video_player cannot play content uri')); + return Future.error( + UnimplementedError( + 'web implementation of video_player cannot play content uri', + ), + ); } - final web.HTMLVideoElement videoElement = web.HTMLVideoElement() - ..id = 'videoElement-$playerId' - ..style.border = 'none' - ..style.height = '100%' - ..style.width = '100%'; + final web.HTMLVideoElement videoElement = + web.HTMLVideoElement() + ..id = 'videoElement-$playerId' + ..style.border = 'none' + ..style.height = '100%' + ..style.width = '100%'; // TODO(hterkelsen): Use initialization parameters once they are available ui_web.platformViewRegistry.registerViewFactory( - 'videoPlayer-$playerId', (int viewId) => videoElement); + 'videoPlayer-$playerId', + (int viewId) => videoElement, + ); final VideoPlayer player = VideoPlayer(videoElement: videoElement) - ..initialize( - src: uri, - ); + ..initialize(src: uri); _videoPlayers[playerId] = player; diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 6b4ee5918bb..ca36ffe35ee 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -5,8 +5,8 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ version: 2.4.0 environment: - sdk: ^3.6.0 - flutter: ">=3.27.0" + sdk: ^3.7.0 + flutter: ">=3.29.0" flutter: plugin: