diff --git a/analysis_options.yaml b/analysis_options.yaml index aa322b6ae..f10e6404c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,22 +1,9 @@ -#include: package:lint/analysis_options_package.yaml - -analyzer: - strong-mode: - implicit-dynamic: false - +include: package:very_good_analysis/analysis_options.yaml linter: rules: - close_sinks: true - sort_constructors_first: false - avoid_classes_with_only_static_members: false - avoid_void_async: false - avoid_positional_boolean_parameters: false - avoid_function_literals_in_foreach_calls: false - prefer_constructors_over_static_methods: false - sort_unnamed_constructors_first: false - sized_box_for_whitespace: false - invalid_dependency: false + public_member_api_docs: false + flutter_style_todos: false + avoid_final_parameters: false sort_pub_dependencies: false - avoid_unnecessary_containers: false - use_setters_to_change_properties: false - +analyzer: + exclude: [build/**, lib/**.freezed.dart, lib/**.g.dart] \ No newline at end of file diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 0d2902135..f10e6404c 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1,28 +1,9 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - +include: package:very_good_analysis/analysis_options.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + public_member_api_docs: false + flutter_style_todos: false + avoid_final_parameters: false + sort_pub_dependencies: false +analyzer: + exclude: [build/**, lib/**.freezed.dart, lib/**.g.dart] \ No newline at end of file diff --git a/example/lib/constants.dart b/example/lib/constants.dart index ec49d02af..7f939646e 100644 --- a/example/lib/constants.dart +++ b/example/lib/constants.dart @@ -1,57 +1,57 @@ class Constants { static const String bugBuckBunnyVideoUrl = - "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; static const String forBiggerBlazesUrl = - "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"; - static const String fileTestVideoUrl = "testvideo.mp4"; - static const String fileTestVideoEncryptUrl = "testvideo_encrypt.mp4"; + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'; + static const String fileTestVideoUrl = 'testvideo.mp4'; + static const String fileTestVideoEncryptUrl = 'testvideo_encrypt.mp4'; static const String networkTestVideoEncryptUrl = - "https://github.com/tinusneethling/betterplayer/raw/ClearKeySupport/example/assets/testvideo_encrypt.mp4"; - static const String fileExampleSubtitlesUrl = "example_subtitles.srt"; + 'https://github.com/tinusneethling/betterplayer/raw/ClearKeySupport/example/assets/testvideo_encrypt.mp4'; + static const String fileExampleSubtitlesUrl = 'example_subtitles.srt'; static const String hlsTestStreamUrl = - "https://mtoczko.github.io/hls-test-streams/test-group/playlist.m3u8"; + 'https://mtoczko.github.io/hls-test-streams/test-group/playlist.m3u8'; static const String hlsPlaylistUrl = - "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"; + 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8'; static const Map exampleResolutionsUrls = { - "LOW": - "https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_480_1_5MG.mp4", - "MEDIUM": - "https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_640_3MG.mp4", - "LARGE": - "https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1280_10MG.mp4", - "EXTRA_LARGE": - "https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1920_18MG.mp4" + 'LOW': + 'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_480_1_5MG.mp4', + 'MEDIUM': + 'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_640_3MG.mp4', + 'LARGE': + 'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1280_10MG.mp4', + 'EXTRA_LARGE': + 'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1920_18MG.mp4', }; static const String phantomVideoUrl = - "http://sample.vodobox.com/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8"; + 'http://sample.vodobox.com/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8'; static const String elephantDreamVideoUrl = - "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"; + 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'; static const String forBiggerJoyridesVideoUrl = - "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4"; + 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4'; static const String verticalVideoUrl = - "http://www.exit109.com/~dnn/clips/RW20seconds_1.mp4"; - static String logo = "logo.png"; + 'http://www.exit109.com/~dnn/clips/RW20seconds_1.mp4'; + static String logo = 'logo.png'; static String placeholderUrl = - "https://imgix.bustle.com/uploads/image/2020/8/5/23905b9c-6b8c-47fa-bc0f-434de1d7e9bf-avengers-5.jpg"; + 'https://imgix.bustle.com/uploads/image/2020/8/5/23905b9c-6b8c-47fa-bc0f-434de1d7e9bf-avengers-5.jpg'; static String elephantDreamStreamUrl = - "http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8"; + 'http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8'; static String tokenEncodedHlsUrl = - "https://amssamples.streaming.mediaservices.windows.net/830584f8-f0c8-4e41-968b-6538b9380aa5/TearsOfSteelTeaser.ism/manifest(format=m3u8-aapl)"; + 'https://amssamples.streaming.mediaservices.windows.net/830584f8-f0c8-4e41-968b-6538b9380aa5/TearsOfSteelTeaser.ism/manifest(format=m3u8-aapl)'; static String tokenEncodedHlsToken = - "Bearer=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cm46bWljcm9zb2Z0OmF6dXJlOm1lZGlhc2VydmljZXM6Y29udGVudGtleWlkZW50aWZpZXIiOiI5ZGRhMGJjYy01NmZiLTQxNDMtOWQzMi0zYWI5Y2M2ZWE4MGIiLCJpc3MiOiJodHRwOi8vdGVzdGFjcy5jb20vIiwiYXVkIjoidXJuOnRlc3QiLCJleHAiOjE3MTA4MDczODl9.lJXm5hmkp5ArRIAHqVJGefW2bcTzd91iZphoKDwa6w8"; + 'Bearer=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cm46bWljcm9zb2Z0OmF6dXJlOm1lZGlhc2VydmljZXM6Y29udGVudGtleWlkZW50aWZpZXIiOiI5ZGRhMGJjYy01NmZiLTQxNDMtOWQzMi0zYWI5Y2M2ZWE4MGIiLCJpc3MiOiJodHRwOi8vdGVzdGFjcy5jb20vIiwiYXVkIjoidXJuOnRlc3QiLCJleHAiOjE3MTA4MDczODl9.lJXm5hmkp5ArRIAHqVJGefW2bcTzd91iZphoKDwa6w8'; static String widevineVideoUrl = - "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd"; + 'https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd'; static String widevineLicenseUrl = - "https://proxy.uat.widevine.com/proxy?provider=widevine_test"; + 'https://proxy.uat.widevine.com/proxy?provider=widevine_test'; static String fairplayHlsUrl = - "https://fps.ezdrm.com/demo/hls/BigBuckBunny_320x180.m3u8"; + 'https://fps.ezdrm.com/demo/hls/BigBuckBunny_320x180.m3u8'; static String fairplayCertificateUrl = - "https://github.com/koldo92/betterplayer/raw/fairplay_ezdrm/example/assets/eleisure.cer"; - static String fairplayLicenseUrl = "https://fps.ezdrm.com/api/licenses/"; + 'https://github.com/koldo92/betterplayer/raw/fairplay_ezdrm/example/assets/eleisure.cer'; + static String fairplayLicenseUrl = 'https://fps.ezdrm.com/api/licenses/'; static String catImageUrl = - "https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/other/cat_relaxing_on_patio_other/1800x1200_cat_relaxing_on_patio_other.jpg"; + 'https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/other/cat_relaxing_on_patio_other/1800x1200_cat_relaxing_on_patio_other.jpg'; static String dashStreamUrl = - "https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd"; + 'https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd'; static String segmentedSubtitlesHlsUrl = - "https://eng-demo.cablecast.tv/segmented-captions/vod.m3u8"; + 'https://eng-demo.cablecast.tv/segmented-captions/vod.m3u8'; } diff --git a/example/lib/main.dart b/example/lib/main.dart index 5a09d4656..622b3d577 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,29 +3,32 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { return Shortcuts( - shortcuts: { - LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), - }, - child: MaterialApp( - title: 'Better player demo', - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: [ - const Locale('en', 'US'), - const Locale('pl', 'PL'), - ], - theme: ThemeData( - primarySwatch: Colors.green, - ), - home: WelcomePage(), - )); + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), + }, + child: MaterialApp( + title: 'Better player demo', + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en', 'US'), + Locale('pl', 'PL'), + ], + theme: ThemeData( + primarySwatch: Colors.green, + ), + home: const WelcomePage(), + ), + ); } } diff --git a/example/lib/model/video_list_data.dart b/example/lib/model/video_list_data.dart index f0e0ea2b1..492775cd9 100644 --- a/example/lib/model/video_list_data.dart +++ b/example/lib/model/video_list_data.dart @@ -1,8 +1,7 @@ class VideoListData { + VideoListData(this.videoTitle, this.videoUrl); final String videoTitle; final String videoUrl; Duration? lastPosition; bool? wasPlaying = false; - - VideoListData(this.videoTitle, this.videoUrl); } diff --git a/example/lib/pages/auto_fullscreen_orientation_page.dart b/example/lib/pages/auto_fullscreen_orientation_page.dart index ed083e60b..b80f385e9 100644 --- a/example/lib/pages/auto_fullscreen_orientation_page.dart +++ b/example/lib/pages/auto_fullscreen_orientation_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class AutoFullscreenOrientationPage extends StatefulWidget { + const AutoFullscreenOrientationPage({super.key}); + @override _AutoFullscreenOrientationPageState createState() => _AutoFullscreenOrientationPageState(); @@ -14,13 +16,15 @@ class _AutoFullscreenOrientationPageState @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( - aspectRatio: 16 / 9, - fit: BoxFit.contain, - autoDetectFullscreenDeviceOrientation: true); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl); + const betterPlayerConfiguration = BetterPlayerConfiguration( + aspectRatio: 16 / 9, + fit: BoxFit.contain, + autoDetectFullscreenDeviceOrientation: true, + ); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.forBiggerBlazesUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); super.initState(); @@ -30,16 +34,16 @@ class _AutoFullscreenOrientationPageState Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Auto full screen orientation"), + title: const Text('Auto full screen orientation'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Aspect ratio and device orientation on full screen will be " - "managed by the BetterPlayer. Click on the fullscreen option.", + 'Aspect ratio and device orientation on full screen will be ' + 'managed by the BetterPlayer. Click on the fullscreen option.', style: TextStyle(fontSize: 16), ), ), @@ -48,20 +52,22 @@ class _AutoFullscreenOrientationPageState child: BetterPlayer(controller: _betterPlayerController), ), ElevatedButton( - child: Text("Play horizontal video"), + child: const Text('Play horizontal video'), onPressed: () { - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, - Constants.forBiggerBlazesUrl); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.forBiggerBlazesUrl, + ); _betterPlayerController.setupDataSource(dataSource); }, ), ElevatedButton( - child: Text("Play vertical video"), + child: const Text('Play vertical video'), onPressed: () async { - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, - Constants.verticalVideoUrl); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.verticalVideoUrl, + ); _betterPlayerController.setupDataSource(dataSource); }, ), diff --git a/example/lib/pages/basic_player_page.dart b/example/lib/pages/basic_player_page.dart index f8c969a4e..7b0aeba59 100644 --- a/example/lib/pages/basic_player_page.dart +++ b/example/lib/pages/basic_player_page.dart @@ -4,6 +4,8 @@ import 'package:example/utils.dart'; import 'package:flutter/material.dart'; class BasicPlayerPage extends StatefulWidget { + const BasicPlayerPage({super.key}); + @override _BasicPlayerPageState createState() => _BasicPlayerPageState(); } @@ -13,15 +15,15 @@ class _BasicPlayerPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Basic player"), + title: const Text('Basic player'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Basic player created with the simplest factory method. Shows video from URL.", + 'Basic player created with the simplest factory method. Shows video from URL.', style: TextStyle(fontSize: 16), ), ), @@ -31,10 +33,10 @@ class _BasicPlayerPageState extends State { Constants.forBiggerBlazesUrl, ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Next player shows video from file.", + 'Next player shows video from file.', style: TextStyle(fontSize: 16), ), ), @@ -48,7 +50,7 @@ class _BasicPlayerPageState extends State { return const SizedBox(); } }, - ) + ), ], ), ); diff --git a/example/lib/pages/cache_page.dart b/example/lib/pages/cache_page.dart index 64a6a6e60..2d2cfc186 100644 --- a/example/lib/pages/cache_page.dart +++ b/example/lib/pages/cache_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class CachePage extends StatefulWidget { + const CachePage({super.key}); + @override _CachePageState createState() => _CachePageState(); } @@ -13,22 +15,19 @@ class _CachePageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); _betterPlayerDataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.phantomVideoUrl, - cacheConfiguration: BetterPlayerCacheConfiguration( + cacheConfiguration: const BetterPlayerCacheConfiguration( useCache: true, preCacheSize: 10 * 1024 * 1024, - maxCacheSize: 10 * 1024 * 1024, - maxCacheFileSize: 10 * 1024 * 1024, ///Android only option to use cached video between app sessions - key: "testCacheKey", + key: 'testCacheKey', ), ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); @@ -39,18 +38,18 @@ class _CachePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Cache"), + title: const Text('Cache'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with cache enabled. To test this feature, first plays " - "video, then leave this page, turn internet off and enter " - "page again. You should be able to play video without " - "internet connection.", + 'Player with cache enabled. To test this feature, first plays ' + 'video, then leave this page, turn internet off and enter ' + 'page again. You should be able to play video without ' + 'internet connection.', style: TextStyle(fontSize: 16), ), ), @@ -59,25 +58,25 @@ class _CachePageState extends State { child: BetterPlayer(controller: _betterPlayerController), ), TextButton( - child: Text("Start pre cache"), + child: const Text('Start pre cache'), onPressed: () { _betterPlayerController.preCache(_betterPlayerDataSource); }, ), TextButton( - child: Text("Stop pre cache"), + child: const Text('Stop pre cache'), onPressed: () { _betterPlayerController.stopPreCache(_betterPlayerDataSource); }, ), TextButton( - child: Text("Play video"), + child: const Text('Play video'), onPressed: () { _betterPlayerController.setupDataSource(_betterPlayerDataSource); }, ), TextButton( - child: Text("Clear cache"), + child: const Text('Clear cache'), onPressed: () { _betterPlayerController.clearCache(); }, diff --git a/example/lib/pages/clearkey_page.dart b/example/lib/pages/clearkey_page.dart index 9074e7f6a..8e1d12930 100644 --- a/example/lib/pages/clearkey_page.dart +++ b/example/lib/pages/clearkey_page.dart @@ -1,12 +1,13 @@ import 'dart:io'; import 'package:better_player/better_player.dart'; -import 'package:flutter/material.dart'; - import 'package:example/constants.dart'; import 'package:example/utils.dart'; +import 'package:flutter/material.dart'; class ClearKeyPage extends StatefulWidget { + const ClearKeyPage({super.key}); + @override State createState() => _ClearKeyState(); } @@ -19,8 +20,7 @@ class _ClearKeyState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); @@ -37,84 +37,88 @@ class _ClearKeyState extends State { super.initState(); } - void _setupDataSources() async { - var _clearKeyDataSourceFile = BetterPlayerDataSource( + Future _setupDataSources() async { + final clearKeyDataSourceFile = BetterPlayerDataSource( BetterPlayerDataSourceType.file, await Utils.getFileUrl(Constants.fileTestVideoEncryptUrl), drmConfiguration: BetterPlayerDrmConfiguration( - drmType: BetterPlayerDrmType.clearKey, - clearKey: BetterPlayerClearKeyUtils.generateKey({ - "f3c5e0361e6654b28f8049c778b23946": - "a4631a153a443df9eed0593043db7519", - "abba271e8bcf552bbd2e86a434a9a5d9": - "69eaa802a6763af979e8d1940fb88392" - })), + drmType: BetterPlayerDrmType.clearKey, + clearKey: BetterPlayerClearKeyUtils.generateKey({ + 'f3c5e0361e6654b28f8049c778b23946': + 'a4631a153a443df9eed0593043db7519', + 'abba271e8bcf552bbd2e86a434a9a5d9': + '69eaa802a6763af979e8d1940fb88392', + }), + ), ); - _clearKeyControllerFile.setupDataSource(_clearKeyDataSourceFile); + _clearKeyControllerFile.setupDataSource(clearKeyDataSourceFile); - BetterPlayerDataSource _clearKeyDataSourceBroken = BetterPlayerDataSource( + final clearKeyDataSourceBroken = BetterPlayerDataSource( BetterPlayerDataSourceType.file, await Utils.getFileUrl(Constants.fileTestVideoEncryptUrl), drmConfiguration: BetterPlayerDrmConfiguration( - drmType: BetterPlayerDrmType.clearKey, - clearKey: BetterPlayerClearKeyUtils.generateKey({ - "f3c5e0361e6654b28f8049c778b23946": - "a4631a153a443df9eed0593043d11111", - "abba271e8bcf552bbd2e86a434a9a5d9": - "69eaa802a6763af979e8d1940fb11111" - })), + drmType: BetterPlayerDrmType.clearKey, + clearKey: BetterPlayerClearKeyUtils.generateKey({ + 'f3c5e0361e6654b28f8049c778b23946': + 'a4631a153a443df9eed0593043d11111', + 'abba271e8bcf552bbd2e86a434a9a5d9': + '69eaa802a6763af979e8d1940fb11111', + }), + ), ); - _clearKeyControllerBroken.setupDataSource(_clearKeyDataSourceBroken); + _clearKeyControllerBroken.setupDataSource(clearKeyDataSourceBroken); - var _clearKeyDataSourceNetwork = BetterPlayerDataSource( + final clearKeyDataSourceNetwork = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.networkTestVideoEncryptUrl, drmConfiguration: BetterPlayerDrmConfiguration( - drmType: BetterPlayerDrmType.clearKey, - clearKey: BetterPlayerClearKeyUtils.generateKey({ - "f3c5e0361e6654b28f8049c778b23946": - "a4631a153a443df9eed0593043db7519", - "abba271e8bcf552bbd2e86a434a9a5d9": - "69eaa802a6763af979e8d1940fb88392" - })), + drmType: BetterPlayerDrmType.clearKey, + clearKey: BetterPlayerClearKeyUtils.generateKey({ + 'f3c5e0361e6654b28f8049c778b23946': + 'a4631a153a443df9eed0593043db7519', + 'abba271e8bcf552bbd2e86a434a9a5d9': + '69eaa802a6763af979e8d1940fb88392', + }), + ), ); - _clearKeyControllerNetwork.setupDataSource(_clearKeyDataSourceNetwork); + _clearKeyControllerNetwork.setupDataSource(clearKeyDataSourceNetwork); - var _clearKeyDataSourceMemory = BetterPlayerDataSource( + final clearKeyDataSourceMemory = BetterPlayerDataSource( BetterPlayerDataSourceType.memory, - "", + '', bytes: File(await Utils.getFileUrl(Constants.fileTestVideoEncryptUrl)) .readAsBytesSync(), drmConfiguration: BetterPlayerDrmConfiguration( - drmType: BetterPlayerDrmType.clearKey, - clearKey: BetterPlayerClearKeyUtils.generateKey({ - "f3c5e0361e6654b28f8049c778b23946": - "a4631a153a443df9eed0593043db7519", - "abba271e8bcf552bbd2e86a434a9a5d9": - "69eaa802a6763af979e8d1940fb88392" - })), + drmType: BetterPlayerDrmType.clearKey, + clearKey: BetterPlayerClearKeyUtils.generateKey({ + 'f3c5e0361e6654b28f8049c778b23946': + 'a4631a153a443df9eed0593043db7519', + 'abba271e8bcf552bbd2e86a434a9a5d9': + '69eaa802a6763af979e8d1940fb88392', + }), + ), ); - _clearKeyControllerMemory.setupDataSource(_clearKeyDataSourceMemory); + _clearKeyControllerMemory.setupDataSource(clearKeyDataSourceMemory); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("ClearKey DRM"), + title: const Text('ClearKey DRM'), ), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "ClearKey Protection with valid key.", + 'ClearKey Protection with valid key.', style: TextStyle(fontSize: 16), ), ), @@ -123,10 +127,10 @@ class _ClearKeyState extends State { child: BetterPlayer(controller: _clearKeyControllerFile), ), const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "ClearKey Protection with invalid key.", + 'ClearKey Protection with invalid key.', style: TextStyle(fontSize: 16), ), ), @@ -135,10 +139,10 @@ class _ClearKeyState extends State { child: BetterPlayer(controller: _clearKeyControllerBroken), ), const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "ClearKey Protection Network with valid key.", + 'ClearKey Protection Network with valid key.', style: TextStyle(fontSize: 16), ), ), @@ -147,10 +151,10 @@ class _ClearKeyState extends State { child: BetterPlayer(controller: _clearKeyControllerNetwork), ), const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "ClearKey Protection Asset with valid key.", + 'ClearKey Protection Asset with valid key.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/controller_controls_page.dart b/example/lib/pages/controller_controls_page.dart index 74c57a9da..81ea4e659 100644 --- a/example/lib/pages/controller_controls_page.dart +++ b/example/lib/pages/controller_controls_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class ControllerControlsPage extends StatefulWidget { + const ControllerControlsPage({super.key}); + @override _ControllerControlsPageState createState() => _ControllerControlsPageState(); } @@ -12,13 +14,14 @@ class _ControllerControlsPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.elephantDreamVideoUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); super.initState(); @@ -28,16 +31,16 @@ class _ControllerControlsPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Controller controls"), + title: const Text('Controller controls'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Control player with BetterPlayerController. You can control all" - "aspects of player without using UI of player.", + 'Control player with BetterPlayerController. You can control all' + 'aspects of player without using UI of player.', style: TextStyle(fontSize: 16), ), ), @@ -48,24 +51,27 @@ class _ControllerControlsPageState extends State { Wrap( children: [ TextButton( - child: Text("Play"), onPressed: _betterPlayerController.play), + onPressed: _betterPlayerController.play, + child: const Text('Play'), + ), TextButton( - child: Text("Pause"), - onPressed: _betterPlayerController.pause), + onPressed: _betterPlayerController.pause, + child: const Text('Pause'), + ), TextButton( - child: Text("Hide controls"), + child: const Text('Hide controls'), onPressed: () { _betterPlayerController.setControlsVisibility(false); }, ), TextButton( - child: Text("Show controls"), + child: const Text('Show controls'), onPressed: () { _betterPlayerController.setControlsVisibility(true); }, ), ], - ) + ), ], ), ); diff --git a/example/lib/pages/controls_always_visible_page.dart b/example/lib/pages/controls_always_visible_page.dart index 937aa00b8..3637ecce0 100644 --- a/example/lib/pages/controls_always_visible_page.dart +++ b/example/lib/pages/controls_always_visible_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class ControlsAlwaysVisiblePage extends StatefulWidget { + const ControlsAlwaysVisiblePage({super.key}); + @override _ControlsAlwaysVisiblePageState createState() => _ControlsAlwaysVisiblePageState(); @@ -13,19 +15,17 @@ class _ControlsAlwaysVisiblePageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, - handleLifecycle: true, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _setupDataSource(); super.initState(); } - void _setupDataSource() async { - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + Future _setupDataSource() async { + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl, ); @@ -36,16 +36,16 @@ class _ControlsAlwaysVisiblePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Controls always visible"), + title: const Text('Controls always visible'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Controls are always visible. Click on button below to" - " enable/disable this mode.", + 'Controls are always visible. Click on button below to' + ' enable/disable this mode.', style: TextStyle(fontSize: 16), ), ), @@ -54,11 +54,13 @@ class _ControlsAlwaysVisiblePageState extends State { child: BetterPlayer(controller: _betterPlayerController), ), ElevatedButton( - onPressed: () { - _betterPlayerController.setControlsAlwaysVisible( - !(_betterPlayerController.controlsAlwaysVisible)); - }, - child: Text("Toggle always visible controls")) + onPressed: () { + _betterPlayerController.setControlsAlwaysVisible( + !_betterPlayerController.controlsAlwaysVisible, + ); + }, + child: const Text('Toggle always visible controls'), + ), ], ), ); diff --git a/example/lib/pages/controls_configuration_page.dart b/example/lib/pages/controls_configuration_page.dart index 584b02bfb..c25076708 100644 --- a/example/lib/pages/controls_configuration_page.dart +++ b/example/lib/pages/controls_configuration_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class ControlsConfigurationPage extends StatefulWidget { + const ControlsConfigurationPage({super.key}); + @override _ControlsConfigurationPageState createState() => _ControlsConfigurationPageState(); @@ -13,8 +15,7 @@ class _ControlsConfigurationPageState extends State { @override void initState() { - BetterPlayerControlsConfiguration controlsConfiguration = - BetterPlayerControlsConfiguration( + final controlsConfiguration = BetterPlayerControlsConfiguration( controlBarColor: Colors.indigoAccent.withAlpha(200), iconsColor: Colors.lightGreen, playIcon: Icons.forward, @@ -29,13 +30,15 @@ class _ControlsConfigurationPageState extends State { overflowMenuIconsColor: Colors.white, ); - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( - aspectRatio: 16 / 9, - fit: BoxFit.contain, - controlsConfiguration: controlsConfiguration); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl); + final betterPlayerConfiguration = BetterPlayerConfiguration( + aspectRatio: 16 / 9, + fit: BoxFit.contain, + controlsConfiguration: controlsConfiguration, + ); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.elephantDreamVideoUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); super.initState(); @@ -45,15 +48,15 @@ class _ControlsConfigurationPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Controls configuration"), + title: const Text('Controls configuration'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with customized controls via BetterPlayerControlsConfiguration.", + 'Player with customized controls via BetterPlayerControlsConfiguration.', style: TextStyle(fontSize: 16), ), ), @@ -65,13 +68,14 @@ class _ControlsConfigurationPageState extends State { onPressed: () { setState(() { _betterPlayerController.setBetterPlayerControlsConfiguration( - BetterPlayerControlsConfiguration( - overflowModalColor: Colors.amberAccent), + const BetterPlayerControlsConfiguration( + overflowModalColor: Colors.amberAccent, + ), ); }); }, - child: Text("Reset settings"), - ) + child: const Text('Reset settings'), + ), ], ), ); diff --git a/example/lib/pages/custom_controls/change_player_theme_page.dart b/example/lib/pages/custom_controls/change_player_theme_page.dart index 5a3e7be6b..a33090940 100644 --- a/example/lib/pages/custom_controls/change_player_theme_page.dart +++ b/example/lib/pages/custom_controls/change_player_theme_page.dart @@ -4,6 +4,8 @@ import 'package:example/pages/custom_controls/custom_controls_widget.dart'; import 'package:flutter/material.dart'; class ChangePlayerThemePage extends StatefulWidget { + const ChangePlayerThemePage({super.key}); + @override _ChangePlayerThemePageState createState() => _ChangePlayerThemePageState(); } @@ -16,12 +18,11 @@ class _ChangePlayerThemePageState extends State { @override void initState() { super.initState(); - String url = Constants.bugBuckBunnyVideoUrl; + const url = Constants.bugBuckBunnyVideoUrl; _dataSource = BetterPlayerDataSource(BetterPlayerDataSourceType.network, url); - _betterPlayerController = new BetterPlayerController( + _betterPlayerController = BetterPlayerController( BetterPlayerConfiguration( - autoDispose: true, controlsConfiguration: BetterPlayerControlsConfiguration( playerTheme: _playerTheme, ), @@ -34,17 +35,17 @@ class _ChangePlayerThemePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Change player theme"), + title: const Text('Change player theme'), ), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with the possibility to change the theme. Click on " - "buttons below to change theme of player.", + 'Player with the possibility to change the theme. Click on ' + 'buttons below to change theme of player.', style: TextStyle(fontSize: 16), ), ), @@ -55,14 +56,13 @@ class _ChangePlayerThemePageState extends State { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ MaterialButton( - child: Text("MATERIAL"), + child: const Text('MATERIAL'), onPressed: () { setState(() { _playerTheme = BetterPlayerTheme.material; _betterPlayerController.pause(); - _betterPlayerController = new BetterPlayerController( + _betterPlayerController = BetterPlayerController( BetterPlayerConfiguration( - autoDispose: true, controlsConfiguration: BetterPlayerControlsConfiguration( playerTheme: _playerTheme, @@ -74,14 +74,13 @@ class _ChangePlayerThemePageState extends State { }, ), MaterialButton( - child: Text("CUPERTINO"), + child: const Text('CUPERTINO'), onPressed: () { setState(() { _playerTheme = BetterPlayerTheme.cupertino; _betterPlayerController.pause(); - _betterPlayerController = new BetterPlayerController( + _betterPlayerController = BetterPlayerController( BetterPlayerConfiguration( - autoDispose: true, controlsConfiguration: BetterPlayerControlsConfiguration( playerTheme: _playerTheme, @@ -93,14 +92,13 @@ class _ChangePlayerThemePageState extends State { }, ), MaterialButton( - child: Text("CUSTOM"), + child: const Text('CUSTOM'), onPressed: () { setState(() { _playerTheme = BetterPlayerTheme.custom; _betterPlayerController.pause(); - _betterPlayerController = new BetterPlayerController( + _betterPlayerController = BetterPlayerController( BetterPlayerConfiguration( - autoDispose: true, controlsConfiguration: BetterPlayerControlsConfiguration( playerTheme: _playerTheme, @@ -119,7 +117,7 @@ class _ChangePlayerThemePageState extends State { }, ), ], - ) + ), ], ), ), diff --git a/example/lib/pages/custom_controls/custom_controls_widget.dart b/example/lib/pages/custom_controls/custom_controls_widget.dart index 1eb230ac0..7d98ae087 100644 --- a/example/lib/pages/custom_controls/custom_controls_widget.dart +++ b/example/lib/pages/custom_controls/custom_controls_widget.dart @@ -2,14 +2,13 @@ import 'package:better_player/better_player.dart'; import 'package:flutter/material.dart'; class CustomControlsWidget extends StatefulWidget { - final BetterPlayerController? controller; - final Function(bool visbility)? onControlsVisibilityChanged; - const CustomControlsWidget({ - Key? key, + super.key, this.controller, this.onControlsVisibilityChanged, - }) : super(key: key); + }); + final BetterPlayerController? controller; + final Function(bool visbility)? onControlsVisibilityChanged; @override _CustomControlsWidgetState createState() => _CustomControlsWidgetState(); @@ -25,7 +24,7 @@ class _CustomControlsWidgetState extends State { Align( alignment: Alignment.topLeft, child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: InkWell( child: Container( decoration: BoxDecoration( @@ -33,7 +32,7 @@ class _CustomControlsWidgetState extends State { borderRadius: BorderRadius.circular(15), ), child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Icon( widget.controller!.isFullScreen ? Icons.fullscreen_exit @@ -44,16 +43,17 @@ class _CustomControlsWidgetState extends State { ), ), onTap: () => setState(() { - if (widget.controller!.isFullScreen) + if (widget.controller!.isFullScreen) { widget.controller!.exitFullScreen(); - else + } else { widget.controller!.enterFullScreen(); + } }), ), ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Container( height: 50, decoration: BoxDecoration( @@ -63,30 +63,30 @@ class _CustomControlsWidgetState extends State { child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ InkWell( onTap: () async { - Duration? videoDuration = await widget + final videoDuration = await widget .controller!.videoPlayerController!.position; setState(() { if (widget.controller!.isPlaying()!) { - Duration rewindDuration = Duration( - seconds: (videoDuration!.inSeconds - 2)); + final rewindDuration = Duration( + seconds: videoDuration!.inSeconds - 2, + ); if (rewindDuration < widget.controller!.videoPlayerController! .value.duration!) { - widget.controller!.seekTo(Duration(seconds: 0)); + widget.controller!.seekTo(const Duration()); } else { widget.controller!.seekTo(rewindDuration); } } }); }, - child: Icon( + child: const Icon( Icons.fast_rewind, color: Colors.white, ), @@ -94,10 +94,11 @@ class _CustomControlsWidgetState extends State { InkWell( onTap: () { setState(() { - if (widget.controller!.isPlaying()!) + if (widget.controller!.isPlaying()!) { widget.controller!.pause(); - else + } else { widget.controller!.play(); + } }); }, child: Icon( @@ -109,16 +110,17 @@ class _CustomControlsWidgetState extends State { ), InkWell( onTap: () async { - Duration? videoDuration = await widget + final videoDuration = await widget .controller!.videoPlayerController!.position; setState(() { if (widget.controller!.isPlaying()!) { - Duration forwardDuration = Duration( - seconds: (videoDuration!.inSeconds + 2)); + final forwardDuration = Duration( + seconds: videoDuration!.inSeconds + 2, + ); if (forwardDuration > widget.controller!.videoPlayerController! .value.duration!) { - widget.controller!.seekTo(Duration(seconds: 0)); + widget.controller!.seekTo(const Duration()); widget.controller!.pause(); } else { widget.controller!.seekTo(forwardDuration); @@ -126,7 +128,7 @@ class _CustomControlsWidgetState extends State { } }); }, - child: Icon( + child: const Icon( Icons.fast_forward, color: Colors.white, ), diff --git a/example/lib/pages/dash_page.dart b/example/lib/pages/dash_page.dart index ce967028e..684010c4b 100644 --- a/example/lib/pages/dash_page.dart +++ b/example/lib/pages/dash_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class DashPage extends StatefulWidget { + const DashPage({super.key}); + @override _DashPageState createState() => _DashPageState(); } @@ -12,14 +14,14 @@ class _DashPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.dashStreamUrl, - useAsmsSubtitles: true, useAsmsTracks: true); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.dashStreamUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); super.initState(); @@ -29,15 +31,15 @@ class _DashPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Dash page"), + title: const Text('Dash page'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with DASH audio tracks, subtitles and tracks.", + 'Player with DASH audio tracks, subtitles and tracks.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/drm_page.dart b/example/lib/pages/drm_page.dart index 0aa74d959..8558e307c 100644 --- a/example/lib/pages/drm_page.dart +++ b/example/lib/pages/drm_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class DrmPage extends StatefulWidget { + const DrmPage({super.key}); + @override _DrmPageState createState() => _DrmPageState(); } @@ -14,35 +16,36 @@ class _DrmPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource _tokenDataSource = BetterPlayerDataSource( + final tokenDataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.tokenEncodedHlsUrl, videoFormat: BetterPlayerVideoFormat.hls, drmConfiguration: BetterPlayerDrmConfiguration( - drmType: BetterPlayerDrmType.token, - token: Constants.tokenEncodedHlsToken), + drmType: BetterPlayerDrmType.token, + token: Constants.tokenEncodedHlsToken, + ), ); _tokenController = BetterPlayerController(betterPlayerConfiguration); - _tokenController.setupDataSource(_tokenDataSource); + _tokenController.setupDataSource(tokenDataSource); _widevineController = BetterPlayerController(betterPlayerConfiguration); - BetterPlayerDataSource _widevineDataSource = BetterPlayerDataSource( + final widevineDataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.widevineVideoUrl, drmConfiguration: BetterPlayerDrmConfiguration( - drmType: BetterPlayerDrmType.widevine, - licenseUrl: Constants.widevineLicenseUrl, - headers: {"Test": "Test2"}), + drmType: BetterPlayerDrmType.widevine, + licenseUrl: Constants.widevineLicenseUrl, + headers: {'Test': 'Test2'}, + ), ); - _widevineController.setupDataSource(_widevineDataSource); + _widevineController.setupDataSource(widevineDataSource); _fairplayController = BetterPlayerController(betterPlayerConfiguration); - BetterPlayerDataSource _fairplayDataSource = BetterPlayerDataSource( + final fairplayDataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.fairplayHlsUrl, drmConfiguration: BetterPlayerDrmConfiguration( @@ -51,7 +54,7 @@ class _DrmPageState extends State { licenseUrl: Constants.fairplayLicenseUrl, ), ); - _fairplayController.setupDataSource(_fairplayDataSource); + _fairplayController.setupDataSource(fairplayDataSource); super.initState(); } @@ -60,16 +63,16 @@ class _DrmPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("DRM player"), + title: const Text('DRM player'), ), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Auth token based DRM.", + 'Auth token based DRM.', style: TextStyle(fontSize: 16), ), ), @@ -78,10 +81,10 @@ class _DrmPageState extends State { child: BetterPlayer(controller: _tokenController), ), const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Widevine - license url based DRM. Works only for Android.", + 'Widevine - license url based DRM. Works only for Android.', style: TextStyle(fontSize: 16), ), ), @@ -90,10 +93,10 @@ class _DrmPageState extends State { child: BetterPlayer(controller: _widevineController), ), const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Fairplay - certificate url based EZDRM. Works only for iOS.", + 'Fairplay - certificate url based EZDRM. Works only for iOS.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/event_listener_page.dart b/example/lib/pages/event_listener_page.dart index f6b39626a..9900766a2 100644 --- a/example/lib/pages/event_listener_page.dart +++ b/example/lib/pages/event_listener_page.dart @@ -5,6 +5,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class EventListenerPage extends StatefulWidget { + const EventListenerPage({super.key}); + @override _EventListenerPageState createState() => _EventListenerPageState(); } @@ -12,18 +14,19 @@ class EventListenerPage extends StatefulWidget { class _EventListenerPageState extends State { late BetterPlayerController _betterPlayerController; List events = []; - StreamController _eventStreamController = + final StreamController _eventStreamController = StreamController.broadcast(); @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.elephantDreamVideoUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); _betterPlayerController.addEventsListener(_handleEvent); @@ -48,16 +51,16 @@ class _EventListenerPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Event listener"), + title: const Text('Event listener'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Better Player exposes events which can be listened with event " - "listener. Start player to see events flowing.", + 'Better Player exposes events which can be listened with event ' + 'listener. Start player to see events flowing.', style: TextStyle(fontSize: 16), ), ), @@ -77,9 +80,9 @@ class _EventListenerPageState extends State { (event) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Event: ${event.betterPlayerEventType} " - "parameters: ${(event.parameters ?? {}).toString()}"), - Divider(), + Text('Event: ${event.betterPlayerEventType} ' + 'parameters: ${event.parameters ?? {}}'), + const Divider(), ], ), ) @@ -87,7 +90,7 @@ class _EventListenerPageState extends State { ); }, ), - ) + ), ], ), ); diff --git a/example/lib/pages/fade_placeholder_page.dart b/example/lib/pages/fade_placeholder_page.dart index 467a284a2..2681d95a5 100644 --- a/example/lib/pages/fade_placeholder_page.dart +++ b/example/lib/pages/fade_placeholder_page.dart @@ -5,25 +5,26 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class FadePlaceholderPage extends StatefulWidget { + const FadePlaceholderPage({super.key}); + @override _FadePlaceholderPageState createState() => _FadePlaceholderPageState(); } class _FadePlaceholderPageState extends State { late BetterPlayerController _betterPlayerController; - StreamController _playController = StreamController.broadcast(); + final StreamController _playController = StreamController.broadcast(); @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + final betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, placeholder: _buildPlaceholder(), showPlaceholderUntilPlay: true, placeholderOnTop: false, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl, ); @@ -41,9 +42,9 @@ class _FadePlaceholderPageState extends State { return StreamBuilder( stream: _playController.stream, builder: (context, snapshot) { - bool showPlaceholder = snapshot.data ?? true; + final showPlaceholder = snapshot.data ?? true; return AnimatedOpacity( - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), opacity: showPlaceholder ? 1.0 : 0.0, child: AspectRatio( aspectRatio: 16 / 9, @@ -61,15 +62,15 @@ class _FadePlaceholderPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Fade placeholder player"), + title: const Text('Fade placeholder player'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Normal player with placeholder which fade.", + 'Normal player with placeholder which fade.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/hls_audio_page.dart b/example/lib/pages/hls_audio_page.dart index a766e9030..f3a8b1d0a 100644 --- a/example/lib/pages/hls_audio_page.dart +++ b/example/lib/pages/hls_audio_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class HlsAudioPage extends StatefulWidget { + const HlsAudioPage({super.key}); + @override _HlsAudioPageState createState() => _HlsAudioPageState(); } @@ -12,12 +14,11 @@ class _HlsAudioPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.elephantDreamStreamUrl, ); @@ -30,17 +31,17 @@ class _HlsAudioPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("HLS Audio"), + title: const Text('HLS Audio'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Click on overflow menu (3 dots) and select Audio. You can choose " - "audio track from HLS stream. Better Player will setup audio" - " automatically for you.", + 'Click on overflow menu (3 dots) and select Audio. You can choose ' + 'audio track from HLS stream. Better Player will setup audio' + ' automatically for you.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/hls_subtitles_page.dart b/example/lib/pages/hls_subtitles_page.dart index 3c1a1c0e6..1afdfe679 100644 --- a/example/lib/pages/hls_subtitles_page.dart +++ b/example/lib/pages/hls_subtitles_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class HlsSubtitlesPage extends StatefulWidget { + const HlsSubtitlesPage({super.key}); + @override _HlsSubtitlesPageState createState() => _HlsSubtitlesPageState(); } @@ -12,27 +14,10 @@ class _HlsSubtitlesPageState extends State { @override void initState() { - BetterPlayerControlsConfiguration controlsConfiguration = - BetterPlayerControlsConfiguration( + const controlsConfiguration = BetterPlayerControlsConfiguration( controlBarColor: Colors.black26, - iconsColor: Colors.white, - playIcon: Icons.play_arrow_outlined, progressBarPlayedColor: Colors.indigo, progressBarHandleColor: Colors.indigo, - skipBackIcon: Icons.replay_10_outlined, - skipForwardIcon: Icons.forward_10_outlined, - backwardSkipTimeInMilliseconds: 10000, - forwardSkipTimeInMilliseconds: 10000, - enableSkips: true, - enableFullscreen: true, - enablePip: true, - enablePlayPause: true, - enableMute: true, - enableAudioTracks: true, - enableProgressText: true, - enableSubtitles: true, - showControlsOnInitialize: true, - enablePlaybackSpeed: true, controlBarHeight: 40, loadingColor: Colors.red, overflowModalColor: Colors.black54, @@ -40,17 +25,18 @@ class _HlsSubtitlesPageState extends State { overflowMenuIconsColor: Colors.white, ); - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( - controlsConfiguration: controlsConfiguration, - aspectRatio: 16 / 9, - fit: BoxFit.contain, - subtitlesConfiguration: BetterPlayerSubtitlesConfiguration( - fontSize: 16.0, - )); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.hlsPlaylistUrl, - useAsmsSubtitles: true); + final betterPlayerConfiguration = const BetterPlayerConfiguration( + controlsConfiguration: controlsConfiguration, + aspectRatio: 16 / 9, + fit: BoxFit.contain, + subtitlesConfiguration: BetterPlayerSubtitlesConfiguration( + fontSize: 16, + ), + ); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.hlsPlaylistUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); super.initState(); @@ -60,17 +46,17 @@ class _HlsSubtitlesPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("HLS subtitles"), + title: const Text('HLS subtitles'), ), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with HLS stream which loads subtitles from HLS." - " You can choose subtitles by using overflow menu (3 dots in right corner).", + 'Player with HLS stream which loads subtitles from HLS.' + ' You can choose subtitles by using overflow menu (3 dots in right corner).', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/hls_tracks_page.dart b/example/lib/pages/hls_tracks_page.dart index 12e93be29..58b926576 100644 --- a/example/lib/pages/hls_tracks_page.dart +++ b/example/lib/pages/hls_tracks_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class HlsTracksPage extends StatefulWidget { + const HlsTracksPage({super.key}); + @override _HlsTracksPageState createState() => _HlsTracksPageState(); } @@ -12,15 +14,13 @@ class _HlsTracksPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.hlsTestStreamUrl, - useAsmsSubtitles: true, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); @@ -31,16 +31,16 @@ class _HlsTracksPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("HLS tracks"), + title: const Text('HLS tracks'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with HLS stream which loads tracks from HLS." - " You can choose tracks by using overflow menu (3 dots in right corner).", + 'Player with HLS stream which loads tracks from HLS.' + ' You can choose tracks by using overflow menu (3 dots in right corner).', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/memory_player_page.dart b/example/lib/pages/memory_player_page.dart index 8ce1adca6..9618ca403 100644 --- a/example/lib/pages/memory_player_page.dart +++ b/example/lib/pages/memory_player_page.dart @@ -6,6 +6,8 @@ import 'package:example/utils.dart'; import 'package:flutter/material.dart'; class MemoryPlayerPage extends StatefulWidget { + const MemoryPlayerPage({super.key}); + @override _MemoryPlayerPageState createState() => _MemoryPlayerPageState(); } @@ -15,8 +17,7 @@ class _MemoryPlayerPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); @@ -26,13 +27,13 @@ class _MemoryPlayerPageState extends State { super.initState(); } - void _setupDataSource() async { - var filePath = await Utils.getFileUrl(Constants.fileTestVideoUrl); - File file = File(filePath); + Future _setupDataSource() async { + final filePath = await Utils.getFileUrl(Constants.fileTestVideoUrl); + final file = File(filePath); - List bytes = file.readAsBytesSync().buffer.asUint8List(); - BetterPlayerDataSource dataSource = - BetterPlayerDataSource.memory(bytes, videoExtension: "mp4"); + final List bytes = file.readAsBytesSync().buffer.asUint8List(); + final dataSource = + BetterPlayerDataSource.memory(bytes, videoExtension: 'mp4'); _betterPlayerController.setupDataSource(dataSource); } @@ -40,16 +41,16 @@ class _MemoryPlayerPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Memory player"), + title: const Text('Memory player'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Memory player with plays video from bytes list. In this example" - "file bytes are read to list and then used in player.", + 'Memory player with plays video from bytes list. In this example' + 'file bytes are read to list and then used in player.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/normal_player_page.dart b/example/lib/pages/normal_player_page.dart index 61549f3c7..3a8fd7d8b 100644 --- a/example/lib/pages/normal_player_page.dart +++ b/example/lib/pages/normal_player_page.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class NormalPlayerPage extends StatefulWidget { + const NormalPlayerPage({super.key}); + @override _NormalPlayerPageState createState() => _NormalPlayerPageState(); } @@ -14,15 +16,14 @@ class _NormalPlayerPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, autoPlay: true, looping: true, deviceOrientationsAfterFullScreen: [ DeviceOrientation.portraitDown, - DeviceOrientation.portraitUp + DeviceOrientation.portraitUp, ], ); _betterPlayerDataSource = BetterPlayerDataSource( @@ -38,7 +39,7 @@ class _NormalPlayerPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Normal player page"), + title: const Text('Normal player page'), ), body: Column( children: [ diff --git a/example/lib/pages/notification_player_page.dart b/example/lib/pages/notification_player_page.dart index 9d772c80f..ca3d644d9 100644 --- a/example/lib/pages/notification_player_page.dart +++ b/example/lib/pages/notification_player_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class NotificationPlayerPage extends StatefulWidget { + const NotificationPlayerPage({super.key}); + @override _NotificationPlayerPageState createState() => _NotificationPlayerPageState(); } @@ -12,26 +14,24 @@ class _NotificationPlayerPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, - handleLifecycle: true, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _setupDataSource(); super.initState(); } - void _setupDataSource() async { + Future _setupDataSource() async { // String imageUrl = await Utils.getFileUrl(Constants.logo); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl, notificationConfiguration: BetterPlayerNotificationConfiguration( showNotification: true, - title: "Elephant dream", - author: "Some author", + title: 'Elephant dream', + author: 'Some author', imageUrl: Constants.catImageUrl, ), ); @@ -42,15 +42,15 @@ class _NotificationPlayerPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Notification player"), + title: const Text('Notification player'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Click play on player to show notification in status bar.", + 'Click play on player to show notification in status bar.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/overridden_aspect_ratio_page.dart b/example/lib/pages/overridden_aspect_ratio_page.dart index 2dba62e7b..581bff439 100644 --- a/example/lib/pages/overridden_aspect_ratio_page.dart +++ b/example/lib/pages/overridden_aspect_ratio_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class OverriddenAspectRatioPage extends StatefulWidget { + const OverriddenAspectRatioPage({super.key}); + @override _OverriddenAspectRatioPageState createState() => _OverriddenAspectRatioPageState(); @@ -13,16 +15,16 @@ class _OverriddenAspectRatioPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, - fit: BoxFit.fill, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.forBiggerBlazesUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); - _betterPlayerController.setOverriddenAspectRatio(1.0); + _betterPlayerController.setOverriddenAspectRatio(1); super.initState(); } @@ -30,20 +32,20 @@ class _OverriddenAspectRatioPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Overridden aspect ratio"), + title: const Text('Overridden aspect ratio'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with different rotation and fit.", + 'Player with different rotation and fit.', style: TextStyle(fontSize: 16), ), ), AspectRatio( - aspectRatio: 1.0, + aspectRatio: 1, child: BetterPlayer(controller: _betterPlayerController), ), ], diff --git a/example/lib/pages/overriden_duration_page.dart b/example/lib/pages/overriden_duration_page.dart index 8a9b308a4..063cafeb9 100644 --- a/example/lib/pages/overriden_duration_page.dart +++ b/example/lib/pages/overriden_duration_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class OverriddenDurationPage extends StatefulWidget { + const OverriddenDurationPage({super.key}); + @override _OverriddenDurationPageState createState() => _OverriddenDurationPageState(); } @@ -12,24 +14,22 @@ class _OverriddenDurationPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, - handleLifecycle: true, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _setupDataSource(); super.initState(); } - void _setupDataSource() async { - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + Future _setupDataSource() async { + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl, ///Play only 10 seconds of this video. - overriddenDuration: Duration(seconds: 10), + overriddenDuration: const Duration(seconds: 10), ); _betterPlayerController.setupDataSource(dataSource); } @@ -38,16 +38,16 @@ class _OverriddenDurationPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Overridden duration"), + title: const Text('Overridden duration'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Duration of this video is overridden. Now this video will have" - " 10 seconds only.", + 'Duration of this video is overridden. Now this video will have' + ' 10 seconds only.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/picture_in_picture_page.dart b/example/lib/pages/picture_in_picture_page.dart index 9c58dc09b..50096db1b 100644 --- a/example/lib/pages/picture_in_picture_page.dart +++ b/example/lib/pages/picture_in_picture_page.dart @@ -3,22 +3,23 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class PictureInPicturePage extends StatefulWidget { + const PictureInPicturePage({super.key}); + @override _PictureInPicturePageState createState() => _PictureInPicturePageState(); } class _PictureInPicturePageState extends State { late BetterPlayerController _betterPlayerController; - GlobalKey _betterPlayerKey = GlobalKey(); + final GlobalKey _betterPlayerKey = GlobalKey(); @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl, ); @@ -32,15 +33,15 @@ class _PictureInPicturePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Picture in Picture player"), + title: const Text('Picture in Picture player'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Example which shows how to use PiP.", + 'Example which shows how to use PiP.', style: TextStyle(fontSize: 16), ), ), @@ -52,13 +53,13 @@ class _PictureInPicturePageState extends State { ), ), ElevatedButton( - child: Text("Show PiP"), + child: const Text('Show PiP'), onPressed: () { _betterPlayerController.enablePictureInPicture(_betterPlayerKey); }, ), ElevatedButton( - child: Text("Disable PiP"), + child: const Text('Disable PiP'), onPressed: () async { _betterPlayerController.disablePictureInPicture(); }, diff --git a/example/lib/pages/placeholder_until_play_page.dart b/example/lib/pages/placeholder_until_play_page.dart index 533c567a4..0215f38b5 100644 --- a/example/lib/pages/placeholder_until_play_page.dart +++ b/example/lib/pages/placeholder_until_play_page.dart @@ -5,6 +5,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class PlaceholderUntilPlayPage extends StatefulWidget { + const PlaceholderUntilPlayPage({super.key}); + @override _PlaceholderUntilPlayPageState createState() => _PlaceholderUntilPlayPageState(); @@ -12,7 +14,7 @@ class PlaceholderUntilPlayPage extends StatefulWidget { class _PlaceholderUntilPlayPageState extends State { late BetterPlayerController _betterPlayerController; - StreamController _placeholderStreamController = + final StreamController _placeholderStreamController = StreamController.broadcast(); bool _showPlaceholder = true; @@ -24,13 +26,12 @@ class _PlaceholderUntilPlayPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + final betterPlayerConfiguration = BetterPlayerConfiguration( fit: BoxFit.contain, placeholder: _buildVideoPlaceholder(), showPlaceholderUntilPlay: true, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.elephantDreamVideoUrl, ); @@ -66,15 +67,15 @@ class _PlaceholderUntilPlayPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Placeholder until play"), + title: const Text('Placeholder until play'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Normal player with placeholder shown until video is started.", + 'Normal player with placeholder shown until video is started.', style: TextStyle(fontSize: 16), ), ), diff --git a/example/lib/pages/playlist_page.dart b/example/lib/pages/playlist_page.dart index 54487cc49..a5bb32ae1 100644 --- a/example/lib/pages/playlist_page.dart +++ b/example/lib/pages/playlist_page.dart @@ -5,22 +5,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class PlaylistPage extends StatefulWidget { + const PlaylistPage({super.key}); + @override _PlaylistPageState createState() => _PlaylistPageState(); } class _PlaylistPageState extends State { - final GlobalKey _betterPlayerPlaylistStateKey = - GlobalKey(); - List _dataSourceList = []; - late BetterPlayerConfiguration _betterPlayerConfiguration; - late BetterPlayerPlaylistConfiguration _betterPlayerPlaylistConfiguration; - _PlaylistPageState() { - _betterPlayerConfiguration = BetterPlayerConfiguration( + _betterPlayerConfiguration = const BetterPlayerConfiguration( aspectRatio: 1, fit: BoxFit.cover, - placeholderOnTop: true, showPlaceholderUntilPlay: true, subtitlesConfiguration: BetterPlayerSubtitlesConfiguration(fontSize: 10), deviceOrientationsAfterFullScreen: [ @@ -28,24 +23,29 @@ class _PlaylistPageState extends State { DeviceOrientation.portraitDown, ], ); - _betterPlayerPlaylistConfiguration = BetterPlayerPlaylistConfiguration( - loopVideos: true, - nextVideoDelay: Duration(seconds: 3), - ); + _betterPlayerPlaylistConfiguration = + const BetterPlayerPlaylistConfiguration(); } + final GlobalKey _betterPlayerPlaylistStateKey = + GlobalKey(); + final List _dataSourceList = []; + late BetterPlayerConfiguration _betterPlayerConfiguration; + late BetterPlayerPlaylistConfiguration _betterPlayerPlaylistConfiguration; Future> setupData() async { _dataSourceList.add( BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl, - subtitles: BetterPlayerSubtitlesSource.single( - type: BetterPlayerSubtitlesSourceType.file, - url: await Utils.getFileUrl(Constants.fileExampleSubtitlesUrl), - ), - placeholder: Image.network( - Constants.catImageUrl, - fit: BoxFit.cover, - )), + BetterPlayerDataSourceType.network, + Constants.forBiggerBlazesUrl, + subtitles: BetterPlayerSubtitlesSource.single( + type: BetterPlayerSubtitlesSourceType.file, + url: await Utils.getFileUrl(Constants.fileExampleSubtitlesUrl), + ), + placeholder: Image.network( + Constants.catImageUrl, + fit: BoxFit.cover, + ), + ), ); _dataSourceList.add( @@ -72,75 +72,78 @@ class _PlaylistPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Playlist"), + title: const Text('Playlist'), ), body: FutureBuilder>( future: setupData(), builder: (context, snapshot) { if (!snapshot.hasData) { - return Text("Building!"); + return const Text('Building!'); } else { - return ListView(children: [ - Padding( - padding: EdgeInsets.all(8), - child: Text( - "Playlist widget will load automatically next video once current " - "finishes. User can't use player controls when video is changing."), - ), - AspectRatio( - child: BetterPlayerPlaylist( - key: _betterPlayerPlaylistStateKey, - betterPlayerConfiguration: _betterPlayerConfiguration, - betterPlayerPlaylistConfiguration: - _betterPlayerPlaylistConfiguration, - betterPlayerDataSourceList: snapshot.data!, + return ListView( + children: [ + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Playlist widget will load automatically next video once current ' + "finishes. User can't use player controls when video is changing."), ), - aspectRatio: 1, - ), - ElevatedButton( - onPressed: () { - _betterPlayerPlaylistController!.setupDataSource(0); - }, - child: Text("Change to first data source"), - ), - ElevatedButton( - onPressed: () { - _betterPlayerPlaylistController!.setupDataSource(2); - }, - child: Text("Change to last source"), - ), - ElevatedButton( - onPressed: () { - print("Currently playing video: " + - _betterPlayerPlaylistController!.currentDataSourceIndex - .toString()); - }, - child: Text("Check currently playing video index"), - ), - ElevatedButton( - onPressed: () { - _betterPlayerPlaylistController!.betterPlayerController! - .pause(); - }, - child: Text("Pause current video with BetterPlayerController"), - ), - ElevatedButton( - onPressed: () { - var list = [ - BetterPlayerDataSource( - BetterPlayerDataSourceType.network, - Constants.bugBuckBunnyVideoUrl, - placeholder: Image.network( - Constants.catImageUrl, - fit: BoxFit.cover, + AspectRatio( + aspectRatio: 1, + child: BetterPlayerPlaylist( + key: _betterPlayerPlaylistStateKey, + betterPlayerConfiguration: _betterPlayerConfiguration, + betterPlayerPlaylistConfiguration: + _betterPlayerPlaylistConfiguration, + betterPlayerDataSourceList: snapshot.data!, + ), + ), + ElevatedButton( + onPressed: () { + _betterPlayerPlaylistController!.setupDataSource(0); + }, + child: const Text('Change to first data source'), + ), + ElevatedButton( + onPressed: () { + _betterPlayerPlaylistController!.setupDataSource(2); + }, + child: const Text('Change to last source'), + ), + ElevatedButton( + onPressed: () { + print( + 'Currently playing video: ${_betterPlayerPlaylistController!.currentDataSourceIndex}', + ); + }, + child: const Text('Check currently playing video index'), + ), + ElevatedButton( + onPressed: () { + _betterPlayerPlaylistController!.betterPlayerController! + .pause(); + }, + child: const Text( + 'Pause current video with BetterPlayerController'), + ), + ElevatedButton( + onPressed: () { + final list = [ + BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.bugBuckBunnyVideoUrl, + placeholder: Image.network( + Constants.catImageUrl, + fit: BoxFit.cover, + ), ), - ) - ]; - _betterPlayerPlaylistController?.setupDataSourceList(list); - }, - child: Text("Setup new data source list"), - ), - ]); + ]; + _betterPlayerPlaylistController?.setupDataSourceList(list); + }, + child: const Text('Setup new data source list'), + ), + ], + ); } }, ), diff --git a/example/lib/pages/resolutions_page.dart b/example/lib/pages/resolutions_page.dart index a5e6899c1..fe7af032d 100644 --- a/example/lib/pages/resolutions_page.dart +++ b/example/lib/pages/resolutions_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class ResolutionsPage extends StatefulWidget { + const ResolutionsPage({super.key}); + @override _ResolutionsPageState createState() => _ResolutionsPageState(); } @@ -12,12 +14,11 @@ class _ResolutionsPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.exampleResolutionsUrls.values.first, resolutions: Constants.exampleResolutionsUrls, @@ -31,23 +32,25 @@ class _ResolutionsPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Resolutions"), + title: const Text('Resolutions'), ), - body: Column(children: [ - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - "Video with different resolutions to select. Click on overflow icon" - " (3 dots in right corner) and select different qualities.", - style: TextStyle(fontSize: 16), + body: Column( + children: [ + const SizedBox(height: 8), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'Video with different resolutions to select. Click on overflow icon' + ' (3 dots in right corner) and select different qualities.', + style: TextStyle(fontSize: 16), + ), + ), + AspectRatio( + aspectRatio: 16 / 9, + child: BetterPlayer(controller: _betterPlayerController), ), - ), - AspectRatio( - aspectRatio: 16 / 9, - child: BetterPlayer(controller: _betterPlayerController), - ), - ]), + ], + ), ); } } diff --git a/example/lib/pages/reusable_video_list/reusable_video_list_controller.dart b/example/lib/pages/reusable_video_list/reusable_video_list_controller.dart index aac6ff523..5d66383d8 100644 --- a/example/lib/pages/reusable_video_list/reusable_video_list_controller.dart +++ b/example/lib/pages/reusable_video_list/reusable_video_list_controller.dart @@ -2,23 +2,23 @@ import 'package:better_player/better_player.dart'; import 'package:collection/collection.dart' show IterableExtension; class ReusableVideoListController { - final List _betterPlayerControllerRegistry = []; - final List _usedBetterPlayerControllerRegistry = []; - ReusableVideoListController() { - for (int index = 0; index < 3; index++) { + for (var index = 0; index < 3; index++) { _betterPlayerControllerRegistry.add( BetterPlayerController( - BetterPlayerConfiguration(handleLifecycle: false, autoDispose: false), + const BetterPlayerConfiguration( + handleLifecycle: false, autoDispose: false), ), ); } } + final List _betterPlayerControllerRegistry = []; + final List _usedBetterPlayerControllerRegistry = []; BetterPlayerController? getBetterPlayerController() { final freeController = _betterPlayerControllerRegistry.firstWhereOrNull( - (controller) => - !_usedBetterPlayerControllerRegistry.contains(controller)); + (controller) => !_usedBetterPlayerControllerRegistry.contains(controller), + ); if (freeController != null) { _usedBetterPlayerControllerRegistry.add(freeController); @@ -28,13 +28,14 @@ class ReusableVideoListController { } void freeBetterPlayerController( - BetterPlayerController? betterPlayerController) { + BetterPlayerController? betterPlayerController, + ) { _usedBetterPlayerControllerRegistry.remove(betterPlayerController); } void dispose() { - _betterPlayerControllerRegistry.forEach((controller) { + for (final controller in _betterPlayerControllerRegistry) { controller.dispose(); - }); + } } } diff --git a/example/lib/pages/reusable_video_list/reusable_video_list_page.dart b/example/lib/pages/reusable_video_list/reusable_video_list_page.dart index c6f44992c..1ea629956 100644 --- a/example/lib/pages/reusable_video_list/reusable_video_list_page.dart +++ b/example/lib/pages/reusable_video_list/reusable_video_list_page.dart @@ -7,6 +7,8 @@ import 'package:example/pages/reusable_video_list/reusable_video_list_widget.dar import 'package:flutter/material.dart'; class ReusableVideoListPage extends StatefulWidget { + const ReusableVideoListPage({super.key}); + @override _ReusableVideoListPageState createState() => _ReusableVideoListPageState(); } @@ -14,13 +16,13 @@ class ReusableVideoListPage extends StatefulWidget { class _ReusableVideoListPageState extends State { ReusableVideoListController videoListController = ReusableVideoListController(); - final _random = new Random(); + final _random = Random(); final List _videos = [ Constants.forBiggerBlazesUrl, Constants.forBiggerJoyridesVideoUrl, ]; List dataList = []; - var value = 0; + int value = 0; final ScrollController _scrollController = ScrollController(); int lastMilli = DateTime.now().millisecondsSinceEpoch; bool _canBuildVideo = true; @@ -32,9 +34,9 @@ class _ReusableVideoListPageState extends State { } void _setupData() { - for (int index = 0; index < 10; index++) { - var randomVideoUrl = _videos[_random.nextInt(_videos.length)]; - dataList.add(VideoListData("Video $index", randomVideoUrl)); + for (var index = 0; index < 10; index++) { + final randomVideoUrl = _videos[_random.nextInt(_videos.length)]; + dataList.add(VideoListData('Video $index', randomVideoUrl)); } } @@ -47,47 +49,49 @@ class _ReusableVideoListPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Reusable video list")), + appBar: AppBar(title: const Text('Reusable video list')), body: Container( color: Colors.grey, - child: Column(children: [ - Expanded( - child: NotificationListener( - onNotification: (notification) { - final now = DateTime.now(); - final timeDiff = now.millisecondsSinceEpoch - lastMilli; - if (notification is ScrollUpdateNotification) { - final pixelsPerMilli = notification.scrollDelta! / timeDiff; - if (pixelsPerMilli.abs() > 1) { - _canBuildVideo = false; - } else { - _canBuildVideo = true; + child: Column( + children: [ + Expanded( + child: NotificationListener( + onNotification: (notification) { + final now = DateTime.now(); + final timeDiff = now.millisecondsSinceEpoch - lastMilli; + if (notification is ScrollUpdateNotification) { + final pixelsPerMilli = notification.scrollDelta! / timeDiff; + if (pixelsPerMilli.abs() > 1) { + _canBuildVideo = false; + } else { + _canBuildVideo = true; + } + lastMilli = DateTime.now().millisecondsSinceEpoch; } - lastMilli = DateTime.now().millisecondsSinceEpoch; - } - if (notification is ScrollEndNotification) { - _canBuildVideo = true; - lastMilli = DateTime.now().millisecondsSinceEpoch; - } + if (notification is ScrollEndNotification) { + _canBuildVideo = true; + lastMilli = DateTime.now().millisecondsSinceEpoch; + } - return true; - }, - child: ListView.builder( - itemCount: dataList.length, - controller: _scrollController, - itemBuilder: (context, index) { - VideoListData videoListData = dataList[index]; - return ReusableVideoListWidget( - videoListData: videoListData, - videoListController: videoListController, - canBuildVideo: _checkCanBuildVideo, - ); + return true; }, + child: ListView.builder( + itemCount: dataList.length, + controller: _scrollController, + itemBuilder: (context, index) { + final videoListData = dataList[index]; + return ReusableVideoListWidget( + videoListData: videoListData, + videoListController: videoListController, + canBuildVideo: _checkCanBuildVideo, + ); + }, + ), ), ), - ) - ]), + ], + ), ), ); } diff --git a/example/lib/pages/reusable_video_list/reusable_video_list_widget.dart b/example/lib/pages/reusable_video_list/reusable_video_list_widget.dart index dbc93d5c3..424f0342f 100644 --- a/example/lib/pages/reusable_video_list/reusable_video_list_widget.dart +++ b/example/lib/pages/reusable_video_list/reusable_video_list_widget.dart @@ -7,16 +7,16 @@ import 'package:flutter/material.dart'; import 'package:visibility_detector/visibility_detector.dart'; class ReusableVideoListWidget extends StatefulWidget { - final VideoListData? videoListData; - final ReusableVideoListController? videoListController; - final Function? canBuildVideo; - const ReusableVideoListWidget({ - Key? key, + super.key, this.videoListData, this.videoListController, this.canBuildVideo, - }) : super(key: key); + }); + + final VideoListData? videoListData; + final ReusableVideoListController? videoListController; + final Function? canBuildVideo; @override _ReusableVideoListWidgetState createState() => @@ -46,10 +46,13 @@ class _ReusableVideoListWidgetState extends State { if (controller == null) { controller = widget.videoListController!.getBetterPlayerController(); if (controller != null) { - controller!.setupDataSource(BetterPlayerDataSource.network( + controller!.setupDataSource( + BetterPlayerDataSource.network( videoListData!.videoUrl, cacheConfiguration: - BetterPlayerCacheConfiguration(useCache: true))); + const BetterPlayerCacheConfiguration(useCache: true), + ), + ); if (!betterPlayerControllerStreamController.isClosed) { betterPlayerControllerStreamController.add(controller); } @@ -77,7 +80,7 @@ class _ReusableVideoListWidgetState extends State { void onPlayerEvent(BetterPlayerEvent event) { if (event.betterPlayerEventType == BetterPlayerEventType.progress) { - videoListData!.lastPosition = event.parameters!["progress"] as Duration?; + videoListData!.lastPosition = event.parameters!['progress'] as Duration?; } if (event.betterPlayerEventType == BetterPlayerEventType.initialized) { if (videoListData!.lastPosition != null) { @@ -94,24 +97,24 @@ class _ReusableVideoListWidgetState extends State { @override Widget build(BuildContext context) { return Card( - margin: EdgeInsets.symmetric(vertical: 16, horizontal: 16), + margin: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Text( videoListData!.videoTitle, - style: TextStyle(fontSize: 50), + style: const TextStyle(fontSize: 50), ), ), VisibilityDetector( key: Key(hashCode.toString() + DateTime.now().toString()), onVisibilityChanged: (info) { - if (!widget.canBuildVideo!()) { + if (!widget.canBuildVideo()) { _timer?.cancel(); _timer = null; - _timer = Timer(Duration(milliseconds: 500), () { + _timer = Timer(const Duration(milliseconds: 500), () { if (info.visibleFraction >= 0.6) { _setupController(); } else { @@ -137,7 +140,7 @@ class _ReusableVideoListWidgetState extends State { ) : Container( color: Colors.black, - child: Center( + child: const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), @@ -148,39 +151,41 @@ class _ReusableVideoListWidgetState extends State { }, ), ), - Padding( + const Padding( padding: EdgeInsets.all(8), child: Text( "Horror: In Steven Spielberg's Jaws, a shark terrorizes a beach " - "town. Plainspoken sheriff Roy Scheider, hippie shark " - "researcher Richard Dreyfuss, and a squirrely boat captain " - "set out to find the beast, but will they escape with their " + 'town. Plainspoken sheriff Roy Scheider, hippie shark ' + 'researcher Richard Dreyfuss, and a squirrely boat captain ' + 'set out to find the beast, but will they escape with their ' "lives? 70's special effects, legendary score, and trademark " - "humor set this classic apart."), + 'humor set this classic apart.'), ), Center( - child: Wrap(children: [ - ElevatedButton( - child: Text("Play"), - onPressed: () { - controller!.play(); - }, - ), - const SizedBox(width: 8), - ElevatedButton( - child: Text("Pause"), - onPressed: () { - controller!.pause(); - }, - ), - const SizedBox(width: 8), - ElevatedButton( - child: Text("Set max volume"), - onPressed: () { - controller!.setVolume(1.0); - }, - ), - ]), + child: Wrap( + children: [ + ElevatedButton( + child: const Text('Play'), + onPressed: () { + controller!.play(); + }, + ), + const SizedBox(width: 8), + ElevatedButton( + child: const Text('Pause'), + onPressed: () { + controller!.pause(); + }, + ), + const SizedBox(width: 8), + ElevatedButton( + child: const Text('Set max volume'), + onPressed: () { + controller!.setVolume(1); + }, + ), + ], + ), ), ], ), diff --git a/example/lib/pages/rotation_and_fit_page.dart b/example/lib/pages/rotation_and_fit_page.dart index d68b650ae..1135aea0a 100644 --- a/example/lib/pages/rotation_and_fit_page.dart +++ b/example/lib/pages/rotation_and_fit_page.dart @@ -3,6 +3,8 @@ import 'package:example/constants.dart'; import 'package:flutter/material.dart'; class RotationAndFitPage extends StatefulWidget { + const RotationAndFitPage({super.key}); + @override _RotationAndFitPageState createState() => _RotationAndFitPageState(); } @@ -12,14 +14,14 @@ class _RotationAndFitPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( - aspectRatio: 1.0, - fit: BoxFit.fill, + const betterPlayerConfiguration = BetterPlayerConfiguration( + aspectRatio: 1, rotation: 90, ); - BetterPlayerDataSource dataSource = BetterPlayerDataSource( - BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl); + final dataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + Constants.forBiggerBlazesUrl, + ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(dataSource); super.initState(); @@ -29,20 +31,20 @@ class _RotationAndFitPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Rotation and fit"), + title: const Text('Rotation and fit'), ), body: Column( children: [ const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Text( - "Player with different rotation and fit.", + 'Player with different rotation and fit.', style: TextStyle(fontSize: 16), ), ), AspectRatio( - aspectRatio: 1.0, + aspectRatio: 1, child: BetterPlayer(controller: _betterPlayerController), ), ], diff --git a/example/lib/pages/subtitles_page.dart b/example/lib/pages/subtitles_page.dart index 9f68ae3d2..03176910f 100644 --- a/example/lib/pages/subtitles_page.dart +++ b/example/lib/pages/subtitles_page.dart @@ -4,6 +4,8 @@ import 'package:example/utils.dart'; import 'package:flutter/material.dart'; class SubtitlesPage extends StatefulWidget { + const SubtitlesPage({super.key}); + @override _SubtitlesPageState createState() => _SubtitlesPageState(); } @@ -13,14 +15,11 @@ class _SubtitlesPageState extends State { @override void initState() { - BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( + const betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, subtitlesConfiguration: BetterPlayerSubtitlesConfiguration( backgroundColor: Colors.green, - fontColor: Colors.white, - outlineColor: Colors.black, fontSize: 20, ), ); @@ -28,22 +27,23 @@ class _SubtitlesPageState extends State { _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.addEventsListener((event) { if (event.betterPlayerEventType == BetterPlayerEventType.progress) { - print("Current subtitle line: " + - _betterPlayerController.renderedSubtitle.toString()); + print( + 'Current subtitle line: ${_betterPlayerController.renderedSubtitle}', + ); } }); _setupDataSource(); super.initState(); } - void _setupDataSource() async { - BetterPlayerDataSource dataSource = BetterPlayerDataSource( + Future _setupDataSource() async { + final dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl, subtitles: BetterPlayerSubtitlesSource.single( type: BetterPlayerSubtitlesSourceType.file, url: await Utils.getFileUrl(Constants.fileExampleSubtitlesUrl), - name: "My subtitles", + name: 'My subtitles', selectedByDefault: true, ), ); @@ -54,23 +54,25 @@ class _SubtitlesPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Subtitles"), + title: const Text('Subtitles'), ), - body: Column(children: [ - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - "Player with subtitles loaded from file. Subtitles are enabled by default." - " You can choose subtitles by using overflow menu (3 dots in right corner).", - style: TextStyle(fontSize: 16), + body: Column( + children: [ + const SizedBox(height: 8), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'Player with subtitles loaded from file. Subtitles are enabled by default.' + ' You can choose subtitles by using overflow menu (3 dots in right corner).', + style: TextStyle(fontSize: 16), + ), + ), + AspectRatio( + aspectRatio: 16 / 9, + child: BetterPlayer(controller: _betterPlayerController), ), - ), - AspectRatio( - aspectRatio: 16 / 9, - child: BetterPlayer(controller: _betterPlayerController), - ) - ]), + ], + ), ); } } diff --git a/example/lib/pages/video_list/video_list_page.dart b/example/lib/pages/video_list/video_list_page.dart index da4fa89fd..93fb84731 100644 --- a/example/lib/pages/video_list/video_list_page.dart +++ b/example/lib/pages/video_list/video_list_page.dart @@ -2,17 +2,18 @@ import 'dart:math'; import 'package:example/constants.dart'; import 'package:example/model/video_list_data.dart'; +import 'package:example/pages/video_list/video_list_widget.dart'; import 'package:flutter/material.dart'; -import 'video_list_widget.dart'; - class VideoListPage extends StatefulWidget { + const VideoListPage({super.key}); + @override _VideoListPageState createState() => _VideoListPageState(); } class _VideoListPageState extends State { - final _random = new Random(); + final _random = Random(); final List _videos = [ Constants.bugBuckBunnyVideoUrl, Constants.forBiggerBlazesUrl, @@ -20,7 +21,7 @@ class _VideoListPageState extends State { Constants.elephantDreamVideoUrl, ]; List dataList = []; - var value = 0; + int value = 0; @override void initState() { @@ -29,39 +30,41 @@ class _VideoListPageState extends State { } void _setupData() { - for (int index = 0; index < 10; index++) { - var randomVideoUrl = _videos[_random.nextInt(_videos.length)]; - dataList.add(VideoListData("Video $index", randomVideoUrl)); + for (var index = 0; index < 10; index++) { + final randomVideoUrl = _videos[_random.nextInt(_videos.length)]; + dataList.add(VideoListData('Video $index', randomVideoUrl)); } } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Video in list")), + appBar: AppBar(title: const Text('Video in list')), body: Container( color: Colors.grey, - child: Column(children: [ - TextButton( - child: Text("Update page state"), - onPressed: () { - setState(() { - value++; - }); - }, - ), - Expanded( - child: ListView.builder( - itemCount: dataList.length, - itemBuilder: (context, index) { - VideoListData videoListData = dataList[index]; - return VideoListWidget( - videoListData: videoListData, - ); + child: Column( + children: [ + TextButton( + child: const Text('Update page state'), + onPressed: () { + setState(() { + value++; + }); }, ), - ) - ]), + Expanded( + child: ListView.builder( + itemCount: dataList.length, + itemBuilder: (context, index) { + final videoListData = dataList[index]; + return VideoListWidget( + videoListData: videoListData, + ); + }, + ), + ), + ], + ), ), ); } diff --git a/example/lib/pages/video_list/video_list_widget.dart b/example/lib/pages/video_list/video_list_widget.dart index 014a3c5d0..aecb52109 100644 --- a/example/lib/pages/video_list/video_list_widget.dart +++ b/example/lib/pages/video_list/video_list_widget.dart @@ -3,10 +3,9 @@ import 'package:example/model/video_list_data.dart'; import 'package:flutter/material.dart'; class VideoListWidget extends StatefulWidget { + const VideoListWidget({super.key, this.videoListData}); final VideoListData? videoListData; - const VideoListWidget({Key? key, this.videoListData}) : super(key: key); - @override _VideoListWidgetState createState() => _VideoListWidgetState(); } @@ -20,7 +19,7 @@ class _VideoListWidgetState extends State { void initState() { super.initState(); controller = BetterPlayerListVideoPlayerController(); - betterPlayerConfiguration = BetterPlayerConfiguration(autoPlay: true); + betterPlayerConfiguration = const BetterPlayerConfiguration(autoPlay: true); } @override @@ -31,73 +30,80 @@ class _VideoListWidgetState extends State { @override Widget build(BuildContext context) { return Card( - margin: EdgeInsets.symmetric(vertical: 16, horizontal: 16), + margin: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Text( videoListData!.videoTitle, - style: TextStyle(fontSize: 50), + style: const TextStyle(fontSize: 50), ), ), AspectRatio( - child: BetterPlayerListVideoPlayer( - BetterPlayerDataSource( - BetterPlayerDataSourceType.network, - videoListData!.videoUrl, - notificationConfiguration: - BetterPlayerNotificationConfiguration( - showNotification: false, - title: videoListData!.videoTitle, - author: "Test"), - bufferingConfiguration: BetterPlayerBufferingConfiguration( - minBufferMs: 2000, - maxBufferMs: 10000, - bufferForPlaybackMs: 1000, - bufferForPlaybackAfterRebufferMs: 2000), + aspectRatio: 1, + child: BetterPlayerListVideoPlayer( + BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + videoListData!.videoUrl, + notificationConfiguration: + BetterPlayerNotificationConfiguration( + showNotification: false, + title: videoListData!.videoTitle, + author: 'Test', + ), + bufferingConfiguration: + const BetterPlayerBufferingConfiguration( + minBufferMs: 2000, + maxBufferMs: 10000, + bufferForPlaybackMs: 1000, + bufferForPlaybackAfterRebufferMs: 2000, ), - configuration: BetterPlayerConfiguration( - autoPlay: false, aspectRatio: 1, handleLifecycle: true), - //key: Key(videoListData.hashCode.toString()), - playFraction: 0.8, - betterPlayerListVideoPlayerController: controller, ), - aspectRatio: 1), - Padding( + configuration: const BetterPlayerConfiguration( + aspectRatio: 1, + ), + //key: Key(videoListData.hashCode.toString()), + playFraction: 0.8, + betterPlayerListVideoPlayerController: controller, + ), + ), + const Padding( padding: EdgeInsets.all(8), child: Text( "Horror: In Steven Spielberg's Jaws, a shark terrorizes a beach " - "town. Plainspoken sheriff Roy Scheider, hippie shark " - "researcher Richard Dreyfuss, and a squirrely boat captain " - "set out to find the beast, but will they escape with their " + 'town. Plainspoken sheriff Roy Scheider, hippie shark ' + 'researcher Richard Dreyfuss, and a squirrely boat captain ' + 'set out to find the beast, but will they escape with their ' "lives? 70's special effects, legendary score, and trademark " - "humor set this classic apart."), + 'humor set this classic apart.'), ), Center( - child: Wrap(children: [ - ElevatedButton( - child: Text("Play"), - onPressed: () { - controller!.play(); - }, - ), - const SizedBox(width: 8), - ElevatedButton( - child: Text("Pause"), - onPressed: () { - controller!.pause(); - }, - ), - const SizedBox(width: 8), - ElevatedButton( - child: Text("Set max volume"), - onPressed: () { - controller!.setVolume(100); - }, - ), - ]), + child: Wrap( + children: [ + ElevatedButton( + child: const Text('Play'), + onPressed: () { + controller!.play(); + }, + ), + const SizedBox(width: 8), + ElevatedButton( + child: const Text('Pause'), + onPressed: () { + controller!.pause(); + }, + ), + const SizedBox(width: 8), + ElevatedButton( + child: const Text('Set max volume'), + onPressed: () { + controller!.setVolume(100); + }, + ), + ], + ), ), ], ), diff --git a/example/lib/pages/welcome_page.dart b/example/lib/pages/welcome_page.dart index d456b2c2e..0ac189957 100644 --- a/example/lib/pages/welcome_page.dart +++ b/example/lib/pages/welcome_page.dart @@ -21,6 +21,7 @@ import 'package:example/pages/normal_player_page.dart'; import 'package:example/pages/notification_player_page.dart'; import 'package:example/pages/overridden_aspect_ratio_page.dart'; import 'package:example/pages/overriden_duration_page.dart'; +import 'package:example/pages/picture_in_picture_page.dart'; import 'package:example/pages/placeholder_until_play_page.dart'; import 'package:example/pages/playlist_page.dart'; import 'package:example/pages/resolutions_page.dart'; @@ -28,12 +29,13 @@ import 'package:example/pages/reusable_video_list/reusable_video_list_page.dart' import 'package:example/pages/rotation_and_fit_page.dart'; import 'package:example/pages/subtitles_page.dart'; import 'package:example/pages/video_list/video_list_page.dart'; -import 'package:example/pages/picture_in_picture_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; class WelcomePage extends StatefulWidget { + const WelcomePage({super.key}); + @override _WelcomePageState createState() => _WelcomePageState(); } @@ -52,7 +54,7 @@ class _WelcomePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Better Player Example"), + title: const Text('Better Player Example'), ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), @@ -60,16 +62,16 @@ class _WelcomePageState extends State { children: [ const SizedBox(height: 8), Image.asset( - "assets/logo.png", + 'assets/logo.png', height: 200, width: 200, ), - Text( - "Welcome to Better Player example app. Click on any element below to see example.", + const Text( + 'Welcome to Better Player example app. Click on any element below to see example.', style: TextStyle(fontSize: 16), ), const SizedBox(height: 8), - ...buildExampleElementWidgets() + ...buildExampleElementWidgets(), ], ), ), @@ -78,89 +80,89 @@ class _WelcomePageState extends State { List buildExampleElementWidgets() { return [ - _buildExampleElementWidget("Basic player", () { - _navigateToPage(BasicPlayerPage()); + _buildExampleElementWidget('Basic player', () { + _navigateToPage(const BasicPlayerPage()); }), - _buildExampleElementWidget("Normal player", () { - _navigateToPage(NormalPlayerPage()); + _buildExampleElementWidget('Normal player', () { + _navigateToPage(const NormalPlayerPage()); }), - _buildExampleElementWidget("Controls configuration", () { - _navigateToPage(ControlsConfigurationPage()); + _buildExampleElementWidget('Controls configuration', () { + _navigateToPage(const ControlsConfigurationPage()); }), - _buildExampleElementWidget("Event listener", () { - _navigateToPage(EventListenerPage()); + _buildExampleElementWidget('Event listener', () { + _navigateToPage(const EventListenerPage()); }), - _buildExampleElementWidget("Subtitles", () { - _navigateToPage(SubtitlesPage()); + _buildExampleElementWidget('Subtitles', () { + _navigateToPage(const SubtitlesPage()); }), - _buildExampleElementWidget("Resolutions", () { - _navigateToPage(ResolutionsPage()); + _buildExampleElementWidget('Resolutions', () { + _navigateToPage(const ResolutionsPage()); }), - _buildExampleElementWidget("HLS subtitles", () { - _navigateToPage(HlsSubtitlesPage()); + _buildExampleElementWidget('HLS subtitles', () { + _navigateToPage(const HlsSubtitlesPage()); }), - _buildExampleElementWidget("HLS tracks", () { - _navigateToPage(HlsTracksPage()); + _buildExampleElementWidget('HLS tracks', () { + _navigateToPage(const HlsTracksPage()); }), - _buildExampleElementWidget("HLS Audio", () { - _navigateToPage(HlsAudioPage()); + _buildExampleElementWidget('HLS Audio', () { + _navigateToPage(const HlsAudioPage()); }), - _buildExampleElementWidget("Cache", () { - _navigateToPage(CachePage()); + _buildExampleElementWidget('Cache', () { + _navigateToPage(const CachePage()); }), - _buildExampleElementWidget("Playlist", () { - _navigateToPage(PlaylistPage()); + _buildExampleElementWidget('Playlist', () { + _navigateToPage(const PlaylistPage()); }), - _buildExampleElementWidget("Video in list", () { - _navigateToPage(VideoListPage()); + _buildExampleElementWidget('Video in list', () { + _navigateToPage(const VideoListPage()); }), - _buildExampleElementWidget("Rotation and fit", () { - _navigateToPage(RotationAndFitPage()); + _buildExampleElementWidget('Rotation and fit', () { + _navigateToPage(const RotationAndFitPage()); }), - _buildExampleElementWidget("Memory player", () { - _navigateToPage(MemoryPlayerPage()); + _buildExampleElementWidget('Memory player', () { + _navigateToPage(const MemoryPlayerPage()); }), - _buildExampleElementWidget("Controller controls", () { - _navigateToPage(ControllerControlsPage()); + _buildExampleElementWidget('Controller controls', () { + _navigateToPage(const ControllerControlsPage()); }), - _buildExampleElementWidget("Auto fullscreen orientation", () { - _navigateToPage(AutoFullscreenOrientationPage()); + _buildExampleElementWidget('Auto fullscreen orientation', () { + _navigateToPage(const AutoFullscreenOrientationPage()); }), - _buildExampleElementWidget("Overridden aspect ratio", () { - _navigateToPage(OverriddenAspectRatioPage()); + _buildExampleElementWidget('Overridden aspect ratio', () { + _navigateToPage(const OverriddenAspectRatioPage()); }), - _buildExampleElementWidget("Notifications player", () { - _navigateToPage(NotificationPlayerPage()); + _buildExampleElementWidget('Notifications player', () { + _navigateToPage(const NotificationPlayerPage()); }), - _buildExampleElementWidget("Reusable video list", () { - _navigateToPage(ReusableVideoListPage()); + _buildExampleElementWidget('Reusable video list', () { + _navigateToPage(const ReusableVideoListPage()); }), - _buildExampleElementWidget("Fade placeholder", () { - _navigateToPage(FadePlaceholderPage()); + _buildExampleElementWidget('Fade placeholder', () { + _navigateToPage(const FadePlaceholderPage()); }), - _buildExampleElementWidget("Placeholder until play", () { - _navigateToPage(PlaceholderUntilPlayPage()); + _buildExampleElementWidget('Placeholder until play', () { + _navigateToPage(const PlaceholderUntilPlayPage()); }), - _buildExampleElementWidget("Change player theme", () { - _navigateToPage(ChangePlayerThemePage()); + _buildExampleElementWidget('Change player theme', () { + _navigateToPage(const ChangePlayerThemePage()); }), - _buildExampleElementWidget("Overridden duration", () { - _navigateToPage(OverriddenDurationPage()); + _buildExampleElementWidget('Overridden duration', () { + _navigateToPage(const OverriddenDurationPage()); }), - _buildExampleElementWidget("Picture in Picture", () { - _navigateToPage(PictureInPicturePage()); + _buildExampleElementWidget('Picture in Picture', () { + _navigateToPage(const PictureInPicturePage()); }), - _buildExampleElementWidget("Controls always visible", () { - _navigateToPage(ControlsAlwaysVisiblePage()); + _buildExampleElementWidget('Controls always visible', () { + _navigateToPage(const ControlsAlwaysVisiblePage()); }), - _buildExampleElementWidget("DRM", () { - _navigateToPage(DrmPage()); + _buildExampleElementWidget('DRM', () { + _navigateToPage(const DrmPage()); }), - _buildExampleElementWidget("ClearKey DRM", () { - _navigateToPage(ClearKeyPage()); + _buildExampleElementWidget('ClearKey DRM', () { + _navigateToPage(const ClearKeyPage()); }), - _buildExampleElementWidget("DASH", () { - _navigateToPage(DashPage()); + _buildExampleElementWidget('DASH', () { + _navigateToPage(const DashPage()); }), ]; } @@ -176,10 +178,10 @@ class _WelcomePageState extends State { padding: const EdgeInsets.symmetric(vertical: 8), child: Text( name, - style: TextStyle(fontSize: 16), + style: const TextStyle(fontSize: 16), ), ), - Divider(), + const Divider(), ], ), ), @@ -195,34 +197,33 @@ class _WelcomePageState extends State { ///Save subtitles to file, so we can use it later Future _saveAssetSubtitleToFile() async { - String content = - await rootBundle.loadString("assets/example_subtitles.srt"); + final content = await rootBundle.loadString('assets/example_subtitles.srt'); final directory = await getApplicationDocumentsDirectory(); - var file = File("${directory.path}/example_subtitles.srt"); + final file = File('${directory.path}/example_subtitles.srt'); file.writeAsString(content); } ///Save video to file, so we can use it later Future _saveAssetVideoToFile() async { - var content = await rootBundle.load("assets/testvideo.mp4"); + final content = await rootBundle.load('assets/testvideo.mp4'); final directory = await getApplicationDocumentsDirectory(); - var file = File("${directory.path}/testvideo.mp4"); + final file = File('${directory.path}/testvideo.mp4'); file.writeAsBytesSync(content.buffer.asUint8List()); } Future _saveAssetEncryptVideoToFile() async { - var content = - await rootBundle.load("assets/${Constants.fileTestVideoEncryptUrl}"); + final content = + await rootBundle.load('assets/${Constants.fileTestVideoEncryptUrl}'); final directory = await getApplicationDocumentsDirectory(); - var file = File("${directory.path}/${Constants.fileTestVideoEncryptUrl}"); + final file = File('${directory.path}/${Constants.fileTestVideoEncryptUrl}'); file.writeAsBytesSync(content.buffer.asUint8List()); } ///Save logo to file, so we can use it later Future _saveLogoToFile() async { - var content = await rootBundle.load("assets/${Constants.logo}"); + final content = await rootBundle.load('assets/${Constants.logo}'); final directory = await getApplicationDocumentsDirectory(); - var file = File("${directory.path}/${Constants.logo}"); + final file = File('${directory.path}/${Constants.logo}'); file.writeAsBytesSync(content.buffer.asUint8List()); } } diff --git a/example/lib/utils.dart b/example/lib/utils.dart index 93b22c79d..614e5f8f4 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -3,6 +3,6 @@ import 'package:path_provider/path_provider.dart'; class Utils { static Future getFileUrl(String fileName) async { final directory = await getApplicationDocumentsDirectory(); - return "${directory.path}/$fileName"; + return '${directory.path}/$fileName'; } } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 7c92b5258..1e9474e3d 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: collection: ^1.18.0 dev_dependencies: + very_good_analysis: ^5.1.0 flutter_test: sdk: flutter diff --git a/lib/better_player.dart b/lib/better_player.dart index d20e1d00b..960aa0a09 100644 --- a/lib/better_player.dart +++ b/lib/better_player.dart @@ -1,5 +1,3 @@ -library better_player; - export 'src/asms/better_player_asms_audio_track.dart'; export 'src/asms/better_player_asms_data_holder.dart'; export 'src/asms/better_player_asms_subtitle.dart'; diff --git a/lib/src/asms/better_player_asms_audio_track.dart b/lib/src/asms/better_player_asms_audio_track.dart index af2aae905..fdc43f76d 100644 --- a/lib/src/asms/better_player_asms_audio_track.dart +++ b/lib/src/asms/better_player_asms_audio_track.dart @@ -1,5 +1,14 @@ ///Representation of HLS / DASH audio track class BetterPlayerAsmsAudioTrack { + BetterPlayerAsmsAudioTrack({ + this.id, + this.segmentAlignment, + this.label, + this.language, + this.url, + this.mimeType, + }); + ///Audio index in DASH xml or Id of track inside HLS playlist final int? id; @@ -17,12 +26,4 @@ class BetterPlayerAsmsAudioTrack { ///mimeType of the audio track final String? mimeType; - - BetterPlayerAsmsAudioTrack( - {this.id, - this.segmentAlignment, - this.label, - this.language, - this.url, - this.mimeType}); } diff --git a/lib/src/asms/better_player_asms_data_holder.dart b/lib/src/asms/better_player_asms_data_holder.dart index 64acc6f07..a701abbf0 100644 --- a/lib/src/asms/better_player_asms_data_holder.dart +++ b/lib/src/asms/better_player_asms_data_holder.dart @@ -3,9 +3,8 @@ import 'package:better_player/src/asms/better_player_asms_subtitle.dart'; import 'package:better_player/src/asms/better_player_asms_track.dart'; class BetterPlayerAsmsDataHolder { + BetterPlayerAsmsDataHolder({this.tracks, this.subtitles, this.audios}); List? tracks; List? subtitles; List? audios; - - BetterPlayerAsmsDataHolder({this.tracks, this.subtitles, this.audios}); } diff --git a/lib/src/asms/better_player_asms_subtitle.dart b/lib/src/asms/better_player_asms_subtitle.dart index 1967edcd8..c42475ad8 100644 --- a/lib/src/asms/better_player_asms_subtitle.dart +++ b/lib/src/asms/better_player_asms_subtitle.dart @@ -2,6 +2,19 @@ import 'package:better_player/src/asms/better_player_asms_subtitle_segment.dart' ///Representation of HLS / DASH subtitle element. class BetterPlayerAsmsSubtitle { + BetterPlayerAsmsSubtitle({ + this.language, + this.name, + this.mimeType, + this.segmentAlignment, + this.url, + this.realUrls, + this.isSegmented, + this.segmentsTime, + this.segments, + this.isDefault, + }); + ///Language of the subtitle final String? language; @@ -32,17 +45,4 @@ class BetterPlayerAsmsSubtitle { ///If the subtitle is the default final bool? isDefault; - - BetterPlayerAsmsSubtitle({ - this.language, - this.name, - this.mimeType, - this.segmentAlignment, - this.url, - this.realUrls, - this.isSegmented, - this.segmentsTime, - this.segments, - this.isDefault, - }); } diff --git a/lib/src/asms/better_player_asms_subtitle_segment.dart b/lib/src/asms/better_player_asms_subtitle_segment.dart index d7c98fbdd..2c430a4cc 100644 --- a/lib/src/asms/better_player_asms_subtitle_segment.dart +++ b/lib/src/asms/better_player_asms_subtitle_segment.dart @@ -2,6 +2,8 @@ ///and end time which are relative from start of the video and real url of the ///video (with domain and all paths). class BetterPlayerAsmsSubtitleSegment { + BetterPlayerAsmsSubtitleSegment(this.startTime, this.endTime, this.realUrl); + ///Start of the subtitles counting from the start of the video. final Duration startTime; @@ -10,6 +12,4 @@ class BetterPlayerAsmsSubtitleSegment { ///Real url of the subtitles (with all domains and paths). final String realUrl; - - BetterPlayerAsmsSubtitleSegment(this.startTime, this.endTime, this.realUrl); } diff --git a/lib/src/asms/better_player_asms_track.dart b/lib/src/asms/better_player_asms_track.dart index 0a11df79b..194684eca 100644 --- a/lib/src/asms/better_player_asms_track.dart +++ b/lib/src/asms/better_player_asms_track.dart @@ -1,5 +1,19 @@ /// Represents HLS / DASH track which can be played within player class BetterPlayerAsmsTrack { + BetterPlayerAsmsTrack( + this.id, + this.width, + this.height, + this.bitrate, + this.frameRate, + this.codecs, + this.mimeType, + ); + + factory BetterPlayerAsmsTrack.defaultTrack() { + return BetterPlayerAsmsTrack('', 0, 0, 0, 0, '', ''); + } + ///Id of the track final String? id; @@ -21,20 +35,6 @@ class BetterPlayerAsmsTrack { ///mimeType of the video track final String? mimeType; - BetterPlayerAsmsTrack( - this.id, - this.width, - this.height, - this.bitrate, - this.frameRate, - this.codecs, - this.mimeType, - ); - - factory BetterPlayerAsmsTrack.defaultTrack() { - return BetterPlayerAsmsTrack('', 0, 0, 0, 0, '', ''); - } - @override // ignore: unnecessary_overrides int get hashCode => super.hashCode; diff --git a/lib/src/asms/better_player_asms_utils.dart b/lib/src/asms/better_player_asms_utils.dart index 6ccfa73db..df9982fad 100644 --- a/lib/src/asms/better_player_asms_utils.dart +++ b/lib/src/asms/better_player_asms_utils.dart @@ -1,15 +1,15 @@ import 'dart:convert'; import 'dart:io'; + +import 'package:better_player/src/asms/better_player_asms_data_holder.dart'; import 'package:better_player/src/core/better_player_utils.dart'; import 'package:better_player/src/dash/better_player_dash_utils.dart'; import 'package:better_player/src/hls/better_player_hls_utils.dart'; -import 'better_player_asms_data_holder.dart'; - ///Base helper class for ASMS parsing. class BetterPlayerAsmsUtils { - static const String _hlsExtension = "m3u8"; - static const String _dashExtension = "mpd"; + static const String _hlsExtension = 'm3u8'; + static const String _dashExtension = 'mpd'; static final HttpClient _httpClient = HttpClient() ..connectionTimeout = const Duration(seconds: 5); @@ -26,7 +26,9 @@ class BetterPlayerAsmsUtils { ///Parse playlist based on type of stream. static Future parse( - String data, String masterPlaylistUrl) async { + String data, + String masterPlaylistUrl, + ) async { return isDataSourceDash(masterPlaylistUrl) ? BetterPlayerDashUtils.parse(data, masterPlaylistUrl) : BetterPlayerHlsUtils.parse(data, masterPlaylistUrl); @@ -45,14 +47,14 @@ class BetterPlayerAsmsUtils { } final response = await request.close(); - var data = ""; + var data = ''; await response.transform(const Utf8Decoder()).listen((content) { - data += content.toString(); + data += content; }).asFuture(); return data; } catch (exception) { - BetterPlayerUtils.log("GetDataFromUrl failed: $exception"); + BetterPlayerUtils.log('GetDataFromUrl failed: $exception'); return null; } } diff --git a/lib/src/clearkey/better_player_clearkey_utils.dart b/lib/src/clearkey/better_player_clearkey_utils.dart index a36f13e11..d37705eb9 100644 --- a/lib/src/clearkey/better_player_clearkey_utils.dart +++ b/lib/src/clearkey/better_player_clearkey_utils.dart @@ -7,12 +7,16 @@ class BetterPlayerClearKeyUtils { static final _byteMask = BigInt.from(0xff); ///The ClearKey from a Map. The key in map should be the kid with the associated value being the key. Both values should be provide in HEX format. - static String generateKey(Map keys, - {String type = "temporary"}) { - final Map keyMap = {"type": type}; - keyMap["keys"] = >[]; - keys.forEach((key, value) => keyMap["keys"] - .add({"kty": "oct", "kid": _base64(key), "k": _base64(value)})); + static String generateKey( + Map keys, { + String type = 'temporary', + }) { + final Map keyMap = {'type': type}; + keyMap['keys'] = >[]; + keys.forEach( + (key, value) => keyMap['keys'] + .add({'kty': 'oct', 'kid': _base64(key), 'k': _base64(value)}), + ); return jsonEncode(keyMap); } @@ -20,16 +24,16 @@ class BetterPlayerClearKeyUtils { static String _base64(String source) { return base64 .encode(_encodeBigInt(BigInt.parse(source, radix: 16))) - .replaceAll("=", ""); + .replaceAll('=', ''); } static Uint8List _encodeBigInt(BigInt number) { var passedNumber = number; - final int size = (number.bitLength + 7) >> 3; + final size = (number.bitLength + 7) >> 3; final result = Uint8List(size); - int pos = size - 1; - for (int i = 0; i < size; i++) { + var pos = size - 1; + for (var i = 0; i < size; i++) { result[pos--] = (passedNumber & _byteMask).toInt(); passedNumber = passedNumber >> 8; } diff --git a/lib/src/configuration/better_player_buffering_configuration.dart b/lib/src/configuration/better_player_buffering_configuration.dart index 1e37212a8..9a32dac5a 100644 --- a/lib/src/configuration/better_player_buffering_configuration.dart +++ b/lib/src/configuration/better_player_buffering_configuration.dart @@ -1,6 +1,14 @@ ///Configuration class used to setup better buffering experience or setup custom ///load settings. Currently used only in Android. class BetterPlayerBufferingConfiguration { + const BetterPlayerBufferingConfiguration({ + this.minBufferMs = defaultMinBufferMs, + this.maxBufferMs = defaultMaxBufferMs, + this.bufferForPlaybackMs = defaultBufferForPlaybackMs, + this.bufferForPlaybackAfterRebufferMs = + defaultBufferForPlaybackAfterRebufferMs, + }); + ///Constants values are from the offical exoplayer documentation ///https://exoplayer.dev/doc/reference/constant-values.html#com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS static const defaultMinBufferMs = 25000; @@ -24,12 +32,4 @@ class BetterPlayerBufferingConfiguration { /// after a rebuffer, in milliseconds. A rebuffer is defined to be caused by /// buffer depletion rather than a user action. final int bufferForPlaybackAfterRebufferMs; - - const BetterPlayerBufferingConfiguration({ - this.minBufferMs = defaultMinBufferMs, - this.maxBufferMs = defaultMaxBufferMs, - this.bufferForPlaybackMs = defaultBufferForPlaybackMs, - this.bufferForPlaybackAfterRebufferMs = - defaultBufferForPlaybackAfterRebufferMs, - }); } diff --git a/lib/src/configuration/better_player_cache_configuration.dart b/lib/src/configuration/better_player_cache_configuration.dart index 62d3444f8..8a98dcf92 100644 --- a/lib/src/configuration/better_player_cache_configuration.dart +++ b/lib/src/configuration/better_player_cache_configuration.dart @@ -3,6 +3,14 @@ ///maxCacheFileSize > 0. On iOS maxCacheSize and maxCacheFileSize take no effect, ///so useCache is used only. class BetterPlayerCacheConfiguration { + const BetterPlayerCacheConfiguration({ + this.useCache = false, + this.maxCacheSize = 10 * 1024 * 1024, + this.maxCacheFileSize = 10 * 1024 * 1024, + this.preCacheSize = 3 * 1024 * 1024, + this.key, + }); + ///Enable cache for network data source final bool useCache; @@ -21,11 +29,4 @@ class BetterPlayerCacheConfiguration { ///Cache key to re-use same cached data between app sessions. final String? key; - - const BetterPlayerCacheConfiguration( - {this.useCache = false, - this.maxCacheSize = 10 * 1024 * 1024, - this.maxCacheFileSize = 10 * 1024 * 1024, - this.preCacheSize = 3 * 1024 * 1024, - this.key}); } diff --git a/lib/src/configuration/better_player_configuration.dart b/lib/src/configuration/better_player_configuration.dart index fa9ec564e..a48b07c8f 100644 --- a/lib/src/configuration/better_player_configuration.dart +++ b/lib/src/configuration/better_player_configuration.dart @@ -6,6 +6,46 @@ import 'package:flutter/services.dart'; ///Master configuration which contains children that configure specific part ///of player. class BetterPlayerConfiguration { + const BetterPlayerConfiguration({ + this.aspectRatio, + this.autoPlay = false, + this.startAt, + this.looping = false, + this.fullScreenByDefault = false, + this.placeholder, + this.showPlaceholderUntilPlay = false, + this.placeholderOnTop = true, + this.overlay, + this.errorBuilder, + this.allowedScreenSleep = true, + this.fullScreenAspectRatio, + this.deviceOrientationsOnFullScreen = const [ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ], + this.systemOverlaysAfterFullScreen = SystemUiOverlay.values, + this.deviceOrientationsAfterFullScreen = const [ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ], + this.routePageBuilder, + this.eventListener, + this.subtitlesConfiguration = const BetterPlayerSubtitlesConfiguration(), + this.controlsConfiguration = const BetterPlayerControlsConfiguration(), + this.fit = BoxFit.fill, + this.rotation = 0, + this.playerVisibilityChangedBehavior, + this.translations, + this.autoDetectFullscreenDeviceOrientation = false, + this.autoDetectFullscreenAspectRatio = false, + this.handleLifecycle = true, + this.autoDispose = true, + this.expandToFill = true, + this.useRootNavigator = false, + }); + /// Play the video as soon as it's displayed final bool autoPlay; @@ -118,46 +158,6 @@ class BetterPlayerConfiguration { ///Default value is false. final bool useRootNavigator; - const BetterPlayerConfiguration({ - this.aspectRatio, - this.autoPlay = false, - this.startAt, - this.looping = false, - this.fullScreenByDefault = false, - this.placeholder, - this.showPlaceholderUntilPlay = false, - this.placeholderOnTop = true, - this.overlay, - this.errorBuilder, - this.allowedScreenSleep = true, - this.fullScreenAspectRatio, - this.deviceOrientationsOnFullScreen = const [ - DeviceOrientation.landscapeLeft, - DeviceOrientation.landscapeRight, - ], - this.systemOverlaysAfterFullScreen = SystemUiOverlay.values, - this.deviceOrientationsAfterFullScreen = const [ - DeviceOrientation.portraitUp, - DeviceOrientation.portraitDown, - DeviceOrientation.landscapeLeft, - DeviceOrientation.landscapeRight, - ], - this.routePageBuilder, - this.eventListener, - this.subtitlesConfiguration = const BetterPlayerSubtitlesConfiguration(), - this.controlsConfiguration = const BetterPlayerControlsConfiguration(), - this.fit = BoxFit.fill, - this.rotation = 0, - this.playerVisibilityChangedBehavior, - this.translations, - this.autoDetectFullscreenDeviceOrientation = false, - this.autoDetectFullscreenAspectRatio = false, - this.handleLifecycle = true, - this.autoDispose = true, - this.expandToFill = true, - this.useRootNavigator = false, - }); - BetterPlayerConfiguration copyWith({ double? aspectRatio, bool? autoPlay, diff --git a/lib/src/configuration/better_player_controls_configuration.dart b/lib/src/configuration/better_player_controls_configuration.dart index 104d1b7ab..df2c0f449 100644 --- a/lib/src/configuration/better_player_controls_configuration.dart +++ b/lib/src/configuration/better_player_controls_configuration.dart @@ -6,6 +6,91 @@ import 'package:flutter/material.dart'; ///of controls. Used in BetterPlayerConfiguration. Configuration applies only ///for player displayed in app, not in notification or PiP mode. class BetterPlayerControlsConfiguration { + const BetterPlayerControlsConfiguration({ + this.controlBarColor = Colors.black87, + this.textColor = Colors.white, + this.iconsColor = Colors.white, + this.playIcon = Icons.play_arrow_outlined, + this.pauseIcon = Icons.pause_outlined, + this.muteIcon = Icons.volume_up_outlined, + this.unMuteIcon = Icons.volume_off_outlined, + this.fullscreenEnableIcon = Icons.fullscreen_outlined, + this.fullscreenDisableIcon = Icons.fullscreen_exit_outlined, + this.skipBackIcon = Icons.replay_10_outlined, + this.skipForwardIcon = Icons.forward_10_outlined, + this.enableFullscreen = true, + this.enableMute = true, + this.enableProgressText = true, + this.enableProgressBar = true, + this.enableProgressBarDrag = true, + this.enablePlayPause = true, + this.enableSkips = true, + this.enableAudioTracks = true, + this.progressBarPlayedColor = Colors.white, + this.progressBarHandleColor = Colors.white, + this.progressBarBufferedColor = Colors.white70, + this.progressBarBackgroundColor = Colors.white60, + this.controlsHideTime = const Duration(milliseconds: 300), + this.customControlsBuilder, + this.playerTheme, + this.showControls = true, + this.showControlsOnInitialize = true, + this.controlBarHeight = 48.0, + this.liveTextColor = Colors.red, + this.enableOverflowMenu = true, + this.enablePlaybackSpeed = true, + this.enableSubtitles = true, + this.enableQualities = true, + this.enablePip = true, + this.enableRetry = true, + this.overflowMenuCustomItems = const [], + this.overflowMenuIcon = Icons.more_vert_outlined, + this.pipMenuIcon = Icons.picture_in_picture_outlined, + this.playbackSpeedIcon = Icons.shutter_speed_outlined, + this.qualitiesIcon = Icons.hd_outlined, + this.subtitlesIcon = Icons.closed_caption_outlined, + this.audioTracksIcon = Icons.audiotrack_outlined, + this.overflowMenuIconsColor = Colors.black, + this.forwardSkipTimeInMilliseconds = 10000, + this.backwardSkipTimeInMilliseconds = 10000, + this.loadingColor = Colors.white, + this.loadingWidget, + this.backgroundColor = Colors.black, + this.overflowModalColor = Colors.white, + this.overflowModalTextColor = Colors.black, + }); + + factory BetterPlayerControlsConfiguration.white() { + return const BetterPlayerControlsConfiguration( + controlBarColor: Colors.white, + textColor: Colors.black, + iconsColor: Colors.black, + progressBarPlayedColor: Colors.black, + progressBarHandleColor: Colors.black, + progressBarBufferedColor: Colors.black54, + progressBarBackgroundColor: Colors.white70, + ); + } + + factory BetterPlayerControlsConfiguration.cupertino() { + return const BetterPlayerControlsConfiguration( + fullscreenEnableIcon: CupertinoIcons.arrow_up_left_arrow_down_right, + fullscreenDisableIcon: CupertinoIcons.arrow_down_right_arrow_up_left, + playIcon: CupertinoIcons.play_arrow_solid, + pauseIcon: CupertinoIcons.pause_solid, + skipBackIcon: CupertinoIcons.gobackward_15, + skipForwardIcon: CupertinoIcons.goforward_15, + ); + } + + ///Setup BetterPlayerControlsConfiguration based on Theme options. + factory BetterPlayerControlsConfiguration.theme(ThemeData theme) { + return BetterPlayerControlsConfiguration( + textColor: theme.textTheme.bodySmall?.color ?? Colors.white, + iconsColor: theme.buttonTheme.colorScheme?.primary ?? Colors.white, + ); + } + ///Color of the control bars final Color controlBarColor; @@ -76,8 +161,10 @@ class BetterPlayerControlsConfiguration { final Duration controlsHideTime; ///Parameter used to build custom controls - final Widget Function(BetterPlayerController controller, - Function(bool) onPlayerVisibilityChanged)? customControlsBuilder; + final Widget Function( + BetterPlayerController controller, + Function(bool) onPlayerVisibilityChanged, + )? customControlsBuilder; ///Parameter used to change theme of the player final BetterPlayerTheme? playerTheme; @@ -160,88 +247,4 @@ class BetterPlayerControlsConfiguration { ///Color of text in bottom modal sheet used for overflow menu items. final Color overflowModalTextColor; - - const BetterPlayerControlsConfiguration({ - this.controlBarColor = Colors.black87, - this.textColor = Colors.white, - this.iconsColor = Colors.white, - this.playIcon = Icons.play_arrow_outlined, - this.pauseIcon = Icons.pause_outlined, - this.muteIcon = Icons.volume_up_outlined, - this.unMuteIcon = Icons.volume_off_outlined, - this.fullscreenEnableIcon = Icons.fullscreen_outlined, - this.fullscreenDisableIcon = Icons.fullscreen_exit_outlined, - this.skipBackIcon = Icons.replay_10_outlined, - this.skipForwardIcon = Icons.forward_10_outlined, - this.enableFullscreen = true, - this.enableMute = true, - this.enableProgressText = true, - this.enableProgressBar = true, - this.enableProgressBarDrag = true, - this.enablePlayPause = true, - this.enableSkips = true, - this.enableAudioTracks = true, - this.progressBarPlayedColor = Colors.white, - this.progressBarHandleColor = Colors.white, - this.progressBarBufferedColor = Colors.white70, - this.progressBarBackgroundColor = Colors.white60, - this.controlsHideTime = const Duration(milliseconds: 300), - this.customControlsBuilder, - this.playerTheme, - this.showControls = true, - this.showControlsOnInitialize = true, - this.controlBarHeight = 48.0, - this.liveTextColor = Colors.red, - this.enableOverflowMenu = true, - this.enablePlaybackSpeed = true, - this.enableSubtitles = true, - this.enableQualities = true, - this.enablePip = true, - this.enableRetry = true, - this.overflowMenuCustomItems = const [], - this.overflowMenuIcon = Icons.more_vert_outlined, - this.pipMenuIcon = Icons.picture_in_picture_outlined, - this.playbackSpeedIcon = Icons.shutter_speed_outlined, - this.qualitiesIcon = Icons.hd_outlined, - this.subtitlesIcon = Icons.closed_caption_outlined, - this.audioTracksIcon = Icons.audiotrack_outlined, - this.overflowMenuIconsColor = Colors.black, - this.forwardSkipTimeInMilliseconds = 10000, - this.backwardSkipTimeInMilliseconds = 10000, - this.loadingColor = Colors.white, - this.loadingWidget, - this.backgroundColor = Colors.black, - this.overflowModalColor = Colors.white, - this.overflowModalTextColor = Colors.black, - }); - - factory BetterPlayerControlsConfiguration.white() { - return const BetterPlayerControlsConfiguration( - controlBarColor: Colors.white, - textColor: Colors.black, - iconsColor: Colors.black, - progressBarPlayedColor: Colors.black, - progressBarHandleColor: Colors.black, - progressBarBufferedColor: Colors.black54, - progressBarBackgroundColor: Colors.white70); - } - - factory BetterPlayerControlsConfiguration.cupertino() { - return const BetterPlayerControlsConfiguration( - fullscreenEnableIcon: CupertinoIcons.arrow_up_left_arrow_down_right, - fullscreenDisableIcon: CupertinoIcons.arrow_down_right_arrow_up_left, - playIcon: CupertinoIcons.play_arrow_solid, - pauseIcon: CupertinoIcons.pause_solid, - skipBackIcon: CupertinoIcons.gobackward_15, - skipForwardIcon: CupertinoIcons.goforward_15, - ); - } - - ///Setup BetterPlayerControlsConfiguration based on Theme options. - factory BetterPlayerControlsConfiguration.theme(ThemeData theme) { - return BetterPlayerControlsConfiguration( - textColor: theme.textTheme.bodySmall?.color ?? Colors.white, - iconsColor: theme.buttonTheme.colorScheme?.primary ?? Colors.white, - ); - } } diff --git a/lib/src/configuration/better_player_data_source.dart b/lib/src/configuration/better_player_data_source.dart index bccdfa4d7..609f4029d 100644 --- a/lib/src/configuration/better_player_data_source.dart +++ b/lib/src/configuration/better_player_data_source.dart @@ -1,4 +1,5 @@ import 'package:better_player/src/configuration/better_player_buffering_configuration.dart'; +import 'package:better_player/src/configuration/better_player_cache_configuration.dart'; import 'package:better_player/src/configuration/better_player_data_source_type.dart'; import 'package:better_player/src/configuration/better_player_drm_configuration.dart'; import 'package:better_player/src/configuration/better_player_notification_configuration.dart'; @@ -6,76 +7,9 @@ import 'package:better_player/src/configuration/better_player_video_format.dart' import 'package:better_player/src/subtitles/better_player_subtitles_source.dart'; import 'package:flutter/widgets.dart'; -import 'better_player_cache_configuration.dart'; - ///Representation of data source which will be played in Better Player. Allows ///to setup all necessary configuration connected to video source. class BetterPlayerDataSource { - ///Type of source of video - final BetterPlayerDataSourceType type; - - ///Url of the video - final String url; - - ///Subtitles configuration - final List? subtitles; - - ///Flag to determine if current data source is live stream - final bool? liveStream; - - /// Custom headers for player - final Map? headers; - - ///Should player use hls / dash subtitles (ASMS - Adaptive Streaming Media Sources). - final bool? useAsmsSubtitles; - - ///Should player use hls tracks - final bool? useAsmsTracks; - - ///Should player use hls /das audio tracks - final bool? useAsmsAudioTracks; - - ///List of strings that represents tracks names. - ///If empty, then better player will choose name based on track parameters - final List? asmsTrackNames; - - ///Optional, alternative resolutions for non-hls/dash video. Used to setup - ///different qualities for video. - ///Data should be in given format: - ///{"360p": "url", "540p": "url2" } - final Map? resolutions; - - ///Optional cache configuration, used only for network data sources - final BetterPlayerCacheConfiguration? cacheConfiguration; - - ///List of bytes, used only in memory player - final List? bytes; - - ///Configuration of remote controls notification - final BetterPlayerNotificationConfiguration? notificationConfiguration; - - ///Duration which will be returned instead of original duration - final Duration? overriddenDuration; - - ///Video format hint when data source url has not valid extension. - final BetterPlayerVideoFormat? videoFormat; - - ///Extension of video without dot. - final String? videoExtension; - - ///Configuration of content protection - final BetterPlayerDrmConfiguration? drmConfiguration; - - ///Placeholder widget which will be shown until video load or play. This - ///placeholder may be useful if you want to show placeholder before each video - ///in playlist. Otherwise, you should use placeholder from - /// BetterPlayerConfiguration. - final Widget? placeholder; - - ///Configuration of video buffering. Currently only supported in Android - ///platform. - final BetterPlayerBufferingConfiguration bufferingConfiguration; - BetterPlayerDataSource( this.type, this.url, { @@ -100,11 +34,12 @@ class BetterPlayerDataSource { this.placeholder, this.bufferingConfiguration = const BetterPlayerBufferingConfiguration(), }) : assert( - (type == BetterPlayerDataSourceType.network || - type == BetterPlayerDataSourceType.file) || - (type == BetterPlayerDataSourceType.memory && - bytes?.isNotEmpty == true), - "Url can't be null in network or file data source | bytes can't be null when using memory data source"); + (type == BetterPlayerDataSourceType.network || + type == BetterPlayerDataSourceType.file) || + (type == BetterPlayerDataSourceType.memory && + bytes?.isNotEmpty == true), + "Url can't be null in network or file data source | bytes can't be null when using memory data source", + ); ///Factory method to build network data source which uses url as data source ///Bytes parameter is not used in this data source. @@ -191,7 +126,7 @@ class BetterPlayerDataSource { }) { return BetterPlayerDataSource( BetterPlayerDataSourceType.memory, - "", + '', videoExtension: videoExtension, bytes: bytes, subtitles: subtitles, @@ -206,6 +141,71 @@ class BetterPlayerDataSource { ); } + ///Type of source of video + final BetterPlayerDataSourceType type; + + ///Url of the video + final String url; + + ///Subtitles configuration + final List? subtitles; + + ///Flag to determine if current data source is live stream + final bool? liveStream; + + /// Custom headers for player + final Map? headers; + + ///Should player use hls / dash subtitles (ASMS - Adaptive Streaming Media Sources). + final bool? useAsmsSubtitles; + + ///Should player use hls tracks + final bool? useAsmsTracks; + + ///Should player use hls /das audio tracks + final bool? useAsmsAudioTracks; + + ///List of strings that represents tracks names. + ///If empty, then better player will choose name based on track parameters + final List? asmsTrackNames; + + ///Optional, alternative resolutions for non-hls/dash video. Used to setup + ///different qualities for video. + ///Data should be in given format: + ///{"360p": "url", "540p": "url2" } + final Map? resolutions; + + ///Optional cache configuration, used only for network data sources + final BetterPlayerCacheConfiguration? cacheConfiguration; + + ///List of bytes, used only in memory player + final List? bytes; + + ///Configuration of remote controls notification + final BetterPlayerNotificationConfiguration? notificationConfiguration; + + ///Duration which will be returned instead of original duration + final Duration? overriddenDuration; + + ///Video format hint when data source url has not valid extension. + final BetterPlayerVideoFormat? videoFormat; + + ///Extension of video without dot. + final String? videoExtension; + + ///Configuration of content protection + final BetterPlayerDrmConfiguration? drmConfiguration; + + ///Placeholder widget which will be shown until video load or play. This + ///placeholder may be useful if you want to show placeholder before each video + ///in playlist. Otherwise, you should use placeholder from + /// BetterPlayerConfiguration. + final Widget? placeholder; + + ///Configuration of video buffering. Currently only supported in Android + ///platform. + final BetterPlayerBufferingConfiguration bufferingConfiguration; + BetterPlayerDataSource copyWith({ BetterPlayerDataSourceType? type, String? url, diff --git a/lib/src/configuration/better_player_drm_configuration.dart b/lib/src/configuration/better_player_drm_configuration.dart index 1145145fb..cd8c1c58a 100644 --- a/lib/src/configuration/better_player_drm_configuration.dart +++ b/lib/src/configuration/better_player_drm_configuration.dart @@ -2,6 +2,15 @@ import 'package:better_player/src/configuration/better_player_drm_type.dart'; ///Configuration of DRM used to protect data source class BetterPlayerDrmConfiguration { + BetterPlayerDrmConfiguration({ + this.drmType, + this.token, + this.licenseUrl, + this.certificateUrl, + this.headers, + this.clearKey, + }); + ///Type of DRM final BetterPlayerDrmType? drmType; @@ -19,12 +28,4 @@ class BetterPlayerDrmConfiguration { ///Additional headers send with auth request, used only for WIDEVINE DRM final Map? headers; - - BetterPlayerDrmConfiguration( - {this.drmType, - this.token, - this.licenseUrl, - this.certificateUrl, - this.headers, - this.clearKey}); } diff --git a/lib/src/configuration/better_player_event.dart b/lib/src/configuration/better_player_event.dart index 19b1536f3..eb9412b93 100644 --- a/lib/src/configuration/better_player_event.dart +++ b/lib/src/configuration/better_player_event.dart @@ -3,8 +3,7 @@ import 'package:better_player/src/configuration/better_player_event_type.dart'; ///Event that happens in player. It can be used to determine current player state ///on higher layer. class BetterPlayerEvent { + BetterPlayerEvent(this.betterPlayerEventType, {this.parameters}); final BetterPlayerEventType betterPlayerEventType; final Map? parameters; - - BetterPlayerEvent(this.betterPlayerEventType, {this.parameters}); } diff --git a/lib/src/configuration/better_player_notification_configuration.dart b/lib/src/configuration/better_player_notification_configuration.dart index 1c9718b74..fc3a62129 100644 --- a/lib/src/configuration/better_player_notification_configuration.dart +++ b/lib/src/configuration/better_player_notification_configuration.dart @@ -1,6 +1,15 @@ ///Configuration of notification which is displayed once user moves app to ///background. class BetterPlayerNotificationConfiguration { + const BetterPlayerNotificationConfiguration({ + this.showNotification, + this.title, + this.author, + this.imageUrl, + this.notificationChannelName, + this.activityName, + }); + ///Is player controls notification enabled final bool? showNotification; @@ -19,13 +28,4 @@ class BetterPlayerNotificationConfiguration { ///Name of activity used to open application from notification. Used only ///in Android. final String? activityName; - - const BetterPlayerNotificationConfiguration({ - this.showNotification, - this.title, - this.author, - this.imageUrl, - this.notificationChannelName, - this.activityName, - }); } diff --git a/lib/src/configuration/better_player_translations.dart b/lib/src/configuration/better_player_translations.dart index 6cae7aeff..12e55382f 100644 --- a/lib/src/configuration/better_player_translations.dart +++ b/lib/src/configuration/better_player_translations.dart @@ -1,140 +1,141 @@ ///Class used to hold translations for all features within Better Player class BetterPlayerTranslations { - final String languageCode; - final String generalDefaultError; - final String generalNone; - final String generalDefault; - final String generalRetry; - final String playlistLoadingNextVideo; - final String controlsLive; - final String controlsNextVideoIn; - final String overflowMenuPlaybackSpeed; - final String overflowMenuSubtitles; - final String overflowMenuQuality; - final String overflowMenuAudioTracks; - final String qualityAuto; - - BetterPlayerTranslations( - {this.languageCode = "en", - this.generalDefaultError = "Video can't be played", - this.generalNone = "None", - this.generalDefault = "Default", - this.generalRetry = "Retry", - this.playlistLoadingNextVideo = "Loading next video", - this.controlsLive = "LIVE", - this.controlsNextVideoIn = "Next video in", - this.overflowMenuPlaybackSpeed = "Playback speed", - this.overflowMenuSubtitles = "Subtitles", - this.overflowMenuQuality = "Quality", - this.overflowMenuAudioTracks = "Audio", - this.qualityAuto = "Auto"}); + BetterPlayerTranslations({ + this.languageCode = 'en', + this.generalDefaultError = "Video can't be played", + this.generalNone = 'None', + this.generalDefault = 'Default', + this.generalRetry = 'Retry', + this.playlistLoadingNextVideo = 'Loading next video', + this.controlsLive = 'LIVE', + this.controlsNextVideoIn = 'Next video in', + this.overflowMenuPlaybackSpeed = 'Playback speed', + this.overflowMenuSubtitles = 'Subtitles', + this.overflowMenuQuality = 'Quality', + this.overflowMenuAudioTracks = 'Audio', + this.qualityAuto = 'Auto', + }); factory BetterPlayerTranslations.polish() => BetterPlayerTranslations( - languageCode: "pl", - generalDefaultError: "Video nie może zostać odtworzone", - generalNone: "Brak", - generalDefault: "Domyślne", - generalRetry: "Spróbuj ponownie", - playlistLoadingNextVideo: "Ładowanie następnego filmu", - controlsNextVideoIn: "Następne video za", - overflowMenuPlaybackSpeed: "Szybkość odtwarzania", - overflowMenuSubtitles: "Napisy", - overflowMenuQuality: "Jakość", - overflowMenuAudioTracks: "Dźwięk", - qualityAuto: "Automatycznie", + languageCode: 'pl', + generalDefaultError: 'Video nie może zostać odtworzone', + generalNone: 'Brak', + generalDefault: 'Domyślne', + generalRetry: 'Spróbuj ponownie', + playlistLoadingNextVideo: 'Ładowanie następnego filmu', + controlsNextVideoIn: 'Następne video za', + overflowMenuPlaybackSpeed: 'Szybkość odtwarzania', + overflowMenuSubtitles: 'Napisy', + overflowMenuQuality: 'Jakość', + overflowMenuAudioTracks: 'Dźwięk', + qualityAuto: 'Automatycznie', ); factory BetterPlayerTranslations.chinese() => BetterPlayerTranslations( - languageCode: "zh", - generalDefaultError: "无法播放视频", - generalNone: "没有", - generalDefault: "默认", - generalRetry: "重試", - playlistLoadingNextVideo: "正在加载下一个视频", - controlsLive: "直播", - controlsNextVideoIn: "下一部影片", - overflowMenuPlaybackSpeed: "播放速度", - overflowMenuSubtitles: "字幕", - overflowMenuQuality: "质量", - overflowMenuAudioTracks: "音訊", - qualityAuto: "汽車", + languageCode: 'zh', + generalDefaultError: '无法播放视频', + generalNone: '没有', + generalDefault: '默认', + generalRetry: '重試', + playlistLoadingNextVideo: '正在加载下一个视频', + controlsLive: '直播', + controlsNextVideoIn: '下一部影片', + overflowMenuPlaybackSpeed: '播放速度', + overflowMenuSubtitles: '字幕', + overflowMenuQuality: '质量', + overflowMenuAudioTracks: '音訊', + qualityAuto: '汽車', ); factory BetterPlayerTranslations.hindi() => BetterPlayerTranslations( - languageCode: "hi", - generalDefaultError: "वीडियो नहीं चलाया जा सकता", - generalNone: "कोई नहीं", - generalDefault: "चूक", - generalRetry: "पुनः प्रयास करें", - playlistLoadingNextVideo: "अगला वीडियो लोड हो रहा है", - controlsLive: "लाइव", - controlsNextVideoIn: "में अगला वीडियो", - overflowMenuPlaybackSpeed: "प्लेबैक की गति", - overflowMenuSubtitles: "उपशीर्षक", - overflowMenuQuality: "गुणवत्ता", - overflowMenuAudioTracks: "ऑडियो", - qualityAuto: "ऑटो", + languageCode: 'hi', + generalDefaultError: 'वीडियो नहीं चलाया जा सकता', + generalNone: 'कोई नहीं', + generalDefault: 'चूक', + generalRetry: 'पुनः प्रयास करें', + playlistLoadingNextVideo: 'अगला वीडियो लोड हो रहा है', + controlsLive: 'लाइव', + controlsNextVideoIn: 'में अगला वीडियो', + overflowMenuPlaybackSpeed: 'प्लेबैक की गति', + overflowMenuSubtitles: 'उपशीर्षक', + overflowMenuQuality: 'गुणवत्ता', + overflowMenuAudioTracks: 'ऑडियो', + qualityAuto: 'ऑटो', ); factory BetterPlayerTranslations.arabic() => BetterPlayerTranslations( - languageCode: "ar", - generalDefaultError: "لا يمكن تشغيل الفيديو", - generalNone: "لا يوجد", - generalDefault: "الاساسي", - generalRetry: "اعادة المحاوله", - playlistLoadingNextVideo: "تحميل الفيديو التالي", - controlsLive: "مباشر", - controlsNextVideoIn: "الفيديو التالي في", - overflowMenuPlaybackSpeed: "سرعة التشغيل", - overflowMenuSubtitles: "الترجمة", - overflowMenuQuality: "الجودة", - overflowMenuAudioTracks: "الصوت", - qualityAuto: "ऑटो", + languageCode: 'ar', + generalDefaultError: 'لا يمكن تشغيل الفيديو', + generalNone: 'لا يوجد', + generalDefault: 'الاساسي', + generalRetry: 'اعادة المحاوله', + playlistLoadingNextVideo: 'تحميل الفيديو التالي', + controlsLive: 'مباشر', + controlsNextVideoIn: 'الفيديو التالي في', + overflowMenuPlaybackSpeed: 'سرعة التشغيل', + overflowMenuSubtitles: 'الترجمة', + overflowMenuQuality: 'الجودة', + overflowMenuAudioTracks: 'الصوت', + qualityAuto: 'ऑटो', ); factory BetterPlayerTranslations.turkish() => BetterPlayerTranslations( - languageCode: "tr", - generalDefaultError: "Video oynatılamıyor", - generalNone: "Hiçbiri", - generalDefault: "Varsayılan", - generalRetry: "Tekrar Dene", - playlistLoadingNextVideo: "Sonraki video yükleniyor", - controlsLive: "CANLI", - controlsNextVideoIn: "Sonraki video oynatılmadan", - overflowMenuPlaybackSpeed: "Oynatma hızı", - overflowMenuSubtitles: "Altyazı", - overflowMenuQuality: "Kalite", - overflowMenuAudioTracks: "Ses", - qualityAuto: "Otomatik"); + languageCode: 'tr', + generalDefaultError: 'Video oynatılamıyor', + generalNone: 'Hiçbiri', + generalDefault: 'Varsayılan', + generalRetry: 'Tekrar Dene', + playlistLoadingNextVideo: 'Sonraki video yükleniyor', + controlsLive: 'CANLI', + controlsNextVideoIn: 'Sonraki video oynatılmadan', + overflowMenuPlaybackSpeed: 'Oynatma hızı', + overflowMenuSubtitles: 'Altyazı', + overflowMenuQuality: 'Kalite', + overflowMenuAudioTracks: 'Ses', + qualityAuto: 'Otomatik', + ); factory BetterPlayerTranslations.vietnamese() => BetterPlayerTranslations( - languageCode: "vi", - generalDefaultError: "Video không thể phát bây giờ", - generalNone: "Không có", - generalDefault: "Mặc định", - generalRetry: "Thử lại ngay", - controlsLive: "Trực tiếp", - playlistLoadingNextVideo: "Đang tải video tiếp theo", - controlsNextVideoIn: "Video tiếp theo", - overflowMenuPlaybackSpeed: "Tốc độ phát", - overflowMenuSubtitles: "Phụ đề", - overflowMenuQuality: "Chất lượng", - overflowMenuAudioTracks: "Âm thanh", - qualityAuto: "Tự động", + languageCode: 'vi', + generalDefaultError: 'Video không thể phát bây giờ', + generalNone: 'Không có', + generalDefault: 'Mặc định', + generalRetry: 'Thử lại ngay', + controlsLive: 'Trực tiếp', + playlistLoadingNextVideo: 'Đang tải video tiếp theo', + controlsNextVideoIn: 'Video tiếp theo', + overflowMenuPlaybackSpeed: 'Tốc độ phát', + overflowMenuSubtitles: 'Phụ đề', + overflowMenuQuality: 'Chất lượng', + overflowMenuAudioTracks: 'Âm thanh', + qualityAuto: 'Tự động', ); factory BetterPlayerTranslations.spanish() => BetterPlayerTranslations( - languageCode: "es", - generalDefaultError: "No se puede reproducir el video", - generalNone: "Ninguno", - generalDefault: "Por defecto", - generalRetry: "Reintentar", - controlsLive: "EN DIRECTO", - playlistLoadingNextVideo: "Cargando siguiente video", - controlsNextVideoIn: "Siguiente video en", - overflowMenuPlaybackSpeed: "Velocidad", - overflowMenuSubtitles: "Subtítulos", - overflowMenuQuality: "Calidad", - qualityAuto: "Automática", + languageCode: 'es', + generalDefaultError: 'No se puede reproducir el video', + generalNone: 'Ninguno', + generalDefault: 'Por defecto', + generalRetry: 'Reintentar', + controlsLive: 'EN DIRECTO', + playlistLoadingNextVideo: 'Cargando siguiente video', + controlsNextVideoIn: 'Siguiente video en', + overflowMenuPlaybackSpeed: 'Velocidad', + overflowMenuSubtitles: 'Subtítulos', + overflowMenuQuality: 'Calidad', + qualityAuto: 'Automática', ); + final String languageCode; + final String generalDefaultError; + final String generalNone; + final String generalDefault; + final String generalRetry; + final String playlistLoadingNextVideo; + final String controlsLive; + final String controlsNextVideoIn; + final String overflowMenuPlaybackSpeed; + final String overflowMenuSubtitles; + final String overflowMenuQuality; + final String overflowMenuAudioTracks; + final String qualityAuto; } diff --git a/lib/src/controls/better_player_clickable_widget.dart b/lib/src/controls/better_player_clickable_widget.dart index da21124cf..b914546b7 100644 --- a/lib/src/controls/better_player_clickable_widget.dart +++ b/lib/src/controls/better_player_clickable_widget.dart @@ -2,14 +2,13 @@ import 'package:flutter/material.dart'; class BetterPlayerMaterialClickableWidget extends StatelessWidget { - final Widget child; - final void Function() onTap; - const BetterPlayerMaterialClickableWidget({ - Key? key, required this.onTap, required this.child, - }) : super(key: key); + super.key, + }); + final Widget child; + final void Function() onTap; @override Widget build(BuildContext context) { diff --git a/lib/src/controls/better_player_controls_state.dart b/lib/src/controls/better_player_controls_state.dart index a6086b68a..312b3a666 100644 --- a/lib/src/controls/better_player_controls_state.dart +++ b/lib/src/controls/better_player_controls_state.dart @@ -37,8 +37,9 @@ abstract class BetterPlayerControlsState final beginning = const Duration().inMilliseconds; final skip = (latestValue!.position - Duration( - milliseconds: betterPlayerControlsConfiguration - .backwardSkipTimeInMilliseconds)) + milliseconds: betterPlayerControlsConfiguration + .backwardSkipTimeInMilliseconds, + )) .inMilliseconds; betterPlayerController! .seekTo(Duration(milliseconds: max(skip, beginning))); @@ -51,8 +52,9 @@ abstract class BetterPlayerControlsState final end = latestValue!.duration!.inMilliseconds; final skip = (latestValue!.position + Duration( - milliseconds: betterPlayerControlsConfiguration - .forwardSkipTimeInMilliseconds)) + milliseconds: betterPlayerControlsConfiguration + .forwardSkipTimeInMilliseconds, + )) .inMilliseconds; betterPlayerController!.seekTo(Duration(milliseconds: min(skip, end))); } @@ -108,7 +110,7 @@ abstract class BetterPlayerControlsState customItem.onClicked.call(); }, ), - ) + ), ], ), ), @@ -116,7 +118,10 @@ abstract class BetterPlayerControlsState } Widget _buildMoreOptionsListRow( - IconData icon, String name, void Function() onTap) { + IconData icon, + String name, + void Function() onTap, + ) { return BetterPlayerMaterialClickableWidget( onTap: onTap, child: Padding( @@ -144,16 +149,16 @@ abstract class BetterPlayerControlsState _buildSpeedRow(0.25), _buildSpeedRow(0.5), _buildSpeedRow(0.75), - _buildSpeedRow(1.0), + _buildSpeedRow(1), _buildSpeedRow(1.25), _buildSpeedRow(1.5), _buildSpeedRow(1.75), - _buildSpeedRow(2.0), + _buildSpeedRow(2), ]); } Widget _buildSpeedRow(double value) { - final bool isSelected = + final isSelected = betterPlayerController!.videoPlayerController!.value.speed == value; return BetterPlayerMaterialClickableWidget( @@ -167,17 +172,17 @@ abstract class BetterPlayerControlsState children: [ SizedBox(width: isSelected ? 8 : 16), Visibility( - visible: isSelected, - child: Icon( - Icons.check_outlined, - color: - betterPlayerControlsConfiguration.overflowModalTextColor, - )), + visible: isSelected, + child: Icon( + Icons.check_outlined, + color: betterPlayerControlsConfiguration.overflowModalTextColor, + ), + ), const SizedBox(width: 16), Text( - "$value x", + '$value x', style: _getOverflowMenuElementTextStyle(isSelected), - ) + ), ], ), ), @@ -191,7 +196,7 @@ abstract class BetterPlayerControlsState return true; } - final Duration position = latestValue.position; + final position = latestValue.position; Duration? bufferedEndPosition; if (latestValue.buffered.isNotEmpty == true) { @@ -215,21 +220,26 @@ abstract class BetterPlayerControlsState final subtitles = List.of(betterPlayerController!.betterPlayerSubtitlesSourceList); final noneSubtitlesElementExists = subtitles.firstWhereOrNull( - (source) => source.type == BetterPlayerSubtitlesSourceType.none) != + (source) => source.type == BetterPlayerSubtitlesSourceType.none, + ) != null; if (!noneSubtitlesElementExists) { - subtitles.add(BetterPlayerSubtitlesSource( - type: BetterPlayerSubtitlesSourceType.none)); + subtitles.add( + BetterPlayerSubtitlesSource( + type: BetterPlayerSubtitlesSourceType.none, + ), + ); } _showModalBottomSheet( - subtitles.map((source) => _buildSubtitlesSourceRow(source)).toList()); + subtitles.map(_buildSubtitlesSourceRow).toList(), + ); } Widget _buildSubtitlesSourceRow(BetterPlayerSubtitlesSource subtitlesSource) { final selectedSourceType = betterPlayerController!.betterPlayerSubtitlesSource; - final bool isSelected = (subtitlesSource == selectedSourceType) || + final isSelected = (subtitlesSource == selectedSourceType) || (subtitlesSource.type == BetterPlayerSubtitlesSourceType.none && subtitlesSource.type == selectedSourceType!.type); @@ -244,12 +254,12 @@ abstract class BetterPlayerControlsState children: [ SizedBox(width: isSelected ? 8 : 16), Visibility( - visible: isSelected, - child: Icon( - Icons.check_outlined, - color: - betterPlayerControlsConfiguration.overflowModalTextColor, - )), + visible: isSelected, + child: Icon( + Icons.check_outlined, + color: betterPlayerControlsConfiguration.overflowModalTextColor, + ), + ), const SizedBox(width: 16), Text( subtitlesSource.type == BetterPlayerSubtitlesSourceType.none @@ -269,11 +279,10 @@ abstract class BetterPlayerControlsState ///Resolution selection is used for normal videos void _showQualitiesSelectionWidget() { // HLS / DASH - final List asmsTrackNames = + final asmsTrackNames = betterPlayerController!.betterPlayerDataSource!.asmsTrackNames ?? []; - final List asmsTracks = - betterPlayerController!.betterPlayerAsmsTracks; - final List children = []; + final asmsTracks = betterPlayerController!.betterPlayerAsmsTracks; + final children = []; for (var index = 0; index < asmsTracks.length; index++) { final track = asmsTracks[index]; @@ -296,8 +305,10 @@ abstract class BetterPlayerControlsState if (children.isEmpty) { children.add( - _buildTrackRow(BetterPlayerAsmsTrack.defaultTrack(), - betterPlayerController!.translations.qualityAuto), + _buildTrackRow( + BetterPlayerAsmsTrack.defaultTrack(), + betterPlayerController!.translations.qualityAuto, + ), ); } @@ -305,16 +316,15 @@ abstract class BetterPlayerControlsState } Widget _buildTrackRow(BetterPlayerAsmsTrack track, String? preferredName) { - final int width = track.width ?? 0; - final int height = track.height ?? 0; - final int bitrate = track.bitrate ?? 0; - final String mimeType = (track.mimeType ?? '').replaceAll('video/', ''); - final String trackName = preferredName ?? - "${width}x$height ${BetterPlayerUtils.formatBitrate(bitrate)} $mimeType"; + final width = track.width ?? 0; + final height = track.height ?? 0; + final bitrate = track.bitrate ?? 0; + final mimeType = (track.mimeType ?? '').replaceAll('video/', ''); + final trackName = preferredName ?? + '${width}x$height ${BetterPlayerUtils.formatBitrate(bitrate)} $mimeType'; - final BetterPlayerAsmsTrack? selectedTrack = - betterPlayerController!.betterPlayerAsmsTrack; - final bool isSelected = selectedTrack != null && selectedTrack == track; + final selectedTrack = betterPlayerController!.betterPlayerAsmsTrack; + final isSelected = selectedTrack != null && selectedTrack == track; return BetterPlayerMaterialClickableWidget( onTap: () { @@ -327,12 +337,12 @@ abstract class BetterPlayerControlsState children: [ SizedBox(width: isSelected ? 8 : 16), Visibility( - visible: isSelected, - child: Icon( - Icons.check_outlined, - color: - betterPlayerControlsConfiguration.overflowModalTextColor, - )), + visible: isSelected, + child: Icon( + Icons.check_outlined, + color: betterPlayerControlsConfiguration.overflowModalTextColor, + ), + ), const SizedBox(width: 16), Text( trackName, @@ -345,7 +355,7 @@ abstract class BetterPlayerControlsState } Widget _buildResolutionSelectionRow(String name, String url) { - final bool isSelected = + final isSelected = url == betterPlayerController!.betterPlayerDataSource!.url; return BetterPlayerMaterialClickableWidget( onTap: () { @@ -358,12 +368,12 @@ abstract class BetterPlayerControlsState children: [ SizedBox(width: isSelected ? 8 : 16), Visibility( - visible: isSelected, - child: Icon( - Icons.check_outlined, - color: - betterPlayerControlsConfiguration.overflowModalTextColor, - )), + visible: isSelected, + child: Icon( + Icons.check_outlined, + color: betterPlayerControlsConfiguration.overflowModalTextColor, + ), + ), const SizedBox(width: 16), Text( name, @@ -377,14 +387,13 @@ abstract class BetterPlayerControlsState void _showAudioTracksSelectionWidget() { //HLS / DASH - final List? asmsTracks = - betterPlayerController!.betterPlayerAsmsAudioTracks; - final List children = []; - final BetterPlayerAsmsAudioTrack? selectedAsmsAudioTrack = + final asmsTracks = betterPlayerController!.betterPlayerAsmsAudioTracks; + final children = []; + final selectedAsmsAudioTrack = betterPlayerController!.betterPlayerAsmsAudioTrack; if (asmsTracks != null) { for (var index = 0; index < asmsTracks.length; index++) { - final bool isSelected = selectedAsmsAudioTrack != null && + final isSelected = selectedAsmsAudioTrack != null && selectedAsmsAudioTrack == asmsTracks[index]; children.add(_buildAudioTrackRow(asmsTracks[index], isSelected)); } @@ -405,7 +414,9 @@ abstract class BetterPlayerControlsState } Widget _buildAudioTrackRow( - BetterPlayerAsmsAudioTrack audioTrack, bool isSelected) { + BetterPlayerAsmsAudioTrack audioTrack, + bool isSelected, + ) { return BetterPlayerMaterialClickableWidget( onTap: () { Navigator.of(context).pop(); @@ -417,12 +428,12 @@ abstract class BetterPlayerControlsState children: [ SizedBox(width: isSelected ? 8 : 16), Visibility( - visible: isSelected, - child: Icon( - Icons.check_outlined, - color: - betterPlayerControlsConfiguration.overflowModalTextColor, - )), + visible: isSelected, + child: Icon( + Icons.check_outlined, + color: betterPlayerControlsConfiguration.overflowModalTextColor, + ), + ), const SizedBox(width: 16), Text( audioTrack.label!, @@ -468,8 +479,9 @@ abstract class BetterPlayerControlsState color: betterPlayerControlsConfiguration.overflowModalColor, /*shape: RoundedRectangleBorder(side: Bor,borderRadius: 24,)*/ borderRadius: const BorderRadius.only( - topLeft: Radius.circular(24.0), - topRight: Radius.circular(24.0)), + topLeft: Radius.circular(24), + topRight: Radius.circular(24), + ), ), child: Column( children: children, @@ -498,8 +510,9 @@ abstract class BetterPlayerControlsState decoration: BoxDecoration( color: betterPlayerControlsConfiguration.overflowModalColor, borderRadius: const BorderRadius.only( - topLeft: Radius.circular(24.0), - topRight: Radius.circular(24.0)), + topLeft: Radius.circular(24), + topRight: Radius.circular(24), + ), ), child: Column( children: children, @@ -522,7 +535,8 @@ abstract class BetterPlayerControlsState setState(() { if (notVisible) { betterPlayerController?.postEvent( - BetterPlayerEvent(BetterPlayerEventType.controlsHiddenStart)); + BetterPlayerEvent(BetterPlayerEventType.controlsHiddenStart), + ); } controlsNotVisible = notVisible; }); diff --git a/lib/src/controls/better_player_cupertino_controls.dart b/lib/src/controls/better_player_cupertino_controls.dart index f77d911b9..eab0ed029 100644 --- a/lib/src/controls/better_player_cupertino_controls.dart +++ b/lib/src/controls/better_player_cupertino_controls.dart @@ -11,18 +11,18 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class BetterPlayerCupertinoControls extends StatefulWidget { + const BetterPlayerCupertinoControls({ + required this.onControlsVisibilityChanged, + required this.controlsConfiguration, + super.key, + }); + ///Callback used to send information if player bar is hidden or not final Function(bool visbility) onControlsVisibilityChanged; ///Controls config final BetterPlayerControlsConfiguration controlsConfiguration; - const BetterPlayerCupertinoControls({ - required this.onControlsVisibilityChanged, - required this.controlsConfiguration, - Key? key, - }) : super(key: key); - @override State createState() { return _BetterPlayerCupertinoControlsState(); @@ -84,24 +84,26 @@ class _BetterPlayerCupertinoControlsState final isFullScreen = _betterPlayerController?.isFullScreen == true; _wasLoading = isLoading(_latestValue); - final controlsColumn = Column(children: [ - _buildTopBar( - backgroundColor, - iconColor, - barHeight, - buttonPadding, - ), - if (_wasLoading) - Expanded(child: Center(child: _buildLoadingWidget())) - else - _buildHitArea(), - _buildNextVideoWidget(), - _buildBottomBar( - backgroundColor, - iconColor, - barHeight, - ), - ]); + final controlsColumn = Column( + children: [ + _buildTopBar( + backgroundColor, + iconColor, + barHeight, + buttonPadding, + ), + if (_wasLoading) + Expanded(child: Center(child: _buildLoadingWidget())) + else + _buildHitArea(), + _buildNextVideoWidget(), + _buildBottomBar( + backgroundColor, + iconColor, + barHeight, + ), + ], + ); return GestureDetector( onTap: () { if (BetterPlayerMultipleGestureDetector.of(context) != null) { @@ -124,9 +126,9 @@ class _BetterPlayerCupertinoControlsState } }, child: AbsorbPointer( - absorbing: controlsNotVisible, - child: - isFullScreen ? SafeArea(child: controlsColumn) : controlsColumn), + absorbing: controlsNotVisible, + child: isFullScreen ? SafeArea(child: controlsColumn) : controlsColumn, + ), ); } @@ -146,11 +148,11 @@ class _BetterPlayerCupertinoControlsState @override void didChangeDependencies() { - final _oldController = _betterPlayerController; + final oldController = _betterPlayerController; _betterPlayerController = BetterPlayerController.of(context); _controller = _betterPlayerController!.videoPlayerController; - if (_oldController != _betterPlayerController) { + if (oldController != _betterPlayerController) { _dispose(); _initialize(); } @@ -218,7 +220,7 @@ class _BetterPlayerCupertinoControlsState if (_controlsConfiguration.enableProgressText) _buildRemaining() else - const SizedBox() + const SizedBox(), ], ), ), @@ -232,8 +234,9 @@ class _BetterPlayerCupertinoControlsState child: Text( _betterPlayerController!.translations.controlsLive, style: TextStyle( - color: _controlsConfiguration.liveTextColor, - fontWeight: FontWeight.bold), + color: _controlsConfiguration.liveTextColor, + fontWeight: FontWeight.bold, + ), ), ); } @@ -305,14 +308,12 @@ class _BetterPlayerCupertinoControlsState double buttonPadding, ) { return GestureDetector( - onTap: () { - onShowMoreClicked(); - }, + onTap: onShowMoreClicked, child: AnimatedOpacity( opacity: controlsNotVisible ? 0.0 : 1.0, duration: _controlsConfiguration.controlsHideTime, child: ClipRRect( - borderRadius: BorderRadius.circular(10.0), + borderRadius: BorderRadius.circular(10), child: Container( decoration: BoxDecoration( color: backgroundColor, @@ -350,14 +351,14 @@ class _BetterPlayerCupertinoControlsState controller!.setVolume(_latestVolume ?? 0.5); } else { _latestVolume = controller!.value.volume; - controller.setVolume(0.0); + controller.setVolume(0); } }, child: AnimatedOpacity( opacity: controlsNotVisible ? 0.0 : 1.0, duration: _controlsConfiguration.controlsHideTime, child: ClipRRect( - borderRadius: BorderRadius.circular(10.0), + borderRadius: BorderRadius.circular(10), child: Container( decoration: BoxDecoration( color: backgroundColor, @@ -408,12 +409,12 @@ class _BetterPlayerCupertinoControlsState _latestValue != null ? _latestValue!.position : const Duration(); return Padding( - padding: const EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.only(right: 12), child: Text( BetterPlayerUtils.formatDuration(position), style: TextStyle( color: _controlsConfiguration.textColor, - fontSize: 12.0, + fontSize: 12, ), ), ); @@ -425,11 +426,10 @@ class _BetterPlayerCupertinoControlsState : const Duration(); return Padding( - padding: const EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.only(right: 12), child: Text( '-${BetterPlayerUtils.formatDuration(position)}', - style: - TextStyle(color: _controlsConfiguration.textColor, fontSize: 12.0), + style: TextStyle(color: _controlsConfiguration.textColor, fontSize: 12), ), ); } @@ -440,7 +440,7 @@ class _BetterPlayerCupertinoControlsState child: Container( height: barHeight, color: Colors.transparent, - margin: const EdgeInsets.only(left: 10.0), + margin: const EdgeInsets.only(left: 10), padding: const EdgeInsets.symmetric( horizontal: 8, ), @@ -460,7 +460,7 @@ class _BetterPlayerCupertinoControlsState height: barHeight, color: Colors.transparent, padding: const EdgeInsets.symmetric(horizontal: 6), - margin: const EdgeInsets.only(right: 8.0), + margin: const EdgeInsets.only(right: 8), child: Icon( _controlsConfiguration.skipForwardIcon, color: iconColor, @@ -565,7 +565,7 @@ class _BetterPlayerCupertinoControlsState child: Padding( padding: const EdgeInsets.all(12), child: Text( - "${_betterPlayerController!.translations.controlsNextVideoIn} $time ...", + '${_betterPlayerController!.translations.controlsNextVideoIn} $time ...', style: const TextStyle(color: Colors.white), ), ), @@ -615,16 +615,14 @@ class _BetterPlayerCupertinoControlsState changePlayerControlsNotVisible(true); _betterPlayerController!.toggleFullScreen(); _expandCollapseTimer = Timer(_controlsConfiguration.controlsHideTime, () { - setState(() { - cancelAndRestartTimer(); - }); + setState(cancelAndRestartTimer); }); } Widget _buildProgressBar() { return Expanded( child: Padding( - padding: const EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.only(right: 12), child: BetterPlayerCupertinoVideoProgressBar( _controller, _betterPlayerController, @@ -638,18 +636,18 @@ class _BetterPlayerCupertinoControlsState cancelAndRestartTimer(); }, colors: BetterPlayerProgressColors( - playedColor: _controlsConfiguration.progressBarPlayedColor, - handleColor: _controlsConfiguration.progressBarHandleColor, - bufferedColor: _controlsConfiguration.progressBarBufferedColor, - backgroundColor: - _controlsConfiguration.progressBarBackgroundColor), + playedColor: _controlsConfiguration.progressBarPlayedColor, + handleColor: _controlsConfiguration.progressBarHandleColor, + bufferedColor: _controlsConfiguration.progressBarBufferedColor, + backgroundColor: _controlsConfiguration.progressBarBackgroundColor, + ), ), ), ); } void _onPlayPause() { - bool isFinished = false; + var isFinished = false; if (_latestValue?.position != null && _latestValue?.duration != null) { isFinished = _latestValue!.position >= _latestValue!.duration!; @@ -713,9 +711,9 @@ class _BetterPlayerCupertinoControlsState _betterPlayerController!.betterPlayerConfiguration.errorBuilder; if (errorBuilder != null) { return errorBuilder( - context, - _betterPlayerController! - .videoPlayerController!.value.errorDescription); + context, + _betterPlayerController!.videoPlayerController!.value.errorDescription, + ); } else { final textStyle = TextStyle(color: _controlsConfiguration.textColor); return Center( @@ -740,7 +738,7 @@ class _BetterPlayerCupertinoControlsState _betterPlayerController!.translations.generalRetry, style: textStyle.copyWith(fontWeight: FontWeight.bold), ), - ) + ), ], ), ); @@ -774,7 +772,8 @@ class _BetterPlayerCupertinoControlsState return GestureDetector( onTap: () { betterPlayerController!.enablePictureInPicture( - betterPlayerController!.betterPlayerGlobalKey!); + betterPlayerController!.betterPlayerGlobalKey!, + ); }, child: AnimatedOpacity( opacity: controlsNotVisible ? 0.0 : 1.0, diff --git a/lib/src/controls/better_player_cupertino_progress_bar.dart b/lib/src/controls/better_player_cupertino_progress_bar.dart index 713cb37c9..d01e82815 100644 --- a/lib/src/controls/better_player_cupertino_progress_bar.dart +++ b/lib/src/controls/better_player_cupertino_progress_bar.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:better_player/src/controls/better_player_progress_colors.dart'; import 'package:better_player/src/core/better_player_controller.dart'; import 'package:better_player/src/video_player/video_player.dart'; -import 'package:better_player/src/video_player/video_player_platform_interface.dart'; import 'package:flutter/material.dart'; class BetterPlayerCupertinoVideoProgressBar extends StatefulWidget { @@ -14,9 +13,8 @@ class BetterPlayerCupertinoVideoProgressBar extends StatefulWidget { this.onDragStart, this.onDragUpdate, this.onTapDown, - Key? key, - }) : colors = colors ?? BetterPlayerProgressColors(), - super(key: key); + super.key, + }) : colors = colors ?? BetterPlayerProgressColors(); final VideoPlayerController? controller; final BetterPlayerController? betterPlayerController; @@ -67,7 +65,7 @@ class _VideoProgressBarState @override Widget build(BuildContext context) { - final bool enableProgressBarDrag = betterPlayerController! + final enableProgressBarDrag = betterPlayerController! .betterPlayerControlsConfiguration.enableProgressBarDrag; return GestureDetector( onHorizontalDragStart: (DragStartDetails details) { @@ -154,14 +152,14 @@ class _VideoProgressBarState } } - void seekToRelativePosition(Offset globalPosition) async { - final RenderObject? renderObject = context.findRenderObject(); + Future seekToRelativePosition(Offset globalPosition) async { + final renderObject = context.findRenderObject(); if (renderObject != null) { final box = renderObject as RenderBox; - final Offset tapPos = box.globalToLocal(globalPosition); - final double relative = tapPos.dx / box.size.width; + final tapPos = box.globalToLocal(globalPosition); + final relative = tapPos.dx / box.size.width; if (relative > 0) { - final Duration position = controller!.value.duration! * relative; + final position = controller!.value.duration! * relative; lastSeek = position; await betterPlayerController!.seekTo(position); onFinishedLastSeek(); @@ -202,30 +200,30 @@ class _ProgressBarPainter extends CustomPainter { canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( - Offset(0.0, baseOffset), + Offset(0, baseOffset), Offset(size.width, baseOffset + barHeight), ), - const Radius.circular(4.0), + const Radius.circular(4), ), colors.backgroundPaint, ); if (!value.initialized) { return; } - final double playedPartPercent = + final playedPartPercent = value.position.inMilliseconds / value.duration!.inMilliseconds; - final double playedPart = + final playedPart = playedPartPercent > 1 ? size.width : playedPartPercent * size.width; - for (final DurationRange range in value.buffered) { - final double start = range.startFraction(value.duration!) * size.width; - final double end = range.endFraction(value.duration!) * size.width; + for (final range in value.buffered) { + final start = range.startFraction(value.duration!) * size.width; + final end = range.endFraction(value.duration!) * size.width; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( Offset(start, baseOffset), Offset(end, baseOffset + barHeight), ), - const Radius.circular(4.0), + const Radius.circular(4), ), colors.bufferedPaint, ); @@ -233,18 +231,21 @@ class _ProgressBarPainter extends CustomPainter { canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( - Offset(0.0, baseOffset), + Offset(0, baseOffset), Offset(playedPart, baseOffset + barHeight), ), - const Radius.circular(4.0), + const Radius.circular(4), ), colors.playedPaint, ); final shadowPath = Path() - ..addOval(Rect.fromCircle( + ..addOval( + Rect.fromCircle( center: Offset(playedPart, baseOffset + barHeight / 2), - radius: handleHeight)); + radius: handleHeight, + ), + ); canvas.drawShadow(shadowPath, Colors.black, 0.2, false); canvas.drawCircle( diff --git a/lib/src/controls/better_player_material_controls.dart b/lib/src/controls/better_player_material_controls.dart index a018854bb..b393b2fb0 100644 --- a/lib/src/controls/better_player_material_controls.dart +++ b/lib/src/controls/better_player_material_controls.dart @@ -13,18 +13,18 @@ import 'package:better_player/src/video_player/video_player.dart'; import 'package:flutter/material.dart'; class BetterPlayerMaterialControls extends StatefulWidget { + const BetterPlayerMaterialControls({ + required this.onControlsVisibilityChanged, + required this.controlsConfiguration, + super.key, + }); + ///Callback used to send information if player bar is hidden or not final Function(bool visbility) onControlsVisibilityChanged; ///Controls config final BetterPlayerControlsConfiguration controlsConfiguration; - const BetterPlayerMaterialControls({ - Key? key, - required this.onControlsVisibilityChanged, - required this.controlsConfiguration, - }) : super(key: key); - @override State createState() { return _BetterPlayerMaterialControlsState(); @@ -130,12 +130,12 @@ class _BetterPlayerMaterialControlsState @override void didChangeDependencies() { - final _oldController = _betterPlayerController; + final oldController = _betterPlayerController; _betterPlayerController = BetterPlayerController.of(context); _controller = _betterPlayerController!.videoPlayerController; _latestValue = _controller!.value; - if (_oldController != _betterPlayerController) { + if (oldController != _betterPlayerController) { _dispose(); _initialize(); } @@ -148,9 +148,9 @@ class _BetterPlayerMaterialControlsState _betterPlayerController!.betterPlayerConfiguration.errorBuilder; if (errorBuilder != null) { return errorBuilder( - context, - _betterPlayerController! - .videoPlayerController!.value.errorDescription); + context, + _betterPlayerController!.videoPlayerController!.value.errorDescription, + ); } else { final textStyle = TextStyle(color: _controlsConfiguration.textColor); return Center( @@ -175,7 +175,7 @@ class _BetterPlayerMaterialControlsState _betterPlayerController!.translations.generalRetry, style: textStyle.copyWith(fontWeight: FontWeight.bold), ), - ) + ), ], ), ); @@ -193,7 +193,7 @@ class _BetterPlayerMaterialControlsState opacity: controlsNotVisible ? 0.0 : 1.0, duration: _controlsConfiguration.controlsHideTime, onEnd: _onPlayerHide, - child: Container( + child: SizedBox( height: _controlsConfiguration.controlBarHeight, width: double.infinity, child: Row( @@ -201,7 +201,9 @@ class _BetterPlayerMaterialControlsState children: [ if (_controlsConfiguration.enablePip) _buildPipButtonWrapperWidget( - controlsNotVisible, _onPlayerHide) + controlsNotVisible, + _onPlayerHide, + ) else const SizedBox(), _buildMoreButton(), @@ -217,7 +219,8 @@ class _BetterPlayerMaterialControlsState return BetterPlayerMaterialClickableWidget( onTap: () { betterPlayerController!.enablePictureInPicture( - betterPlayerController!.betterPlayerGlobalKey!); + betterPlayerController!.betterPlayerGlobalKey!, + ); }, child: Padding( padding: const EdgeInsets.all(8), @@ -230,18 +233,20 @@ class _BetterPlayerMaterialControlsState } Widget _buildPipButtonWrapperWidget( - bool hideStuff, void Function() onPlayerHide) { + bool hideStuff, + void Function() onPlayerHide, + ) { return FutureBuilder( future: betterPlayerController!.isPictureInPictureSupported(), builder: (context, snapshot) { - final bool isPipSupported = snapshot.data ?? false; + final isPipSupported = snapshot.data ?? false; if (isPipSupported && _betterPlayerController!.betterPlayerGlobalKey != null) { return AnimatedOpacity( opacity: hideStuff ? 0.0 : 1.0, duration: betterPlayerControlsConfiguration.controlsHideTime, onEnd: onPlayerHide, - child: Container( + child: SizedBox( height: betterPlayerControlsConfiguration.controlBarHeight, child: Row( mainAxisAlignment: MainAxisAlignment.end, @@ -260,9 +265,7 @@ class _BetterPlayerMaterialControlsState Widget _buildMoreButton() { return BetterPlayerMaterialClickableWidget( - onTap: () { - onShowMoreClicked(); - }, + onTap: onShowMoreClicked, child: Padding( padding: const EdgeInsets.all(8), child: Icon( @@ -281,7 +284,7 @@ class _BetterPlayerMaterialControlsState opacity: controlsNotVisible ? 0.0 : 1.0, duration: _controlsConfiguration.controlsHideTime, onEnd: _onPlayerHide, - child: Container( + child: SizedBox( height: _controlsConfiguration.controlBarHeight + 20.0, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -328,14 +331,15 @@ class _BetterPlayerMaterialControlsState return Text( _betterPlayerController!.translations.controlsLive, style: TextStyle( - color: _controlsConfiguration.liveTextColor, - fontWeight: FontWeight.bold), + color: _controlsConfiguration.liveTextColor, + fontWeight: FontWeight.bold, + ), ); } Widget _buildExpandButton() { return Padding( - padding: EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.only(right: 12), child: BetterPlayerMaterialClickableWidget( onTap: _onExpandCollapse, child: AnimatedOpacity( @@ -343,7 +347,7 @@ class _BetterPlayerMaterialControlsState duration: _controlsConfiguration.controlsHideTime, child: Container( height: _controlsConfiguration.controlBarHeight, - padding: const EdgeInsets.symmetric(horizontal: 8.0), + padding: const EdgeInsets.symmetric(horizontal: 8), child: Center( child: Icon( _betterPlayerController!.isFullScreen @@ -397,10 +401,12 @@ class _BetterPlayerMaterialControlsState ); } - Widget _buildHitAreaClickableButton( - {Widget? icon, required void Function() onClicked}) { + Widget _buildHitAreaClickableButton({ + required void Function() onClicked, + Widget? icon, + }) { return Container( - constraints: const BoxConstraints(maxHeight: 80.0, maxWidth: 80.0), + constraints: const BoxConstraints(maxHeight: 80, maxWidth: 80), child: BetterPlayerMaterialClickableWidget( onTap: onClicked, child: Align( @@ -444,7 +450,7 @@ class _BetterPlayerMaterialControlsState } Widget _buildReplayButton(VideoPlayerController controller) { - final bool isFinished = isVideoFinished(_latestValue); + final isFinished = isVideoFinished(_latestValue); return _buildHitAreaClickableButton( icon: isFinished ? Icon( @@ -492,8 +498,9 @@ class _BetterPlayerMaterialControlsState alignment: Alignment.bottomRight, child: Container( margin: EdgeInsets.only( - bottom: _controlsConfiguration.controlBarHeight + 20, - right: 24), + bottom: _controlsConfiguration.controlBarHeight + 20, + right: 24, + ), decoration: BoxDecoration( color: _controlsConfiguration.controlBarColor, borderRadius: BorderRadius.circular(16), @@ -501,7 +508,7 @@ class _BetterPlayerMaterialControlsState child: Padding( padding: const EdgeInsets.all(12), child: Text( - "${_betterPlayerController!.translations.controlsNextVideoIn} $time...", + '${_betterPlayerController!.translations.controlsNextVideoIn} $time...', style: const TextStyle(color: Colors.white), ), ), @@ -525,7 +532,7 @@ class _BetterPlayerMaterialControlsState _betterPlayerController!.setVolume(_latestVolume ?? 0.5); } else { _latestVolume = controller!.value.volume; - _betterPlayerController!.setVolume(0.0); + _betterPlayerController!.setVolume(0); } }, child: AnimatedOpacity( @@ -549,7 +556,7 @@ class _BetterPlayerMaterialControlsState Widget _buildPlayPause(VideoPlayerController controller) { return BetterPlayerMaterialClickableWidget( - key: const Key("better_player_material_controls_play_pause_button"), + key: const Key('better_player_material_controls_play_pause_button'), onTap: _onPlayPause, child: Container( height: double.infinity, @@ -578,22 +585,23 @@ class _BetterPlayerMaterialControlsState : const EdgeInsets.symmetric(horizontal: 22), child: RichText( text: TextSpan( - text: BetterPlayerUtils.formatDuration(position), - style: TextStyle( - fontSize: 10.0, - color: _controlsConfiguration.textColor, - decoration: TextDecoration.none, + text: BetterPlayerUtils.formatDuration(position), + style: TextStyle( + fontSize: 10, + color: _controlsConfiguration.textColor, + decoration: TextDecoration.none, + ), + children: [ + TextSpan( + text: ' / ${BetterPlayerUtils.formatDuration(duration)}', + style: TextStyle( + fontSize: 10, + color: _controlsConfiguration.textColor, + decoration: TextDecoration.none, + ), ), - children: [ - TextSpan( - text: ' / ${BetterPlayerUtils.formatDuration(duration)}', - style: TextStyle( - fontSize: 10.0, - color: _controlsConfiguration.textColor, - decoration: TextDecoration.none, - ), - ) - ]), + ], + ), ), ); } @@ -637,14 +645,12 @@ class _BetterPlayerMaterialControlsState _betterPlayerController!.toggleFullScreen(); _showAfterExpandCollapseTimer = Timer(_controlsConfiguration.controlsHideTime, () { - setState(() { - cancelAndRestartTimer(); - }); + setState(cancelAndRestartTimer); }); } void _onPlayPause() { - bool isFinished = false; + var isFinished = false; if (_latestValue?.position != null && _latestValue?.duration != null) { isFinished = _latestValue!.position >= _latestValue!.duration!; @@ -713,11 +719,11 @@ class _BetterPlayerMaterialControlsState cancelAndRestartTimer(); }, colors: BetterPlayerProgressColors( - playedColor: _controlsConfiguration.progressBarPlayedColor, - handleColor: _controlsConfiguration.progressBarHandleColor, - bufferedColor: _controlsConfiguration.progressBarBufferedColor, - backgroundColor: - _controlsConfiguration.progressBarBackgroundColor), + playedColor: _controlsConfiguration.progressBarPlayedColor, + handleColor: _controlsConfiguration.progressBarHandleColor, + bufferedColor: _controlsConfiguration.progressBarBufferedColor, + backgroundColor: _controlsConfiguration.progressBarBackgroundColor, + ), ), ), ); diff --git a/lib/src/controls/better_player_material_progress_bar.dart b/lib/src/controls/better_player_material_progress_bar.dart index 4d5d07acc..e07a88e20 100644 --- a/lib/src/controls/better_player_material_progress_bar.dart +++ b/lib/src/controls/better_player_material_progress_bar.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:better_player/better_player.dart'; import 'package:better_player/src/video_player/video_player.dart'; -import 'package:better_player/src/video_player/video_player_platform_interface.dart'; import 'package:flutter/material.dart'; class BetterPlayerMaterialVideoProgressBar extends StatefulWidget { @@ -13,9 +12,8 @@ class BetterPlayerMaterialVideoProgressBar extends StatefulWidget { this.onDragStart, this.onDragUpdate, this.onTapDown, - Key? key, - }) : colors = colors ?? BetterPlayerProgressColors(), - super(key: key); + super.key, + }) : colors = colors ?? BetterPlayerProgressColors(); final VideoPlayerController? controller; final BetterPlayerController? betterPlayerController; @@ -66,7 +64,7 @@ class _VideoProgressBarState @override Widget build(BuildContext context) { - final bool enableProgressBarDrag = betterPlayerController! + final enableProgressBarDrag = betterPlayerController! .betterPlayerConfiguration.controlsConfiguration.enableProgressBarDrag; return GestureDetector( @@ -156,14 +154,14 @@ class _VideoProgressBarState } } - void seekToRelativePosition(Offset globalPosition) async { - final RenderObject? renderObject = context.findRenderObject(); + Future seekToRelativePosition(Offset globalPosition) async { + final renderObject = context.findRenderObject(); if (renderObject != null) { final box = renderObject as RenderBox; - final Offset tapPos = box.globalToLocal(globalPosition); - final double relative = tapPos.dx / box.size.width; + final tapPos = box.globalToLocal(globalPosition); + final relative = tapPos.dx / box.size.width; if (relative > 0) { - final Duration position = controller!.value.duration! * relative; + final position = controller!.value.duration! * relative; lastSeek = position; await betterPlayerController!.seekTo(position); onFinishedLastSeek(); @@ -202,29 +200,29 @@ class _ProgressBarPainter extends CustomPainter { canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( - Offset(0.0, size.height / 2), + Offset(0, size.height / 2), Offset(size.width, size.height / 2 + height), ), - const Radius.circular(4.0), + const Radius.circular(4), ), colors.backgroundPaint, ); if (!value.initialized) { return; } - double playedPartPercent = + var playedPartPercent = value.position.inMilliseconds / value.duration!.inMilliseconds; if (playedPartPercent.isNaN) { playedPartPercent = 0; } - final double playedPart = + final playedPart = playedPartPercent > 1 ? size.width : playedPartPercent * size.width; - for (final DurationRange range in value.buffered) { - double start = range.startFraction(value.duration!) * size.width; + for (final range in value.buffered) { + var start = range.startFraction(value.duration!) * size.width; if (start.isNaN) { start = 0; } - double end = range.endFraction(value.duration!) * size.width; + var end = range.endFraction(value.duration!) * size.width; if (end.isNaN) { end = 0; } @@ -234,7 +232,7 @@ class _ProgressBarPainter extends CustomPainter { Offset(start, size.height / 2), Offset(end, size.height / 2 + height), ), - const Radius.circular(4.0), + const Radius.circular(4), ), colors.bufferedPaint, ); @@ -242,10 +240,10 @@ class _ProgressBarPainter extends CustomPainter { canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromPoints( - Offset(0.0, size.height / 2), + Offset(0, size.height / 2), Offset(playedPart, size.height / 2 + height), ), - const Radius.circular(4.0), + const Radius.circular(4), ), colors.playedPaint, ); diff --git a/lib/src/controls/better_player_multiple_gesture_detector.dart b/lib/src/controls/better_player_multiple_gesture_detector.dart index c43f00e4a..0bc0a71cc 100644 --- a/lib/src/controls/better_player_multiple_gesture_detector.dart +++ b/lib/src/controls/better_player_multiple_gesture_detector.dart @@ -3,17 +3,16 @@ import 'package:flutter/material.dart'; ///Helper class for GestureDetector used within Better Player. Used to pass ///gestures to upper GestureDetectors. class BetterPlayerMultipleGestureDetector extends InheritedWidget { - final void Function()? onTap; - final void Function()? onDoubleTap; - final void Function()? onLongPress; - const BetterPlayerMultipleGestureDetector({ - Key? key, - required Widget child, + required super.child, + super.key, this.onTap, this.onDoubleTap, this.onLongPress, - }) : super(key: key, child: child); + }); + final void Function()? onTap; + final void Function()? onDoubleTap; + final void Function()? onLongPress; static BetterPlayerMultipleGestureDetector? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType< diff --git a/lib/src/controls/better_player_overflow_menu_item.dart b/lib/src/controls/better_player_overflow_menu_item.dart index 9148dbfe2..90bcdac69 100644 --- a/lib/src/controls/better_player_overflow_menu_item.dart +++ b/lib/src/controls/better_player_overflow_menu_item.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; ///Menu item data used in overflow menu (3 dots). class BetterPlayerOverflowMenuItem { + BetterPlayerOverflowMenuItem(this.icon, this.title, this.onClicked); + ///Icon of menu item final IconData icon; @@ -11,6 +13,4 @@ class BetterPlayerOverflowMenuItem { ///Callback when item is clicked final Function() onClicked; - - BetterPlayerOverflowMenuItem(this.icon, this.title, this.onClicked); } diff --git a/lib/src/controls/better_player_progress_colors.dart b/lib/src/controls/better_player_progress_colors.dart index 9148c7999..b3dbfba2f 100644 --- a/lib/src/controls/better_player_progress_colors.dart +++ b/lib/src/controls/better_player_progress_colors.dart @@ -6,7 +6,7 @@ class BetterPlayerProgressColors { BetterPlayerProgressColors({ Color playedColor = const Color.fromRGBO(255, 0, 0, 0.7), Color bufferedColor = const Color.fromRGBO(30, 30, 200, 0.2), - Color handleColor = const Color.fromRGBO(200, 200, 200, 1.0), + Color handleColor = const Color.fromRGBO(200, 200, 200, 1), Color backgroundColor = const Color.fromRGBO(200, 200, 200, 0.5), }) : playedPaint = Paint()..color = playedColor, bufferedPaint = Paint()..color = bufferedColor, diff --git a/lib/src/core/better_player.dart b/lib/src/core/better_player.dart index 7160e9314..88d28a4b5 100644 --- a/lib/src/core/better_player.dart +++ b/lib/src/core/better_player.dart @@ -10,7 +10,7 @@ import 'package:wakelock_plus/wakelock_plus.dart'; ///Widget which uses provided controller to render video player. class BetterPlayer extends StatefulWidget { - const BetterPlayer({Key? key, required this.controller}) : super(key: key); + const BetterPlayer({required this.controller, super.key}); factory BetterPlayer.network( String url, { @@ -84,7 +84,7 @@ class _BetterPlayerState extends State widget.controller.controllerEventStream.listen(onControllerEvent); //Default locale - var locale = const Locale("en", "US"); + var locale = const Locale('en', 'US'); try { if (mounted) { final contextLocale = Localizations.localeOf(context); @@ -104,17 +104,20 @@ class _BetterPlayerState extends State if (_isFullScreen) { WakelockPlus.disable(); _navigatorState.maybePop(); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: _betterPlayerConfiguration.systemOverlaysAfterFullScreen); + SystemChrome.setEnabledSystemUIMode( + SystemUiMode.manual, + overlays: _betterPlayerConfiguration.systemOverlaysAfterFullScreen, + ); SystemChrome.setPreferredOrientations( - _betterPlayerConfiguration.deviceOrientationsAfterFullScreen); + _betterPlayerConfiguration.deviceOrientationsAfterFullScreen, + ); } WidgetsBinding.instance.removeObserver(this); _controllerEventSubscription?.cancel(); widget.controller.dispose(); VisibilityDetectorController.instance - .forget(Key("${widget.controller.hashCode}_key")); + .forget(Key('${widget.controller.hashCode}_key')); super.dispose(); } @@ -132,10 +135,8 @@ class _BetterPlayerState extends State switch (event) { case BetterPlayerControllerEvent.openFullscreen: onFullScreenChanged(); - break; case BetterPlayerControllerEvent.hideFullscreen: onFullScreenChanged(); - break; default: setState(() {}); break; @@ -167,9 +168,10 @@ class _BetterPlayerState extends State } Widget _buildFullScreenVideo( - BuildContext context, - Animation animation, - BetterPlayerControllerProvider controllerProvider) { + BuildContext context, + Animation animation, + BetterPlayerControllerProvider controllerProvider, + ) { return Scaffold( resizeToAvoidBottomInset: false, body: Container( @@ -181,10 +183,11 @@ class _BetterPlayerState extends State } AnimatedWidget _defaultRoutePageBuilder( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - BetterPlayerControllerProvider controllerProvider) { + BuildContext context, + Animation animation, + Animation secondaryAnimation, + BetterPlayerControllerProvider controllerProvider, + ) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget? child) { @@ -199,16 +202,26 @@ class _BetterPlayerState extends State Animation secondaryAnimation, ) { final controllerProvider = BetterPlayerControllerProvider( - controller: widget.controller, child: _buildPlayer()); + controller: widget.controller, + child: _buildPlayer(), + ); final routePageBuilder = _betterPlayerConfiguration.routePageBuilder; if (routePageBuilder == null) { return _defaultRoutePageBuilder( - context, animation, secondaryAnimation, controllerProvider); + context, + animation, + secondaryAnimation, + controllerProvider, + ); } return routePageBuilder( - context, animation, secondaryAnimation, controllerProvider); + context, + animation, + secondaryAnimation, + controllerProvider, + ); } Future _pushFullScreenWidget(BuildContext context) async { @@ -227,12 +240,12 @@ class _BetterPlayerState extends State if (aspectRatio < 1.0) { deviceOrientations = [ DeviceOrientation.portraitUp, - DeviceOrientation.portraitDown + DeviceOrientation.portraitDown, ]; } else { deviceOrientations = [ DeviceOrientation.landscapeLeft, - DeviceOrientation.landscapeRight + DeviceOrientation.landscapeRight, ]; } await SystemChrome.setPreferredOrientations(deviceOrientations); @@ -255,15 +268,18 @@ class _BetterPlayerState extends State // so we do not need to check Wakelock.isEnabled. WakelockPlus.disable(); - await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: _betterPlayerConfiguration.systemOverlaysAfterFullScreen); + await SystemChrome.setEnabledSystemUIMode( + SystemUiMode.manual, + overlays: _betterPlayerConfiguration.systemOverlaysAfterFullScreen, + ); await SystemChrome.setPreferredOrientations( - _betterPlayerConfiguration.deviceOrientationsAfterFullScreen); + _betterPlayerConfiguration.deviceOrientationsAfterFullScreen, + ); } Widget _buildPlayer() { return VisibilityDetector( - key: Key("${widget.controller.hashCode}_key"), + key: Key('${widget.controller.hashCode}_key'), onVisibilityChanged: (VisibilityInfo info) => widget.controller.onPlayerVisibilityChanged(info.visibleFraction), child: BetterPlayerWithControls( @@ -281,7 +297,8 @@ class _BetterPlayerState extends State ///Page route builder used in fullscreen mode. typedef BetterPlayerRoutePageBuilder = Widget Function( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - BetterPlayerControllerProvider controllerProvider); + BuildContext context, + Animation animation, + Animation secondaryAnimation, + BetterPlayerControllerProvider controllerProvider, +); diff --git a/lib/src/core/better_player_controller.dart b/lib/src/core/better_player_controller.dart index d1a53bc31..39a964151 100644 --- a/lib/src/core/better_player_controller.dart +++ b/lib/src/core/better_player_controller.dart @@ -14,13 +14,25 @@ import 'package:path_provider/path_provider.dart'; ///Class used to control overall Better Player behavior. Main class to change ///state of Better Player. class BetterPlayerController { - static const String _durationParameter = "duration"; - static const String _progressParameter = "progress"; - static const String _bufferedParameter = "buffered"; - static const String _volumeParameter = "volume"; - static const String _speedParameter = "speed"; - static const String _dataSourceParameter = "dataSource"; - static const String _authorizationHeader = "Authorization"; + BetterPlayerController( + this.betterPlayerConfiguration, { + this.betterPlayerPlaylistConfiguration, + BetterPlayerDataSource? betterPlayerDataSource, + }) { + _betterPlayerControlsConfiguration = + betterPlayerConfiguration.controlsConfiguration; + _eventListeners.add(eventListener); + if (betterPlayerDataSource != null) { + setupDataSource(betterPlayerDataSource); + } + } + static const String _durationParameter = 'duration'; + static const String _progressParameter = 'progress'; + static const String _bufferedParameter = 'buffered'; + static const String _volumeParameter = 'volume'; + static const String _speedParameter = 'speed'; + static const String _dataSourceParameter = 'dataSource'; + static const String _authorizationHeader = 'Authorization'; ///General configuration used in controller instance. final BetterPlayerConfiguration betterPlayerConfiguration; @@ -210,19 +222,6 @@ class BetterPlayerController { ///Currently displayed [BetterPlayerSubtitle]. BetterPlayerSubtitle? renderedSubtitle; - BetterPlayerController( - this.betterPlayerConfiguration, { - this.betterPlayerPlaylistConfiguration, - BetterPlayerDataSource? betterPlayerDataSource, - }) { - this._betterPlayerControlsConfiguration = - betterPlayerConfiguration.controlsConfiguration; - _eventListeners.add(eventListener); - if (betterPlayerDataSource != null) { - setupDataSource(betterPlayerDataSource); - } - } - ///Get BetterPlayerController from context. Used in InheritedWidget. static BetterPlayerController of(BuildContext context) { final betterPLayerControllerProvider = context @@ -233,10 +232,14 @@ class BetterPlayerController { ///Setup new data source in Better Player. Future setupDataSource(BetterPlayerDataSource betterPlayerDataSource) async { - postEvent(BetterPlayerEvent(BetterPlayerEventType.setupDataSource, + postEvent( + BetterPlayerEvent( + BetterPlayerEventType.setupDataSource, parameters: { _dataSourceParameter: betterPlayerDataSource, - })); + }, + ), + ); _postControllerEvent(BetterPlayerControllerEvent.setupDataSource); _hasCurrentDataSourceStarted = false; _hasCurrentDataSourceInitialized = false; @@ -246,8 +249,8 @@ class BetterPlayerController { ///Build videoPlayerController if null if (videoPlayerController == null) { videoPlayerController = VideoPlayerController( - bufferingConfiguration: - betterPlayerDataSource.bufferingConfiguration); + bufferingConfiguration: betterPlayerDataSource.bufferingConfiguration, + ); videoPlayerController?.addListener(_onVideoPlayerChanged); } @@ -255,8 +258,7 @@ class BetterPlayerController { betterPlayerAsmsTracks.clear(); ///Setup subtitles - final List? betterPlayerSubtitlesSourceList = - betterPlayerDataSource.subtitles; + final betterPlayerSubtitlesSourceList = betterPlayerDataSource.subtitles; if (betterPlayerSubtitlesSourceList != null) { _betterPlayerSubtitlesSourceList .addAll(betterPlayerDataSource.subtitles!); @@ -285,8 +287,9 @@ class BetterPlayerController { ///Setup subtitles (none is default) setupSubtitleSource( - defaultSubtitle ?? _betterPlayerSubtitlesSourceList.last, - sourceInitialize: true); + defaultSubtitle ?? _betterPlayerSubtitlesSourceList.last, + sourceInitialize: true, + ); } ///Check if given [betterPlayerDataSource] is HLS / DASH-type data source. @@ -300,24 +303,23 @@ class BetterPlayerController { ///This method configures tracks, subtitles and audio tracks from given ///master playlist. Future _setupAsmsDataSource(BetterPlayerDataSource source) async { - final String? data = await BetterPlayerAsmsUtils.getDataFromUrl( + final data = await BetterPlayerAsmsUtils.getDataFromUrl( betterPlayerDataSource!.url, _getHeaders(), ); if (data != null) { - final BetterPlayerAsmsDataHolder _response = + final response = await BetterPlayerAsmsUtils.parse(data, betterPlayerDataSource!.url); /// Load tracks if (_betterPlayerDataSource?.useAsmsTracks == true) { - _betterPlayerAsmsTracks = _response.tracks ?? []; + _betterPlayerAsmsTracks = response.tracks ?? []; } /// Load subtitles if (betterPlayerDataSource?.useAsmsSubtitles == true) { - final List asmsSubtitles = - _response.subtitles ?? []; - asmsSubtitles.forEach((BetterPlayerAsmsSubtitle asmsSubtitle) { + final asmsSubtitles = response.subtitles ?? []; + for (final asmsSubtitle in asmsSubtitles) { _betterPlayerSubtitlesSourceList.add( BetterPlayerSubtitlesSource( type: BetterPlayerSubtitlesSourceType.network, @@ -329,13 +331,13 @@ class BetterPlayerController { selectedByDefault: asmsSubtitle.isDefault, ), ); - }); + } } ///Load audio tracks if (betterPlayerDataSource?.useAsmsAudioTracks == true && _isDataSourceAsms(betterPlayerDataSource!)) { - _betterPlayerAsmsAudioTracks = _response.audios ?? []; + _betterPlayerAsmsAudioTracks = response.audios ?? []; if (_betterPlayerAsmsAudioTracks?.isNotEmpty == true) { setAudioTrack(_betterPlayerAsmsAudioTracks!.first); } @@ -346,8 +348,10 @@ class BetterPlayerController { ///Setup subtitles to be displayed from given subtitle source. ///If subtitles source is segmented then don't load videos at start. Videos ///will load with just in time policy. - Future setupSubtitleSource(BetterPlayerSubtitlesSource subtitlesSource, - {bool sourceInitialize = false}) async { + Future setupSubtitleSource( + BetterPlayerSubtitlesSource subtitlesSource, { + bool sourceInitialize = false, + }) async { _betterPlayerSubtitlesSource = subtitlesSource; subtitlesLines.clear(); _asmsSegmentsLoaded.clear(); @@ -381,10 +385,11 @@ class BetterPlayerController { return; } _asmsSegmentsLoading = true; - final BetterPlayerSubtitlesSource? source = _betterPlayerSubtitlesSource; - final Duration loadDurationEnd = Duration( - milliseconds: position.inMilliseconds + - 5 * (_betterPlayerSubtitlesSource?.asmsSegmentsTime ?? 5000)); + final source = _betterPlayerSubtitlesSource; + final loadDurationEnd = Duration( + milliseconds: position.inMilliseconds + + 5 * (_betterPlayerSubtitlesSource?.asmsSegmentsTime ?? 5000), + ); final segmentsToLoad = _betterPlayerSubtitlesSource?.asmsSegments ?.where((segment) { @@ -398,11 +403,12 @@ class BetterPlayerController { if (segmentsToLoad != null && segmentsToLoad.isNotEmpty) { final subtitlesParsed = await BetterPlayerSubtitlesFactory.parseSubtitles( - BetterPlayerSubtitlesSource( - type: _betterPlayerSubtitlesSource!.type, - headers: _betterPlayerSubtitlesSource!.headers, - urls: segmentsToLoad, - )); + BetterPlayerSubtitlesSource( + type: _betterPlayerSubtitlesSource!.type, + headers: _betterPlayerSubtitlesSource!.headers, + urls: segmentsToLoad, + ), + ); ///Additional check if current source of subtitles is same as source ///used to start loading subtitles. It can be different when user @@ -414,14 +420,15 @@ class BetterPlayerController { } _asmsSegmentsLoading = false; } catch (exception) { - BetterPlayerUtils.log("Load ASMS subtitle segments failed: $exception"); + BetterPlayerUtils.log('Load ASMS subtitle segments failed: $exception'); } } ///Get VideoFormat from BetterPlayerVideoFormat (adapter method which translates ///to video_player supported format). VideoFormat? _getVideoFormat( - BetterPlayerVideoFormat? betterPlayerVideoFormat) { + BetterPlayerVideoFormat? betterPlayerVideoFormat, + ) { if (betterPlayerVideoFormat == null) { return null; } @@ -472,18 +479,39 @@ class BetterPlayerController { videoExtension: _betterPlayerDataSource!.videoExtension, ); - break; case BetterPlayerDataSourceType.file: final file = File(betterPlayerDataSource.url); if (!file.existsSync()) { BetterPlayerUtils.log( "File ${file.path} doesn't exists. This may be because " "you're acessing file from native path and Flutter doesn't " - "recognize this path."); + 'recognize this path.'); } await videoPlayerController?.setFileDataSource( - File(betterPlayerDataSource.url), + File(betterPlayerDataSource.url), + showNotification: _betterPlayerDataSource + ?.notificationConfiguration?.showNotification, + title: _betterPlayerDataSource?.notificationConfiguration?.title, + author: _betterPlayerDataSource?.notificationConfiguration?.author, + imageUrl: + _betterPlayerDataSource?.notificationConfiguration?.imageUrl, + notificationChannelName: _betterPlayerDataSource + ?.notificationConfiguration?.notificationChannelName, + overriddenDuration: _betterPlayerDataSource!.overriddenDuration, + activityName: + _betterPlayerDataSource?.notificationConfiguration?.activityName, + clearKey: _betterPlayerDataSource?.drmConfiguration?.clearKey, + ); + case BetterPlayerDataSourceType.memory: + final file = await _createFile( + _betterPlayerDataSource!.bytes!, + extension: _betterPlayerDataSource!.videoExtension, + ); + + if (file.existsSync()) { + await videoPlayerController?.setFileDataSource( + file, showNotification: _betterPlayerDataSource ?.notificationConfiguration?.showNotification, title: _betterPlayerDataSource?.notificationConfiguration?.title, @@ -495,47 +523,31 @@ class BetterPlayerController { overriddenDuration: _betterPlayerDataSource!.overriddenDuration, activityName: _betterPlayerDataSource ?.notificationConfiguration?.activityName, - clearKey: _betterPlayerDataSource?.drmConfiguration?.clearKey); - break; - case BetterPlayerDataSourceType.memory: - final file = await _createFile(_betterPlayerDataSource!.bytes!, - extension: _betterPlayerDataSource!.videoExtension); - - if (file.existsSync()) { - await videoPlayerController?.setFileDataSource(file, - showNotification: _betterPlayerDataSource - ?.notificationConfiguration?.showNotification, - title: _betterPlayerDataSource?.notificationConfiguration?.title, - author: - _betterPlayerDataSource?.notificationConfiguration?.author, - imageUrl: - _betterPlayerDataSource?.notificationConfiguration?.imageUrl, - notificationChannelName: _betterPlayerDataSource - ?.notificationConfiguration?.notificationChannelName, - overriddenDuration: _betterPlayerDataSource!.overriddenDuration, - activityName: _betterPlayerDataSource - ?.notificationConfiguration?.activityName, - clearKey: _betterPlayerDataSource?.drmConfiguration?.clearKey); + clearKey: _betterPlayerDataSource?.drmConfiguration?.clearKey, + ); _tempFiles.add(file); } else { throw ArgumentError("Couldn't create file from memory."); } - break; default: throw UnimplementedError( - "${betterPlayerDataSource.type} is not implemented"); + '${betterPlayerDataSource.type} is not implemented', + ); } await _initializeVideo(); } ///Create file from provided list of bytes. File will be created in temporary ///directory. - Future _createFile(List bytes, - {String? extension = "temp"}) async { - final String dir = (await getTemporaryDirectory()).path; - final File temp = File( - '$dir/better_player_${DateTime.now().millisecondsSinceEpoch}.$extension'); + Future _createFile( + List bytes, { + String? extension = 'temp', + }) async { + final dir = (await getTemporaryDirectory()).path; + final temp = File( + '$dir/better_player_${DateTime.now().millisecondsSinceEpoch}.$extension', + ); await temp.writeAsBytes(bytes); return temp; } @@ -612,7 +624,7 @@ class BetterPlayerController { ///is resumed. Future play() async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } if (_appLifecycleState == AppLifecycleState.resumed) { @@ -627,7 +639,7 @@ class BetterPlayerController { ///Enables/disables looping (infinity playback) mode. Future setLooping(bool looping) async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } await videoPlayerController!.setLooping(looping); @@ -636,7 +648,7 @@ class BetterPlayerController { ///Stop video playback. Future pause() async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } await videoPlayerController!.pause(); @@ -646,18 +658,22 @@ class BetterPlayerController { ///Move player to specific position/moment of the video. Future seekTo(Duration moment) async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } if (videoPlayerController?.value.duration == null) { - throw StateError("The video has not been initialized yet."); + throw StateError('The video has not been initialized yet.'); } await videoPlayerController!.seekTo(moment); - _postEvent(BetterPlayerEvent(BetterPlayerEventType.seekTo, - parameters: {_durationParameter: moment})); + _postEvent( + BetterPlayerEvent( + BetterPlayerEventType.seekTo, + parameters: {_durationParameter: moment}, + ), + ); - final Duration? currentDuration = videoPlayerController!.value.duration; + final currentDuration = videoPlayerController!.value.duration; if (currentDuration == null) { return; } @@ -671,29 +687,31 @@ class BetterPlayerController { ///Set volume of player. Allows values from 0.0 to 1.0. Future setVolume(double volume) async { if (volume < 0.0 || volume > 1.0) { - BetterPlayerUtils.log("Volume must be between 0.0 and 1.0"); - throw ArgumentError("Volume must be between 0.0 and 1.0"); + BetterPlayerUtils.log('Volume must be between 0.0 and 1.0'); + throw ArgumentError('Volume must be between 0.0 and 1.0'); } if (videoPlayerController == null) { - BetterPlayerUtils.log("The data source has not been initialized"); - throw StateError("The data source has not been initialized"); + BetterPlayerUtils.log('The data source has not been initialized'); + throw StateError('The data source has not been initialized'); } await videoPlayerController!.setVolume(volume); - _postEvent(BetterPlayerEvent( - BetterPlayerEventType.setVolume, - parameters: {_volumeParameter: volume}, - )); + _postEvent( + BetterPlayerEvent( + BetterPlayerEventType.setVolume, + parameters: {_volumeParameter: volume}, + ), + ); } ///Set playback speed of video. Allows to set speed value between 0 and 2. Future setSpeed(double speed) async { if (speed <= 0 || speed > 2) { - BetterPlayerUtils.log("Speed must be between 0 and 2"); - throw ArgumentError("Speed must be between 0 and 2"); + BetterPlayerUtils.log('Speed must be between 0 and 2'); + throw ArgumentError('Speed must be between 0 and 2'); } if (videoPlayerController == null) { - BetterPlayerUtils.log("The data source has not been initialized"); - throw StateError("The data source has not been initialized"); + BetterPlayerUtils.log('The data source has not been initialized'); + throw StateError('The data source has not been initialized'); } await videoPlayerController?.setSpeed(speed); _postEvent( @@ -709,7 +727,7 @@ class BetterPlayerController { ///Flag which determines whenever player is playing or not. bool? isPlaying() { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } return videoPlayerController!.value.isPlaying; } @@ -717,7 +735,7 @@ class BetterPlayerController { ///Flag which determines whenever player is loading video data or not. bool? isBuffering() { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } return videoPlayerController!.value.isBuffering; } @@ -738,9 +756,11 @@ class BetterPlayerController { ///Internal method, used to trigger CONTROLS_VISIBLE or CONTROLS_HIDDEN event ///once controls state changed. void toggleControlsVisibility(bool isVisible) { - _postEvent(isVisible - ? BetterPlayerEvent(BetterPlayerEventType.controlsVisible) - : BetterPlayerEvent(BetterPlayerEventType.controlsHiddenEnd)); + _postEvent( + isVisible + ? BetterPlayerEvent(BetterPlayerEventType.controlsVisible) + : BetterPlayerEvent(BetterPlayerEventType.controlsHiddenEnd), + ); } ///Send player event. Shouldn't be used manually. @@ -750,7 +770,7 @@ class BetterPlayerController { ///Send player event to all listeners. void _postEvent(BetterPlayerEvent betterPlayerEvent) { - for (final Function(BetterPlayerEvent)? eventListener in _eventListeners) { + for (final eventListener in _eventListeners) { if (eventListener != null) { eventListener(betterPlayerEvent); } @@ -758,10 +778,9 @@ class BetterPlayerController { } ///Listener used to handle video player changes. - void _onVideoPlayerChanged() async { - final VideoPlayerValue currentVideoPlayerValue = - videoPlayerController?.value ?? - VideoPlayerValue(duration: const Duration()); + Future _onVideoPlayerChanged() async { + final currentVideoPlayerValue = videoPlayerController?.value ?? + VideoPlayerValue(duration: const Duration()); if (currentVideoPlayerValue.hasError) { _videoPlayerValueOnError ??= currentVideoPlayerValue; @@ -769,7 +788,7 @@ class BetterPlayerController { BetterPlayerEvent( BetterPlayerEventType.exception, parameters: { - "exception": currentVideoPlayerValue.errorDescription + 'exception': currentVideoPlayerValue.errorDescription, }, ), ); @@ -797,7 +816,7 @@ class BetterPlayerController { _loadAsmsSubtitlesSegments(currentVideoPlayerValue.position); } - final int now = DateTime.now().millisecondsSinceEpoch; + final now = DateTime.now().millisecondsSinceEpoch; if (now - _lastPositionSelection > 500) { _lastPositionSelection = now; _postEvent( @@ -805,7 +824,7 @@ class BetterPlayerController { BetterPlayerEventType.progress, parameters: { _progressParameter: currentVideoPlayerValue.position, - _durationParameter: currentVideoPlayerValue.duration + _durationParameter: currentVideoPlayerValue.duration, }, ), ); @@ -826,8 +845,8 @@ class BetterPlayerController { ///Flag which determines whenever player is playing live data source. bool isLiveStream() { if (_betterPlayerDataSource == null) { - BetterPlayerUtils.log("The data source has not been initialized"); - throw StateError("The data source has not been initialized"); + BetterPlayerUtils.log('The data source has not been initialized'); + throw StateError('The data source has not been initialized'); } return _betterPlayerDataSource!.liveStream == true; } @@ -835,8 +854,8 @@ class BetterPlayerController { ///Flag which determines whenever player data source has been initialized. bool? isVideoInitialized() { if (videoPlayerController == null) { - BetterPlayerUtils.log("The data source has not been initialized"); - throw StateError("The data source has not been initialized"); + BetterPlayerUtils.log('The data source has not been initialized'); + throw StateError('The data source has not been initialized'); } return videoPlayerController?.value.initialized; } @@ -847,9 +866,11 @@ class BetterPlayerController { if (_nextVideoTimer == null) { if (betterPlayerPlaylistConfiguration == null) { BetterPlayerUtils.log( - "BettterPlayerPlaylistConifugration has not been set!"); + 'BettterPlayerPlaylistConifugration has not been set!', + ); throw StateError( - "BettterPlayerPlaylistConifugration has not been set!"); + 'BettterPlayerPlaylistConifugration has not been set!', + ); } _nextVideoTime = @@ -860,9 +881,9 @@ class BetterPlayerController { } _nextVideoTimer = - Timer.periodic(const Duration(milliseconds: 1000), (_timer) async { + Timer.periodic(const Duration(milliseconds: 1000), (timer) async { if (_nextVideoTime == 1) { - _timer.cancel(); + timer.cancel(); _nextVideoTimer = null; } if (_nextVideoTime != null) { @@ -893,18 +914,22 @@ class BetterPlayerController { ///data source. void setTrack(BetterPlayerAsmsTrack track) { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } - _postEvent(BetterPlayerEvent(BetterPlayerEventType.changedTrack, + _postEvent( + BetterPlayerEvent( + BetterPlayerEventType.changedTrack, parameters: { - "id": track.id, - "width": track.width, - "height": track.height, - "bitrate": track.bitrate, - "frameRate": track.frameRate, - "codecs": track.codecs, - "mimeType": track.mimeType, - })); + 'id': track.id, + 'width': track.width, + 'height': track.height, + 'bitrate': track.bitrate, + 'frameRate': track.frameRate, + 'codecs': track.codecs, + 'mimeType': track.mimeType, + }, + ), + ); videoPlayerController! .setTrackParameters(track.width, track.height, track.bitrate); @@ -924,13 +949,14 @@ class BetterPlayerController { ///will play again. If there's different handler of visibility then it will be ///used. If showNotification is set in data source or handleLifecycle is false /// then this logic will be ignored. - void onPlayerVisibilityChanged(double visibilityFraction) async { + Future onPlayerVisibilityChanged(double visibilityFraction) async { _isPlayerVisible = visibilityFraction > 0; if (_disposed) { return; } _postEvent( - BetterPlayerEvent(BetterPlayerEventType.changedPlayerVisibility)); + BetterPlayerEvent(BetterPlayerEventType.changedPlayerVisibility), + ); if (_isAutomaticPlayPauseHandled()) { if (betterPlayerConfiguration.playerVisibilityChangedBehavior != null) { @@ -950,9 +976,9 @@ class BetterPlayerController { } ///Set different resolution (quality) for video - void setResolution(String url) async { + Future setResolution(String url) async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } final position = await videoPlayerController!.position; final wasPlayingBeforeChange = isPlaying()!; @@ -962,10 +988,12 @@ class BetterPlayerController { if (wasPlayingBeforeChange) { play(); } - _postEvent(BetterPlayerEvent( - BetterPlayerEventType.changedResolution, - parameters: {"url": url}, - )); + _postEvent( + BetterPlayerEvent( + BetterPlayerEventType.changedResolution, + parameters: {'url': url}, + ), + ); } ///Setup translations for given locale. In normal use cases it shouldn't be @@ -973,9 +1001,10 @@ class BetterPlayerController { void setupTranslations(Locale locale) { // ignore: unnecessary_null_comparison if (locale != null) { - final String languageCode = locale.languageCode; + final languageCode = locale.languageCode; translations = betterPlayerConfiguration.translations?.firstWhereOrNull( - (translations) => translations.languageCode == languageCode) ?? + (translations) => translations.languageCode == languageCode, + ) ?? _getDefaultTranslations(locale); } else { BetterPlayerUtils.log("Locale is null. Couldn't setup translations."); @@ -985,19 +1014,19 @@ class BetterPlayerController { ///Setup default translations for selected user locale. These translations ///are pre-build in. BetterPlayerTranslations _getDefaultTranslations(Locale locale) { - final String languageCode = locale.languageCode; + final languageCode = locale.languageCode; switch (languageCode) { - case "pl": + case 'pl': return BetterPlayerTranslations.polish(); - case "zh": + case 'zh': return BetterPlayerTranslations.chinese(); - case "hi": + case 'hi': return BetterPlayerTranslations.hindi(); - case "tr": + case 'tr': return BetterPlayerTranslations.turkish(); - case "vi": + case 'vi': return BetterPlayerTranslations.vietnamese(); - case "es": + case 'es': return BetterPlayerTranslations.spanish(); default: return BetterPlayerTranslations(); @@ -1057,10 +1086,10 @@ class BetterPlayerController { ///open. Future? enablePictureInPicture(GlobalKey betterPlayerGlobalKey) async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } - final bool isPipSupported = + final isPipSupported = (await videoPlayerController!.isPictureInPictureSupported()) ?? false; if (isPipSupported) { @@ -1070,21 +1099,25 @@ class BetterPlayerController { if (Platform.isAndroid) { _wasInFullScreenBeforePiP = _isFullScreen; await videoPlayerController?.enablePictureInPicture( - left: 0, top: 0, width: 0, height: 0); + left: 0, + top: 0, + width: 0, + height: 0, + ); enterFullScreen(); _postEvent(BetterPlayerEvent(BetterPlayerEventType.pipStart)); return; } if (Platform.isIOS) { - final RenderBox? renderBox = betterPlayerGlobalKey.currentContext! + final renderBox = betterPlayerGlobalKey.currentContext! .findRenderObject() as RenderBox?; if (renderBox == null) { BetterPlayerUtils.log( "Can't show PiP. RenderBox is null. Did you provide valid global" - " key?"); + ' key?'); return; } - final Offset position = renderBox.localToGlobal(Offset.zero); + final position = renderBox.localToGlobal(Offset.zero); return videoPlayerController?.enablePictureInPicture( left: position.dx, top: position.dy, @@ -1092,20 +1125,20 @@ class BetterPlayerController { height: renderBox.size.height, ); } else { - BetterPlayerUtils.log("Unsupported PiP in current platform."); + BetterPlayerUtils.log('Unsupported PiP in current platform.'); } } else { BetterPlayerUtils.log( "Picture in picture is not supported in this device. If you're " "using Android, please check if you're using activity v2 " - "embedding."); + 'embedding.'); } } ///Disable Picture in Picture mode if it's enabled. Future? disablePictureInPicture() { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } return videoPlayerController!.disablePictureInPicture(); } @@ -1119,51 +1152,48 @@ class BetterPlayerController { ///Check if picture in picture mode is supported in this device. Future isPictureInPictureSupported() async { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } - final bool isPipSupported = + final isPipSupported = (await videoPlayerController!.isPictureInPictureSupported()) ?? false; return isPipSupported && !_isFullScreen; } ///Handle VideoEvent when remote controls notification / PiP is shown - void _handleVideoEvent(VideoEvent event) async { + Future _handleVideoEvent(VideoEvent event) async { switch (event.eventType) { case VideoEventType.play: _postEvent(BetterPlayerEvent(BetterPlayerEventType.play)); - break; case VideoEventType.pause: _postEvent(BetterPlayerEvent(BetterPlayerEventType.pause)); - break; case VideoEventType.seek: _postEvent(BetterPlayerEvent(BetterPlayerEventType.seekTo)); - break; case VideoEventType.completed: - final VideoPlayerValue? videoValue = videoPlayerController?.value; + final videoValue = videoPlayerController?.value; _postEvent( BetterPlayerEvent( BetterPlayerEventType.finished, parameters: { _progressParameter: videoValue?.position, - _durationParameter: videoValue?.duration + _durationParameter: videoValue?.duration, }, ), ); - break; case VideoEventType.bufferingStart: _postEvent(BetterPlayerEvent(BetterPlayerEventType.bufferingStart)); - break; case VideoEventType.bufferingUpdate: - _postEvent(BetterPlayerEvent(BetterPlayerEventType.bufferingUpdate, + _postEvent( + BetterPlayerEvent( + BetterPlayerEventType.bufferingUpdate, parameters: { _bufferedParameter: event.buffered, - })); - break; + }, + ), + ); case VideoEventType.bufferingEnd: _postEvent(BetterPlayerEvent(BetterPlayerEventType.bufferingEnd)); - break; default: ///TODO: Handle when needed @@ -1191,7 +1221,7 @@ class BetterPlayerController { ///Set [audioTrack] in player. Works only for HLS or DASH streams. void setAudioTrack(BetterPlayerAsmsAudioTrack audioTrack) { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } if (audioTrack.language == null) { @@ -1206,7 +1236,7 @@ class BetterPlayerController { ///Enable or disable audio mixing with other sound within device. void setMixWithOthers(bool mixWithOthers) { if (videoPlayerController == null) { - throw StateError("The data source has not been initialized"); + throw StateError('The data source has not been initialized'); } videoPlayerController!.setMixWithOthers(mixWithOthers); @@ -1259,16 +1289,20 @@ class BetterPlayerController { ///Stop pre cache for given [betterPlayerDataSource]. If there was no pre ///cache started for given [betterPlayerDataSource] then it will be ignored. Future stopPreCache( - BetterPlayerDataSource betterPlayerDataSource) async { - return VideoPlayerController?.stopPreCache(betterPlayerDataSource.url, - betterPlayerDataSource.cacheConfiguration?.key); + BetterPlayerDataSource betterPlayerDataSource, + ) async { + return VideoPlayerController.stopPreCache( + betterPlayerDataSource.url, + betterPlayerDataSource.cacheConfiguration?.key, + ); } /// Sets the new [betterPlayerControlsConfiguration] instance in the /// controller. void setBetterPlayerControlsConfiguration( - BetterPlayerControlsConfiguration betterPlayerControlsConfiguration) { - this._betterPlayerControlsConfiguration = betterPlayerControlsConfiguration; + BetterPlayerControlsConfiguration betterPlayerControlsConfiguration, + ) { + _betterPlayerControlsConfiguration = betterPlayerControlsConfiguration; } /// Add controller internal event. @@ -1301,7 +1335,9 @@ class BetterPlayerController { _controllerEventStreamController.close(); ///Delete files async - _tempFiles.forEach((file) => file.delete()); + for (final file in _tempFiles) { + file.delete(); + } } } } diff --git a/lib/src/core/better_player_controller_provider.dart b/lib/src/core/better_player_controller_provider.dart index 903b5b54d..cdea3e2a5 100644 --- a/lib/src/core/better_player_controller_provider.dart +++ b/lib/src/core/better_player_controller_provider.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; ///Widget which is used to inherit BetterPlayerController through widget tree. class BetterPlayerControllerProvider extends InheritedWidget { const BetterPlayerControllerProvider({ - Key? key, required this.controller, - required Widget child, - }) : super(key: key, child: child); + required super.child, + super.key, + }); final BetterPlayerController controller; diff --git a/lib/src/core/better_player_utils.dart b/lib/src/core/better_player_utils.dart index 610f04082..12090590e 100644 --- a/lib/src/core/better_player_utils.dart +++ b/lib/src/core/better_player_utils.dart @@ -5,21 +5,21 @@ import 'package:flutter/material.dart'; class BetterPlayerUtils { static String formatBitrate(int bitrate) { if (bitrate < 1000) { - return "$bitrate bit/s"; + return '$bitrate bit/s'; } if (bitrate < 1000000) { final kbit = (bitrate / 1000).floor(); - return "~$kbit KBit/s"; + return '~$kbit KBit/s'; } final mbit = (bitrate / 1000000).floor(); - return "~$mbit MBit/s"; + return '~$mbit MBit/s'; } static String formatDuration(Duration position) { final ms = position.inMilliseconds; - int seconds = ms ~/ 1000; - final int hours = seconds ~/ 3600; + var seconds = ms ~/ 1000; + final hours = seconds ~/ 3600; seconds = seconds % 3600; final minutes = seconds ~/ 60; seconds = seconds % 60; diff --git a/lib/src/core/better_player_with_controls.dart b/lib/src/core/better_player_with_controls.dart index 49c91f94e..21369bf68 100644 --- a/lib/src/core/better_player_with_controls.dart +++ b/lib/src/core/better_player_with_controls.dart @@ -11,10 +11,9 @@ import 'package:better_player/src/video_player/video_player.dart'; import 'package:flutter/material.dart'; class BetterPlayerWithControls extends StatefulWidget { + const BetterPlayerWithControls({super.key, this.controller}); final BetterPlayerController? controller; - const BetterPlayerWithControls({Key? key, this.controller}) : super(key: key); - @override _BetterPlayerWithControlsState createState() => _BetterPlayerWithControlsState(); @@ -69,8 +68,7 @@ class _BetterPlayerWithControlsState extends State { @override Widget build(BuildContext context) { - final BetterPlayerController betterPlayerController = - BetterPlayerController.of(context); + final betterPlayerController = BetterPlayerController.of(context); double? aspectRatio; if (betterPlayerController.isFullScreen) { @@ -109,12 +107,14 @@ class _BetterPlayerWithControlsState extends State { } Container _buildPlayerWithControls( - BetterPlayerController betterPlayerController, BuildContext context) { + BetterPlayerController betterPlayerController, + BuildContext context, + ) { final configuration = betterPlayerController.betterPlayerConfiguration; var rotation = configuration.rotation; if (!(rotation <= 360 && rotation % 90 == 0)) { - BetterPlayerUtils.log("Invalid rotation provided. Using rotation = 0"); + BetterPlayerUtils.log('Invalid rotation provided. Using rotation = 0'); rotation = 0; } if (betterPlayerController.betterPlayerDataSource == null) { @@ -122,7 +122,7 @@ class _BetterPlayerWithControlsState extends State { } _initialized = true; - final bool placeholderOnTop = + final placeholderOnTop = betterPlayerController.betterPlayerConfiguration.placeholderOnTop; // ignore: avoid_unnecessary_containers return Container( @@ -163,7 +163,7 @@ class _BetterPlayerWithControlsState extends State { BetterPlayerController betterPlayerController, ) { if (controlsConfiguration.showControls) { - BetterPlayerTheme? playerTheme = controlsConfiguration.playerTheme; + var playerTheme = controlsConfiguration.playerTheme; if (playerTheme == null) { if (Platform.isAndroid) { playerTheme = BetterPlayerTheme.material; @@ -175,7 +175,9 @@ class _BetterPlayerWithControlsState extends State { if (controlsConfiguration.customControlsBuilder != null && playerTheme == BetterPlayerTheme.custom) { return controlsConfiguration.customControlsBuilder!( - betterPlayerController, onControlsVisibilityChanged); + betterPlayerController, + onControlsVisibilityChanged, + ); } else if (playerTheme == BetterPlayerTheme.material) { return _buildMaterialControl(); } else if (playerTheme == BetterPlayerTheme.cupertino) { @@ -209,9 +211,8 @@ class _BetterPlayerWithControlsState extends State { class _BetterPlayerVideoFitWidget extends StatefulWidget { const _BetterPlayerVideoFitWidget( this.betterPlayerController, - this.boxFit, { - Key? key, - }) : super(key: key); + this.boxFit, + ); final BetterPlayerController betterPlayerController; final BoxFit boxFit; @@ -300,7 +301,7 @@ class _BetterPlayerVideoFitWidgetState if (_initialized && _started) { return Center( child: ClipRect( - child: Container( + child: SizedBox( width: double.infinity, height: double.infinity, child: FittedBox( diff --git a/lib/src/dash/better_player_dash_utils.dart b/lib/src/dash/better_player_dash_utils.dart index c137599c7..a7dd34d5b 100644 --- a/lib/src/dash/better_player_dash_utils.dart +++ b/lib/src/dash/better_player_dash_utils.dart @@ -9,15 +9,17 @@ import 'package:xml/xml.dart'; ///DASH helper class class BetterPlayerDashUtils { static Future parse( - String data, String masterPlaylistUrl) async { - List tracks = []; - final List audios = []; - final List subtitles = []; + String data, + String masterPlaylistUrl, + ) async { + var tracks = []; + final audios = []; + final subtitles = []; try { - int audiosCount = 0; + var audiosCount = 0; final document = XmlDocument.parse(data); final adaptationSets = document.findAllElements('AdaptationSet'); - adaptationSets.forEach((node) { + for (final node in adaptationSets) { final mimeType = node.getAttribute('mimeType'); if (mimeType != null) { @@ -30,73 +32,84 @@ class BetterPlayerDashUtils { subtitles.add(parseSubtitle(masterPlaylistUrl, node)); } } - }); + } } catch (exception) { - BetterPlayerUtils.log("Exception on dash parse: $exception"); + BetterPlayerUtils.log('Exception on dash parse: $exception'); } return BetterPlayerAsmsDataHolder( - tracks: tracks, audios: audios, subtitles: subtitles); + tracks: tracks, + audios: audios, + subtitles: subtitles, + ); } static List parseVideo(XmlElement node) { - final List tracks = []; + final tracks = []; final representations = node.findAllElements('Representation'); - representations.forEach((representation) { - final String? id = representation.getAttribute('id'); - final int width = int.parse(representation.getAttribute('width') ?? '0'); - final int height = - int.parse(representation.getAttribute('height') ?? '0'); - final int bitrate = + for (final representation in representations) { + final id = representation.getAttribute('id'); + final width = int.parse(representation.getAttribute('width') ?? '0'); + final height = int.parse(representation.getAttribute('height') ?? '0'); + final bitrate = int.parse(representation.getAttribute('bandwidth') ?? '0'); - final int frameRate = + final frameRate = int.parse(representation.getAttribute('frameRate') ?? '0'); - final String? codecs = representation.getAttribute('codecs'); - final String? mimeType = MimeTypes.getMediaMimeType(codecs ?? ''); - tracks.add(BetterPlayerAsmsTrack( - id, width, height, bitrate, frameRate, codecs, mimeType)); - }); + final codecs = representation.getAttribute('codecs'); + final mimeType = MimeTypes.getMediaMimeType(codecs ?? ''); + tracks.add( + BetterPlayerAsmsTrack( + id, + width, + height, + bitrate, + frameRate, + codecs, + mimeType, + ), + ); + } return tracks; } static BetterPlayerAsmsAudioTrack parseAudio(XmlElement node, int index) { - final String segmentAlignmentStr = - node.getAttribute('segmentAlignment') ?? ''; - String? label = node.getAttribute('label'); - final String? language = node.getAttribute('lang'); - final String? mimeType = node.getAttribute('mimeType'); + final segmentAlignmentStr = node.getAttribute('segmentAlignment') ?? ''; + var label = node.getAttribute('label'); + final language = node.getAttribute('lang'); + final mimeType = node.getAttribute('mimeType'); label ??= language; return BetterPlayerAsmsAudioTrack( - id: index, - segmentAlignment: segmentAlignmentStr.toLowerCase() == 'true', - label: label, - language: language, - mimeType: mimeType); + id: index, + segmentAlignment: segmentAlignmentStr.toLowerCase() == 'true', + label: label, + language: language, + mimeType: mimeType, + ); } static BetterPlayerAsmsSubtitle parseSubtitle( - String masterPlaylistUrl, XmlElement node) { - final String segmentAlignmentStr = - node.getAttribute('segmentAlignment') ?? ''; - String? name = node.getAttribute('label'); - final String? language = node.getAttribute('lang'); - final String? mimeType = node.getAttribute('mimeType'); - String? url = - node.getElement('Representation')?.getElement('BaseURL')?.text; - if (url?.contains("http") == false) { - final Uri masterPlaylistUri = Uri.parse(masterPlaylistUrl); + String masterPlaylistUrl, + XmlElement node, + ) { + final segmentAlignmentStr = node.getAttribute('segmentAlignment') ?? ''; + var name = node.getAttribute('label'); + final language = node.getAttribute('lang'); + final mimeType = node.getAttribute('mimeType'); + var url = node.getElement('Representation')?.getElement('BaseURL')?.text; + if (url?.contains('http') == false) { + final masterPlaylistUri = Uri.parse(masterPlaylistUrl); final pathSegments = [...masterPlaylistUri.pathSegments]; pathSegments[pathSegments.length - 1] = url!; url = Uri( - scheme: masterPlaylistUri.scheme, - host: masterPlaylistUri.host, - port: masterPlaylistUri.port, - pathSegments: pathSegments) - .toString(); + scheme: masterPlaylistUri.scheme, + host: masterPlaylistUri.host, + port: masterPlaylistUri.port, + pathSegments: pathSegments, + ).toString(); } if (url != null && url.startsWith('//')) { @@ -106,11 +119,12 @@ class BetterPlayerDashUtils { name ??= language; return BetterPlayerAsmsSubtitle( - name: name, - language: language, - mimeType: mimeType, - segmentAlignment: segmentAlignmentStr.toLowerCase() == 'true', - url: url, - realUrls: [url ?? '']); + name: name, + language: language, + mimeType: mimeType, + segmentAlignment: segmentAlignmentStr.toLowerCase() == 'true', + url: url, + realUrls: [url ?? ''], + ); } } diff --git a/lib/src/hls/better_player_hls_utils.dart b/lib/src/hls/better_player_hls_utils.dart index 0b10ff749..56ecfe68f 100644 --- a/lib/src/hls/better_player_hls_utils.dart +++ b/lib/src/hls/better_player_hls_utils.dart @@ -9,66 +9,81 @@ import 'package:better_player/src/hls/hls_parser/hls_master_playlist.dart'; import 'package:better_player/src/hls/hls_parser/hls_media_playlist.dart'; import 'package:better_player/src/hls/hls_parser/hls_playlist_parser.dart'; import 'package:better_player/src/hls/hls_parser/rendition.dart'; -import 'package:better_player/src/hls/hls_parser/segment.dart'; import 'package:better_player/src/hls/hls_parser/util.dart'; ///HLS helper class class BetterPlayerHlsUtils { static Future parse( - String data, String masterPlaylistUrl) async { - List tracks = []; - List subtitles = []; - List audios = []; + String data, + String masterPlaylistUrl, + ) async { + var tracks = []; + var subtitles = []; + var audios = []; try { - final List> list = await Future.wait([ + final list = await Future.wait([ parseTracks(data, masterPlaylistUrl), parseSubtitles(data, masterPlaylistUrl), - parseLanguages(data, masterPlaylistUrl) + parseLanguages(data, masterPlaylistUrl), ]); tracks = list[0] as List; subtitles = list[1] as List; audios = list[2] as List; } catch (exception) { - BetterPlayerUtils.log("Exception on hls parse: $exception"); + BetterPlayerUtils.log('Exception on hls parse: $exception'); } return BetterPlayerAsmsDataHolder( - tracks: tracks, audios: audios, subtitles: subtitles); + tracks: tracks, + audios: audios, + subtitles: subtitles, + ); } static Future> parseTracks( - String data, String masterPlaylistUrl) async { - final List tracks = []; + String data, + String masterPlaylistUrl, + ) async { + final tracks = []; try { final parsedPlaylist = await HlsPlaylistParser.create() .parseString(Uri.parse(masterPlaylistUrl), data); if (parsedPlaylist is HlsMasterPlaylist) { - parsedPlaylist.variants.forEach( - (variant) { - tracks.add(BetterPlayerAsmsTrack('', variant.format.width, - variant.format.height, variant.format.bitrate, 0, '', '')); - }, - ); + for (final variant in parsedPlaylist.variants) { + tracks.add( + BetterPlayerAsmsTrack( + '', + variant.format.width, + variant.format.height, + variant.format.bitrate, + 0, + '', + '', + ), + ); + } } if (tracks.isNotEmpty) { tracks.insert(0, BetterPlayerAsmsTrack.defaultTrack()); } } catch (exception) { - BetterPlayerUtils.log("Exception on parseSubtitles: $exception"); + BetterPlayerUtils.log('Exception on parseSubtitles: $exception'); } return tracks; } ///Parse subtitles from provided m3u8 url static Future> parseSubtitles( - String data, String masterPlaylistUrl) async { - final List subtitles = []; + String data, + String masterPlaylistUrl, + ) async { + final subtitles = []; try { final parsedPlaylist = await HlsPlaylistParser.create() .parseString(Uri.parse(masterPlaylistUrl), data); if (parsedPlaylist is HlsMasterPlaylist) { - for (final Rendition element in parsedPlaylist.subtitles) { + for (final element in parsedPlaylist.subtitles) { final hlsSubtitle = await _parseSubtitlesPlaylist(element); if (hlsSubtitle != null) { subtitles.add(hlsSubtitle); @@ -76,7 +91,7 @@ class BetterPlayerHlsUtils { } } } catch (exception) { - BetterPlayerUtils.log("Exception on parseSubtitles: $exception"); + BetterPlayerUtils.log('Exception on parseSubtitles: $exception'); } return subtitles; @@ -89,9 +104,10 @@ class BetterPlayerHlsUtils { ///filled segments list which contains start, end and url of subtitles based ///on time in playlist. static Future _parseSubtitlesPlaylist( - Rendition rendition) async { + Rendition rendition, + ) async { try { - final HlsPlaylistParser _hlsPlaylistParser = HlsPlaylistParser.create(); + final hlsPlaylistParser = HlsPlaylistParser.create(); final subtitleData = await BetterPlayerAsmsUtils.getDataFromUrl(rendition.url.toString()); if (subtitleData == null) { @@ -99,21 +115,21 @@ class BetterPlayerHlsUtils { } final parsedSubtitle = - await _hlsPlaylistParser.parseString(rendition.url, subtitleData); + await hlsPlaylistParser.parseString(rendition.url, subtitleData); final hlsMediaPlaylist = parsedSubtitle as HlsMediaPlaylist; final hlsSubtitlesUrls = []; - final List asmsSegments = []; - final bool isSegmented = hlsMediaPlaylist.segments.length > 1; - int microSecondsFromStart = 0; - for (final Segment segment in hlsMediaPlaylist.segments) { - final split = rendition.url.toString().split("/"); - var realUrl = ""; + final asmsSegments = []; + final isSegmented = hlsMediaPlaylist.segments.length > 1; + var microSecondsFromStart = 0; + for (final segment in hlsMediaPlaylist.segments) { + final split = rendition.url.toString().split('/'); + var realUrl = ''; for (var index = 0; index < split.length - 1; index++) { // ignore: use_string_buffers - realUrl += "${split[index]}/"; + realUrl += '${split[index]}/'; } - if (segment.url?.startsWith("http") == true) { + if (segment.url?.startsWith('http') == true) { realUrl = segment.url!; } else { realUrl += segment.url!; @@ -121,7 +137,7 @@ class BetterPlayerHlsUtils { hlsSubtitlesUrls.add(realUrl); if (isSegmented) { - final int nextMicroSecondsFromStart = + final nextMicroSecondsFromStart = microSecondsFromStart + segment.durationUs!; microSecondsFromStart = nextMicroSecondsFromStart; asmsSegments.add( @@ -134,12 +150,12 @@ class BetterPlayerHlsUtils { } } - int targetDuration = 0; + var targetDuration = 0; if (parsedSubtitle.targetDurationUs != null) { targetDuration = parsedSubtitle.targetDurationUs! ~/ 1000; } - bool isDefault = false; + var isDefault = false; if (rendition.format.selectionFlags != null) { isDefault = @@ -147,34 +163,39 @@ class BetterPlayerHlsUtils { } return BetterPlayerAsmsSubtitle( - name: rendition.format.label, - language: rendition.format.language, - url: rendition.url.toString(), - realUrls: hlsSubtitlesUrls, - isSegmented: isSegmented, - segmentsTime: targetDuration, - segments: asmsSegments, - isDefault: isDefault); + name: rendition.format.label, + language: rendition.format.language, + url: rendition.url.toString(), + realUrls: hlsSubtitlesUrls, + isSegmented: isSegmented, + segmentsTime: targetDuration, + segments: asmsSegments, + isDefault: isDefault, + ); } catch (exception) { - BetterPlayerUtils.log("Failed to process subtitles playlist: $exception"); + BetterPlayerUtils.log('Failed to process subtitles playlist: $exception'); return null; } } static Future> parseLanguages( - String data, String masterPlaylistUrl) async { - final List audios = []; + String data, + String masterPlaylistUrl, + ) async { + final audios = []; final parsedPlaylist = await HlsPlaylistParser.create() .parseString(Uri.parse(masterPlaylistUrl), data); if (parsedPlaylist is HlsMasterPlaylist) { - for (int index = 0; index < parsedPlaylist.audios.length; index++) { - final Rendition audio = parsedPlaylist.audios[index]; - audios.add(BetterPlayerAsmsAudioTrack( - id: index, - label: audio.name, - language: audio.format.language, - url: audio.url.toString(), - )); + for (var index = 0; index < parsedPlaylist.audios.length; index++) { + final audio = parsedPlaylist.audios[index]; + audios.add( + BetterPlayerAsmsAudioTrack( + id: index, + label: audio.name, + language: audio.format.language, + url: audio.url.toString(), + ), + ); } } diff --git a/lib/src/hls/hls_parser/drm_init_data.dart b/lib/src/hls/hls_parser/drm_init_data.dart index dbca2a269..37c1dc3f0 100644 --- a/lib/src/hls/hls_parser/drm_init_data.dart +++ b/lib/src/hls/hls_parser/drm_init_data.dart @@ -1,8 +1,7 @@ +import 'package:better_player/src/hls/hls_parser/scheme_data.dart'; import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; -import 'scheme_data.dart'; - class DrmInitData { DrmInitData({this.schemeType, this.schemeData = const []}); diff --git a/lib/src/hls/hls_parser/exception.dart b/lib/src/hls/hls_parser/exception.dart index faa4487b8..ca8db4451 100644 --- a/lib/src/hls/hls_parser/exception.dart +++ b/lib/src/hls/hls_parser/exception.dart @@ -8,7 +8,7 @@ class ParserException implements Exception { } class UnrecognizedInputFormatException extends ParserException { - UnrecognizedInputFormatException(String message, this.uri) : super(message); + UnrecognizedInputFormatException(super.message, this.uri); final Uri? uri; } diff --git a/lib/src/hls/hls_parser/format.dart b/lib/src/hls/hls_parser/format.dart index 1b6f4ce9b..55d92055e 100644 --- a/lib/src/hls/hls_parser/format.dart +++ b/lib/src/hls/hls_parser/format.dart @@ -1,6 +1,6 @@ -import 'drm_init_data.dart'; -import 'metadata.dart'; -import 'util.dart'; +import 'package:better_player/src/hls/hls_parser/drm_init_data.dart'; +import 'package:better_player/src/hls/hls_parser/metadata.dart'; +import 'package:better_player/src/hls/hls_parser/util.dart'; /// Representation of a media format. class Format { @@ -27,16 +27,16 @@ class Format { }) : language = language?.toLowerCase(); factory Format.createVideoContainerFormat({ + required String? codecs, + required int? width, + required int? height, + required double? frameRate, String? id, String? label, String? containerMimeType, String? sampleMimeType, - required String? codecs, int? bitrate, int? averageBitrate, - required int? width, - required int? height, - required double? frameRate, int selectionFlags = Util.selectionFlagDefault, int? roleFlags, bool? isDefault, diff --git a/lib/src/hls/hls_parser/hls_master_playlist.dart b/lib/src/hls/hls_parser/hls_master_playlist.dart index 0eb1aefbc..d645f9b4f 100644 --- a/lib/src/hls/hls_parser/hls_master_playlist.dart +++ b/lib/src/hls/hls_parser/hls_master_playlist.dart @@ -1,13 +1,13 @@ -import 'drm_init_data.dart'; -import 'format.dart'; -import 'playlist.dart'; -import 'rendition.dart'; -import 'variant.dart'; +import 'package:better_player/src/hls/hls_parser/drm_init_data.dart'; +import 'package:better_player/src/hls/hls_parser/format.dart'; +import 'package:better_player/src/hls/hls_parser/playlist.dart'; +import 'package:better_player/src/hls/hls_parser/rendition.dart'; +import 'package:better_player/src/hls/hls_parser/variant.dart'; class HlsMasterPlaylist extends HlsPlaylist { HlsMasterPlaylist({ - String? baseUri, - List tags = const [], // ignore: always_specify_types + super.baseUri, + super.tags = const [], // ignore: always_specify_types this.variants = const [], // ignore: always_specify_types this.videos = const [], // ignore: always_specify_types this.audios = const [], // ignore: always_specify_types @@ -15,16 +15,13 @@ class HlsMasterPlaylist extends HlsPlaylist { this.closedCaptions = const [], // ignore: always_specify_types this.muxedAudioFormat, this.muxedCaptionFormats = const [], // ignore: always_specify_types - bool hasIndependentSegments = false, + super.hasIndependentSegments = false, this.variableDefinitions = const {}, // ignore: always_specify_types this.sessionKeyDrmInitData = const [], // ignore: always_specify_types - }) : mediaPlaylistUrls = _getMediaPlaylistUrls( - variants, [videos, audios, subtitles, closedCaptions]), - // ignore: always_specify_types - super( - baseUri: baseUri, - tags: tags, - hasIndependentSegments: hasIndependentSegments); + }) : mediaPlaylistUrls = _getMediaPlaylistUrls( + variants, + [videos, audios, subtitles, closedCaptions], + ); /// All of the media playlist URLs referenced by the playlist. final List mediaPlaylistUrls; @@ -59,16 +56,18 @@ class HlsMasterPlaylist extends HlsPlaylist { final List sessionKeyDrmInitData; static List _getMediaPlaylistUrls( - List variants, List> renditionList) { + List variants, + List> renditionList, + ) { final uriList = []; - variants.forEach((element) { + for (final element in variants) { uriList.add(element.url); - }); - renditionList.forEach((element) { + } + for (final element in renditionList) { for (final value in element) { uriList.add(value.url); } - }); + } return uriList; } } diff --git a/lib/src/hls/hls_parser/hls_media_playlist.dart b/lib/src/hls/hls_parser/hls_media_playlist.dart index 4ad1bb5c3..7f7f7232c 100644 --- a/lib/src/hls/hls_parser/hls_media_playlist.dart +++ b/lib/src/hls/hls_parser/hls_media_playlist.dart @@ -17,14 +17,10 @@ class HlsMediaPlaylist extends HlsPlaylist { required this.protectionSchemes, required this.segments, required this.durationUs, - required String baseUri, - required List tags, - required bool hasIndependentSegments, - }) : super( - baseUri: baseUri, - tags: tags, - hasIndependentSegments: hasIndependentSegments, - ); + required String super.baseUri, + required super.tags, + required super.hasIndependentSegments, + }); factory HlsMediaPlaylist.create({ required int playlistType, @@ -43,7 +39,7 @@ class HlsMediaPlaylist extends HlsPlaylist { required List tags, required bool hasIndependentSegments, }) { - final int? durationUs = segments.isNotEmpty + final durationUs = segments.isNotEmpty ? segments.last.relativeStartTimeUs ?? 0 + segments.last.durationUs! : null; diff --git a/lib/src/hls/hls_parser/hls_playlist_parser.dart b/lib/src/hls/hls_parser/hls_playlist_parser.dart index b44f353fe..69b156763 100644 --- a/lib/src/hls/hls_parser/hls_playlist_parser.dart +++ b/lib/src/hls/hls_parser/hls_playlist_parser.dart @@ -65,25 +65,25 @@ class HlsPlaylistParser { static const String booleanTrue = 'YES'; static const String booleanFalse = 'NO'; static const String attrClosedCaptionsNone = 'CLOSED-CAPTIONS=NONE'; - static const String regexpAverageBandwidth = 'AVERAGE-BANDWIDTH=(\\d+)\\b'; + static const String regexpAverageBandwidth = r'AVERAGE-BANDWIDTH=(\d+)\b'; static const String regexpVideo = 'VIDEO="(.+?)"'; static const String regexpAudio = 'AUDIO="(.+?)"'; static const String regexpSubtitles = 'SUBTITLES="(.+?)"'; static const String regexpClosedCaptions = 'CLOSED-CAPTIONS="(.+?)"'; - static const String regexpBandwidth = '[^-]BANDWIDTH=(\\d+)\\b'; + static const String regexpBandwidth = r'[^-]BANDWIDTH=(\d+)\b'; static const String regexpChannels = 'CHANNELS="(.+?)"'; static const String regexpCodecs = 'CODECS="(.+?)"'; - static const String regexpResolutions = 'RESOLUTION=(\\d+x\\d+)'; - static const String regexpFrameRate = 'FRAME-RATE=([\\d\\.]+)\\b'; + static const String regexpResolutions = r'RESOLUTION=(\d+x\d+)'; + static const String regexpFrameRate = r'FRAME-RATE=([\d\.]+)\b'; static const String regexpTargetDuration = '$tagTargetDuration:(\\d+)\\b'; static const String regexpVersion = '$tagVersion:(\\d+)\\b'; static const String regexpPlaylistType = '$tagPlaylistType:(.+)\\b'; static const String regexpMediaSequence = '$tagMediaSequence:(\\d+)\\b'; static const String regexpMediaDuration = '$tagMediaDuration:([\\d\\.]+)\\b'; static const String regexpMediaTitle = '$tagMediaDuration:[\\d\\.]+\\b,(.+)'; - static const String regexpTimeOffset = 'TIME-OFFSET=(-?[\\d\\.]+)\\b'; + static const String regexpTimeOffset = r'TIME-OFFSET=(-?[\d\.]+)\b'; static const String regexpByteRange = '$tagByteRange:(\\d+(?:@\\d+)?)\\b'; - static const String regexpAttrByteRange = 'BYTERANGE="(\\d+(?:@\\d+)?)\\b"'; + static const String regexpAttrByteRange = r'BYTERANGE="(\d+(?:@\d+)?)\b"'; static const String regexpMethod = 'METHOD=($methodNone|$methodAes128|$methodSampleAes|$methodSampleAesCenc|$methodSampleAesCtr)\\s*(?:,|\$)'; static const String regexpKeyFormat = 'KEYFORMAT="(.+?)"'; @@ -96,7 +96,7 @@ class HlsPlaylistParser { static const String regexpName = 'NAME="(.+?)"'; static const String regexpGroupId = 'GROUP-ID="(.+?)"'; static const String regexpCharacteristics = 'CHARACTERISTICS="(.+?)"'; - static const String regexpInStreamId = 'INSTREAM-ID="((?:CC|SERVICE)\\d+)"'; + static const String regexpInStreamId = r'INSTREAM-ID="((?:CC|SERVICE)\d+)"'; static final String regexpAutoSelect = // ignore: non_constant_identifier_names _compileBooleanAttrPattern('AUTOSELECT'); @@ -108,27 +108,28 @@ class HlsPlaylistParser { static final String regexpForced = _compileBooleanAttrPattern('FORCED'); static const String regexpValue = 'VALUE="(.+?)"'; static const String regexpImport = 'IMPORT="(.+?)"'; - static const String regexpVariableReference = '\\{\\\$([a-zA-Z0-9\\-_]+)\\}'; + static const String regexpVariableReference = r'\{\$([a-zA-Z0-9\-_]+)\}'; final HlsMasterPlaylist masterPlaylist; Future parseString(Uri? uri, String inputString) async { - final List lines = const LineSplitter().convert(inputString); + final lines = const LineSplitter().convert(inputString); return parse(uri, lines); } Future parse(Uri? uri, List inputLineList) async { - final List lineList = inputLineList + final lineList = inputLineList .where((line) => line.trim().isNotEmpty) // ignore: always_specify_types .toList(); if (!_checkPlaylistHeader(lineList[0])) { throw UnrecognizedInputFormatException( - 'Input does not start with the #EXTM3U header.', uri); + 'Input does not start with the #EXTM3U header.', + uri, + ); } - final List extraLines = - lineList.getRange(1, lineList.length).toList(); + final extraLines = lineList.getRange(1, lineList.length).toList(); bool? isMasterPlayList; for (final line in extraLines) { @@ -159,7 +160,7 @@ class HlsPlaylistParser { '$attribute=($booleanFalse|$booleanTrue)'; static bool _checkPlaylistHeader(String string) { - List codeUnits = LibUtil.excludeWhiteSpace(string).codeUnits; + var codeUnits = LibUtil.excludeWhiteSpace(string).codeUnits; if (codeUnits[0] == 0xEF) { if (LibUtil.startsWith(codeUnits, [0xEF, 0xBB, 0xBF])) { @@ -177,38 +178,42 @@ class HlsPlaylistParser { } HlsMasterPlaylist _parseMasterPlaylist( - Iterator extraLines, String baseUri) { - final List tags = []; // ignore: always_specify_types - final List mediaTags = []; // ignore: always_specify_types - final List sessionKeyDrmInitData = - []; // ignore: always_specify_types - final List variants = []; // ignore: always_specify_types - final List videos = []; // ignore: always_specify_types - final List audios = []; // ignore: always_specify_types - final List subtitles = []; // ignore: always_specify_types - final List closedCaptions = []; // ignore: always_specify_types - final Map> urlToVariantInfos = - {}; // ignore: always_specify_types + Iterator extraLines, + String baseUri, + ) { + final tags = []; // ignore: always_specify_types + final mediaTags = []; // ignore: always_specify_types + final sessionKeyDrmInitData = + []; // ignore: always_specify_types + final variants = []; // ignore: always_specify_types + final videos = []; // ignore: always_specify_types + final audios = []; // ignore: always_specify_types + final subtitles = []; // ignore: always_specify_types + final closedCaptions = []; // ignore: always_specify_types + final urlToVariantInfos = + >{}; // ignore: always_specify_types Format? muxedAudioFormat; - bool noClosedCaptions = false; - bool hasIndependentSegmentsTag = false; + var noClosedCaptions = false; + var hasIndependentSegmentsTag = false; List? muxedCaptionFormats; - final Map variableDefinitions = - {}; // ignore: always_specify_types + final variableDefinitions = + {}; // ignore: always_specify_types while (extraLines.moveNext()) { - final String line = extraLines.current; + final line = extraLines.current; if (line.startsWith(tagDefine)) { - final String? key = _parseStringAttr( - source: line, - pattern: regexpName, - variableDefinitions: variableDefinitions); - final String? val = _parseStringAttr( - source: line, - pattern: regexpValue, - variableDefinitions: variableDefinitions); + final key = _parseStringAttr( + source: line, + pattern: regexpName, + variableDefinitions: variableDefinitions, + ); + final val = _parseStringAttr( + source: line, + pattern: regexpValue, + variableDefinitions: variableDefinitions, + ); if (key == null) { throw ParserException("Couldn't match $regexpName in $line"); } @@ -221,51 +226,59 @@ class HlsPlaylistParser { } else if (line.startsWith(tagMedia)) { mediaTags.add(line); } else if (line.startsWith(tagSessionKey)) { - final String? keyFormat = _parseStringAttr( - source: line, - pattern: regexpKeyFormat, - defaultValue: keyFormatIdentity, - variableDefinitions: variableDefinitions); - final SchemeData? schemeData = _parseDrmSchemeData( - line: line, - keyFormat: keyFormat, - variableDefinitions: variableDefinitions); + final keyFormat = _parseStringAttr( + source: line, + pattern: regexpKeyFormat, + defaultValue: keyFormatIdentity, + variableDefinitions: variableDefinitions, + ); + final schemeData = _parseDrmSchemeData( + line: line, + keyFormat: keyFormat, + variableDefinitions: variableDefinitions, + ); if (schemeData != null) { - final String? method = _parseStringAttr( - source: line, - pattern: regexpMethod, - variableDefinitions: variableDefinitions); - final String scheme = _parseEncryptionScheme(method); - final DrmInitData drmInitData = DrmInitData( - schemeType: scheme, - schemeData: [schemeData]); // ignore: always_specify_types + final method = _parseStringAttr( + source: line, + pattern: regexpMethod, + variableDefinitions: variableDefinitions, + ); + final scheme = _parseEncryptionScheme(method); + final drmInitData = DrmInitData( + schemeType: scheme, + schemeData: [schemeData], + ); // ignore: always_specify_types sessionKeyDrmInitData.add(drmInitData); } } else if (line.startsWith(tagStreamInf)) { noClosedCaptions |= line.contains(attrClosedCaptionsNone); //todo 再検討 - final int bitrate = int.parse( - _parseStringAttr(source: line, pattern: regexpBandwidth)!); - int averageBitrate = 0; - final String? averageBandwidthString = _parseStringAttr( - source: line, - pattern: regexpAverageBandwidth, - variableDefinitions: variableDefinitions); + final bitrate = int.parse( + _parseStringAttr(source: line, pattern: regexpBandwidth)!, + ); + var averageBitrate = 0; + final averageBandwidthString = _parseStringAttr( + source: line, + pattern: regexpAverageBandwidth, + variableDefinitions: variableDefinitions, + ); if (averageBandwidthString != null) { averageBitrate = int.parse(averageBandwidthString); } - final String? codecs = _parseStringAttr( - source: line, - pattern: regexpCodecs, - variableDefinitions: variableDefinitions); - final String? resolutionString = _parseStringAttr( - source: line, - pattern: regexpResolutions, - variableDefinitions: variableDefinitions); + final codecs = _parseStringAttr( + source: line, + pattern: regexpCodecs, + variableDefinitions: variableDefinitions, + ); + final resolutionString = _parseStringAttr( + source: line, + pattern: regexpResolutions, + variableDefinitions: variableDefinitions, + ); int? width; int? height; if (resolutionString != null) { - final List widthAndHeight = resolutionString.split('x'); + final widthAndHeight = resolutionString.split('x'); width = int.parse(widthAndHeight[0]); height = int.parse(widthAndHeight[1]); if (width <= 0 || height <= 0) { @@ -276,176 +289,199 @@ class HlsPlaylistParser { } double? frameRate; - final String? frameRateString = _parseStringAttr( - source: line, - pattern: regexpFrameRate, - variableDefinitions: variableDefinitions); + final frameRateString = _parseStringAttr( + source: line, + pattern: regexpFrameRate, + variableDefinitions: variableDefinitions, + ); if (frameRateString != null) { frameRate = double.parse(frameRateString); } - final String? videoGroupId = _parseStringAttr( - source: line, - pattern: regexpVideo, - variableDefinitions: variableDefinitions); - final String? audioGroupId = _parseStringAttr( - source: line, - pattern: regexpAudio, - variableDefinitions: variableDefinitions); - final String? subtitlesGroupId = _parseStringAttr( - source: line, - pattern: regexpSubtitles, - variableDefinitions: variableDefinitions); - final String? closedCaptionsGroupId = _parseStringAttr( - source: line, - pattern: regexpClosedCaptions, - variableDefinitions: variableDefinitions); + final videoGroupId = _parseStringAttr( + source: line, + pattern: regexpVideo, + variableDefinitions: variableDefinitions, + ); + final audioGroupId = _parseStringAttr( + source: line, + pattern: regexpAudio, + variableDefinitions: variableDefinitions, + ); + final subtitlesGroupId = _parseStringAttr( + source: line, + pattern: regexpSubtitles, + variableDefinitions: variableDefinitions, + ); + final closedCaptionsGroupId = _parseStringAttr( + source: line, + pattern: regexpClosedCaptions, + variableDefinitions: variableDefinitions, + ); extraLines.moveNext(); - final String referenceUri = _parseStringAttr( - source: extraLines.current, - variableDefinitions: variableDefinitions)!; - final Uri uri = Uri.parse(baseUri).resolve(referenceUri); - - final Format format = Format.createVideoContainerFormat( - id: variants.length.toString(), - containerMimeType: MimeTypes.applicationM3u8, - codecs: codecs, - bitrate: bitrate, - averageBitrate: averageBitrate, - width: width, - height: height, - frameRate: frameRate); - - variants.add(Variant( - url: uri, - format: format, - videoGroupId: videoGroupId, - audioGroupId: audioGroupId, - subtitleGroupId: subtitlesGroupId, - captionGroupId: closedCaptionsGroupId, - )); - - List? variantInfosForUrl = urlToVariantInfos[uri]; + final referenceUri = _parseStringAttr( + source: extraLines.current, + variableDefinitions: variableDefinitions, + )!; + final uri = Uri.parse(baseUri).resolve(referenceUri); + + final format = Format.createVideoContainerFormat( + id: variants.length.toString(), + containerMimeType: MimeTypes.applicationM3u8, + codecs: codecs, + bitrate: bitrate, + averageBitrate: averageBitrate, + width: width, + height: height, + frameRate: frameRate, + ); + + variants.add( + Variant( + url: uri, + format: format, + videoGroupId: videoGroupId, + audioGroupId: audioGroupId, + subtitleGroupId: subtitlesGroupId, + captionGroupId: closedCaptionsGroupId, + ), + ); + + var variantInfosForUrl = urlToVariantInfos[uri]; if (variantInfosForUrl == null) { variantInfosForUrl = []; // ignore: always_specify_types urlToVariantInfos[uri] = variantInfosForUrl; } - variantInfosForUrl.add(VariantInfo( - bitrate: bitrate != 0 ? bitrate : averageBitrate, - videoGroupId: videoGroupId, - audioGroupId: audioGroupId, - subtitleGroupId: subtitlesGroupId, - captionGroupId: closedCaptionsGroupId, - )); + variantInfosForUrl.add( + VariantInfo( + bitrate: bitrate != 0 ? bitrate : averageBitrate, + videoGroupId: videoGroupId, + audioGroupId: audioGroupId, + subtitleGroupId: subtitlesGroupId, + captionGroupId: closedCaptionsGroupId, + ), + ); } } // TODO: Don't deduplicate variants by URL. - final List deduplicatedVariants = - []; // ignore: always_specify_types - final Set urlsInDeduplicatedVariants = - {}; // ignore: always_specify_types - for (int i = 0; i < variants.length; i++) { - final Variant variant = variants[i]; + final deduplicatedVariants = []; // ignore: always_specify_types + final urlsInDeduplicatedVariants = {}; // ignore: always_specify_types + for (var i = 0; i < variants.length; i++) { + final variant = variants[i]; if (urlsInDeduplicatedVariants.add(variant.url)) { assert(variant.format.metadata == null); - final HlsTrackMetadataEntry hlsMetadataEntry = + final hlsMetadataEntry = HlsTrackMetadataEntry(variantInfos: urlToVariantInfos[variant.url]); - final Metadata metadata = Metadata([hlsMetadataEntry]); + final metadata = Metadata([hlsMetadataEntry]); deduplicatedVariants.add( - variant.copyWithFormat(variant.format.copyWithMetadata(metadata))); + variant.copyWithFormat(variant.format.copyWithMetadata(metadata)), + ); } } // ignore: always_specify_types - mediaTags.forEach((line) { - final String? groupId = _parseStringAttr( - source: line, - pattern: regexpGroupId, - variableDefinitions: variableDefinitions); - final String? name = _parseStringAttr( - source: line, - pattern: regexpName, - variableDefinitions: variableDefinitions); - final String? referenceUri = _parseStringAttr( - source: line, - pattern: regexpUri, - variableDefinitions: variableDefinitions); + for (final line in mediaTags) { + final groupId = _parseStringAttr( + source: line, + pattern: regexpGroupId, + variableDefinitions: variableDefinitions, + ); + final name = _parseStringAttr( + source: line, + pattern: regexpName, + variableDefinitions: variableDefinitions, + ); + final referenceUri = _parseStringAttr( + source: line, + pattern: regexpUri, + variableDefinitions: variableDefinitions, + ); - Uri uri = Uri.parse(baseUri); + var uri = Uri.parse(baseUri); if (referenceUri != null) uri = uri.resolve(referenceUri); - final String? language = _parseStringAttr( - source: line, - pattern: regexpLanguage, - variableDefinitions: variableDefinitions); - final int selectionFlags = _parseSelectionFlags(line); - final int roleFlags = _parseRoleFlags(line, variableDefinitions); - final String formatId = '$groupId:$name'; + final language = _parseStringAttr( + source: line, + pattern: regexpLanguage, + variableDefinitions: variableDefinitions, + ); + final selectionFlags = _parseSelectionFlags(line); + final roleFlags = _parseRoleFlags(line, variableDefinitions); + final formatId = '$groupId:$name'; Format format; - final HlsTrackMetadataEntry entry = HlsTrackMetadataEntry( - groupId: groupId, name: name, variantInfos: []); - final Metadata metadata = Metadata([entry]); + final entry = HlsTrackMetadataEntry( + groupId: groupId, + name: name, + variantInfos: [], + ); + final metadata = Metadata([entry]); switch (_parseStringAttr( - source: line, - pattern: regexpType, - variableDefinitions: variableDefinitions)) { + source: line, + pattern: regexpType, + variableDefinitions: variableDefinitions, + )) { case typeVideo: { - final Variant? variant = + final variant = variants.firstWhereOrNull((it) => it.videoGroupId == groupId); String? codecs; int? width; int? height; double? frameRate; if (variant != null) { - final Format variantFormat = variant.format; + final variantFormat = variant.format; codecs = LibUtil.getCodecsOfType( - variantFormat.codecs, Util.trackTypeVideo); + variantFormat.codecs, + Util.trackTypeVideo, + ); width = variantFormat.width; height = variantFormat.height; frameRate = variantFormat.frameRate; } - final String? sampleMimeType = + final sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null; format = Format.createVideoContainerFormat( - id: formatId, - label: name, - containerMimeType: MimeTypes.applicationM3u8, - sampleMimeType: sampleMimeType, - codecs: codecs, - width: width, - height: height, - frameRate: frameRate, - selectionFlags: selectionFlags, - roleFlags: roleFlags) - .copyWithMetadata(metadata); - - videos.add(Rendition( - url: uri, - format: format, - groupId: groupId, - name: name, - )); + id: formatId, + label: name, + containerMimeType: MimeTypes.applicationM3u8, + sampleMimeType: sampleMimeType, + codecs: codecs, + width: width, + height: height, + frameRate: frameRate, + selectionFlags: selectionFlags, + roleFlags: roleFlags, + ).copyWithMetadata(metadata); + + videos.add( + Rendition( + url: uri, + format: format, + groupId: groupId, + name: name, + ), + ); break; } case typeAudio: { - final Variant? variant = - _getVariantWithAudioGroup(variants, groupId); - final String? codecs = variant != null + final variant = _getVariantWithAudioGroup(variants, groupId); + final codecs = variant != null ? LibUtil.getCodecsOfType( - variant.format.codecs, Util.trackTypeAudio) + variant.format.codecs, + Util.trackTypeAudio, + ) : null; - final int? channelCount = + final channelCount = _parseChannelsAttribute(line, variableDefinitions); - final String? sampleMimeType = + final sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null; - final Format format = Format( + final format = Format( id: formatId, label: name, containerMimeType: MimeTypes.applicationM3u8, @@ -461,40 +497,45 @@ class HlsPlaylistParser { if (uri == null) { muxedAudioFormat = format; } else { - audios.add(Rendition( - url: uri, - format: format.copyWithMetadata(metadata), - groupId: groupId, - name: name, - )); + audios.add( + Rendition( + url: uri, + format: format.copyWithMetadata(metadata), + groupId: groupId, + name: name, + ), + ); } break; } case typeSubtitles: { - final Format format = Format( - id: formatId, - label: name, - containerMimeType: MimeTypes.applicationM3u8, - sampleMimeType: MimeTypes.textVtt, - selectionFlags: selectionFlags, - roleFlags: roleFlags, - language: language) - .copyWithMetadata(metadata); - subtitles.add(Rendition( - url: uri, - format: format, - groupId: groupId, - name: name, - )); + final format = Format( + id: formatId, + label: name, + containerMimeType: MimeTypes.applicationM3u8, + sampleMimeType: MimeTypes.textVtt, + selectionFlags: selectionFlags, + roleFlags: roleFlags, + language: language, + ).copyWithMetadata(metadata); + subtitles.add( + Rendition( + url: uri, + format: format, + groupId: groupId, + name: name, + ), + ); break; } case typeClosedCaptions: { - final String instreamId = _parseStringAttr( - source: line, - pattern: regexpInStreamId, - variableDefinitions: variableDefinitions)!; + final instreamId = _parseStringAttr( + source: line, + pattern: regexpInStreamId, + variableDefinitions: variableDefinitions, + )!; String mimeType; int accessibilityChannel; if (instreamId.startsWith('CC')) { @@ -505,39 +546,42 @@ class HlsPlaylistParser { accessibilityChannel = int.parse(instreamId.substring(7)); } muxedCaptionFormats ??= []; // ignore: always_specify_types - muxedCaptionFormats!.add(Format( - id: formatId, - label: name, - sampleMimeType: mimeType, - selectionFlags: selectionFlags, - roleFlags: roleFlags, - language: language, - accessibilityChannel: accessibilityChannel, - )); + muxedCaptionFormats.add( + Format( + id: formatId, + label: name, + sampleMimeType: mimeType, + selectionFlags: selectionFlags, + roleFlags: roleFlags, + language: language, + accessibilityChannel: accessibilityChannel, + ), + ); break; } default: break; } - }); + } if (noClosedCaptions) { muxedCaptionFormats = []; } // ignore: always_specify_types return HlsMasterPlaylist( - baseUri: baseUri, - tags: tags, - variants: deduplicatedVariants, - videos: videos, - audios: audios, - subtitles: subtitles, - closedCaptions: closedCaptions, - muxedAudioFormat: muxedAudioFormat, - muxedCaptionFormats: muxedCaptionFormats, - hasIndependentSegments: hasIndependentSegmentsTag, - variableDefinitions: variableDefinitions, - sessionKeyDrmInitData: sessionKeyDrmInitData); + baseUri: baseUri, + tags: tags, + variants: deduplicatedVariants, + videos: videos, + audios: audios, + subtitles: subtitles, + closedCaptions: closedCaptions, + muxedAudioFormat: muxedAudioFormat, + muxedCaptionFormats: muxedCaptionFormats, + hasIndependentSegments: hasIndependentSegmentsTag, + variableDefinitions: variableDefinitions, + sessionKeyDrmInitData: sessionKeyDrmInitData, + ); } static String? _parseStringAttr({ @@ -555,16 +599,18 @@ class HlsPlaylistParser { } return value?.replaceAllMapped( - RegExp(regexpVariableReference), - (Match match) => variableDefinitions![match.group(1)] ??= - value!.substring(match.start, match.end)); + RegExp(regexpVariableReference), + (Match match) => variableDefinitions![match.group(1)] ??= + value!.substring(match.start, match.end), + ); } - static SchemeData? _parseDrmSchemeData( - {String? line, - String? keyFormat, - Map? variableDefinitions}) { - final String? keyFormatVersions = _parseStringAttr( + static SchemeData? _parseDrmSchemeData({ + String? line, + String? keyFormat, + Map? variableDefinitions, + }) { + final keyFormatVersions = _parseStringAttr( source: line, pattern: regexpKeyFormatVersions, defaultValue: '1', @@ -572,26 +618,30 @@ class HlsPlaylistParser { ); if (keyFormatWidevinePsshBinary == keyFormat) { - final String uriString = _parseStringAttr( - source: line, - pattern: regexpUri, - variableDefinitions: variableDefinitions)!; - final Uint8List data = _getBase64FromUri(uriString); + final uriString = _parseStringAttr( + source: line, + pattern: regexpUri, + variableDefinitions: variableDefinitions, + )!; + final data = _getBase64FromUri(uriString); return SchemeData( // uuid: '', //todo 保留 - mimeType: MimeTypes.videoMp4, - data: data); + mimeType: MimeTypes.videoMp4, + data: data, + ); } else if (keyFormatWidevinePsshJson == keyFormat) { return SchemeData( // uuid: '', //todo 保留 - mimeType: MimeTypes.hls, - data: const Utf8Encoder().convert(line!)); + mimeType: MimeTypes.hls, + data: const Utf8Encoder().convert(line!), + ); } else if (keyFormatPlayReady == keyFormat && '1' == keyFormatVersions) { - final String uriString = _parseStringAttr( - source: line, - pattern: regexpUri, - variableDefinitions: variableDefinitions)!; - final Uint8List data = _getBase64FromUri(uriString); + final uriString = _parseStringAttr( + source: line, + pattern: regexpUri, + variableDefinitions: variableDefinitions, + )!; + final data = _getBase64FromUri(uriString); // Uint8List psshData; //todo 保留 return SchemeData(mimeType: MimeTypes.videoMp4, data: data); } @@ -600,20 +650,23 @@ class HlsPlaylistParser { } static int _parseSelectionFlags(String line) { - int flags = 0; + var flags = 0; if (parseOptionalBooleanAttribute( - line: line, - pattern: regexpDefault, - defaultValue: false)) flags |= Util.selectionFlagDefault; + line: line, + pattern: regexpDefault, + defaultValue: false, + )) flags |= Util.selectionFlagDefault; if (parseOptionalBooleanAttribute( - line: line, - pattern: regexpForced, - defaultValue: false)) flags |= Util.selectionFlagForced; + line: line, + pattern: regexpForced, + defaultValue: false, + )) flags |= Util.selectionFlagForced; if (parseOptionalBooleanAttribute( - line: line, - pattern: regexpAutoSelect, - defaultValue: false)) flags |= Util.selectionFlagAutoSelect; + line: line, + pattern: regexpAutoSelect, + defaultValue: false, + )) flags |= Util.selectionFlagAutoSelect; return flags; } @@ -633,15 +686,17 @@ class HlsPlaylistParser { } static int _parseRoleFlags( - String line, Map variableDefinitions) { - final String? concatenatedCharacteristics = _parseStringAttr( - source: line, - pattern: regexpCharacteristics, - variableDefinitions: variableDefinitions); + String line, + Map variableDefinitions, + ) { + final concatenatedCharacteristics = _parseStringAttr( + source: line, + pattern: regexpCharacteristics, + variableDefinitions: variableDefinitions, + ); if (concatenatedCharacteristics?.isEmpty != false) return 0; - final List characteristics = - concatenatedCharacteristics!.split(','); - int roleFlags = 0; + final characteristics = concatenatedCharacteristics!.split(','); + var roleFlags = 0; if (characteristics.contains('public.accessibility.describes-video')) { roleFlags |= Util.roleFlagDescribesVideo; } @@ -664,18 +719,23 @@ class HlsPlaylistParser { } static int? _parseChannelsAttribute( - String line, Map variableDefinitions) { - final String? channelsString = _parseStringAttr( - source: line, - pattern: regexpChannels, - variableDefinitions: variableDefinitions); + String line, + Map variableDefinitions, + ) { + final channelsString = _parseStringAttr( + source: line, + pattern: regexpChannels, + variableDefinitions: variableDefinitions, + ); return channelsString != null ? int.parse(channelsString.split('/')[0]) : null; } static Variant? _getVariantWithAudioGroup( - List variants, String? groupId) { + List variants, + String? groupId, + ) { for (final variant in variants) { if (variant.audioGroupId == groupId) return variant; } @@ -688,39 +748,42 @@ class HlsPlaylistParser { : CencType.cnbs; static Uint8List _getBase64FromUri(String uriString) { - final String uriPre = uriString.substring(uriString.indexOf(',') + 1); + final uriPre = uriString.substring(uriString.indexOf(',') + 1); return const Base64Decoder().convert(uriPre); } - static HlsMediaPlaylist _parseMediaPlaylist(HlsMasterPlaylist masterPlaylist, - List extraLines, String baseUri) { - int playlistType = HlsMediaPlaylist.playlistTypeUnknown; + static HlsMediaPlaylist _parseMediaPlaylist( + HlsMasterPlaylist masterPlaylist, + List extraLines, + String baseUri, + ) { + var playlistType = HlsMediaPlaylist.playlistTypeUnknown; int? startOffsetUs; int? mediaSequence; int? version; int? targetDurationUs; - bool hasIndependentSegmentsTag = masterPlaylist.hasIndependentSegments; - bool hasEndTag = false; + var hasIndependentSegmentsTag = masterPlaylist.hasIndependentSegments; + var hasEndTag = false; int? segmentByteRangeOffset; Segment? initializationSegment; - final Map variableDefinitions = {}; - final List segments = []; - final List tags = []; // ignore: always_specify_types + final variableDefinitions = {}; + final segments = []; + final tags = []; // ignore: always_specify_types int? segmentByteRangeLength; int? segmentMediaSequence = 0; int? segmentDurationUs; String? segmentTitle; - final Map currentSchemeDatas = - {}; // ignore: always_specify_types + final currentSchemeDatas = + {}; // ignore: always_specify_types DrmInitData? cachedDrmInitData; String? encryptionScheme; DrmInitData? playlistProtectionSchemes; - bool hasDiscontinuitySequence = false; - int playlistDiscontinuitySequence = 0; + var hasDiscontinuitySequence = false; + var playlistDiscontinuitySequence = 0; int? relativeDiscontinuitySequence; int? playlistStartTimeUs; int? segmentStartTimeUs; - bool hasGapTag = false; + var hasGapTag = false; String? fullSegmentEncryptionKeyUri; String? fullSegmentEncryptionIV; @@ -732,32 +795,36 @@ class HlsPlaylistParser { } if (line.startsWith(tagPlaylistType)) { - final String? playlistTypeString = _parseStringAttr( - source: line, - pattern: regexpPlaylistType, - variableDefinitions: variableDefinitions); + final playlistTypeString = _parseStringAttr( + source: line, + pattern: regexpPlaylistType, + variableDefinitions: variableDefinitions, + ); if ('VOD' == playlistTypeString) { playlistType = HlsMediaPlaylist.playlistTypeVod; } else if ('EVENT' == playlistTypeString) { playlistType = HlsMediaPlaylist.playlistTypeEvent; } } else if (line.startsWith(tagStart)) { - final String string = _parseStringAttr( - source: line, - pattern: regexpTimeOffset, - variableDefinitions: {})!; // ignore: always_specify_types + final string = _parseStringAttr( + source: line, + pattern: regexpTimeOffset, + variableDefinitions: {}, + )!; // ignore: always_specify_types startOffsetUs = (double.parse(string) * 1000000).toInt(); } else if (line.startsWith(tagInitSegment)) { - final String? uri = _parseStringAttr( - source: line, - pattern: regexpUri, - variableDefinitions: variableDefinitions); - final String? byteRange = _parseStringAttr( - source: line, - pattern: regexpAttrByteRange, - variableDefinitions: variableDefinitions); + final uri = _parseStringAttr( + source: line, + pattern: regexpUri, + variableDefinitions: variableDefinitions, + ); + final byteRange = _parseStringAttr( + source: line, + pattern: regexpAttrByteRange, + variableDefinitions: variableDefinitions, + ); if (byteRange != null) { - final List splitByteRange = byteRange.split('@'); + final splitByteRange = byteRange.split('@'); segmentByteRangeLength = int.parse(splitByteRange[0]); if (splitByteRange.length > 1) { segmentByteRangeOffset = int.parse(splitByteRange[1]); @@ -767,70 +834,83 @@ class HlsPlaylistParser { if (fullSegmentEncryptionKeyUri != null && fullSegmentEncryptionIV == null) { throw ParserException( - 'The encryption IV attribute must be present when an initialization segment is encrypted with METHOD=AES-128.'); + 'The encryption IV attribute must be present when an initialization segment is encrypted with METHOD=AES-128.', + ); } initializationSegment = Segment( - url: uri, - byterangeOffset: segmentByteRangeOffset, - byterangeLength: segmentByteRangeLength, - fullSegmentEncryptionKeyUri: fullSegmentEncryptionKeyUri, - encryptionIV: fullSegmentEncryptionIV); + url: uri, + byterangeOffset: segmentByteRangeOffset, + byterangeLength: segmentByteRangeLength, + fullSegmentEncryptionKeyUri: fullSegmentEncryptionKeyUri, + encryptionIV: fullSegmentEncryptionIV, + ); segmentByteRangeOffset = null; segmentByteRangeLength = null; } else if (line.startsWith(tagTargetDuration)) { - targetDurationUs = int.parse(_parseStringAttr( - source: line, pattern: regexpTargetDuration)!) * + targetDurationUs = int.parse( + _parseStringAttr( + source: line, + pattern: regexpTargetDuration, + )!, + ) * 1000000; } else if (line.startsWith(tagMediaSequence)) { mediaSequence = int.parse( - _parseStringAttr(source: line, pattern: regexpMediaSequence)!); + _parseStringAttr(source: line, pattern: regexpMediaSequence)!, + ); segmentMediaSequence = mediaSequence; } else if (line.startsWith(tagVersion)) { version = int.parse(_parseStringAttr(source: line, pattern: regexpVersion)!); } else if (line.startsWith(tagDefine)) { - final String? importName = _parseStringAttr( - source: line, - pattern: regexpImport, - variableDefinitions: variableDefinitions); + final importName = _parseStringAttr( + source: line, + pattern: regexpImport, + variableDefinitions: variableDefinitions, + ); if (importName != null) { - final String? value = masterPlaylist.variableDefinitions[importName]; + final value = masterPlaylist.variableDefinitions[importName]; if (value != null) { variableDefinitions[importName] = value; } else { // The master playlist does not declare the imported variable. Ignore. } } else { - final String? key = _parseStringAttr( - source: line, - pattern: regexpName, - variableDefinitions: variableDefinitions); - final String? value = _parseStringAttr( - source: line, - pattern: regexpValue, - variableDefinitions: variableDefinitions); + final key = _parseStringAttr( + source: line, + pattern: regexpName, + variableDefinitions: variableDefinitions, + ); + final value = _parseStringAttr( + source: line, + pattern: regexpValue, + variableDefinitions: variableDefinitions, + ); variableDefinitions[key] = value; } } else if (line.startsWith(tagMediaDuration)) { - final String string = + final string = _parseStringAttr(source: line, pattern: regexpMediaDuration)!; segmentDurationUs = (double.parse(string) * 1000000).toInt(); segmentTitle = _parseStringAttr( - source: line, - pattern: regexpMediaTitle, - defaultValue: '', - variableDefinitions: variableDefinitions); + source: line, + pattern: regexpMediaTitle, + defaultValue: '', + variableDefinitions: variableDefinitions, + ); } else if (line.startsWith(tagKey)) { - final String? method = _parseStringAttr( - source: line, - pattern: regexpMethod, - variableDefinitions: variableDefinitions); - final String? keyFormat = _parseStringAttr( - source: line, - pattern: regexpKeyFormat, - defaultValue: keyFormatIdentity, - variableDefinitions: variableDefinitions); + final method = _parseStringAttr( + source: line, + pattern: regexpMethod, + variableDefinitions: variableDefinitions, + ); + final keyFormat = _parseStringAttr( + source: line, + pattern: regexpKeyFormat, + defaultValue: keyFormatIdentity, + variableDefinitions: variableDefinitions, + ); fullSegmentEncryptionKeyUri = null; fullSegmentEncryptionIV = null; if (methodNone == method) { @@ -838,26 +918,29 @@ class HlsPlaylistParser { cachedDrmInitData = null; } else /* !METHOD_NONE.equals(method) */ { fullSegmentEncryptionIV = _parseStringAttr( - source: line, - pattern: regexpIv, - variableDefinitions: variableDefinitions); + source: line, + pattern: regexpIv, + variableDefinitions: variableDefinitions, + ); if (keyFormatIdentity == keyFormat) { if (methodAes128 == method) { // The segment is fully encrypted using an identity key. fullSegmentEncryptionKeyUri = _parseStringAttr( - source: line, - pattern: regexpUri, - variableDefinitions: variableDefinitions); + source: line, + pattern: regexpUri, + variableDefinitions: variableDefinitions, + ); } else { // Do nothing. Samples are encrypted using an identity key, but this is not supported. // Hopefully, a traditional DRM alternative is also provided. } } else { encryptionScheme ??= _parseEncryptionScheme(method); - final SchemeData? schemeData = _parseDrmSchemeData( - line: line, - keyFormat: keyFormat, - variableDefinitions: variableDefinitions); + final schemeData = _parseDrmSchemeData( + line: line, + keyFormat: keyFormat, + variableDefinitions: variableDefinitions, + ); if (schemeData != null) { cachedDrmInitData = null; currentSchemeDatas[keyFormat] = schemeData; @@ -865,11 +948,12 @@ class HlsPlaylistParser { } } } else if (line.startsWith(tagByteRange)) { - final String byteRange = _parseStringAttr( - source: line, - pattern: regexpByteRange, - variableDefinitions: variableDefinitions)!; - final List splitByteRange = byteRange.split('@'); + final byteRange = _parseStringAttr( + source: line, + pattern: regexpByteRange, + variableDefinitions: variableDefinitions, + )!; + final splitByteRange = byteRange.split('@'); segmentByteRangeLength = int.parse(splitByteRange[0]); if (splitByteRange.length > 1) { segmentByteRangeOffset = int.parse(splitByteRange[1]); @@ -883,7 +967,7 @@ class HlsPlaylistParser { relativeDiscontinuitySequence++; } else if (line.startsWith(tagProgramDateTime)) { if (playlistStartTimeUs == null) { - final int programDatetimeUs = + final programDatetimeUs = LibUtil.parseXsDateTime(line.substring(line.indexOf(':') + 1)); playlistStartTimeUs = programDatetimeUs - (segmentStartTimeUs ?? 0); } @@ -908,21 +992,27 @@ class HlsPlaylistParser { if (cachedDrmInitData?.schemeData.isNotEmpty != true && currentSchemeDatas.isNotEmpty) { - final List schemeDatas = - currentSchemeDatas.values.toList(); + final schemeDatas = currentSchemeDatas.values.toList(); cachedDrmInitData = DrmInitData( - schemeType: encryptionScheme, schemeData: schemeDatas); + schemeType: encryptionScheme, + schemeData: schemeDatas, + ); if (playlistProtectionSchemes == null) { - final List playlistSchemeDatas = + final playlistSchemeDatas = schemeDatas.map((it) => it.copyWithData(null)).toList(); playlistProtectionSchemes = DrmInitData( - schemeType: encryptionScheme, schemeData: playlistSchemeDatas); + schemeType: encryptionScheme, + schemeData: playlistSchemeDatas, + ); } } - final String? url = _parseStringAttr( - source: line, variableDefinitions: variableDefinitions); - segments.add(Segment( + final url = _parseStringAttr( + source: line, + variableDefinitions: variableDefinitions, + ); + segments.add( + Segment( url: url, initializationSegment: initializationSegment, title: segmentTitle, @@ -934,7 +1024,9 @@ class HlsPlaylistParser { encryptionIV: segmentEncryptionIV, byterangeOffset: segmentByteRangeOffset, byterangeLength: segmentByteRangeLength, - hasGapTag: hasGapTag)); + hasGapTag: hasGapTag, + ), + ); if (segmentDurationUs != null) { segmentStartTimeUs ??= 0; @@ -953,20 +1045,21 @@ class HlsPlaylistParser { } return HlsMediaPlaylist.create( - playlistType: playlistType, - baseUri: baseUri, - tags: tags, - startOffsetUs: startOffsetUs, - startTimeUs: playlistStartTimeUs, - hasDiscontinuitySequence: hasDiscontinuitySequence, - discontinuitySequence: playlistDiscontinuitySequence, - mediaSequence: mediaSequence, - version: version, - targetDurationUs: targetDurationUs, - hasIndependentSegments: hasIndependentSegmentsTag, - hasEndTag: hasEndTag, - hasProgramDateTime: playlistStartTimeUs != null, - protectionSchemes: playlistProtectionSchemes, - segments: segments); + playlistType: playlistType, + baseUri: baseUri, + tags: tags, + startOffsetUs: startOffsetUs, + startTimeUs: playlistStartTimeUs, + hasDiscontinuitySequence: hasDiscontinuitySequence, + discontinuitySequence: playlistDiscontinuitySequence, + mediaSequence: mediaSequence, + version: version, + targetDurationUs: targetDurationUs, + hasIndependentSegments: hasIndependentSegmentsTag, + hasEndTag: hasEndTag, + hasProgramDateTime: playlistStartTimeUs != null, + protectionSchemes: playlistProtectionSchemes, + segments: segments, + ); } } diff --git a/lib/src/hls/hls_parser/mime_types.dart b/lib/src/hls/hls_parser/mime_types.dart index c17711be7..8f6893dea 100644 --- a/lib/src/hls/hls_parser/mime_types.dart +++ b/lib/src/hls/hls_parser/mime_types.dart @@ -1,4 +1,4 @@ -import 'util.dart'; +import 'package:better_player/src/hls/hls_parser/util.dart'; class MimeTypes { static const String baseTypeVideo = 'video'; @@ -125,7 +125,7 @@ class MimeTypes { } static String? getMediaMimeType(String codecValue) { - String codec = codecValue; + var codec = codecValue; codec = codec.trim().toLowerCase(); if (codec.startsWith('avc1') || codec.startsWith('avc3')) { @@ -152,12 +152,12 @@ class MimeTypes { if (codec.startsWith('mp4a')) { String? mimeType; if (codec.startsWith('mp4a.')) { - final String objectTypeString = codec.substring(5); + final objectTypeString = codec.substring(5); if (objectTypeString.length >= 2) { try { - final String objectTypeHexString = + final objectTypeHexString = objectTypeString.substring(0, 2).toUpperCase(); - final int objectTypeInt = int.parse(objectTypeHexString, radix: 16); + final objectTypeInt = int.parse(objectTypeHexString, radix: 16); mimeType = _getMimeTypeFromMp4ObjectType(objectTypeInt); } on FormatException { //do nothing @@ -243,7 +243,7 @@ class MimeTypes { static String? getTopLevelType(String? mimeType) { if (mimeType == null) return null; - final int indexOfSlash = mimeType.indexOf('/'); + final indexOfSlash = mimeType.indexOf('/'); if (indexOfSlash == -1) return null; return mimeType.substring(0, indexOfSlash); } diff --git a/lib/src/hls/hls_parser/rendition.dart b/lib/src/hls/hls_parser/rendition.dart index 1033c4e14..d3ebaf632 100644 --- a/lib/src/hls/hls_parser/rendition.dart +++ b/lib/src/hls/hls_parser/rendition.dart @@ -1,11 +1,12 @@ import 'package:better_player/src/hls/hls_parser/format.dart'; class Rendition { - Rendition( - {this.url, - required this.format, - required this.groupId, - required this.name}); + Rendition({ + required this.format, + required this.groupId, + required this.name, + this.url, + }); /// The rendition's url, or null if the tag does not have a URI attribute. final Uri? url; diff --git a/lib/src/hls/hls_parser/scheme_data.dart b/lib/src/hls/hls_parser/scheme_data.dart index 49557dde0..3a8524cd4 100644 --- a/lib/src/hls/hls_parser/scheme_data.dart +++ b/lib/src/hls/hls_parser/scheme_data.dart @@ -4,9 +4,8 @@ import 'package:flutter/material.dart'; class SchemeData { SchemeData({ -// @required this.uuid, + required this.mimeType, // @required this.uuid, this.licenseServerUrl, - required this.mimeType, this.data, this.requiresSecureDecryption, }); @@ -50,9 +49,10 @@ class SchemeData { @override int get hashCode => hashValues( - /*uuid, */ - licenseServerUrl, - mimeType, - data, - requiresSecureDecryption); + /*uuid, */ + licenseServerUrl, + mimeType, + data, + requiresSecureDecryption, + ); } diff --git a/lib/src/hls/hls_parser/segment.dart b/lib/src/hls/hls_parser/segment.dart index 0e3e2796f..fa0856933 100644 --- a/lib/src/hls/hls_parser/segment.dart +++ b/lib/src/hls/hls_parser/segment.dart @@ -3,16 +3,16 @@ import 'package:better_player/src/hls/hls_parser/drm_init_data.dart'; class Segment { Segment({ required this.url, + required this.fullSegmentEncryptionKeyUri, + required this.encryptionIV, + required this.byterangeOffset, + required this.byterangeLength, this.initializationSegment, this.durationUs, this.title, this.relativeDiscontinuitySequence, this.relativeStartTimeUs, this.drmInitData, - required this.fullSegmentEncryptionKeyUri, - required this.encryptionIV, - required this.byterangeOffset, - required this.byterangeLength, this.hasGapTag = false, }); diff --git a/lib/src/hls/hls_parser/util.dart b/lib/src/hls/hls_parser/util.dart index 8f3aa025f..8103675b6 100644 --- a/lib/src/hls/hls_parser/util.dart +++ b/lib/src/hls/hls_parser/util.dart @@ -3,7 +3,7 @@ import 'package:better_player/src/hls/hls_parser/mime_types.dart'; class LibUtil { static bool startsWith(List source, List checker) { - for (int i = 0; i < checker.length; i++) { + for (var i = 0; i < checker.length; i++) { if (source[i] != checker[i]) return false; } @@ -45,13 +45,13 @@ class LibUtil { } static int parseXsDateTime(String value) { - const String pattern = - '(\\d\\d\\d\\d)\\-(\\d\\d)\\-(\\d\\d)[Tt](\\d\\d):(\\d\\d):(\\d\\d)([\\.,](\\d+))?([Zz]|((\\+|\\-)(\\d?\\d):?(\\d\\d)))?'; + const pattern = + r'(\d\d\d\d)\-(\d\d)\-(\d\d)[Tt](\d\d):(\d\d):(\d\d)([\.,](\d+))?([Zz]|((\+|\-)(\d?\d):?(\d\d)))?'; final List matchList = RegExp(pattern).allMatches(value).toList(); if (matchList.isEmpty) { throw ParserException('Invalid date/time format: $value'); } - final Match match = matchList[0]; + final match = matchList[0]; int timezoneShift; if (match.group(9) == null) { // No time zone specified. @@ -65,18 +65,19 @@ class LibUtil { } //todo UTCではなくGMT? - final DateTime dateTime = DateTime.utc( - int.parse(match.group(1)!), - int.parse(match.group(2)!), - int.parse(match.group(3)!), - int.parse(match.group(4)!), - int.parse(match.group(5)!), - int.parse(match.group(6)!)); + final dateTime = DateTime.utc( + int.parse(match.group(1)!), + int.parse(match.group(2)!), + int.parse(match.group(3)!), + int.parse(match.group(4)!), + int.parse(match.group(5)!), + int.parse(match.group(6)!), + ); if (match.group(8)?.isNotEmpty == true) { //todo ここ実装再検討 } - int time = dateTime.millisecondsSinceEpoch; + var time = dateTime.millisecondsSinceEpoch; if (timezoneShift != 0) { time -= timezoneShift * 60000; } @@ -125,7 +126,7 @@ class Util { static List splitCodecs(String? codecs) => codecs?.isNotEmpty != true ? [] - : codecs!.trim().split(RegExp('(\\s*,\\s*)')); + : codecs!.trim().split(RegExp(r'(\s*,\s*)')); static bool checkBitPositionIsSet(int number, int bitPosition) { if ((number & (1 << (bitPosition - 1))) > 0) { diff --git a/lib/src/hls/hls_parser/variant_info.dart b/lib/src/hls/hls_parser/variant_info.dart index 74c4ecb38..5c00928b5 100644 --- a/lib/src/hls/hls_parser/variant_info.dart +++ b/lib/src/hls/hls_parser/variant_info.dart @@ -42,5 +42,10 @@ class VariantInfo { @override int get hashCode => hashValues( - bitrate, videoGroupId, audioGroupId, subtitleGroupId, captionGroupId); + bitrate, + videoGroupId, + audioGroupId, + subtitleGroupId, + captionGroupId, + ); } diff --git a/lib/src/list/better_player_list_video_player.dart b/lib/src/list/better_player_list_video_player.dart index 79683ddf4..aa62379ce 100644 --- a/lib/src/list/better_player_list_video_player.dart +++ b/lib/src/list/better_player_list_video_player.dart @@ -4,6 +4,19 @@ import 'package:flutter/material.dart'; ///Special version of Better Player which is used to play video in list view. class BetterPlayerListVideoPlayer extends StatefulWidget { + const BetterPlayerListVideoPlayer( + this.dataSource, { + this.configuration = const BetterPlayerConfiguration(), + this.playFraction = 0.6, + this.autoPlay = true, + this.autoPause = true, + this.betterPlayerListVideoPlayerController, + super.key, + }) : assert( + playFraction >= 0.0 && playFraction <= 1.0, + "Play fraction can't be null and must be between 0.0 and 1.0", + ); + ///Video to show final BetterPlayerDataSource dataSource; @@ -24,18 +37,6 @@ class BetterPlayerListVideoPlayer extends StatefulWidget { final BetterPlayerListVideoPlayerController? betterPlayerListVideoPlayerController; - const BetterPlayerListVideoPlayer( - this.dataSource, { - this.configuration = const BetterPlayerConfiguration(), - this.playFraction = 0.6, - this.autoPlay = true, - this.autoPause = true, - this.betterPlayerListVideoPlayerController, - Key? key, - }) : assert(playFraction >= 0.0 && playFraction <= 1.0, - "Play fraction can't be null and must be between 0.0 and 1.0"), - super(key: key); - @override _BetterPlayerListVideoPlayerState createState() => _BetterPlayerListVideoPlayerState(); @@ -79,15 +80,15 @@ class _BetterPlayerListVideoPlayerState aspectRatio: _betterPlayerController!.getAspectRatio() ?? BetterPlayerUtils.calculateAspectRatio(context), child: BetterPlayer( - key: Key("${_getUniqueKey()}_player"), + key: Key('${_getUniqueKey()}_player'), controller: _betterPlayerController!, ), ); } - void onVisibilityChanged(double visibleFraction) async { - final bool? isPlaying = _betterPlayerController!.isPlaying(); - final bool? initialized = _betterPlayerController!.isVideoInitialized(); + Future onVisibilityChanged(double visibleFraction) async { + final isPlaying = _betterPlayerController!.isPlaying(); + final initialized = _betterPlayerController!.isVideoInitialized(); if (visibleFraction >= widget.playFraction) { if (widget.autoPlay && initialized! && !isPlaying! && !_isDisposing) { _betterPlayerController!.play(); diff --git a/lib/src/list/better_player_list_video_player_controller.dart b/lib/src/list/better_player_list_video_player_controller.dart index e26e804d0..03ba3add4 100644 --- a/lib/src/list/better_player_list_video_player_controller.dart +++ b/lib/src/list/better_player_list_video_player_controller.dart @@ -22,7 +22,8 @@ class BetterPlayerListVideoPlayerController { // ignore: use_setters_to_change_properties void setBetterPlayerController( - BetterPlayerController? betterPlayerController) { + BetterPlayerController? betterPlayerController, + ) { _betterPlayerController = betterPlayerController; } diff --git a/lib/src/playlist/better_player_playlist.dart b/lib/src/playlist/better_player_playlist.dart index 574d32fa1..36251dbb5 100644 --- a/lib/src/playlist/better_player_playlist.dart +++ b/lib/src/playlist/better_player_playlist.dart @@ -6,16 +6,15 @@ import 'package:flutter/material.dart'; ///Special version of Better Player used to play videos in playlist. class BetterPlayerPlaylist extends StatefulWidget { - final List betterPlayerDataSourceList; - final BetterPlayerConfiguration betterPlayerConfiguration; - final BetterPlayerPlaylistConfiguration betterPlayerPlaylistConfiguration; - const BetterPlayerPlaylist({ - Key? key, required this.betterPlayerDataSourceList, required this.betterPlayerConfiguration, required this.betterPlayerPlaylistConfiguration, - }) : super(key: key); + super.key, + }); + final List betterPlayerDataSourceList; + final BetterPlayerConfiguration betterPlayerConfiguration; + final BetterPlayerPlaylistConfiguration betterPlayerPlaylistConfiguration; @override BetterPlayerPlaylistState createState() => BetterPlayerPlaylistState(); @@ -35,10 +34,11 @@ class BetterPlayerPlaylistState extends State { @override void initState() { _betterPlayerPlaylistController = BetterPlayerPlaylistController( - widget.betterPlayerDataSourceList, - betterPlayerConfiguration: widget.betterPlayerConfiguration, - betterPlayerPlaylistConfiguration: - widget.betterPlayerPlaylistConfiguration); + widget.betterPlayerDataSourceList, + betterPlayerConfiguration: widget.betterPlayerConfiguration, + betterPlayerPlaylistConfiguration: + widget.betterPlayerPlaylistConfiguration, + ); super.initState(); } diff --git a/lib/src/playlist/better_player_playlist_configuration.dart b/lib/src/playlist/better_player_playlist_configuration.dart index e3f39d331..a0406fa9e 100644 --- a/lib/src/playlist/better_player_playlist_configuration.dart +++ b/lib/src/playlist/better_player_playlist_configuration.dart @@ -1,5 +1,11 @@ ///Additional configuration used in Better Player Playlist player. class BetterPlayerPlaylistConfiguration { + const BetterPlayerPlaylistConfiguration({ + this.nextVideoDelay = const Duration(milliseconds: 3000), + this.loopVideos = true, + this.initialStartIndex = 0, + }); + ///How long user should wait for next video final Duration nextVideoDelay; @@ -9,10 +15,4 @@ class BetterPlayerPlaylistConfiguration { ///Index of video that will start on playlist start. Id must be less than ///elements in data source list. Default is 0. final int initialStartIndex; - - const BetterPlayerPlaylistConfiguration({ - this.nextVideoDelay = const Duration(milliseconds: 3000), - this.loopVideos = true, - this.initialStartIndex = 0, - }); } diff --git a/lib/src/playlist/better_player_playlist_controller.dart b/lib/src/playlist/better_player_playlist_controller.dart index 5ed7d2c78..971f14cf2 100644 --- a/lib/src/playlist/better_player_playlist_controller.dart +++ b/lib/src/playlist/better_player_playlist_controller.dart @@ -3,6 +3,18 @@ import 'package:better_player/better_player.dart'; ///Controller used to manage playlist player. class BetterPlayerPlaylistController { + BetterPlayerPlaylistController( + this._betterPlayerDataSourceList, { + this.betterPlayerConfiguration = const BetterPlayerConfiguration(), + this.betterPlayerPlaylistConfiguration = + const BetterPlayerPlaylistConfiguration(), + }) : assert( + _betterPlayerDataSourceList.isNotEmpty, + "Better Player data source list can't be empty", + ) { + _setup(); + } + ///List of data sources set for playlist. final List _betterPlayerDataSourceList; @@ -24,16 +36,6 @@ class BetterPlayerPlaylistController { ///Flag that determines whenever player is changing video bool _changingToNextVideo = false; - BetterPlayerPlaylistController( - this._betterPlayerDataSourceList, { - this.betterPlayerConfiguration = const BetterPlayerConfiguration(), - this.betterPlayerPlaylistConfiguration = - const BetterPlayerPlaylistConfiguration(), - }) : assert(_betterPlayerDataSourceList.isNotEmpty, - "Better Player data source list can't be empty") { - _setup(); - } - ///Initialize controller and listeners. void _setup() { _betterPlayerController ??= BetterPlayerController( @@ -72,7 +74,7 @@ class BetterPlayerPlaylistController { if (_changingToNextVideo) { return; } - final int nextDataSourceId = _getNextDataSourceIndex(); + final nextDataSourceId = _getNextDataSourceIndex(); if (nextDataSourceId == -1) { return; } @@ -101,8 +103,8 @@ class BetterPlayerPlaylistController { void setupDataSource(int index) { assert( index >= 0 && index < _betterPlayerDataSourceList.length, - "Index must be greater than 0 and less than size of data source " - "list - 1"); + 'Index must be greater than 0 and less than size of data source ' + 'list - 1'); if (index <= _dataSourceLength) { _currentDataSourceIndex = index; _betterPlayerController! diff --git a/lib/src/subtitles/better_player_subtitle.dart b/lib/src/subtitles/better_player_subtitle.dart index 00955abca..a73917e99 100644 --- a/lib/src/subtitles/better_player_subtitle.dart +++ b/lib/src/subtitles/better_player_subtitle.dart @@ -1,19 +1,6 @@ import 'package:better_player/src/core/better_player_utils.dart'; class BetterPlayerSubtitle { - static const String timerSeparator = ' --> '; - final int? index; - final Duration? start; - final Duration? end; - final List? texts; - - BetterPlayerSubtitle._({ - this.index, - this.start, - this.end, - this.texts, - }); - factory BetterPlayerSubtitle(String value, bool isWebVTT) { try { final scanner = value.split('\n'); @@ -25,11 +12,23 @@ class BetterPlayerSubtitle { } return BetterPlayerSubtitle._(); } catch (exception) { - BetterPlayerUtils.log("Failed to parse subtitle line: $value"); + BetterPlayerUtils.log('Failed to parse subtitle line: $value'); return BetterPlayerSubtitle._(); } } + BetterPlayerSubtitle._({ + this.index, + this.start, + this.end, + this.texts, + }); + static const String timerSeparator = ' --> '; + final int? index; + final Duration? start; + final Duration? end; + final List? texts; + static BetterPlayerSubtitle _handle2LinesSubtitles(List scanner) { try { final timeSplit = scanner[0].split(timerSeparator); @@ -38,19 +37,25 @@ class BetterPlayerSubtitle { final texts = scanner.sublist(1, scanner.length); return BetterPlayerSubtitle._( - index: -1, start: start, end: end, texts: texts); + index: -1, + start: start, + end: end, + texts: texts, + ); } catch (exception) { - BetterPlayerUtils.log("Failed to parse subtitle line: $scanner"); + BetterPlayerUtils.log('Failed to parse subtitle line: $scanner'); return BetterPlayerSubtitle._(); } } static BetterPlayerSubtitle _handle3LinesAndMoreSubtitles( - List scanner, bool isWebVTT) { + List scanner, + bool isWebVTT, + ) { try { int? index = -1; - List timeSplit = []; - int firstLineOfText = 0; + var timeSplit = []; + var firstLineOfText = 0; if (scanner[0].contains(timerSeparator)) { timeSplit = scanner[0].split(timerSeparator); firstLineOfText = 1; @@ -64,16 +69,20 @@ class BetterPlayerSubtitle { final end = _stringToDuration(timeSplit[1]); final texts = scanner.sublist(firstLineOfText, scanner.length); return BetterPlayerSubtitle._( - index: index, start: start, end: end, texts: texts); + index: index, + start: start, + end: end, + texts: texts, + ); } catch (exception) { - BetterPlayerUtils.log("Failed to parse subtitle line: $scanner"); + BetterPlayerUtils.log('Failed to parse subtitle line: $scanner'); return BetterPlayerSubtitle._(); } } static Duration _stringToDuration(String value) { try { - final valueSplit = value.split(" "); + final valueSplit = value.split(' '); String componentValue; if (valueSplit.length > 1) { @@ -85,7 +94,7 @@ class BetterPlayerSubtitle { final component = componentValue.split(':'); // Interpret a missing hour component to mean 00 hours if (component.length == 2) { - component.insert(0, "00"); + component.insert(0, '00'); } else if (component.length != 3) { return const Duration(); } @@ -97,13 +106,14 @@ class BetterPlayerSubtitle { } final result = Duration( - hours: int.tryParse(component[0])!, - minutes: int.tryParse(component[1])!, - seconds: int.tryParse(secsAndMillsSplit[0])!, - milliseconds: int.tryParse(secsAndMillsSplit[1])!); + hours: int.tryParse(component[0])!, + minutes: int.tryParse(component[1])!, + seconds: int.tryParse(secsAndMillsSplit[0])!, + milliseconds: int.tryParse(secsAndMillsSplit[1])!, + ); return result; } catch (exception) { - BetterPlayerUtils.log("Failed to process value: $value"); + BetterPlayerUtils.log('Failed to process value: $value'); return const Duration(); } } diff --git a/lib/src/subtitles/better_player_subtitles_configuration.dart b/lib/src/subtitles/better_player_subtitles_configuration.dart index 5410c82c1..225f60f02 100644 --- a/lib/src/subtitles/better_player_subtitles_configuration.dart +++ b/lib/src/subtitles/better_player_subtitles_configuration.dart @@ -4,6 +4,20 @@ import 'package:flutter/material.dart'; ///Configuration of subtitles - colors/padding/font. Used in ///BetterPlayerConfiguration. class BetterPlayerSubtitlesConfiguration { + const BetterPlayerSubtitlesConfiguration({ + this.fontSize = 14, + this.fontColor = Colors.white, + this.outlineEnabled = true, + this.outlineColor = Colors.black, + this.outlineSize = 2.0, + this.fontFamily = 'Roboto', + this.leftPadding = 8.0, + this.rightPadding = 8.0, + this.bottomPadding = 20.0, + this.alignment = Alignment.center, + this.backgroundColor = Colors.transparent, + }); + ///Subtitle font size final double fontSize; @@ -36,18 +50,4 @@ class BetterPlayerSubtitlesConfiguration { ///Background color of the subtitle final Color backgroundColor; - - const BetterPlayerSubtitlesConfiguration({ - this.fontSize = 14, - this.fontColor = Colors.white, - this.outlineEnabled = true, - this.outlineColor = Colors.black, - this.outlineSize = 2.0, - this.fontFamily = "Roboto", - this.leftPadding = 8.0, - this.rightPadding = 8.0, - this.bottomPadding = 20.0, - this.alignment = Alignment.center, - this.backgroundColor = Colors.transparent, - }); } diff --git a/lib/src/subtitles/better_player_subtitles_drawer.dart b/lib/src/subtitles/better_player_subtitles_drawer.dart index b6c084b0a..1fbed62c3 100644 --- a/lib/src/subtitles/better_player_subtitles_drawer.dart +++ b/lib/src/subtitles/better_player_subtitles_drawer.dart @@ -5,18 +5,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; class BetterPlayerSubtitlesDrawer extends StatefulWidget { - final List subtitles; - final BetterPlayerController betterPlayerController; - final BetterPlayerSubtitlesConfiguration? betterPlayerSubtitlesConfiguration; - final Stream playerVisibilityStream; - const BetterPlayerSubtitlesDrawer({ - Key? key, required this.subtitles, required this.betterPlayerController, - this.betterPlayerSubtitlesConfiguration, required this.playerVisibilityStream, - }) : super(key: key); + super.key, + this.betterPlayerSubtitlesConfiguration, + }); + final List subtitles; + final BetterPlayerController betterPlayerController; + final BetterPlayerSubtitlesConfiguration? betterPlayerSubtitlesConfiguration; + final Stream playerVisibilityStream; @override _BetterPlayerSubtitlesDrawerState createState() => @@ -27,7 +26,7 @@ class _BetterPlayerSubtitlesDrawerState extends State { final RegExp htmlRegExp = // ignore: unnecessary_raw_strings - RegExp(r"<[^>]*>", multiLine: true); + RegExp(r'<[^>]*>', multiLine: true); late TextStyle _innerTextStyle; late TextStyle _outerTextStyle; @@ -57,17 +56,19 @@ class _BetterPlayerSubtitlesDrawerState .addListener(_updateState); _outerTextStyle = TextStyle( - fontSize: _configuration!.fontSize, - fontFamily: _configuration!.fontFamily, - foreground: Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = _configuration!.outlineSize - ..color = _configuration!.outlineColor); + fontSize: _configuration!.fontSize, + fontFamily: _configuration!.fontFamily, + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = _configuration!.outlineSize + ..color = _configuration!.outlineColor, + ); _innerTextStyle = TextStyle( - fontFamily: _configuration!.fontFamily, - color: _configuration!.fontColor, - fontSize: _configuration!.fontSize); + fontFamily: _configuration!.fontFamily, + color: _configuration!.fontColor, + fontSize: _configuration!.fontSize, + ); super.initState(); } @@ -92,22 +93,22 @@ class _BetterPlayerSubtitlesDrawerState @override Widget build(BuildContext context) { - final BetterPlayerSubtitle? subtitle = _getSubtitleAtCurrentPosition(); + final subtitle = _getSubtitleAtCurrentPosition(); widget.betterPlayerController.renderedSubtitle = subtitle; - final List subtitles = subtitle?.texts ?? []; - final List textWidgets = - subtitles.map((text) => _buildSubtitleTextWidget(text)).toList(); + final subtitles = subtitle?.texts ?? []; + final textWidgets = subtitles.map(_buildSubtitleTextWidget).toList(); - return Container( + return SizedBox( height: double.infinity, width: double.infinity, child: Padding( padding: EdgeInsets.only( - bottom: _playerVisible - ? _configuration!.bottomPadding + 30 - : _configuration!.bottomPadding, - left: _configuration!.leftPadding, - right: _configuration!.rightPadding), + bottom: _playerVisible + ? _configuration!.bottomPadding + 30 + : _configuration!.bottomPadding, + left: _configuration!.leftPadding, + right: _configuration!.rightPadding, + ), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: textWidgets, @@ -121,9 +122,8 @@ class _BetterPlayerSubtitlesDrawerState return null; } - final Duration position = _latestValue!.position; - for (final BetterPlayerSubtitle subtitle - in widget.betterPlayerController.subtitlesLines) { + final position = _latestValue!.position; + for (final subtitle in widget.betterPlayerController.subtitlesLines) { if (subtitle.start! <= position && subtitle.end! >= position) { return subtitle; } @@ -132,14 +132,16 @@ class _BetterPlayerSubtitlesDrawerState } Widget _buildSubtitleTextWidget(String subtitleText) { - return Row(children: [ - Expanded( - child: Align( - alignment: _configuration!.alignment, - child: _getTextWithStroke(subtitleText), + return Row( + children: [ + Expanded( + child: Align( + alignment: _configuration!.alignment, + child: _getTextWithStroke(subtitleText), + ), ), - ), - ]); + ], + ); } Widget _getTextWithStroke(String subtitleText) { @@ -151,7 +153,7 @@ class _BetterPlayerSubtitlesDrawerState _buildHtmlWidget(subtitleText, _outerTextStyle) else const SizedBox(), - _buildHtmlWidget(subtitleText, _innerTextStyle) + _buildHtmlWidget(subtitleText, _innerTextStyle), ], ), ); diff --git a/lib/src/subtitles/better_player_subtitles_factory.dart b/lib/src/subtitles/better_player_subtitles_factory.dart index e21a91d69..7aaff08ae 100644 --- a/lib/src/subtitles/better_player_subtitles_factory.dart +++ b/lib/src/subtitles/better_player_subtitles_factory.dart @@ -2,11 +2,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:better_player/better_player.dart'; import 'package:better_player/src/core/better_player_utils.dart'; -import 'better_player_subtitle.dart'; +import 'package:better_player/src/subtitles/better_player_subtitle.dart'; class BetterPlayerSubtitlesFactory { static Future> parseSubtitles( - BetterPlayerSubtitlesSource source) async { + BetterPlayerSubtitlesSource source, + ) async { switch (source.type) { case BetterPlayerSubtitlesSourceType.file: return _parseSubtitlesFromFile(source); @@ -20,13 +21,14 @@ class BetterPlayerSubtitlesFactory { } static Future> _parseSubtitlesFromFile( - BetterPlayerSubtitlesSource source) async { + BetterPlayerSubtitlesSource source, + ) async { try { - final List subtitles = []; - for (final String? url in source.urls!) { + final subtitles = []; + for (final url in source.urls!) { final file = File(url!); if (file.existsSync()) { - final String fileContent = await file.readAsString(); + final fileContent = await file.readAsString(); final subtitlesCache = _parseString(fileContent); subtitles.addAll(subtitlesCache); } else { @@ -35,17 +37,18 @@ class BetterPlayerSubtitlesFactory { } return subtitles; } catch (exception) { - BetterPlayerUtils.log("Failed to read subtitles from file: $exception"); + BetterPlayerUtils.log('Failed to read subtitles from file: $exception'); } return []; } static Future> _parseSubtitlesFromNetwork( - BetterPlayerSubtitlesSource source) async { + BetterPlayerSubtitlesSource source, + ) async { try { final client = HttpClient(); - final List subtitles = []; - for (final String? url in source.urls!) { + final subtitles = []; + for (final url in source.urls!) { final request = await client.getUrl(Uri.parse(url!)); source.headers?.keys.forEach((key) { final value = source.headers![key]; @@ -60,27 +63,29 @@ class BetterPlayerSubtitlesFactory { } client.close(); - BetterPlayerUtils.log("Parsed total subtitles: ${subtitles.length}"); + BetterPlayerUtils.log('Parsed total subtitles: ${subtitles.length}'); return subtitles; } catch (exception) { BetterPlayerUtils.log( - "Failed to read subtitles from network: $exception"); + 'Failed to read subtitles from network: $exception', + ); } return []; } static List _parseSubtitlesFromMemory( - BetterPlayerSubtitlesSource source) { + BetterPlayerSubtitlesSource source, + ) { try { return _parseString(source.content!); } catch (exception) { - BetterPlayerUtils.log("Failed to read subtitles from memory: $exception"); + BetterPlayerUtils.log('Failed to read subtitles from memory: $exception'); } return []; } static List _parseString(String value) { - List components = value.split('\r\n\r\n'); + var components = value.split('\r\n\r\n'); if (components.length == 1) { components = value.split('\n\n'); } @@ -90,9 +95,9 @@ class BetterPlayerSubtitlesFactory { return []; } - final List subtitlesObj = []; + final subtitlesObj = []; - final bool isWebVTT = components.contains("WEBVTT"); + final isWebVTT = components.contains('WEBVTT'); for (final component in components) { if (component.isEmpty) { continue; diff --git a/lib/src/subtitles/better_player_subtitles_source.dart b/lib/src/subtitles/better_player_subtitles_source.dart index 2e6b6a018..93620b512 100644 --- a/lib/src/subtitles/better_player_subtitles_source.dart +++ b/lib/src/subtitles/better_player_subtitles_source.dart @@ -1,10 +1,22 @@ import 'package:better_player/src/asms/better_player_asms_subtitle_segment.dart'; -import 'better_player_subtitles_source_type.dart'; +import 'package:better_player/src/subtitles/better_player_subtitles_source_type.dart'; ///Representation of subtitles source. Used to define subtitles in Better /// Player. class BetterPlayerSubtitlesSource { + BetterPlayerSubtitlesSource({ + this.type, + this.name = 'Default subtitles', + this.urls, + this.content, + this.selectedByDefault, + this.headers, + this.asmsIsSegmented, + this.asmsSegmentsTime, + this.asmsSegments, + }); + ///Source type final BetterPlayerSubtitlesSourceType? type; @@ -36,22 +48,10 @@ class BetterPlayerSubtitlesSource { ///configured manually. final List? asmsSegments; - BetterPlayerSubtitlesSource({ - this.type, - this.name = "Default subtitles", - this.urls, - this.content, - this.selectedByDefault, - this.headers, - this.asmsIsSegmented, - this.asmsSegmentsTime, - this.asmsSegments, - }); - ///Creates list with only one subtitles static List single({ BetterPlayerSubtitlesSourceType? type, - String name = "Default subtitles", + String name = 'Default subtitles', String? url, String? content, bool? selectedByDefault, @@ -65,6 +65,6 @@ class BetterPlayerSubtitlesSource { content: content, selectedByDefault: selectedByDefault, headers: headers, - ) + ), ]; } diff --git a/lib/src/video_player/method_channel_video_player.dart b/lib/src/video_player/method_channel_video_player.dart index a4066d5f0..e15f1b317 100644 --- a/lib/src/video_player/method_channel_video_player.dart +++ b/lib/src/video_player/method_channel_video_player.dart @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; + import 'package:better_player/src/configuration/better_player_buffering_configuration.dart'; import 'package:better_player/src/core/better_player_utils.dart'; +import 'package:better_player/src/video_player/video_player_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'video_player_platform_interface.dart'; const MethodChannel _channel = MethodChannel('better_player_channel'); @@ -70,9 +71,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'imageUrl': dataSource.imageUrl, 'notificationChannelName': dataSource.notificationChannelName, 'overriddenDuration': dataSource.overriddenDuration?.inMilliseconds, - 'activityName': dataSource.activityName + 'activityName': dataSource.activityName, }; - break; case DataSourceType.network: dataSourceDescription = { 'key': dataSource.key, @@ -96,7 +96,6 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'clearKey': dataSource.clearKey, 'videoExtension': dataSource.videoExtension, }; - break; case DataSourceType.file: dataSourceDescription = { 'key': dataSource.key, @@ -111,9 +110,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'notificationChannelName': dataSource.notificationChannelName, 'overriddenDuration': dataSource.overriddenDuration?.inMilliseconds, 'activityName': dataSource.activityName, - 'clearKey': dataSource.clearKey + 'clearKey': dataSource.clearKey, }; - break; } await _channel.invokeMethod( 'setDataSource', @@ -176,7 +174,11 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { @override Future setTrackParameters( - int? textureId, int? width, int? height, int? bitrate) { + int? textureId, + int? width, + int? height, + int? bitrate, + ) { return _channel.invokeMethod( 'setTrackParameters', { @@ -202,16 +204,17 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { @override Future getPosition(int? textureId) async { return Duration( - milliseconds: await _channel.invokeMethod( - 'position', - {'textureId': textureId}, - ) ?? - 0); + milliseconds: await _channel.invokeMethod( + 'position', + {'textureId': textureId}, + ) ?? + 0, + ); } @override Future getAbsolutePosition(int? textureId) async { - final int milliseconds = await _channel.invokeMethod( + final milliseconds = await _channel.invokeMethod( 'absolutePosition', {'textureId': textureId}, ) ?? @@ -223,8 +226,13 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { } @override - Future enablePictureInPicture(int? textureId, double? top, double? left, - double? width, double? height) async { + Future enablePictureInPicture( + int? textureId, + double? top, + double? left, + double? width, + double? height, + ) async { return _channel.invokeMethod( 'enablePictureInPicture', { @@ -290,7 +298,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { @override Future preCache(DataSource dataSource, int preCacheSize) { - final Map dataSourceDescription = { + final dataSourceDescription = { 'key': dataSource.key, 'uri': dataSource.uri, 'certificateUrl': dataSource.certificateUrl, @@ -326,27 +334,27 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { if (event is Map) { map = event; } - final String? eventType = map["event"] as String?; - final String? key = map["key"] as String?; + final eventType = map['event'] as String?; + final key = map['key'] as String?; switch (eventType) { case 'initialized': - double width = 0; - double height = 0; + var width = 0.0; + var height = 0.0; try { - if (map.containsKey("width")) { - final num widthNum = map["width"] as num; + if (map.containsKey('width')) { + final widthNum = map['width'] as num; width = widthNum.toDouble(); } - if (map.containsKey("height")) { - final num heightNum = map["height"] as num; + if (map.containsKey('height')) { + final heightNum = map['height'] as num; height = heightNum.toDouble(); } } catch (exception) { BetterPlayerUtils.log(exception.toString()); } - final Size size = Size(width, height); + final size = Size(width, height); return VideoEvent( eventType: VideoEventType.initialized, @@ -360,7 +368,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { key: key, ); case 'bufferingUpdate': - final List values = map['values'] as List; + final values = map['values'] as List; return VideoEvent( eventType: VideoEventType.bufferingUpdate, @@ -436,7 +444,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { } DurationRange _toDurationRange(dynamic value) { - final List pair = value as List; + final pair = value as List; return DurationRange( Duration(milliseconds: pair[0] as int), Duration(milliseconds: pair[1] as int), diff --git a/lib/src/video_player/video_player.dart b/lib/src/video_player/video_player.dart index 7a8453f13..11be0c1ae 100644 --- a/lib/src/video_player/video_player.dart +++ b/lib/src/video_player/video_player.dart @@ -5,7 +5,15 @@ // Dart imports: import 'dart:async'; import 'dart:io'; +import 'package:better_player/better_player.dart'; +import 'package:better_player/better_player.dart'; +import 'package:better_player/better_player.dart'; +import 'package:better_player/better_player.dart'; import 'package:better_player/src/configuration/better_player_buffering_configuration.dart'; +import 'package:better_player/src/video_player/video_player.dart'; +import 'package:better_player/src/video_player/video_player.dart'; +import 'package:better_player/src/video_player/video_player.dart'; +import 'package:better_player/src/video_player/video_player.dart'; import 'package:better_player/src/video_player/video_player_platform_interface.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -98,11 +106,11 @@ class VideoPlayerValue { /// size is null or the aspect ratio would be less than or equal to 0.0. double get aspectRatio { if (size == null) { - return 1.0; + return 1; } - final double aspectRatio = size!.width / size!.height; + final aspectRatio = size!.width / size!.height; if (aspectRatio <= 0) { - return 1.0; + return 1; } return aspectRatio; } @@ -167,8 +175,6 @@ class VideoPlayerValue { /// /// After [dispose] all further calls are ignored. class VideoPlayerController extends ValueNotifier { - final BetterPlayerBufferingConfiguration bufferingConfiguration; - /// Constructs a [VideoPlayerController] and creates video controller on platform side. VideoPlayerController({ this.bufferingConfiguration = const BetterPlayerBufferingConfiguration(), @@ -178,6 +184,7 @@ class VideoPlayerController extends ValueNotifier { _create(); } } + final BetterPlayerBufferingConfiguration bufferingConfiguration; final StreamController videoEventStreamController = StreamController.broadcast(); @@ -220,38 +227,28 @@ class VideoPlayerController extends ValueNotifier { ); _initializingCompleter.complete(null); _applyPlayPause(); - break; case VideoEventType.completed: value = value.copyWith(isPlaying: false, position: value.duration); _timer?.cancel(); - break; case VideoEventType.bufferingUpdate: value = value.copyWith(buffered: event.buffered); - break; case VideoEventType.bufferingStart: value = value.copyWith(isBuffering: true); - break; case VideoEventType.bufferingEnd: if (value.isBuffering) { value = value.copyWith(isBuffering: false); } - break; case VideoEventType.play: play(); - break; case VideoEventType.pause: pause(); - break; case VideoEventType.seek: seekTo(event.position); - break; case VideoEventType.pipStart: value = value.copyWith(isPip: true); - break; case VideoEventType.pipStop: value = value.copyWith(isPip: false); - break; case VideoEventType.unknown: break; } @@ -259,7 +256,7 @@ class VideoPlayerController extends ValueNotifier { void errorListener(Object object) { if (object is PlatformException) { - final PlatformException e = object; + final e = object; value = value.copyWith(errorDescription: e.message); } else { value.copyWith(errorDescription: object.toString()); @@ -366,27 +363,30 @@ class VideoPlayerController extends ValueNotifier { /// /// This will load the file from the file-URI given by: /// `'file://${file.path}'`. - Future setFileDataSource(File file, - {bool? showNotification, - String? title, - String? author, - String? imageUrl, - String? notificationChannelName, - Duration? overriddenDuration, - String? activityName, - String? clearKey}) { + Future setFileDataSource( + File file, { + bool? showNotification, + String? title, + String? author, + String? imageUrl, + String? notificationChannelName, + Duration? overriddenDuration, + String? activityName, + String? clearKey, + }) { return _setDataSource( DataSource( - sourceType: DataSourceType.file, - uri: 'file://${file.path}', - showNotification: showNotification, - title: title, - author: author, - imageUrl: imageUrl, - notificationChannelName: notificationChannelName, - overriddenDuration: overriddenDuration, - activityName: activityName, - clearKey: clearKey), + sourceType: DataSourceType.file, + uri: 'file://${file.path}', + showNotification: showNotification, + title: title, + author: author, + imageUrl: imageUrl, + notificationChannelName: notificationChannelName, + overriddenDuration: overriddenDuration, + activityName: activityName, + clearKey: clearKey, + ), ); } @@ -468,8 +468,8 @@ class VideoPlayerController extends ValueNotifier { if (_isDisposed) { return; } - final Duration? newPosition = await position; - final DateTime? newAbsolutePosition = await absolutePosition; + final newPosition = await position; + final newAbsolutePosition = await absolutePosition; // ignore: invariant_booleans if (_isDisposed) { return; @@ -527,9 +527,9 @@ class VideoPlayerController extends ValueNotifier { /// and silently clamped. Future seekTo(Duration? position) async { _timer?.cancel(); - bool isPlaying = value.isPlaying; - final int positionInMs = value.position.inMilliseconds; - final int durationInMs = value.duration?.inMilliseconds ?? 0; + var isPlaying = value.isPlaying; + final positionInMs = value.position.inMilliseconds; + final durationInMs = value.duration?.inMilliseconds ?? 0; if (positionInMs >= durationInMs && position?.inMilliseconds == 0) { isPlaying = true; @@ -538,7 +538,7 @@ class VideoPlayerController extends ValueNotifier { return; } - Duration? positionToSeek = position; + var positionToSeek = position; if (position! > value.duration!) { positionToSeek = value.duration; } else if (position < const Duration()) { @@ -569,7 +569,7 @@ class VideoPlayerController extends ValueNotifier { /// /// [speed] indicates a value between 0.0 and 2.0 on a linear scale. Future setSpeed(double speed) async { - final double previousSpeed = value.speed; + final previousSpeed = value.speed; try { value = value.copyWith(speed: speed); await _applySpeed(); @@ -586,13 +586,26 @@ class VideoPlayerController extends ValueNotifier { /// [bitrate] specifies bitrate of the selected track Future setTrackParameters(int? width, int? height, int? bitrate) async { await _videoPlayerPlatform.setTrackParameters( - _textureId, width, height, bitrate); + _textureId, + width, + height, + bitrate, + ); } - Future enablePictureInPicture( - {double? top, double? left, double? width, double? height}) async { + Future enablePictureInPicture({ + double? top, + double? left, + double? width, + double? height, + }) async { await _videoPlayerPlatform.enablePictureInPicture( - textureId, top, left, width, height); + textureId, + top, + left, + width, + height, + ); } Future disablePictureInPicture() async { @@ -641,7 +654,7 @@ class VideoPlayerController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller, {super.key}); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. @@ -654,7 +667,7 @@ class VideoPlayer extends StatefulWidget { class _VideoPlayerState extends State { _VideoPlayerState() { _listener = () { - final int? newTextureId = widget.controller!.textureId; + final newTextureId = widget.controller!.textureId; if (newTextureId != _textureId) { setState(() { _textureId = newTextureId; @@ -758,12 +771,12 @@ class _VideoScrubberState extends State<_VideoScrubber> { @override Widget build(BuildContext context) { void seekToRelativePosition(Offset globalPosition) { - final RenderObject? renderObject = context.findRenderObject(); + final renderObject = context.findRenderObject(); if (renderObject != null) { - final RenderBox box = renderObject as RenderBox; - final Offset tapPos = box.globalToLocal(globalPosition); - final double relative = tapPos.dx / box.size.width; - final Duration position = controller.value.duration! * relative; + final box = renderObject as RenderBox; + final tapPos = box.globalToLocal(globalPosition); + final relative = tapPos.dx / box.size.width; + final position = controller.value.duration! * relative; controller.seekTo(position); } } @@ -819,10 +832,9 @@ class VideoProgressIndicator extends StatefulWidget { this.controller, { VideoProgressColors? colors, this.allowScrubbing, - this.padding = const EdgeInsets.only(top: 5.0), - Key? key, - }) : colors = colors ?? VideoProgressColors(), - super(key: key); + this.padding = const EdgeInsets.only(top: 5), + super.key, + }) : colors = colors ?? VideoProgressColors(); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -881,12 +893,12 @@ class _VideoProgressIndicatorState extends State { Widget build(BuildContext context) { Widget progressIndicator; if (controller.value.initialized) { - final int duration = controller.value.duration!.inMilliseconds; - final int position = controller.value.position.inMilliseconds; + final duration = controller.value.duration!.inMilliseconds; + final position = controller.value.position.inMilliseconds; - int maxBuffering = 0; - for (final DurationRange range in controller.value.buffered) { - final int end = range.end.inMilliseconds; + var maxBuffering = 0; + for (final range in controller.value.buffered) { + final end = range.end.inMilliseconds; if (end > maxBuffering) { maxBuffering = end; } @@ -951,7 +963,7 @@ class ClosedCaption extends StatelessWidget { /// [VideoPlayerValue.caption]. /// /// If [text] is null, nothing will be displayed. - const ClosedCaption({Key? key, this.text, this.textStyle}) : super(key: key); + const ClosedCaption({super.key, this.text, this.textStyle}); /// The text that will be shown in the closed caption, or null if no caption /// should be shown. @@ -965,9 +977,9 @@ class ClosedCaption extends StatelessWidget { @override Widget build(BuildContext context) { - final TextStyle effectiveTextStyle = textStyle ?? + final effectiveTextStyle = textStyle ?? DefaultTextStyle.of(context).style.copyWith( - fontSize: 36.0, + fontSize: 36, color: Colors.white, ); @@ -978,14 +990,14 @@ class ClosedCaption extends StatelessWidget { return Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 24.0), + padding: const EdgeInsets.only(bottom: 24), child: DecoratedBox( decoration: BoxDecoration( color: const Color(0xB8000000), - borderRadius: BorderRadius.circular(2.0), + borderRadius: BorderRadius.circular(2), ), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 2.0), + padding: const EdgeInsets.symmetric(horizontal: 2), child: Text(text!, style: effectiveTextStyle), ), ), diff --git a/lib/src/video_player/video_player_platform_interface.dart b/lib/src/video_player/video_player_platform_interface.dart index 336f54b04..e29179a30 100644 --- a/lib/src/video_player/video_player_platform_interface.dart +++ b/lib/src/video_player/video_player_platform_interface.dart @@ -7,9 +7,10 @@ import 'dart:async'; // Flutter imports: import 'package:better_player/src/configuration/better_player_buffering_configuration.dart'; +import 'package:better_player/src/video_player/method_channel_video_player.dart'; +import 'package:better_player/src/video_player/video_player.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'method_channel_video_player.dart'; /// The interface that implementations of video_player must implement. /// @@ -46,7 +47,8 @@ abstract class VideoPlayerPlatform { instance._verifyProvidesDefaultImplementations(); } catch (_) { throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); + 'Platform interfaces must not be implemented with `implements`', + ); } } _instance = instance; @@ -66,8 +68,9 @@ abstract class VideoPlayerPlatform { } /// Creates an instance of a video player and returns its textureId. - Future create( - {BetterPlayerBufferingConfiguration? bufferingConfiguration}) { + Future create({ + BetterPlayerBufferingConfiguration? bufferingConfiguration, + }) { throw UnimplementedError('create() has not been implemented.'); } @@ -118,7 +121,11 @@ abstract class VideoPlayerPlatform { /// Sets the video track parameters (used to select quality of the video) Future setTrackParameters( - int? textureId, int? width, int? height, int? bitrate) { + int? textureId, + int? width, + int? height, + int? bitrate, + ) { throw UnimplementedError('setTrackParameters() has not been implemented.'); } @@ -138,21 +145,29 @@ abstract class VideoPlayerPlatform { } ///Enables PiP mode. - Future enablePictureInPicture(int? textureId, double? top, double? left, - double? width, double? height) { + Future enablePictureInPicture( + int? textureId, + double? top, + double? left, + double? width, + double? height, + ) { throw UnimplementedError( - 'enablePictureInPicture() has not been implemented.'); + 'enablePictureInPicture() has not been implemented.', + ); } ///Disables PiP mode. Future disablePictureInPicture(int? textureId) { throw UnimplementedError( - 'disablePictureInPicture() has not been implemented.'); + 'disablePictureInPicture() has not been implemented.', + ); } Future isPictureInPictureEnabled(int? textureId) { throw UnimplementedError( - 'isPictureInPictureEnabled() has not been implemented.'); + 'isPictureInPictureEnabled() has not been implemented.', + ); } Future setAudioTrack(int? textureId, String? name, int? index) { @@ -184,12 +199,6 @@ abstract class VideoPlayerPlatform { /// Description of the data source used to create an instance of /// the video player. class DataSource { - /// The maximum cache size to keep on disk in bytes. - static const int _maxCacheSize = 100 * 1024 * 1024; - - /// The maximum size of each individual file in bytes. - static const int _maxCacheFileSize = 10 * 1024 * 1024; - /// Constructs an instance of [DataSource]. /// /// The [sourceType] is always required. @@ -229,6 +238,12 @@ class DataSource { this.videoExtension, }) : assert(uri == null || asset == null); + /// The maximum cache size to keep on disk in bytes. + static const int _maxCacheSize = 100 * 1024 * 1024; + + /// The maximum size of each individual file in bytes. + static const int _maxCacheFileSize = 10 * 1024 * 1024; + /// Describes the type of data source this [VideoPlayerController] /// is constructed with. /// @@ -307,18 +322,18 @@ class DataSource { /// Key to compare DataSource String get key { - String? result = ""; + String? result = ''; if (uri != null && uri!.isNotEmpty) { result = uri; } else if (package != null && package!.isNotEmpty) { - result = "$package:$asset"; + result = '$package:$asset'; } else { result = asset; } if (formatHint != null) { - result = "$result:$rawFormalHint"; + result = '$result:$rawFormalHint'; } return result!; diff --git a/pubspec.lock b/pubspec.lock index 11837d5a6..993c57db6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -181,14 +181,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - lint: - dependency: "direct dev" - description: - name: lint - sha256: d758a5211fce7fd3f5e316f804daefecdc34c7e53559716125e6da7388ae8565 - url: "https://pub.dev" - source: hosted - version: "2.3.0" logging: dependency: transitive description: @@ -394,6 +386,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + very_good_analysis: + dependency: "direct dev" + description: + name: very_good_analysis + sha256: "9ae7f3a3bd5764fb021b335ca28a34f040cd0ab6eec00a1b213b445dae58a4b8" + url: "https://pub.dev" + source: hosted + version: "5.1.0" visibility_detector: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 31b237d02..3d86027b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,7 +21,7 @@ dependencies: xml: ^6.5.0 dev_dependencies: - lint: ^2.3.0 + very_good_analysis: ^5.1.0 flutter_test: sdk: flutter flutter_localizations: diff --git a/test/better_player_controller_test.dart b/test/better_player_controller_test.dart index e6117df33..0f81e5836 100644 --- a/test/better_player_controller_test.dart +++ b/test/better_player_controller_test.dart @@ -7,38 +7,42 @@ import 'mock_video_player_controller.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final MockMethodChannel mockMethodChannel = MockMethodChannel(); + final mockMethodChannel = MockMethodChannel(); group( - "BetterPlayerController tests", + 'BetterPlayerController tests', () { setUp( () => { - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler( - mockMethodChannel.channel, mockMethodChannel.handle) + mockMethodChannel.channel, + mockMethodChannel.handle, + ), }, ); - test("Create controller without data source", () { - final BetterPlayerMockController betterPlayerMockController = + test('Create controller without data source', () { + final betterPlayerMockController = BetterPlayerMockController(const BetterPlayerConfiguration()); expect(betterPlayerMockController.betterPlayerDataSource, null); expect(betterPlayerMockController.videoPlayerController, null); }); - test("Setup data source in controller", () async { - final BetterPlayerMockController betterPlayerMockController = + test('Setup data source in controller', () async { + final betterPlayerMockController = BetterPlayerMockController(const BetterPlayerConfiguration()); await betterPlayerMockController.setupDataSource( - BetterPlayerDataSource.network( - BetterPlayerTestUtils.forBiggerBlazesUrl)); + BetterPlayerDataSource.network( + BetterPlayerTestUtils.forBiggerBlazesUrl, + ), + ); expect(betterPlayerMockController.betterPlayerDataSource != null, true); expect(betterPlayerMockController.videoPlayerController != null, true); }); test( - "play should change isPlaying flag", + 'play should change isPlaying flag', () async { final BetterPlayerController betterPlayerController = BetterPlayerTestUtils.setupBetterPlayerMockController(); @@ -52,7 +56,7 @@ void main() { ); test( - "pause should change isPlaying flag", + 'pause should change isPlaying flag', () async { final BetterPlayerController betterPlayerController = BetterPlayerTestUtils.setupBetterPlayerMockController(); @@ -68,7 +72,7 @@ void main() { ); test( - "seekTo should change player position", + 'seekTo should change player position', () async { final BetterPlayerController betterPlayerController = BetterPlayerTestUtils.setupBetterPlayerMockController(); @@ -77,7 +81,7 @@ void main() { videoPlayerController.setDuration(const Duration(seconds: 100)); betterPlayerController.videoPlayerController = videoPlayerController; betterPlayerController.seekTo(const Duration(seconds: 5)); - Duration? position = + var position = await betterPlayerController.videoPlayerController!.position; expect(position, const Duration(seconds: 5)); betterPlayerController.seekTo(const Duration(seconds: 30)); @@ -88,7 +92,7 @@ void main() { ); test( - "seekTo should send event", + 'seekTo should send event', () async { final BetterPlayerController betterPlayerController = BetterPlayerTestUtils.setupBetterPlayerMockController(); @@ -97,8 +101,8 @@ void main() { videoPlayerController.setDuration(const Duration(seconds: 100)); betterPlayerController.videoPlayerController = videoPlayerController; - int seekEventCalls = 0; - int finishEventCalls = 0; + var seekEventCalls = 0; + var finishEventCalls = 0; betterPlayerController.addEventsListener((event) { if (event.betterPlayerEventType == BetterPlayerEventType.seekTo) { seekEventCalls += 1; @@ -117,25 +121,27 @@ void main() { }, ); - test("full screen and auto play should work", () async { - final BetterPlayerMockController betterPlayerMockController = - BetterPlayerMockController( + test('full screen and auto play should work', () async { + final betterPlayerMockController = BetterPlayerMockController( const BetterPlayerConfiguration( - fullScreenByDefault: true, autoPlay: true), + fullScreenByDefault: true, + autoPlay: true, + ), ); betterPlayerMockController.videoPlayerController = MockVideoPlayerController(); await betterPlayerMockController.setupDataSource( BetterPlayerDataSource.network( - BetterPlayerTestUtils.forBiggerBlazesUrl), + BetterPlayerTestUtils.forBiggerBlazesUrl, + ), ); await Future.delayed(const Duration(seconds: 1), () {}); expect(betterPlayerMockController.isFullScreen, true); expect(betterPlayerMockController.isPlaying(), true); }); - test("exitFullScreen should exit full screen", () async { - final BetterPlayerMockController betterPlayerMockController = + test('exitFullScreen should exit full screen', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController( controller: MockVideoPlayerController(), ); @@ -144,24 +150,26 @@ void main() { expect(betterPlayerMockController.isFullScreen, false); }); - test("enterFullScreen should enter full screen", () async { - final BetterPlayerMockController betterPlayerMockController = + test('enterFullScreen should enter full screen', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); await betterPlayerMockController.setupDataSource( BetterPlayerDataSource.network( - BetterPlayerTestUtils.forBiggerBlazesUrl), + BetterPlayerTestUtils.forBiggerBlazesUrl, + ), ); expect(betterPlayerMockController.isFullScreen, false); betterPlayerMockController.enterFullScreen(); expect(betterPlayerMockController.isFullScreen, true); }); - test("toggleFullScreen should change full screen state", () async { - final BetterPlayerMockController betterPlayerMockController = + test('toggleFullScreen should change full screen state', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); await betterPlayerMockController.setupDataSource( BetterPlayerDataSource.network( - BetterPlayerTestUtils.forBiggerBlazesUrl), + BetterPlayerTestUtils.forBiggerBlazesUrl, + ), ); expect(betterPlayerMockController.isFullScreen, false); @@ -171,9 +179,9 @@ void main() { expect(betterPlayerMockController.isFullScreen, false); }); - test("setLooping changes looping state", () async { + test('setLooping changes looping state', () async { final mockVideoPlayerController = MockVideoPlayerController(); - final BetterPlayerMockController betterPlayerMockController = + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); mockVideoPlayerController .setNetworkDataSource(BetterPlayerTestUtils.bugBuckBunnyVideoUrl); @@ -187,8 +195,8 @@ void main() { expect(mockVideoPlayerController.isLoopingState, false); }); - test("setControlsVisibility updates controlVisiblityStream", () async { - final BetterPlayerMockController betterPlayerMockController = + test('setControlsVisibility updates controlVisiblityStream', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); var showCalls = 0; var hideCalls = 0; @@ -209,8 +217,8 @@ void main() { expect(showCalls, 2); }); - test("setControlsEnabled updates values correctly", () async { - final BetterPlayerMockController betterPlayerMockController = + test('setControlsEnabled updates values correctly', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); var hideCalls = 0; betterPlayerMockController.controlsVisibilityStream.listen((event) { @@ -225,8 +233,8 @@ void main() { expect(betterPlayerMockController.controlsEnabled, true); }); - test("toggleControlsVisibility sends correct events", () async { - final BetterPlayerMockController betterPlayerMockController = + test('toggleControlsVisibility sends correct events', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); var controlsVisibleEventCount = 0; var controlsHiddenEventCount = 0; @@ -248,12 +256,12 @@ void main() { expect(controlsHiddenEventCount, 1); }); - test("postEvent sends events to listeners", () async { - final BetterPlayerMockController betterPlayerMockController = + test('postEvent sends events to listeners', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); - int firstEventCounter = 0; - int secondEventCounter = 0; + var firstEventCounter = 0; + var secondEventCounter = 0; betterPlayerMockController.addEventsListener((event) { firstEventCounter++; @@ -273,8 +281,8 @@ void main() { expect(secondEventCounter, 3); }); - test("addEventsListener update list of event listener", () async { - final BetterPlayerMockController betterPlayerMockController = + test('addEventsListener update list of event listener', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); betterPlayerMockController.addEventsListener((event) {}); betterPlayerMockController.addEventsListener((event) {}); @@ -283,8 +291,8 @@ void main() { void dummyEventListener(BetterPlayerEvent event) {} - test("removeEventsListener update list of event listener", () async { - final BetterPlayerMockController betterPlayerMockController = + test('removeEventsListener update list of event listener', () async { + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); betterPlayerMockController.addEventsListener(dummyEventListener); betterPlayerMockController.addEventsListener((event) {}); @@ -293,22 +301,22 @@ void main() { expect(betterPlayerMockController.eventListeners.length, 1); }); - test("setVolume changes volume", () async { + test('setVolume changes volume', () async { final mockVideoPlayerController = MockVideoPlayerController(); - final BetterPlayerMockController betterPlayerMockController = + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); mockVideoPlayerController .setNetworkDataSource(BetterPlayerTestUtils.bugBuckBunnyVideoUrl); betterPlayerMockController.videoPlayerController = mockVideoPlayerController; - betterPlayerMockController.setVolume(1.0); + betterPlayerMockController.setVolume(1); expect(mockVideoPlayerController.volume, 1.0); betterPlayerMockController.setVolume(0.5); expect(mockVideoPlayerController.volume, 0.5); }); test( - "setVolume should send event", + 'setVolume should send event', () async { final BetterPlayerController betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); @@ -317,25 +325,25 @@ void main() { betterPlayerMockController.videoPlayerController = videoPlayerController; - int setVolumeCalls = 0; + var setVolumeCalls = 0; betterPlayerMockController.addEventsListener((event) { if (event.betterPlayerEventType == BetterPlayerEventType.setVolume) { setVolumeCalls += 1; } }); - betterPlayerMockController.setVolume(1.0); + betterPlayerMockController.setVolume(1); await Future.delayed(const Duration(milliseconds: 100), () {}); expect(setVolumeCalls, 1); - betterPlayerMockController.setVolume(1.0); + betterPlayerMockController.setVolume(1); await Future.delayed(const Duration(milliseconds: 100), () {}); expect(setVolumeCalls, 2); }, ); - test("setSpeed changes speed", () async { + test('setSpeed changes speed', () async { final mockVideoPlayerController = MockVideoPlayerController(); - final BetterPlayerMockController betterPlayerMockController = + final betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); mockVideoPlayerController .setNetworkDataSource(BetterPlayerTestUtils.bugBuckBunnyVideoUrl); @@ -345,16 +353,20 @@ void main() { expect(mockVideoPlayerController.speed, 1.1); betterPlayerMockController.setSpeed(0.5); expect(mockVideoPlayerController.speed, 0.5); - expect(() => betterPlayerMockController.setSpeed(2.5), - throwsA(isA())); + expect( + () => betterPlayerMockController.setSpeed(2.5), + throwsA(isA()), + ); expect(mockVideoPlayerController.speed, 0.5); - expect(() => betterPlayerMockController.setSpeed(0.0), - throwsA(isA())); + expect( + () => betterPlayerMockController.setSpeed(0), + throwsA(isA()), + ); expect(mockVideoPlayerController.speed, 0.5); }); test( - "setSpeed should send event", + 'setSpeed should send event', () async { final BetterPlayerController betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); @@ -363,7 +375,7 @@ void main() { betterPlayerMockController.videoPlayerController = videoPlayerController; - int setSpeedCalls = 0; + var setSpeedCalls = 0; betterPlayerMockController.addEventsListener((event) { if (event.betterPlayerEventType == BetterPlayerEventType.setSpeed) { setSpeedCalls += 1; @@ -372,13 +384,13 @@ void main() { betterPlayerMockController.setSpeed(1.5); await Future.delayed(const Duration(milliseconds: 100), () {}); expect(setSpeedCalls, 1); - betterPlayerMockController.setSpeed(1.0); + betterPlayerMockController.setSpeed(1); await Future.delayed(const Duration(milliseconds: 100), () {}); expect(setSpeedCalls, 2); }, ); - test("isBuffering returns valid value", () async { + test('isBuffering returns valid value', () async { final BetterPlayerController betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); final videoPlayerController = @@ -391,15 +403,20 @@ void main() { expect(betterPlayerMockController.isBuffering(), true); }); - test("isLiveStream returns valid value", () async { + test('isLiveStream returns valid value', () async { final BetterPlayerController betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); - expect(() => betterPlayerMockController.isLiveStream(), - throwsA(isA())); - betterPlayerMockController.setupDataSource(BetterPlayerDataSource( + expect( + betterPlayerMockController.isLiveStream, + throwsA(isA()), + ); + betterPlayerMockController.setupDataSource( + BetterPlayerDataSource( BetterPlayerDataSourceType.network, BetterPlayerTestUtils.forBiggerBlazesUrl, - liveStream: true)); + liveStream: true, + ), + ); final videoPlayerController = BetterPlayerTestUtils.setupMockVideoPlayerControler(); betterPlayerMockController.videoPlayerController = @@ -407,11 +424,13 @@ void main() { expect(betterPlayerMockController.isLiveStream(), true); }); - test("isVideoInitalized returns valid value", () async { + test('isVideoInitalized returns valid value', () async { final BetterPlayerController betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); - expect(() => betterPlayerMockController.isVideoInitialized(), - throwsA(isA())); + expect( + betterPlayerMockController.isVideoInitialized, + throwsA(isA()), + ); final videoPlayerController = BetterPlayerTestUtils.setupMockVideoPlayerControler(); betterPlayerMockController.videoPlayerController = @@ -420,10 +439,10 @@ void main() { expect(betterPlayerMockController.isVideoInitialized(), true); }); - test("startNextVideoTimer starts next video timer", () async { + test('startNextVideoTimer starts next video timer', () async { final BetterPlayerController betterPlayerMockController = BetterPlayerTestUtils.setupBetterPlayerMockController(); - int eventCount = 0; + var eventCount = 0; betterPlayerMockController.nextVideoTimeStream.listen((event) { eventCount += 1; }); diff --git a/test/better_player_controls_test.dart b/test/better_player_controls_test.dart index 773687b6b..43fac6e26 100644 --- a/test/better_player_controls_test.dart +++ b/test/better_player_controls_test.dart @@ -7,31 +7,33 @@ import 'package:visibility_detector/visibility_detector.dart'; import 'better_player_mock_controller.dart'; void main() { - late BetterPlayerMockController _mockController; + late BetterPlayerMockController mockController; setUpAll(() { VisibilityDetectorController.instance.updateInterval = Duration.zero; }); setUp(() { - _mockController = + mockController = BetterPlayerMockController(const BetterPlayerConfiguration()); }); testWidgets( - "One of children is BetterPlayerWithControls", + 'One of children is BetterPlayerWithControls', (WidgetTester tester) async { await tester.pumpWidget( _wrapWidget( BetterPlayer( - controller: _mockController, + controller: mockController, ), ), ); expect( - find.byWidgetPredicate( - (widget) => widget is BetterPlayerWithControls), - findsOneWidget); + find.byWidgetPredicate( + (widget) => widget is BetterPlayerWithControls, + ), + findsOneWidget, + ); }, ); } diff --git a/test/better_player_mock_controller.dart b/test/better_player_mock_controller.dart index 207262421..e43f8a0d2 100644 --- a/test/better_player_mock_controller.dart +++ b/test/better_player_mock_controller.dart @@ -2,10 +2,8 @@ import 'package:better_player/better_player.dart'; class BetterPlayerMockController extends BetterPlayerController { BetterPlayerMockController( - BetterPlayerConfiguration betterPlayerConfiguration, { - BetterPlayerPlaylistConfiguration betterPlayerPlaylistConfiguration = + super.betterPlayerConfiguration, { + BetterPlayerPlaylistConfiguration super.betterPlayerPlaylistConfiguration = const BetterPlayerPlaylistConfiguration(), - }) : super(betterPlayerConfiguration, - betterPlayerPlaylistConfiguration: - betterPlayerPlaylistConfiguration); + }); } diff --git a/test/better_player_test.dart b/test/better_player_test.dart index c794b963d..a11ddd533 100644 --- a/test/better_player_test.dart +++ b/test/better_player_test.dart @@ -10,30 +10,46 @@ void main() { VisibilityDetectorController.instance.updateInterval = Duration.zero; }); - testWidgets("Better Player simple player - network", + testWidgets('Better Player simple player - network', (WidgetTester tester) async { - await tester.pumpWidget(_wrapWidget( - BetterPlayer.network(BetterPlayerTestUtils.bugBuckBunnyVideoUrl))); - expect(find.byWidgetPredicate((widget) => widget is BetterPlayer), - findsOneWidget); + await tester.pumpWidget( + _wrapWidget( + BetterPlayer.network(BetterPlayerTestUtils.bugBuckBunnyVideoUrl), + ), + ); + expect( + find.byWidgetPredicate((widget) => widget is BetterPlayer), + findsOneWidget, + ); }); - testWidgets("Better Player simple player - file", + testWidgets('Better Player simple player - file', (WidgetTester tester) async { - await tester.pumpWidget(_wrapWidget( - BetterPlayer.network(BetterPlayerTestUtils.bugBuckBunnyVideoUrl))); - expect(find.byWidgetPredicate((widget) => widget is BetterPlayer), - findsOneWidget); + await tester.pumpWidget( + _wrapWidget( + BetterPlayer.network(BetterPlayerTestUtils.bugBuckBunnyVideoUrl), + ), + ); + expect( + find.byWidgetPredicate((widget) => widget is BetterPlayer), + findsOneWidget, + ); }); - testWidgets("BetterPlayer - with controller", (WidgetTester tester) async { - final BetterPlayerMockController betterPlayerController = + testWidgets('BetterPlayer - with controller', (WidgetTester tester) async { + final betterPlayerController = BetterPlayerMockController(const BetterPlayerConfiguration()); - await tester.pumpWidget(_wrapWidget(BetterPlayer( - controller: betterPlayerController, - ))); - expect(find.byWidgetPredicate((widget) => widget is BetterPlayer), - findsOneWidget); + await tester.pumpWidget( + _wrapWidget( + BetterPlayer( + controller: betterPlayerController, + ), + ), + ); + expect( + find.byWidgetPredicate((widget) => widget is BetterPlayer), + findsOneWidget, + ); }); } diff --git a/test/better_player_test_utils.dart b/test/better_player_test_utils.dart index c3678433f..35b22622c 100644 --- a/test/better_player_test_utils.dart +++ b/test/better_player_test_utils.dart @@ -6,14 +6,15 @@ import 'mock_video_player_controller.dart'; class BetterPlayerTestUtils { static const String bugBuckBunnyVideoUrl = - "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; static const String forBiggerBlazesUrl = - "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"; + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'; static const String elephantDreamStreamUrl = - "http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8"; + 'http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8'; - static BetterPlayerMockController setupBetterPlayerMockController( - {VideoPlayerController? controller}) { + static BetterPlayerMockController setupBetterPlayerMockController({ + VideoPlayerController? controller, + }) { final mockController = BetterPlayerMockController(const BetterPlayerConfiguration()); if (controller != null) { diff --git a/test/mock_method_channel.dart b/test/mock_method_channel.dart index 2e256ab59..7776a9512 100644 --- a/test/mock_method_channel.dart +++ b/test/mock_method_channel.dart @@ -2,16 +2,16 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; class MockMethodChannel { - final MethodChannel channel = const MethodChannel("better_player_channel"); + final MethodChannel channel = const MethodChannel('better_player_channel'); final List eventsChannels = []; Future? handle(MethodCall methodCall) async { - if (methodCall.method == "create") { - final int id = getNextId(); + if (methodCall.method == 'create') { + final id = getNextId(); _createEventChannel(id); return _getCreateResult(id); } - if (methodCall.method == "setDataSource") { + if (methodCall.method == 'setDataSource') { //return } return {}; @@ -22,26 +22,25 @@ class MockMethodChannel { } Map _getCreateResult(int id) => - {"textureId": id}; + {'textureId': id}; Map _getInitResult() => { - "event": "initialized", - "height": 720.0, - "width:": 1280.0, - "duration": 100 + 'event': 'initialized', + 'height': 720.0, + 'width:': 1280.0, + 'duration': 100, }; void _createEventChannel(int id) { - final MethodChannel eventChannel = - MethodChannel("better_player_channel/videoEvents$id"); - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + final eventChannel = MethodChannel('better_player_channel/videoEvents$id'); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(eventChannel, (MethodCall methodCall) async { - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .handlePlatformMessage( - "better_player_channel/videoEvents$id", - const StandardMethodCodec() - .encodeSuccessEnvelope(_getInitResult()), - (ByteData? data) {}); + 'better_player_channel/videoEvents$id', + const StandardMethodCodec().encodeSuccessEnvelope(_getInitResult()), + (ByteData? data) {}, + ); return null; }); diff --git a/test/mock_video_player_controller.dart b/test/mock_video_player_controller.dart index b9bceebab..fabe3915a 100644 --- a/test/mock_video_player_controller.dart +++ b/test/mock_video_player_controller.dart @@ -7,8 +7,8 @@ class MockVideoPlayerController extends VideoPlayerController { } bool isLoopingState = false; - double volume = 0.0; - double speed = 1.0; + double volume = 0; + double speed = 1; @override Future play() async {