From 17c171f98478612f20c2fac5a55c27978d908e23 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Apr 2020 11:55:05 +0200 Subject: [PATCH] extract videoattachment --- example/lib/main.dart | 1 + lib/src/attachment_error.dart | 35 +++++++++ lib/src/message_widget.dart | 140 +++++----------------------------- lib/src/utils.dart | 14 ++++ lib/src/video_attachment.dart | 99 ++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 123 deletions(-) create mode 100644 lib/src/attachment_error.dart create mode 100644 lib/src/utils.dart create mode 100644 lib/src/video_attachment.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 7de283d0..0cb0f098 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -67,6 +67,7 @@ void main() async { final client = Client( 'b67pax5b2wdq', logLevel: Level.INFO, + persistenceEnabled: false, showLocalNotification: Platform.isAndroid ? showLocalNotification : null, ); diff --git a/lib/src/attachment_error.dart b/lib/src/attachment_error.dart new file mode 100644 index 00000000..e8f87ab4 --- /dev/null +++ b/lib/src/attachment_error.dart @@ -0,0 +1,35 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:stream_chat/stream_chat.dart'; + +class AttachmentError extends StatelessWidget { + final Attachment attachment; + + const AttachmentError({ + Key key, + @required this.attachment, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (attachment.localUri != null) { + return Image.file( + File(attachment.localUri.path), + ); + } + return Center( + child: Container( + width: 200, + height: 140, + color: Color(0xffd0021B).withOpacity(.1), + child: Center( + child: Icon( + Icons.error_outline, + color: Colors.white, + ), + ), + ), + ); + } +} diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 7289678d..8ff582f6 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -1,13 +1,11 @@ -import 'dart:io'; - import 'package:cached_network_image/cached_network_image.dart'; -import 'package:chewie/chewie.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:jiffy/jiffy.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_flutter/src/attachment_error.dart'; import 'package:stream_chat_flutter/src/full_screen_image.dart'; import 'package:stream_chat_flutter/src/message_input.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; @@ -17,9 +15,9 @@ import 'package:stream_chat_flutter/src/sending_indicator.dart'; import 'package:stream_chat_flutter/src/stream_channel.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/user_avatar.dart'; +import 'package:stream_chat_flutter/src/utils.dart'; +import 'package:stream_chat_flutter/src/video_attachment.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:url_launcher/url_launcher.dart'; -import 'package:video_player/video_player.dart'; import 'deleted_message.dart'; import 'stream_chat.dart'; @@ -81,9 +79,6 @@ class MessageWidget extends StatefulWidget { class _MessageWidgetState extends State with AutomaticKeepAliveClientMixin, TickerProviderStateMixin { - final Map _videoControllers = {}; - final Map _chuwieControllers = {}; - MessageTheme _messageTheme; StreamChatState _streamChat; StreamChannelState _streamChannel; @@ -219,7 +214,10 @@ class _MessageWidgetState extends State Widget attachmentWidget; if (attachment.type == 'video') { - attachmentWidget = _buildVideo(attachment); + attachmentWidget = VideoAttachment( + attachment: attachment, + enableFullScreen: widget.showVideoFullScreen, + ); } else if (attachment.type == 'image' || attachment.type == 'giphy') { attachmentWidget = _buildImage(attachment); @@ -227,7 +225,7 @@ class _MessageWidgetState extends State attachmentWidget = Material( child: InkWell( onTap: () { - _launchURL(attachment.assetUrl); + launchURL(context, attachment.assetUrl); }, child: Container( width: 100, @@ -478,7 +476,7 @@ class _MessageWidgetState extends State print('tap on ${mentionedUser.name}'); } } else { - _launchURL(link); + launchURL(context, link); } }, styleSheet: MarkdownStyleSheet.fromTheme( @@ -558,7 +556,7 @@ class _MessageWidgetState extends State child: Material( color: Colors.transparent, child: InkWell( - onTap: () => _launchURL(attachment.titleLink), + onTap: () => launchURL(context, attachment.titleLink), ), ), ); @@ -568,7 +566,7 @@ class _MessageWidgetState extends State return GestureDetector( onTap: () { if (attachment.titleLink != null) { - _launchURL(attachment.titleLink); + launchURL(context, attachment.titleLink); } }, child: Container( @@ -896,7 +894,9 @@ class _MessageWidgetState extends State if (attachment.thumbUrl == null && attachment.imageUrl == null && attachment.assetUrl == null) { - return _buildErrorImage(attachment); + return AttachmentError( + attachment: attachment, + ); } return Hero( @@ -924,122 +924,16 @@ class _MessageWidgetState extends State }, imageUrl: attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl, - errorWidget: (context, url, error) => _buildErrorImage(attachment), + errorWidget: (context, url, error) => AttachmentError( + attachment: attachment, + ), fit: BoxFit.cover, ), ); } - Widget _buildErrorImage(Attachment attachment) { - if (attachment.localUri != null) { - return Image.file( - File(attachment.localUri.path), - ); - } - return Center( - child: Container( - width: 200, - height: 140, - color: Color(0xffd0021B).withOpacity(.1), - child: Center( - child: Icon( - Icons.error_outline, - color: Colors.white, - ), - ), - ), - ); - } - - Widget _buildVideo( - Attachment attachment, - ) { - VideoPlayerController videoController; - if (_videoControllers.containsKey(attachment.assetUrl)) { - videoController = _videoControllers[attachment.assetUrl]; - } else { - videoController = VideoPlayerController.network(attachment.assetUrl); - _videoControllers[attachment.assetUrl] = videoController; - } - - return FutureBuilder( - future: videoController.value.initialized - ? Future.value(true) - : videoController.initialize(), - builder: (_, snapshot) { - if (snapshot.connectionState != ConnectionState.done) { - return Container( - height: 100, - width: 100, - child: Center( - child: CircularProgressIndicator(), - ), - ); - } - ChewieController chewieController; - if (_chuwieControllers.containsKey(attachment.assetUrl)) { - chewieController = _chuwieControllers[attachment.assetUrl]; - } else { - chewieController = ChewieController( - allowFullScreen: widget.showVideoFullScreen, - videoPlayerController: videoController, - autoInitialize: false, - aspectRatio: videoController.value.aspectRatio, - errorBuilder: (_, e) { - if (attachment.thumbUrl != null) { - return Stack( - children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: CachedNetworkImageProvider( - attachment.thumbUrl, - ), - ), - ), - ), - if (attachment.titleLink != null) - Material( - color: Colors.transparent, - child: InkWell( - onTap: () => _launchURL(attachment.titleLink), - ), - ), - ], - ); - } - - return _buildErrorImage(attachment); - }); - _chuwieControllers[attachment.assetUrl] = chewieController; - } - return Chewie( - key: ValueKey( - 'ATTACHMENT-${attachment.title}-${widget.message.id}'), - controller: chewieController, - ); - }, - ); - } - - Future _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text('Cannot launch the url'), - ), - ); - } - } - @override void dispose() { - _videoControllers.values.forEach((element) { - element.dispose(); - }); super.dispose(); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart new file mode 100644 index 00000000..c427099b --- /dev/null +++ b/lib/src/utils.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +Future launchURL(BuildContext context, String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text('Cannot launch the url'), + ), + ); + } +} diff --git a/lib/src/video_attachment.dart b/lib/src/video_attachment.dart new file mode 100644 index 00000000..60967184 --- /dev/null +++ b/lib/src/video_attachment.dart @@ -0,0 +1,99 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:chewie/chewie.dart'; +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/utils.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:video_player/video_player.dart'; + +import 'attachment_error.dart'; + +class VideoAttachment extends StatefulWidget { + final Attachment attachment; + final bool enableFullScreen; + + VideoAttachment({ + Key key, + @required this.attachment, + this.enableFullScreen = true, + }) : super(key: key); + + @override + _VideoAttachmentState createState() => _VideoAttachmentState(); +} + +class _VideoAttachmentState extends State { + ChewieController _chewieController; + VideoPlayerController _videoPlayerController; + bool initialized = false; + + @override + Widget build(BuildContext context) { + if (!initialized) { + return Container( + height: 100, + width: 100, + child: Center( + child: CircularProgressIndicator(), + ), + ); + } + _chewieController = ChewieController( + allowFullScreen: widget.enableFullScreen, + videoPlayerController: _videoPlayerController, + autoInitialize: false, + aspectRatio: _videoPlayerController.value.aspectRatio, + errorBuilder: (_, e) { + if (widget.attachment.thumbUrl != null) { + return Stack( + children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.cover, + image: CachedNetworkImageProvider( + widget.attachment.thumbUrl, + ), + ), + ), + ), + if (widget.attachment.titleLink != null) + Material( + color: Colors.transparent, + child: InkWell( + onTap: () => + launchURL(context, widget.attachment.titleLink), + ), + ), + ], + ); + } + return AttachmentError( + attachment: widget.attachment, + ); + }); + + return Chewie( + controller: _chewieController, + ); + } + + @override + void initState() { + super.initState(); + _videoPlayerController = VideoPlayerController.network( + widget.attachment.localUri ?? widget.attachment.assetUrl); + _videoPlayerController.initialize().then((_) { + if (_videoPlayerController.value.initialized) { + setState(() { + initialized = true; + }); + } + }); + } + + @override + void dispose() { + _videoPlayerController.dispose(); + super.dispose(); + } +}