From a1a3cb177df571696cf70c5160ef1ecd6c13edf6 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 13 May 2020 15:51:45 +0200 Subject: [PATCH] fix video attachment fullscreen --- example/lib/main.dart | 2 +- lib/src/full_screen_video.dart | 25 +++++---- lib/src/image_attachment.dart | 96 ++++++++++++++++++---------------- lib/src/message_widget.dart | 66 +++++++++++++++++++++-- lib/src/stream_chat_theme.dart | 11 +++- lib/src/video_attachment.dart | 6 +-- lib/stream_chat_flutter.dart | 1 + pubspec.yaml | 4 +- 8 files changed, 142 insertions(+), 69 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 4355e1f9..aa454649 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -94,7 +94,7 @@ class MyApp extends StatelessWidget { return MaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), - themeMode: ThemeMode.system, + themeMode: ThemeMode.dark, home: Container( child: StreamChat( client: client, diff --git a/lib/src/full_screen_video.dart b/lib/src/full_screen_video.dart index 3bed2d09..000f13c1 100644 --- a/lib/src/full_screen_video.dart +++ b/lib/src/full_screen_video.dart @@ -32,7 +32,6 @@ class _FullScreenVideoState extends State { child: CircularProgressIndicator(), ); } - return Chewie( controller: _chewieController, ); @@ -49,28 +48,28 @@ class _FullScreenVideoState extends State { _videoPlayerController.initialize().whenComplete(() { setState(() { initialized = true; + _chewieController = ChewieController( + videoPlayerController: _videoPlayerController, + autoInitialize: false, + aspectRatio: _videoPlayerController.value.aspectRatio, + ); }); }); - _chewieController = ChewieController( - videoPlayerController: _videoPlayerController, - autoInitialize: false, - aspectRatio: _videoPlayerController.value.aspectRatio, - ); - _videoPlayerController.addListener(() { + VoidCallback errorListener; + errorListener = () { if (_videoPlayerController.value.hasError) { - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Navigator.pop(context); - launchURL(context, widget.attachment.titleLink); - }); + Navigator.pop(context); + launchURL(context, widget.attachment.titleLink); } - }); + _videoPlayerController.removeListener(errorListener); + }; + _videoPlayerController.addListener(errorListener); } @override void dispose() { _videoPlayerController.dispose(); - _chewieController.dispose(); super.dispose(); } } diff --git a/lib/src/image_attachment.dart b/lib/src/image_attachment.dart index d1673dad..7c27178d 100644 --- a/lib/src/image_attachment.dart +++ b/lib/src/image_attachment.dart @@ -32,55 +32,61 @@ class ImageAttachment extends StatelessWidget { size: size, child: Stack( children: [ - Hero( - tag: attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl, - child: GestureDetector( - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (_) { - return FullScreenImage( - url: attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl, - ); - })); - }, - child: CachedNetworkImage( - height: size?.height, - width: size?.width, - placeholder: (_, __) { - return Container( - width: size?.width, - height: size?.height, - child: Center( - child: CircularProgressIndicator(), + Column( + children: [ + Expanded( + child: Hero( + tag: attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl, + child: GestureDetector( + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (_) { + return FullScreenImage( + url: attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl, + ); + })); + }, + child: CachedNetworkImage( + height: size?.height, + width: size?.width, + placeholder: (_, __) { + return Container( + width: size?.width, + height: size?.height, + child: Center( + child: CircularProgressIndicator(), + ), + ); + }, + imageUrl: attachment.thumbUrl ?? + attachment.imageUrl ?? + attachment.assetUrl, + errorWidget: (context, url, error) => AttachmentError( + attachment: attachment, + size: size, + ), + fit: BoxFit.cover, ), - ); - }, - imageUrl: attachment.thumbUrl ?? - attachment.imageUrl ?? - attachment.assetUrl, - errorWidget: (context, url, error) => AttachmentError( - attachment: attachment, - size: size, - ), - fit: BoxFit.cover, - ), - ), - ), - if (attachment.title != null) - Positioned.fill( - child: Align( - alignment: Alignment.bottomCenter, - child: Material( - child: AttachmentTitle( - messageTheme: messageTheme, - attachment: attachment, ), ), ), - ), + if (attachment.title != null) + Positioned.fill( + child: Align( + alignment: Alignment.bottomCenter, + child: Material( + child: AttachmentTitle( + messageTheme: messageTheme, + attachment: attachment, + ), + ), + ), + ), + ], + ), if (attachment.titleLink != null || attachment.ogScrapeUrl != null) Positioned.fill( child: Material( diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index a9f0e642..124e7e65 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -13,41 +13,98 @@ import 'message_text.dart'; typedef AttachmentBuilder = Widget Function(BuildContext, Message, Attachment); +/// The display behaviour of a widget enum DisplayWidget { + /// Hides the widget replacing its space with a spacer hide, + + /// Hides the widget not replacing its space gone, + + /// Shows the widget normally show, } +/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_widget.png) +/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_widget_paint.png) +/// +/// It shows a message with reactions, replies and user avatar. +/// +/// Usually you don't use this widget as it's the default message widget used by [MessageListView]. +/// +/// The widget components render the ui based on the first ancestor of type [StreamChatTheme]. +/// Modify it to change the widget appearance. class MessageWidget extends StatelessWidget { + /// Function called on mention tap final void Function(User) onMentionTap; + + /// The function called when tapping on replies final void Function(Message) onThreadTap; final Widget Function(BuildContext, Message) editMessageInputBuilder; final Widget Function(BuildContext, Message) textBuilder; + + /// Function called on long press final void Function(BuildContext, Message) onMessageActions; + + /// The message final Message message; + + /// The message theme final MessageTheme messageTheme; + + /// If true the widget will be mirrored final bool reverse; + + /// The shape of the message text final ShapeBorder shape; + + /// The shape of an attachment final ShapeBorder attachmentShape; + + /// The borderside of the message text final BorderSide borderSide; + + /// The borderside of an attachment final BorderSide attachmentBorderSide; + + /// The border radius of the message text final BorderRadiusGeometry borderRadiusGeometry; + + /// The border radius of an attachment final BorderRadiusGeometry attachmentBorderRadiusGeometry; + + /// The padding of the widget final EdgeInsetsGeometry padding; + + /// The internal padding of the message text final EdgeInsetsGeometry textPadding; + + /// The internal padding of an attachment final EdgeInsetsGeometry attachmentPadding; + + /// It controls the display behaviour of the user avatar final DisplayWidget showUserAvatar; + + /// It controls the display behaviour of the sending indicator final DisplayWidget showSendingIndicator; + + /// If true the widget will show the reactions final bool showReactions; + + /// If true the widget will show the reply indicator final bool showReplyIndicator; - final bool isParent; + + /// The function called when tapping on UserAvatar + final void Function(User) onUserAvatarTap; + + /// If true show the users username next to the timestamp of the message final bool showUsername; final bool showTimestamp; final bool showDeleteMessage; final bool showEditMessage; final Map attachmentBuilders; - final Map reactionToEmoji = { + + final Map _reactionToEmoji = { 'love': '❤️️', 'haha': '😂', 'like': '👍', @@ -71,13 +128,13 @@ class MessageWidget extends StatelessWidget { this.showUserAvatar = DisplayWidget.show, this.showSendingIndicator = DisplayWidget.show, this.showReplyIndicator = true, - this.isParent = false, this.onThreadTap, this.showUsername = true, this.showTimestamp = true, this.showReactions = true, this.showDeleteMessage = true, this.showEditMessage = true, + this.onUserAvatarTap, this.onMessageActions, this.editMessageInputBuilder, this.textBuilder, @@ -338,7 +395,7 @@ class MessageWidget extends StatelessWidget { Text _buildReactionsText(BuildContext context) { return Text( message.reactionCounts.keys.map((reactionType) { - return reactionToEmoji[reactionType] ?? '?'; + return _reactionToEmoji[reactionType] ?? '?'; }).join(' ') + ' ${message.reactionCounts.values.fold(0, (t, v) => v + t).toString()}', style: TextStyle( @@ -499,6 +556,7 @@ class MessageWidget extends StatelessWidget { Offset(0, messageTheme.avatarTheme.constraints.maxHeight / 2), child: UserAvatar( user: message.user, + onTap: onUserAvatarTap, constraints: messageTheme.avatarTheme.constraints, ), ), diff --git a/lib/src/stream_chat_theme.dart b/lib/src/stream_chat_theme.dart index 5e06672e..e75d30c3 100644 --- a/lib/src/stream_chat_theme.dart +++ b/lib/src/stream_chat_theme.dart @@ -24,7 +24,16 @@ class StreamChatTheme extends InheritedWidget { /// Use this method to get the current [StreamChatThemeData] instance static StreamChatThemeData of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType().data; + final streamChatTheme = + context.dependOnInheritedWidgetOfExactType(); + + if (streamChatTheme == null) { + throw Exception( + 'You must have a StreamChatTheme widget at the top of your widget tree', + ); + } + + return streamChatTheme.data; } } diff --git a/lib/src/video_attachment.dart b/lib/src/video_attachment.dart index 2b4b1c7c..e81ea220 100644 --- a/lib/src/video_attachment.dart +++ b/lib/src/video_attachment.dart @@ -43,7 +43,7 @@ class _VideoAttachmentState extends State { } _chewieController = ChewieController( videoPlayerController: _videoPlayerController, - autoInitialize: false, + autoInitialize: true, showControls: false, aspectRatio: _videoPlayerController.value.aspectRatio, errorBuilder: (_, e) { @@ -137,8 +137,8 @@ class _VideoAttachmentState extends State { @override void initState() { super.initState(); - _videoPlayerController = VideoPlayerController.network( - widget.attachment.localUri ?? widget.attachment.assetUrl); + _videoPlayerController = + VideoPlayerController.network(widget.attachment.assetUrl); _videoPlayerController.initialize().whenComplete(() { setState(() { initialized = true; diff --git a/lib/stream_chat_flutter.dart b/lib/stream_chat_flutter.dart index 17b018a6..9411aea1 100644 --- a/lib/stream_chat_flutter.dart +++ b/lib/stream_chat_flutter.dart @@ -9,6 +9,7 @@ export 'src/channels_bloc.dart'; export 'src/date_divider.dart'; export 'src/deleted_message.dart'; export 'src/file_attachment.dart'; +export 'src/full_screen_video.dart'; export 'src/giphy_attachment.dart'; export 'src/image_attachment.dart'; export 'src/message_input.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 06d6b00a..031dbddc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,10 +15,10 @@ dependencies: flutter_portal: ^0.1.0 cached_network_image: ^2.2.0+1 flutter_markdown: ^0.3.5 - url_launcher: ^5.4.5 + url_launcher: ^5.4.7 video_player: ^0.10.10 chewie: ^0.9.10 - file_picker: ^1.8.0+2 + file_picker: ^1.9.0+1 image_picker: ^0.6.6+1 flutter_keyboard_visibility: ^2.0.0 stream_chat: