fix: infinite loading on youtube media player, and upgrade version of youtube_player_widget

This commit is contained in:
Naresh Pratista 2024-11-09 06:15:20 +07:00
parent e41d9dab23
commit 6937c5e403
7 changed files with 166 additions and 177 deletions

View File

@ -1 +1,2 @@
const String baseUrl = 'https://7558-114-6-25-184.ngrok-free.app/'; const String baseUrl =
'https://ebdc-2001-448a-50a0-3463-a4de-673f-afb-724b.ngrok-free.app/';

View File

@ -169,13 +169,13 @@ class _MaterialScreenState extends State<MaterialScreen>
), ),
AudioExtension( AudioExtension(
builder: (context) => AudioPlayerWidget( builder: (context) => AudioPlayerWidget(
key: _audioPlayerKey, key: ValueKey('audio_${context.attributes['src']}'),
audioFileName: context.attributes['src'] ?? '', audioFileName: context.attributes['src'] ?? '',
), ),
), ),
VideoExtension( VideoExtension(
builder: (context) => VideoPlayerWidget( builder: (context) => VideoPlayerWidget(
key: _videoPlayerKey, key: ValueKey('video_${context.attributes['src']}'),
videoUrl: context.attributes['src'] ?? '', videoUrl: context.attributes['src'] ?? '',
), ),
), ),

View File

