Merge branch 'html-content' into 'master'

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

See merge request profile-image/kedaireka/polinema-adapative-learning/mobile-adaptive-learning!7
This commit is contained in:
Naresh Pratista 2024-11-08 23:16:52 +00:00
commit 6283e9bf75
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(
builder: (context) => AudioPlayerWidget(
key: _audioPlayerKey,
key: ValueKey('audio_${context.attributes['src']}'),
audioFileName: context.attributes['src'] ?? '',
),
),
VideoExtension(
builder: (context) => VideoPlayerWidget(
key: _videoPlayerKey,
key: ValueKey('video_${context.attributes['src']}'),
videoUrl: context.attributes['src'] ?? '',
),
),

View File

@ -21,36 +21,60 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
FlickManager? _flickManager;
bool _isLoading = true;
String? _error;
bool _isYoutubeReady = false;
String? _youtubeId;
bool _isDisposed = false;
@override
void initState() {
super.initState();
_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) {
try {
RegExp regExp = RegExp(
r'^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*',
caseSensitive: false,
multiLine: false,
);
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('youtube.com/watch?v=')) {
return url.split('watch?v=')[1].split('&')[0];
}
if (url.contains('youtube.com/embed/')) {
return url.split('youtube.com/embed/')[1].split('?')[0];
}
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;
} catch (e) {
print('Error extracting YouTube ID: $e');
@ -60,6 +84,9 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
String _getPlayableUrl(String url) {
if (url.contains('drive.google.com')) {
if (url.contains('/view')) {
url = url.replaceAll('/view', '');
}
final regex = RegExp(r'/d/([a-zA-Z0-9-_]+)');
final match = regex.firstMatch(url);
if (match != null) {
@ -70,53 +97,27 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
return url;
}
Future<void> _initializeVideoPlayerWidget() 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 {
Future<void> _initializeRegularVideo() async {
try {
final playableUrl = _getPlayableUrl(widget.videoUrl);
_videoController = VideoPlayerController.networkUrl(
Uri.parse(_getPlayableUrl(widget.videoUrl)),
Uri.parse(playableUrl),
);
await _videoController!.initialize();
if (!_isDisposed) {
_flickManager = FlickManager(
videoPlayerController: _videoController!,
autoPlay: false,
);
if (mounted) {
setState(() {
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
if (!_isDisposed) {
setState(() {
_error = "Error initializing video player: $e";
_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
void dispose() {
_isDisposed = true;
_videoController?.dispose();
_youtubeController?.dispose();
_flickManager?.dispose();
@ -143,79 +137,28 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
@override
Widget build(BuildContext context) {
if (_youtubeId != null && _youtubeController != null) {
if (_error != null) {
return _buildErrorContainer();
}
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: AspectRatio(
aspectRatio: 16 / 9,
child: YoutubePlayerBuilder(
player: YoutubePlayer(
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),
),
),
),
);
},
),
child:
_youtubeId != null ? _buildYoutubePlayer() : _buildRegularVideo(),
),
);
}
Widget _buildErrorContainer() {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
color: Colors.black,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: _buildContent(),
),
),
),
);
}
Widget _buildContent() {
if (_error != null) {
return Center(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
@ -224,14 +167,47 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
textAlign: TextAlign.center,
),
),
),
),
),
);
}
if (_isLoading) {
return const Center(
Widget _buildYoutubePlayer() {
if (_youtubeController == null) {
return Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator(
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
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.11.0"
version: "2.12.0"
audio_session:
dependency: transitive
description:
@ -85,10 +85,10 @@ packages:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
bootstrap_icons:
dependency: "direct main"
description:
@ -157,10 +157,10 @@ packages:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
collection:
dependency: transitive
description:
@ -229,10 +229,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
ffi:
dependency: transitive
description:
@ -322,18 +322,18 @@ packages:
dependency: "direct main"
description:
name: flutter_inappwebview
sha256: "3e9a443a18ecef966fb930c3a76ca5ab6a7aafc0c7b5e14a4a850cf107b09959"
sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
version: "6.1.5"
flutter_inappwebview_android:
dependency: transitive
description:
name: flutter_inappwebview_android
sha256: d247f6ed417f1f8c364612fa05a2ecba7f775c8d0c044c1d3b9ee33a6515c421
sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
url: "https://pub.dev"
source: hosted
version: "1.0.13"
version: "1.1.3"
flutter_inappwebview_internal_annotations:
dependency: transitive
description:
@ -346,34 +346,42 @@ packages:
dependency: transitive
description:
name: flutter_inappwebview_ios
sha256: f363577208b97b10b319cd0c428555cd8493e88b468019a8c5635a0e4312bd0f
sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
url: "https://pub.dev"
source: hosted
version: "1.0.13"
version: "1.1.2"
flutter_inappwebview_macos:
dependency: transitive
description:
name: flutter_inappwebview_macos
sha256: b55b9e506c549ce88e26580351d2c71d54f4825901666bd6cfa4be9415bb2636
sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
url: "https://pub.dev"
source: hosted
version: "1.0.11"
version: "1.1.2"
flutter_inappwebview_platform_interface:
dependency: transitive
description:
name: flutter_inappwebview_platform_interface
sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187"
sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
url: "https://pub.dev"
source: hosted
version: "1.0.10"
version: "1.3.0+1"
flutter_inappwebview_web:
dependency: transitive
description:
name: flutter_inappwebview_web
sha256: d8c680abfb6fec71609a700199635d38a744df0febd5544c5a020bd73de8ee07
sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
url: "https://pub.dev"
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:
dependency: "direct dev"
description:
@ -676,18 +684,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.8"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.9"
leak_tracker_testing:
dependency: transitive
description:
@ -788,10 +796,10 @@ packages:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
version: "1.9.1"
path_parsing:
dependency: transitive
description:
@ -956,7 +964,7 @@ packages:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
source_span:
dependency: transitive
description:
@ -993,10 +1001,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.12.0"
stream_channel:
dependency: transitive
description:
@ -1009,10 +1017,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.4.0"
synchronized:
dependency: transitive
description:
@ -1209,10 +1217,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev"
source: hosted
version: "14.2.4"
version: "14.3.1"
wakelock_plus:
dependency: transitive
description:
@ -1297,10 +1305,10 @@ packages:
dependency: "direct main"
description:
name: youtube_player_flutter
sha256: "30f84e2f7063c56e536f507e37c1e803546842707cf58e5b5a71253b9ff9b455"
sha256: "4d14aa47f9c84929b5400a87ade4dcfdab87a2ca2e0b18ecc2ef852b1440e123"
url: "https://pub.dev"
source: hosted
version: "9.0.4"
version: "9.1.1"
sdks:
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
shimmer: ^3.0.0
video_player: ^2.9.1
youtube_player_flutter: ^9.0.4
youtube_player_flutter: ^9.1.1
dev_dependencies:
flutter_lints: ^4.0.0

View File

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

View File

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