diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 383615ffc3..b99e050d0d 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -87,6 +87,10 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: "Install required native library" + run: | + sudo apt-get update + sudo apt-get install -y libmpv-dev - name: "Install Flutter" uses: subosito/flutter-action@v2 with: diff --git a/packages/stream_chat_flutter/example/linux/flutter/generated_plugin_registrant.cc b/packages/stream_chat_flutter/example/linux/flutter/generated_plugin_registrant.cc index 5d1305adbe..c39054118e 100644 --- a/packages/stream_chat_flutter/example/linux/flutter/generated_plugin_registrant.cc +++ b/packages/stream_chat_flutter/example/linux/flutter/generated_plugin_registrant.cc @@ -6,22 +6,26 @@ #include "generated_plugin_registrant.h" -#include #include #include +#include +#include #include #include void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) dart_vlc_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin"); - dart_vlc_plugin_register_with_registrar(dart_vlc_registrar); g_autoptr(FlPluginRegistrar) desktop_drop_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); + media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); + g_autoptr(FlPluginRegistrar) media_kit_video_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); + media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); diff --git a/packages/stream_chat_flutter/example/linux/flutter/generated_plugins.cmake b/packages/stream_chat_flutter/example/linux/flutter/generated_plugins.cmake index 631f39a5f7..8eb14aa51b 100644 --- a/packages/stream_chat_flutter/example/linux/flutter/generated_plugins.cmake +++ b/packages/stream_chat_flutter/example/linux/flutter/generated_plugins.cmake @@ -3,14 +3,16 @@ # list(APPEND FLUTTER_PLUGIN_LIST - dart_vlc desktop_drop file_selector_linux + media_kit_libs_linux + media_kit_video sqlite3_flutter_libs url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + media_kit_native_event_loop ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/packages/stream_chat_flutter/example/windows/flutter/generated_plugin_registrant.cc b/packages/stream_chat_flutter/example/windows/flutter/generated_plugin_registrant.cc index 0402854c0d..cbf06202de 100644 --- a/packages/stream_chat_flutter/example/windows/flutter/generated_plugin_registrant.cc +++ b/packages/stream_chat_flutter/example/windows/flutter/generated_plugin_registrant.cc @@ -7,9 +7,11 @@ #include "generated_plugin_registrant.h" #include -#include #include #include +#include +#include +#include #include #include #include @@ -18,12 +20,16 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); - DartVlcPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("DartVlcPlugin")); DesktopDropPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopDropPlugin")); FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); + MediaKitVideoPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); + ScreenBrightnessWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); Sqlite3FlutterLibsPluginRegisterWithRegistrar( diff --git a/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake b/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake index aeb58c2280..f405b03c90 100644 --- a/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake +++ b/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake @@ -4,9 +4,11 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus - dart_vlc desktop_drop file_selector_windows + media_kit_libs_windows_video + media_kit_video + screen_brightness_windows share_plus sqlite3_flutter_libs thumblr_windows @@ -14,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + media_kit_native_event_loop ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/packages/stream_chat_flutter/example/windows/runner/Runner.rc b/packages/stream_chat_flutter/example/windows/runner/Runner.rc index 5fdea291cf..0f5c085711 100644 --- a/packages/stream_chat_flutter/example/windows/runner/Runner.rc +++ b/packages/stream_chat_flutter/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/fsm_stub.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/fsm_stub.dart deleted file mode 100644 index 1bf7c435b3..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/fsm_stub.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Stub function for returning an instance of either [FullScreenMedia] or -/// [FullScreenMediaDesktop]. -/// -/// This should ONLY be used in [FullScreenMediaBuilder]. -FullScreenMediaWidget getFsm({ - Key? key, - required List mediaAttachmentPackages, - required int startIndex, - required String userName, - ShowMessageCallback? onShowMessage, - ReplyMessageCallback? onReplyMessage, - AttachmentActionsBuilder? attachmentActionsModalBuilder, - bool? autoplayVideos, -}) => - throw UnsupportedError('Cannot create FullScreenMedia'); diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart index 53e6f1a136..3db03b0c59 100644 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart @@ -5,14 +5,13 @@ import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; import 'package:stream_chat_flutter/src/attachment/thumbnail/media_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; import 'package:stream_chat_flutter/src/fullscreen_media/gallery_navigation_item.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; -/// A full screen image widget -class StreamFullScreenMedia extends FullScreenMediaWidget { - /// Instantiate a new FullScreenImage +/// A full screen media widget +class StreamFullScreenMedia extends StatefulWidget { + /// Instantiate a new StreamFullScreenMedia const StreamFullScreenMedia({ super.key, required this.mediaAttachmentPackages, diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart index f1919db192..073c96278b 100644 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart +++ b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart @@ -1,30 +1,10 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/fsm_stub.dart' - if (dart.library.io) 'full_screen_media_desktop.dart' as desktop_fsm; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@template fsmBuilder} -/// A wrapper widget for conditionally providing the proper -/// StreamFullScreenMedia widget when writing an application that targets -/// all available Flutter platforms (Android, iOS, macOS, Windows, Linux, -/// & Web). -/// -/// This is required because: -/// * `package:video_player` and `package:chewie` do not support macOS, Windows, -/// & Linux, but _do_ support Android, iOS, & Web. -/// * `package:dart_vlc` _does_ support macOS, Windows, & Linux via FFI. This -/// has the unfortunate consequence of not supporting Web. -/// -/// This widget makes use of dart's conditional imports to ensure that Stream's -/// desktop implementation of StreamFullScreenMedia is not imported when -/// building applications that target web. Additionally, this widget ensures -/// that applications targeting mobile platforms do not build the version of -/// StreamFullScreenMedia that targets desktop platforms (even though -/// `package:dart_vlc` technically supports iOS). -/// {@endtemplate} +/// A full screen media widget +@Deprecated('Use StreamFullScreenMedia instead.') class StreamFullScreenMediaBuilder extends StatelessWidget { - /// {@macro fsmBuilder} + /// Instantiate a new StreamFullScreenMediaBuilder const StreamFullScreenMediaBuilder({ super.key, required this.mediaAttachmentPackages, @@ -61,18 +41,6 @@ class StreamFullScreenMediaBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - if (!kIsWeb && isDesktopVideoPlayerSupported) { - return desktop_fsm.getFsm( - mediaAttachmentPackages: mediaAttachmentPackages, - startIndex: startIndex, - userName: userName, - autoplayVideos: autoplayVideos, - onShowMessage: onShowMessage, - onReplyMessage: onReplyMessage, - attachmentActionsModalBuilder: attachmentActionsModalBuilder, - ); - } - return StreamFullScreenMedia( mediaAttachmentPackages: mediaAttachmentPackages, startIndex: startIndex, diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart deleted file mode 100644 index 787b4b3d15..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart +++ /dev/null @@ -1,434 +0,0 @@ -import 'package:contextmenu/contextmenu.dart'; -import 'package:dart_vlc/dart_vlc.dart'; -import 'package:flutter/material.dart'; -import 'package:photo_view/photo_view.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/media_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/download_menu_item.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/gallery_navigation_item.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Returns an instance of [FullScreenMediaDesktop]. -/// -/// This should ONLY be used in [FullScreenMediaBuilder]. -FullScreenMediaWidget getFsm({ - Key? key, - required List mediaAttachmentPackages, - required int startIndex, - required String userName, - ShowMessageCallback? onShowMessage, - ReplyMessageCallback? onReplyMessage, - AttachmentActionsBuilder? attachmentActionsModalBuilder, - bool? autoplayVideos, -}) { - return FullScreenMediaDesktop( - key: key, - mediaAttachmentPackages: mediaAttachmentPackages, - startIndex: startIndex, - userName: userName, - onReplyMessage: onReplyMessage, - onShowMessage: onShowMessage, - attachmentActionsModalBuilder: attachmentActionsModalBuilder, - autoplayVideos: autoplayVideos ?? false, - ); -} - -/// A full screen image widget -class FullScreenMediaDesktop extends FullScreenMediaWidget { - /// Instantiate a new FullScreenImage - const FullScreenMediaDesktop({ - super.key, - required this.mediaAttachmentPackages, - this.startIndex = 0, - String? userName, - this.onShowMessage, - this.onReplyMessage, - this.attachmentActionsModalBuilder, - this.autoplayVideos = false, - }) : userName = userName ?? ''; - - /// The url of the image - final List mediaAttachmentPackages; - - /// First index of media shown - final int startIndex; - - /// Username of sender - final String userName; - - /// Callback for when show message is tapped - final ShowMessageCallback? onShowMessage; - - /// Callback for when reply message is tapped - final ReplyMessageCallback? onReplyMessage; - - /// Widget builder for attachment actions modal - /// [defaultActionsModal] is the default [AttachmentActionsModal] config - /// Use [defaultActionsModal.copyWith] to easily customize it - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// Auto-play videos when page is opened - final bool autoplayVideos; - - @override - _FullScreenMediaDesktopState createState() => _FullScreenMediaDesktopState(); -} - -class _FullScreenMediaDesktopState extends State { - late final PageController _pageController; - - late final _currentPage = ValueNotifier(widget.startIndex); - late final _isDisplayingDetail = ValueNotifier(true); - - void switchDisplayingDetail() { - _isDisplayingDetail.value = !_isDisplayingDetail.value; - } - - final videoPackages = {}; - - @override - void initState() { - super.initState(); - _pageController = PageController(initialPage: widget.startIndex); - for (var i = 0; i < widget.mediaAttachmentPackages.length; i++) { - final attachment = widget.mediaAttachmentPackages[i].attachment; - if (attachment.type != AttachmentType.video) continue; - final package = DesktopVideoPackage(attachment); - videoPackages[attachment.id] = package; - } - } - - @override - void dispose() { - _currentPage.dispose(); - _pageController.dispose(); - _isDisplayingDetail.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final containsOnlyVideos = - widget.mediaAttachmentPackages.length == videoPackages.length; - - return Scaffold( - resizeToAvoidBottomInset: false, - body: containsOnlyVideos ? _buildVideoPageView() : _buildPageView(), - ); - } - - Widget _buildVideoPageView() { - return Stack( - children: [ - ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: - widget.mediaAttachmentPackages[_currentPage.value].attachment, - ), - ], - child: _PlaylistPlayer( - packages: videoPackages.values.toList(), - autoStart: widget.autoplayVideos, - ), - ), - Positioned( - left: 8, - top: 8, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - videoPackages.values.first.player.stop(); - Navigator.of(context).pop(); - }, - child: StreamSvgIcon.close( - size: 30, - ), - ), - ), - ), - ], - ); - } - - Widget _buildPageView() { - return ValueListenableBuilder( - valueListenable: _currentPage, - builder: (context, currentPage, child) { - final _currentAttachmentPackage = - widget.mediaAttachmentPackages[currentPage]; - final _currentMessage = _currentAttachmentPackage.message; - final _currentAttachment = _currentAttachmentPackage.attachment; - return Stack( - children: [ - child!, - ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - final mediaQuery = MediaQuery.of(context); - final topPadding = mediaQuery.padding.top; - return AnimatedPositionedDirectional( - duration: kThemeAnimationDuration, - curve: Curves.easeInOut, - top: isDisplayingDetail ? 0 : -(topPadding + kToolbarHeight), - start: 0, - end: 0, - height: topPadding + kToolbarHeight, - child: StreamGalleryHeader( - userName: widget.userName, - sentAt: context.translations.sentAtText( - date: _currentAttachmentPackage.message.createdAt, - time: _currentAttachmentPackage.message.createdAt, - ), - onBackPressed: Navigator.of(context).pop, - message: _currentMessage, - attachment: _currentAttachment, - onShowMessage: () { - widget.onShowMessage?.call( - _currentMessage, - StreamChannel.of(context).channel, - ); - }, - attachmentActionsModalBuilder: - widget.attachmentActionsModalBuilder, - ), - ); - }, - ), - if (!_currentMessage.isEphemeral) - ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - final mediaQuery = MediaQuery.of(context); - final bottomPadding = mediaQuery.padding.bottom; - return AnimatedPositionedDirectional( - duration: kThemeAnimationDuration, - curve: Curves.easeInOut, - bottom: isDisplayingDetail - ? 0 - : -(bottomPadding + kToolbarHeight), - start: 0, - end: 0, - height: bottomPadding + kToolbarHeight, - child: StreamGalleryFooter( - currentPage: currentPage, - totalPages: widget.mediaAttachmentPackages.length, - mediaAttachmentPackages: widget.mediaAttachmentPackages, - mediaSelectedCallBack: (val) { - _currentPage.value = val; - _pageController.animateToPage( - val, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - Navigator.pop(context); - }, - ), - ); - }, - ), - if (widget.mediaAttachmentPackages.length > 1) ...[ - if (currentPage > 0) - GalleryNavigationItem( - left: 8, - opacityAnimation: _isDisplayingDetail, - icon: const Icon(Icons.chevron_left_rounded), - onPressed: () { - _currentPage.value--; - _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - ), - if (currentPage < widget.mediaAttachmentPackages.length - 1) - GalleryNavigationItem( - right: 8, - opacityAnimation: _isDisplayingDetail, - icon: const Icon(Icons.chevron_right_rounded), - onPressed: () { - _currentPage.value++; - _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - ), - ], - ], - ); - }, - child: InkWell( - onTap: switchDisplayingDetail, - child: KeyboardShortcutRunner( - onEscapeKeypress: Navigator.of(context).pop, - onLeftArrowKeypress: () { - if (_currentPage.value > 0) { - _currentPage.value--; - _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - onRightArrowKeypress: () { - if (_currentPage.value < - widget.mediaAttachmentPackages.length - 1) { - _currentPage.value++; - _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - child: PageView.builder( - controller: _pageController, - itemCount: widget.mediaAttachmentPackages.length, - onPageChanged: (val) { - _currentPage.value = val; - if (videoPackages.isEmpty) return; - final currentAttachment = - widget.mediaAttachmentPackages[val].attachment; - for (final p in videoPackages.values) { - if (p.attachment != currentAttachment) { - p.player.pause(); - } - } - if (widget.autoplayVideos && - currentAttachment.type == AttachmentType.video) { - final package = videoPackages[currentAttachment.id]!; - package.player.play(); - } - }, - itemBuilder: (context, index) { - final currentAttachmentPackage = - widget.mediaAttachmentPackages[index]; - final attachment = currentAttachmentPackage.attachment; - - return ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - return AnimatedContainer( - duration: kThemeChangeDuration, - color: isDisplayingDetail - ? StreamChannelHeaderTheme.of(context).color - : Colors.black, - child: Builder( - builder: (context) { - if (attachment.type == AttachmentType.image || - attachment.type == AttachmentType.giphy) { - return PhotoView.customChild( - maxScale: PhotoViewComputedScale.covered, - minScale: PhotoViewComputedScale.contained, - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - child: StreamMediaAttachmentThumbnail( - media: attachment, - width: double.infinity, - height: double.infinity, - ), - ); - } else if (attachment.type == AttachmentType.video) { - final package = videoPackages[attachment.id]!; - package.player.open( - Playlist( - medias: [ - Media.network(package.attachment.assetUrl), - ], - ), - autoStart: widget.autoplayVideos, - ); - - final mediaQuery = MediaQuery.of(context); - final bottomPadding = mediaQuery.padding.bottom; - - return AnimatedPadding( - duration: kThemeChangeDuration, - padding: EdgeInsets.symmetric( - vertical: isDisplayingDetail - ? kToolbarHeight + bottomPadding - : 0, - ), - child: ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: attachment, - ), - ], - child: Video( - player: package.player, - ), - ), - ); - } - - return const SizedBox(); - }, - ), - ); - }, - ); - }, - ), - ), - ), - ); - } -} - -/// Class for packaging up things required for videos -class DesktopVideoPackage { - /// Constructor for creating [VideoPackage] - DesktopVideoPackage( - this.attachment, { - this.showControls = true, - }) : player = Player( - id: int.parse( - attachment.id.characters - .getRange(0, 10) - .toString() - .replaceAll(RegExp('[^0-9]'), ''), - ), - ); - - /// The video attachment to play. - final Attachment attachment; - - /// The VLC player to use. - final Player player; - - /// Whether to show the player controls or not. - final bool showControls; -} - -class _PlaylistPlayer extends StatelessWidget { - const _PlaylistPlayer({ - required this.packages, - required this.autoStart, - }); - - final List packages; - final bool autoStart; - - @override - Widget build(BuildContext context) { - final _media = []; - for (final package in packages) { - _media.add(Media.network(package.attachment.assetUrl)); - } - packages.first.player.open( - Playlist( - medias: _media, - ), - autoStart: autoStart, - ); - return Video( - player: packages.first.player, - fit: BoxFit.cover, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_widget.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_widget.dart deleted file mode 100644 index adcac9d3ff..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_widget.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template fsmWidget} -/// An ultra-simple abstract class that allows [FullScreenMediaBuilder] -/// to call `getFsm()` and build the correct version of FullScreenMedia. -/// {@endtemplate} -abstract class FullScreenMediaWidget extends StatefulWidget { - /// {@macro fsmWidget} - const FullScreenMediaWidget({super.key}); -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart b/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart index c10f2cedb6..0ae7f38a3b 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart @@ -84,7 +84,7 @@ class ParseAttachments extends StatelessWidget { builder: (context) { return StreamChannel( channel: channel, - child: StreamFullScreenMediaBuilder( + child: StreamFullScreenMedia( userName: message.user!.name, mediaAttachmentPackages: attachments, startIndex: attachments.indexWhere( diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart index 6b0d0e609d..7fe94afa5c 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:video_player_media_kit/video_player_media_kit.dart'; /// {@template streamChat} /// Widget used to provide information about the chat to the widget tree @@ -98,49 +98,63 @@ class StreamChatState extends State { @override void initState() { super.initState(); - // Ensures that VLC only initializes in real desktop environments - if (!isTestEnvironment && isDesktopVideoPlayerSupported) { - VlcManager.instance.initialize(); + if (isDesktopVideoPlayerSupported) { + VideoPlayerMediaKit.ensureInitialized( + windows: true, + linux: true, + ); } } @override Widget build(BuildContext context) { final theme = _getTheme(context, widget.streamChatThemeData); - return Portal( - child: StreamChatConfiguration( - data: streamChatConfigData, - child: StreamChatTheme( - data: theme, - child: Builder( - builder: (context) { - final materialTheme = Theme.of(context); - final streamTheme = StreamChatTheme.of(context); - return Theme( - data: materialTheme.copyWith( - primaryIconTheme: streamTheme.primaryIconTheme, - colorScheme: materialTheme.colorScheme.copyWith( - secondary: streamTheme.colorTheme.accentPrimary, + return Theme( + // package:media_kit or [Texture] can have issues with + // [ZoomPageTransitionsBuilder] on GNU/Linux. Thus, we use use + // [FadeUpwardsPageTransitionsBuilder] instead. + data: Theme.of(context).copyWith( + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(), + }, + ), + ), + child: Portal( + child: StreamChatConfiguration( + data: streamChatConfigData, + child: StreamChatTheme( + data: theme, + child: Builder( + builder: (context) { + final materialTheme = Theme.of(context); + final streamTheme = StreamChatTheme.of(context); + return Theme( + data: materialTheme.copyWith( + primaryIconTheme: streamTheme.primaryIconTheme, + colorScheme: materialTheme.colorScheme.copyWith( + secondary: streamTheme.colorTheme.accentPrimary, + ), ), - ), - child: StreamChatCore( - client: client, - onBackgroundEventReceived: widget.onBackgroundEventReceived, - backgroundKeepAlive: widget.backgroundKeepAlive, - connectivityStream: widget.connectivityStream, - child: Builder( - builder: (context) { - StreamChatClient.additionalHeaders = { - 'X-Stream-Client': - '${StreamChatClient.defaultUserAgent}-' - 'ui-${StreamChatClient.packageVersion}', - }; - return widget.child ?? const Offstage(); - }, + child: StreamChatCore( + client: client, + onBackgroundEventReceived: widget.onBackgroundEventReceived, + backgroundKeepAlive: widget.backgroundKeepAlive, + connectivityStream: widget.connectivityStream, + child: Builder( + builder: (context) { + StreamChatClient.additionalHeaders = { + 'X-Stream-Client': + '${StreamChatClient.defaultUserAgent}-' + 'ui-${StreamChatClient.packageVersion}', + }; + return widget.child ?? const Offstage(); + }, + ), ), - ), - ); - }, + ); + }, + ), ), ), ), diff --git a/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart b/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart index bd65820c5e..be817b6c0c 100644 --- a/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart +++ b/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart @@ -12,9 +12,9 @@ bool get isDesktopDevice => CurrentPlatform.isWindows || CurrentPlatform.isLinux; -/// Returns true if the app is running on windows or linux platform. +/// Returns true if the app is running on Microsoft Windows or GNU/Linux. bool get isDesktopVideoPlayerSupported => - // Dart VLC is not supported on MacOS. + // package:media_kit is not used on macOS. !CurrentPlatform.isMacOS && (CurrentPlatform.isWindows || CurrentPlatform.isLinux); diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager.dart deleted file mode 100644 index adecfc56b9..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:stream_chat_flutter/src/video/vlc/vlc_stub.dart' - if (dart.library.io) 'vlc_manager_desktop.dart' - if (dart.library.html) 'vlc_manager_web.dart'; - -/// {@template vlcManager} -/// An abstract class for the purpose of ensuring Flutter applications that -/// target both desktop & web do not crash when building for web targets. -/// {@endtemplate} -abstract class VlcManager { - // ignore: use_late_for_private_fields_and_variables - static VlcManager? _instance; - - /// The current instance of [VlcManager]. - static VlcManager get instance { - _instance = getVlc(); - - return _instance!; - } - - /// Initializes VLC. - void initialize(); -} diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_desktop.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_desktop.dart deleted file mode 100644 index 3f86a6e6d9..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_desktop.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:dart_vlc/dart_vlc.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; - -/// The desktop implementation of [VlcManager]. It simply initializes VLC. -class VlcManagerDesktop extends VlcManager { - @override - void initialize() { - DartVLC.initialize(); - } -} - -/// Allows [VlcManager] to return the correct implementation. -VlcManager getVlc() => VlcManagerDesktop(); diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_web.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_web.dart deleted file mode 100644 index 3462350b1e..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_web.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/rendering.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; - -/// The web implementation of [VlcManager]. Naturally, it does nothing. It -/// exists simply to satisfy the requirements of conditional imports. -class VlcManagerWeb extends VlcManager { - @override - void initialize() { - debugPrint('Stub initialization for VLC.'); - } -} - -/// Allows [VlcManager] to return the correct implementation. -VlcManager getVlc() => VlcManagerWeb(); diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_stub.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_stub.dart deleted file mode 100644 index 289961fd0b..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_stub.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; - -/// Method stub for getting the right instance of [VlcManager] on the right -/// platform. -VlcManager getVlc() => throw UnsupportedError('Cannot create VLC Manager'); diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 3606f5d842..65d1c1a234 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -14,7 +14,6 @@ dependencies: chewie: ^1.7.0 collection: ^1.17.2 contextmenu: ^3.0.0 - dart_vlc: ^0.4.0 desktop_drop: ^0.4.1 diacritic: ^0.1.4 dio: ^5.3.2 @@ -32,6 +31,8 @@ dependencies: image_size_getter: ^2.1.2 jiffy: ^6.2.1 lottie: ">=2.6.0 <4.0.0" + media_kit_libs_linux: ^1.1.3 + media_kit_libs_windows_video: ^1.0.9 meta: ^1.9.1 path_provider: ^2.1.0 photo_manager: ^3.0.0 @@ -44,6 +45,7 @@ dependencies: thumblr: ^0.0.4 url_launcher: ^6.1.12 video_player: ^2.7.0 + video_player_media_kit: ^1.0.4 video_thumbnail: ^0.5.3 flutter: @@ -74,4 +76,4 @@ dev_dependencies: sdk: flutter golden_toolkit: ^0.15.0 mocktail: ^1.0.0 - path: ^1.8.3 \ No newline at end of file + path: ^1.8.3 diff --git a/packages/stream_chat_flutter/test/src/mocks.dart b/packages/stream_chat_flutter/test/src/mocks.dart index df99765933..ace17b3091 100644 --- a/packages/stream_chat_flutter/test/src/mocks.dart +++ b/packages/stream_chat_flutter/test/src/mocks.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager_desktop.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class MockClient extends Mock implements StreamChatClient { @@ -60,8 +59,6 @@ class MockOwnUser extends Mock implements OwnUser {} class MockAttachment extends Mock implements Attachment {} -class MockVlcManagerDesktop extends Mock implements VlcManagerDesktop {} - class MockStreamMemberListController extends Mock implements StreamMemberListController { @override