@ -21,36 +21,60 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
FlickManager? _flickManager; FlickManager? _flickManager;
bool _isLoading = true; bool _isLoading = true;
String? _error; String? _error;
bool _isYoutubeReady = false;
String? _youtubeId; String? _youtubeId;
bool _isDisposed = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_youtubeId = _extractYoutubeId(widget.videoUrl); _youtubeId = _extractYoutubeId(widget.videoUrl);
_initializeVideoPlayerWidget();
if (_youtubeId != null) {
_youtubeController = YoutubePlayerController(
initialVideoId: _youtubeId!,
flags: const YoutubePlayerFlags(
mute: false,
autoPlay: false,
disableDragSeek: true,
loop: false,
enableCaption: false,
hideThumbnail: false,
),
);
if (!_isDisposed) {
setState(() {
_isLoading = false;
});
}
} else {
_initializeVideo();
}
}
Future<void> _initializeVideo() async {
try {
await _initializeRegularVideo();
} catch (e) {
if (!_isDisposed) {
setState(() {
_error = "Error initializing video player: $e";
_isLoading = false;
});
}
}
} }
String? _extractYoutubeId(String url) { String? _extractYoutubeId(String url) {
try { try {
RegExp regExp = RegExp( if (url.contains('youtube.com/watch?v=')) {
r'^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*', return url.split('watch?v=')[1].split('&')[0];
caseSensitive: false, }
multiLine: false, if (url.contains('youtube.com/embed/')) {
); return url.split('youtube.com/embed/')[1].split('?')[0];
String? videoId = YoutubePlayer.convertUrlToId(url);
if (videoId != null) return videoId;
Match? match = regExp.firstMatch(url);
if (match != null && match.groupCount >= 1) {
return match.group(1);
} }
if (url.contains('youtu.be/')) { if (url.contains('youtu.be/')) {
return url.split('youtu.be/')[1].split(RegExp(r'[?&]'))[0]; return url.split('youtu.be/')[1].split('?')[0];
} }
return null; return null;
} catch (e) { } catch (e) {
print('Error extracting YouTube ID: $e'); print('Error extracting YouTube ID: $e');
@ -60,6 +84,9 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
String _getPlayableUrl(String url) { String _getPlayableUrl(String url) {
if (url.contains('drive.google.com')) { if (url.contains('drive.google.com')) {
if (url.contains('/view')) {
url = url.replaceAll('/view', '');
}
final regex = RegExp(r'/d/([a-zA-Z0-9-_]+)'); final regex = RegExp(r'/d/([a-zA-Z0-9-_]+)');
final match = regex.firstMatch(url); final match = regex.firstMatch(url);
if (match != null) { if (match != null) {
@ -70,53 +97,27 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
return url; return url;
} }
Future<void> _initializeVideoPlayerWidget() async { Future<void> _initializeRegularVideo() async {
if (_youtubeId != null) {
try {
_youtubeController = YoutubePlayerController(
initialVideoId: _youtubeId!,
flags: const YoutubePlayerFlags(
autoPlay: false,
mute: false,
hideControls: false,
controlsVisibleAtStart: true,
enableCaption: true,
useHybridComposition: true,
forceHD: true,
),
);
if (mounted) {
setState(() {}); // Trigger rebuild with controller
}
} catch (e) {
if (mounted) {
setState(() {
_error = "Error initializing YouTube player: $e";
_isLoading = false;
});
}
}
} else {
try { try {
final playableUrl = _getPlayableUrl(widget.videoUrl);
_videoController = VideoPlayerController.networkUrl( _videoController = VideoPlayerController.networkUrl(
Uri.parse(_getPlayableUrl(widget.videoUrl)), Uri.parse(playableUrl),
); );
await _videoController!.initialize(); await _videoController!.initialize();
if (!_isDisposed) {
_flickManager = FlickManager( _flickManager = FlickManager(
videoPlayerController: _videoController!, videoPlayerController: _videoController!,
autoPlay: false, autoPlay: false,
); );
if (mounted) {
setState(() { setState(() {
_isLoading = false; _isLoading = false;
}); });
} }
} catch (e) { } catch (e) {
if (mounted) { if (!_isDisposed) {
setState(() { setState(() {
_error = "Error initializing video player: $e"; _error = "Error initializing video player: $e";
_isLoading = false; _isLoading = false;
@ -124,17 +125,10 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
} }
} }
} }
}
void _youtubeListener() {
if (_youtubeController?.value.playerState == PlayerState.ended) {
_youtubeController?.seekTo(Duration.zero);
_youtubeController?.pause();
}
}
@override @override
void dispose() { void dispose() {
_isDisposed = true;
_videoController?.dispose(); _videoController?.dispose();
_youtubeController?.dispose(); _youtubeController?.dispose();
_flickManager?.dispose(); _flickManager?.dispose();
@ -143,79 +137,28 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_youtubeId != null && _youtubeController != null) { if (_error != null) {
return _buildErrorContainer();
}
return ClipRRect( return ClipRRect(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: YoutubePlayerBuilder( child:
player: YoutubePlayer( _youtubeId != null ? _buildYoutubePlayer() : _buildRegularVideo(),
controller: _youtubeController!,
showVideoProgressIndicator: true,
progressIndicatorColor: Colors.red,
progressColors: const ProgressBarColors(
playedColor: Colors.red,
handleColor: Colors.redAccent,
),
onReady: () {
if (mounted) {
setState(() {
_isYoutubeReady = true;
_isLoading = false;
});
}
_youtubeController!.addListener(_youtubeListener);
},
onEnded: (YoutubeMetaData metaData) {
_youtubeController!.seekTo(Duration.zero);
_youtubeController!.pause();
},
bottomActions: [
CurrentPosition(),
ProgressBar(isExpanded: true),
RemainingDuration(),
PlaybackSpeedButton(),
],
),
builder: (context, player) {
return Container(
color: Colors.black,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: _isYoutubeReady
? player
: const Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.white),
),
),
),
);
},
),
), ),
); );
} }
Widget _buildErrorContainer() {
return ClipRRect( return ClipRRect(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: Container( child: Container(
color: Colors.black, color: Colors.black,
child: AnimatedSwitcher( child: Center(
duration: const Duration(milliseconds: 300),
child: _buildContent(),
),
),
),
);
}
Widget _buildContent() {
if (_error != null) {
return Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text( child: Text(
@ -224,14 +167,47 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
),
),
),
); );
} }
if (_isLoading) { Widget _buildYoutubePlayer() {
return const Center( if (_youtubeController == null) {
return Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white), valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
), ),
),
);
}
return YoutubePlayer(
controller: _youtubeController!,
showVideoProgressIndicator: true,
progressIndicatorColor: Colors.red,
progressColors: const ProgressBarColors(
playedColor: Colors.red,
handleColor: Colors.redAccent,
),
onReady: () {
print('YouTube Player Ready');
},
);
}
Widget _buildRegularVideo() {
if (_isLoading) {
return Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
); );
} }

View File

