diff --git a/CHANGELOG.md b/CHANGELOG.md index f1952e17..f36528a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## 0.1.19 + +- Fix video aspect ratio + +- Add property to decide whether to enable video fullscreen + +- Add property to hide the attachment button + +- Do not show send button if an attachment is still uploading + +- Unfocus and disable the TextField before opening the camera (workaround for flutter/flutter#42417) + +- Add gesture (vertical drag down) to close the keyboard + +- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will even close the keyboard) + +The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261 + ## 0.1.18 - Add message list date separators diff --git a/README.md b/README.md index eff6966c..1c1c6b4a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ ![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square) ![CI](https://github.com/GetStream/stream-chat-flutter/workflows/CI/badge.svg?branch=master) [![codecov](https://codecov.io/gh/GetStream/stream-chat-flutter/branch/master/graph/badge.svg)](https://codecov.io/gh/GetStream/stream-chat-flutter) +[![Gitter](https://badges.gitter.im/GetStream/stream-chat-flutter.svg)](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) **Quick Links** @@ -30,7 +31,7 @@ The example is available under the [example](https://github.com/GetStream/stream ```yaml dependencies: - stream_chat_flutter: ^0.1.15 + stream_chat_flutter: ^0.1.19 ``` You should then run `flutter packages get` diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index d3eff1b6..b7ada2e7 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -63,6 +63,8 @@ class MessageInput extends StatefulWidget { this.parentMessage, this.editMessage, this.maxHeight = 150, + this.keyboardType = TextInputType.multiline, + this.disableAttachments = false, }) : super(key: key); /// Message to edit @@ -77,6 +79,12 @@ class MessageInput extends StatefulWidget { /// Maximum Height for the TextField to grow before it starts scrolling final double maxHeight; + /// The keyboard type assigned to the TextField + final TextInputType keyboardType; + + /// If true the attachments button will not be displayed + final bool disableAttachments; + @override _MessageInputState createState() => _MessageInputState(); } @@ -87,6 +95,7 @@ class _MessageInputState extends State { final List _mentionedUsers = []; TextEditingController _textController; + bool _inputEnabled = true; bool _messageIsPresent = false; bool _typingStarted = false; OverlayEntry _commandsOverlay, _mentionsOverlay; @@ -94,20 +103,27 @@ class _MessageInputState extends State { @override Widget build(BuildContext context) { return SafeArea( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Stack( - overflow: Overflow.visible, - children: [ - _buildBorder(context), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildAttachments(), - _buildTextField(context), - ], - ), - ], + child: GestureDetector( + onPanUpdate: (details) { + if (details.delta.dy > 0) { + _focusNode.unfocus(); + } + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Stack( + overflow: Overflow.visible, + children: [ + _buildBorder(context), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildAttachments(), + _buildTextField(context), + ], + ), + ], + ), ), ), ); @@ -118,7 +134,7 @@ class _MessageInputState extends State { direction: Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.end, children: [ - _buildAttachmentButton(), + if (!widget.disableAttachments) _buildAttachmentButton(), _buildTextInput(context), _animateSendButton(context), ], @@ -127,7 +143,8 @@ class _MessageInputState extends State { AnimatedCrossFade _animateSendButton(BuildContext context) { return AnimatedCrossFade( - crossFadeState: (_messageIsPresent || _attachments.isNotEmpty) + crossFadeState: ((_messageIsPresent || _attachments.isNotEmpty) && + _attachments.every((a) => a.uploaded == true)) ? CrossFadeState.showFirst : CrossFadeState.showSecond, firstChild: _buildSendButton(context), @@ -143,11 +160,13 @@ class _MessageInputState extends State { maxHeight: widget.maxHeight, child: TextField( key: Key('messageInputText'), + enabled: _inputEnabled, minLines: null, maxLines: null, onSubmitted: (_) { _sendMessage(context); }, + keyboardType: widget.keyboardType, controller: _textController, focusNode: _focusNode, onChanged: (s) { @@ -507,6 +526,10 @@ class _MessageInputState extends State { } void _showAttachmentModal() { + if (_focusNode.hasFocus) { + _focusNode.unfocus(); + } + showModalBottomSheet( clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( @@ -549,7 +572,6 @@ class _MessageInputState extends State { leading: Icon(Icons.camera_alt), title: Text('Photo from camera'), onTap: () { - ImagePicker.pickImage(source: ImageSource.camera); _pickFile(FileType.image, true); Navigator.pop(context); }, @@ -576,6 +598,10 @@ class _MessageInputState extends State { } void _pickFile(FileType type, bool camera) async { + setState(() { + _inputEnabled = false; + }); + File file; if (camera) { @@ -588,6 +614,10 @@ class _MessageInputState extends State { file = await FilePicker.getFile(type: type); } + setState(() { + _inputEnabled = true; + }); + if (file == null) { return; } diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index e3fe6f92..d2c84efb 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -43,6 +43,7 @@ class MessageWidget extends StatefulWidget { this.isParent = false, this.onMentionTap, this.showOtherMessageUsername = false, + this.showVideoFullScreen = true, }) : super(key: key); /// Function called on mention tap @@ -69,6 +70,9 @@ class MessageWidget extends StatefulWidget { /// True if this is the parent of the thread being showed final bool isParent; + /// True if the video player will allow fullscreen mode + final bool showVideoFullScreen; + @override _MessageWidgetState createState() => _MessageWidgetState(); } @@ -1030,45 +1034,53 @@ class _MessageWidgetState extends State _videoControllers[attachment.assetUrl] = videoController; } - ChewieController chewieController; - if (_chuwieControllers.containsKey(attachment.assetUrl)) { - chewieController = _chuwieControllers[attachment.assetUrl]; - } else { - chewieController = ChewieController( - videoPlayerController: videoController, - autoInitialize: true, - errorBuilder: (_, e) { - if (attachment.thumbUrl == null) { - return _buildErrorImage(attachment); - } - return Stack( - children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: CachedNetworkImageProvider( - attachment.thumbUrl, + return FutureBuilder( + future: videoController.initialize(), + builder: (_, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return 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) { + return Stack( + children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.cover, + image: CachedNetworkImageProvider( + attachment.thumbUrl, + ), + ), ), ), - ), - ), - Material( - color: Colors.transparent, - child: InkWell( - onTap: () => _launchURL(attachment.titleLink), - ), - ), - ], - ); - }); - _chuwieControllers[attachment.assetUrl] = chewieController; - } - - return Chewie( - key: ValueKey( - 'ATTACHMENT-${attachment.title}-${widget.message.id}'), - controller: chewieController, + Material( + color: Colors.transparent, + child: InkWell( + onTap: () => _launchURL(attachment.titleLink), + ), + ), + ], + ); + }); + _chuwieControllers[attachment.assetUrl] = chewieController; + } + return Chewie( + key: ValueKey( + 'ATTACHMENT-${attachment.title}-${widget.message.id}'), + controller: chewieController, + ); + }, ); } diff --git a/pubspec.yaml b/pubspec.yaml index da01a6f2..ea855aa7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 0.1.18 +version: 0.1.19 environment: sdk: ">=2.3.0 <3.0.0"