@ -13,10 +13,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.11.0" version: "2.12.0"
audio_session: audio_session:
dependency: transitive dependency: transitive
description: description:
@ -85,10 +85,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
bootstrap_icons: bootstrap_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -157,10 +157,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.2"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -229,10 +229,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.2"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@ -322,18 +322,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_inappwebview name: flutter_inappwebview
sha256: "3e9a443a18ecef966fb930c3a76ca5ab6a7aafc0c7b5e14a4a850cf107b09959" sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.1.5"
flutter_inappwebview_android: flutter_inappwebview_android:
dependency: transitive dependency: transitive
description: description:
name: flutter_inappwebview_android name: flutter_inappwebview_android
sha256: d247f6ed417f1f8c364612fa05a2ecba7f775c8d0c044c1d3b9ee33a6515c421 sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.13" version: "1.1.3"
flutter_inappwebview_internal_annotations: flutter_inappwebview_internal_annotations:
dependency: transitive dependency: transitive
description: description:
@ -346,34 +346,42 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_inappwebview_ios name: flutter_inappwebview_ios
sha256: f363577208b97b10b319cd0c428555cd8493e88b468019a8c5635a0e4312bd0f sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.13" version: "1.1.2"
flutter_inappwebview_macos: flutter_inappwebview_macos:
dependency: transitive dependency: transitive
description: description:
name: flutter_inappwebview_macos name: flutter_inappwebview_macos
sha256: b55b9e506c549ce88e26580351d2c71d54f4825901666bd6cfa4be9415bb2636 sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.11" version: "1.1.2"
flutter_inappwebview_platform_interface: flutter_inappwebview_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: flutter_inappwebview_platform_interface name: flutter_inappwebview_platform_interface
sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187" sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.10" version: "1.3.0+1"
flutter_inappwebview_web: flutter_inappwebview_web:
dependency: transitive dependency: transitive
description: description:
name: flutter_inappwebview_web name: flutter_inappwebview_web
sha256: d8c680abfb6fec71609a700199635d38a744df0febd5544c5a020bd73de8ee07 sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.1.2"
flutter_inappwebview_windows:
dependency: transitive
description:
name: flutter_inappwebview_windows
sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -676,18 +684,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.5" version: "10.0.8"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.5" version: "3.0.9"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
@ -788,10 +796,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.0" version: "1.9.1"
path_parsing: path_parsing:
dependency: transitive dependency: transitive
description: description:
@ -956,7 +964,7 @@ packages:
dependency: transitive dependency: transitive
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.99" version: "0.0.0"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@ -993,10 +1001,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.1" version: "1.12.0"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -1009,10 +1017,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.4.0"
synchronized: synchronized:
dependency: transitive dependency: transitive
description: description:
@ -1209,10 +1217,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.2.4" version: "14.3.1"
wakelock_plus: wakelock_plus:
dependency: transitive dependency: transitive
description: description:
@ -1297,10 +1305,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: youtube_player_flutter name: youtube_player_flutter
sha256: "30f84e2f7063c56e536f507e37c1e803546842707cf58e5b5a71253b9ff9b455" sha256: "4d14aa47f9c84929b5400a87ade4dcfdab87a2ca2e0b18ecc2ef852b1440e123"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.0.4" version: "9.1.1"
sdks: sdks:
dart: ">=3.6.0-122.0.dev <4.0.0" dart: ">=3.6.0-122.0.dev <4.0.0"
flutter: ">=3.22.0" flutter: ">=3.24.0"

View File

@ -51,7 +51,7 @@ dependencies:
shared_preferences: ^2.3.2 shared_preferences: ^2.3.2
shimmer: ^3.0.0 shimmer: ^3.0.0
video_player: ^2.9.1 video_player: ^2.9.1
youtube_player_flutter: ^9.0.4 youtube_player_flutter: ^9.1.1
dev_dependencies: dev_dependencies:
flutter_lints: ^4.0.0 flutter_lints: ^4.0.0

View File

@ -8,6 +8,7 @@
#include <audioplayers_windows/audioplayers_windows_plugin.h> #include <audioplayers_windows/audioplayers_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar( FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows audioplayers_windows
file_selector_windows file_selector_windows
flutter_inappwebview_windows
flutter_secure_storage_windows flutter_secure_storage_windows
url_launcher_windows url_launcher_windows
) )