fix: add more actions locations, merge actions and add showCommandsButton option (#333)

* add more actions locations and merge actions

* remove print

* fix command chip

* fix ui

* rotate icon
This commit is contained in:
Salvatore Giordano
2021-03-15 14:40:36 +01:00
committed by GitHub
parent 9f002f32b3
commit f60f43af49
@@ -12,12 +12,12 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/video_service.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart'; import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:stream_chat_flutter/src/video_service.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:substring_highlight/substring_highlight.dart'; import 'package:substring_highlight/substring_highlight.dart';
@@ -35,6 +35,8 @@ typedef AttachmentThumbnailBuilder = Widget Function(
enum ActionsLocation { enum ActionsLocation {
left, left,
right, right,
leftInside,
rightInside,
} }
enum DefaultAttachmentTypes { enum DefaultAttachmentTypes {
@@ -122,6 +124,7 @@ class MessageInput extends StatefulWidget {
this.hideSendAsDm = false, this.hideSendAsDm = false,
this.idleSendButton, this.idleSendButton,
this.activeSendButton, this.activeSendButton,
this.showCommandsButton = true,
}) : super(key: key); }) : super(key: key);
/// Message to edit /// Message to edit
@@ -149,6 +152,9 @@ class MessageInput extends StatefulWidget {
/// If true the attachments button will not be displayed /// If true the attachments button will not be displayed
final bool disableAttachments; final bool disableAttachments;
/// Use this property to hide/show the commands button
final bool showCommandsButton;
/// Hide send as dm checkbox /// Hide send as dm checkbox
final bool hideSendAsDm; final bool hideSendAsDm;
@@ -336,12 +342,11 @@ class MessageInputState extends State<MessageInput> {
direction: Axis.horizontal, direction: Axis.horizontal,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
if (!_commandEnabled) _buildExpandActionsButton(), if (!_commandEnabled && widget.actionsLocation == ActionsLocation.left)
if (widget.actionsLocation == ActionsLocation.left) _buildExpandActionsButton(),
...widget.actions ?? [],
_buildTextInput(context), _buildTextInput(context),
if (widget.actionsLocation == ActionsLocation.right) if (!_commandEnabled && widget.actionsLocation == ActionsLocation.right)
...widget.actions ?? [], _buildExpandActionsButton(),
if (widget.sendButtonLocation == SendButtonLocation.outside) if (widget.sendButtonLocation == SendButtonLocation.outside)
_animateSendButton(context), _animateSendButton(context),
], ],
@@ -421,18 +426,15 @@ class MessageInputState extends State<MessageInput> {
child: widget.activeSendButton, child: widget.activeSendButton,
) )
: _buildSendButton(context); : _buildSendButton(context);
return Padding( return AnimatedCrossFade(
padding: const EdgeInsets.all(8.0), crossFadeState: (_messageIsPresent || _attachments.isNotEmpty)
child: AnimatedCrossFade( ? CrossFadeState.showFirst
crossFadeState: (_messageIsPresent || _attachments.isNotEmpty) : CrossFadeState.showSecond,
? CrossFadeState.showFirst firstChild: sendButton,
: CrossFadeState.showSecond, secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
firstChild: sendButton, duration:
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context), StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration,
duration: alignment: Alignment.center,
StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration,
alignment: Alignment.center,
),
); );
} }
@@ -445,8 +447,15 @@ class MessageInputState extends State<MessageInput> {
: CrossFadeState.showSecond, : CrossFadeState.showSecond,
firstChild: IconButton( firstChild: IconButton(
onPressed: () => setState(() => _actionsShrunk = false), onPressed: () => setState(() => _actionsShrunk = false),
icon: StreamSvgIcon.emptyCircleLeft( icon: Transform.rotate(
color: StreamChatTheme.of(context).colorTheme.accentBlue, alignment: Alignment.center,
angle: (widget.actionsLocation == ActionsLocation.right ||
widget.actionsLocation == ActionsLocation.rightInside)
? pi
: 0,
child: StreamSvgIcon.emptyCircleLeft(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tightFor( constraints: BoxConstraints.tightFor(
@@ -457,10 +466,12 @@ class MessageInputState extends State<MessageInput> {
), ),
secondChild: FittedBox( secondChild: FittedBox(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: <Widget>[
if (!widget.disableAttachments) _buildAttachmentButton(), if (!widget.disableAttachments) _buildAttachmentButton(),
if (widget.editMessage == null && if (widget.showCommandsButton &&
widget.editMessage == null &&
StreamChannel.of(context) StreamChannel.of(context)
.channel .channel
?.config ?.config
@@ -468,6 +479,7 @@ class MessageInputState extends State<MessageInput> {
?.isNotEmpty == ?.isNotEmpty ==
true) true)
_buildCommandButton(), _buildCommandButton(),
...widget.actions ?? [],
].insertBetween(const SizedBox(width: 8)), ].insertBetween(const SizedBox(width: 8)),
), ),
), ),
@@ -565,51 +577,75 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
contentPadding: const EdgeInsets.fromLTRB(16, 12, 13, 11), contentPadding: const EdgeInsets.fromLTRB(16, 12, 13, 11),
prefixIconConstraints: BoxConstraints.tight(Size(78, 24)),
suffixIconConstraints: BoxConstraints.tight(Size(40, 40)),
prefixIcon: _commandEnabled prefixIcon: _commandEnabled
? Container( ? Row(
decoration: BoxDecoration( mainAxisSize: MainAxisSize.min,
borderRadius: BorderRadius.circular(12), children: [
color: theme.colorTheme.accentBlue, Padding(
), padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.only(right: 4, left: 8), child: Container(
alignment: Alignment.center, constraints: BoxConstraints.tight(Size(64, 24)),
child: Row( decoration: BoxDecoration(
mainAxisSize: MainAxisSize.min, borderRadius: BorderRadius.circular(12),
children: [ color: theme.colorTheme.accentBlue,
StreamSvgIcon.lightning( ),
color: Colors.white, alignment: Alignment.center,
size: 16.0, child: Row(
), mainAxisSize: MainAxisSize.min,
Text( children: [
_chosenCommand?.name?.toUpperCase() ?? '', StreamSvgIcon.lightning(
style: StreamChatTheme.of(context)
.textTheme
.footnoteBold
.copyWith(
color: Colors.white, color: Colors.white,
size: 16.0,
), ),
Text(
_chosenCommand?.name?.toUpperCase() ?? '',
style: StreamChatTheme.of(context)
.textTheme
.footnoteBold
.copyWith(
color: Colors.white,
),
),
],
),
), ),
], ),
), ],
) )
: null, : (widget.actionsLocation == ActionsLocation.leftInside
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildExpandActionsButton(),
],
)
: null),
suffixIconConstraints: BoxConstraints.tightFor(height: 40),
prefixIconConstraints: BoxConstraints.tightFor(height: 40),
suffixIcon: Row( suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
if (_commandEnabled) if (_commandEnabled)
IconButton( Padding(
icon: StreamSvgIcon.closeSmall(), padding: const EdgeInsets.only(right: 8.0),
splashRadius: 24, child: IconButton(
padding: const EdgeInsets.all(0), icon: StreamSvgIcon.closeSmall(),
constraints: BoxConstraints.tightFor( splashRadius: 24,
height: 24, padding: const EdgeInsets.all(0),
width: 24, constraints: BoxConstraints.tightFor(
height: 24,
width: 24,
),
onPressed: () {
setState(() => _commandEnabled = false);
},
), ),
onPressed: () {
setState(() => _commandEnabled = false);
},
), ),
if (!_commandEnabled &&
widget.actionsLocation == ActionsLocation.rightInside)
_buildExpandActionsButton(),
if (widget.sendButtonLocation == SendButtonLocation.inside) if (widget.sendButtonLocation == SendButtonLocation.inside)
_animateSendButton(context), _animateSendButton(context),
], ],
@@ -619,7 +655,13 @@ class MessageInputState extends State<MessageInput> {
Timer _debounce; Timer _debounce;
String _previousValue;
void _onChanged(BuildContext context, String s) { void _onChanged(BuildContext context, String s) {
if (s == _previousValue) {
return;
}
_previousValue = s;
if (_debounce?.isActive == true) _debounce.cancel(); if (_debounce?.isActive == true) _debounce.cancel();
_debounce = Timer( _debounce = Timer(
const Duration(milliseconds: 350), const Duration(milliseconds: 350),
@@ -631,7 +673,11 @@ class MessageInputState extends State<MessageInput> {
setState(() { setState(() {
_messageIsPresent = s.trim().isNotEmpty; _messageIsPresent = s.trim().isNotEmpty;
_actionsShrunk = s.trim().isNotEmpty; _actionsShrunk = s.trim().isNotEmpty &&
((widget.actions?.length ?? 0) +
(widget.showCommandsButton ? 1 : 0) +
(widget.disableAttachments ? 0 : 1) >
1);
}); });
_commandsOverlay?.remove(); _commandsOverlay?.remove();
@@ -1684,13 +1730,19 @@ class MessageInputState extends State<MessageInput> {
} }
Widget _buildCommandButton() { Widget _buildCommandButton() {
final s = textEditingController.text.trim();
return IconButton( return IconButton(
icon: StreamSvgIcon.lightning( icon: StreamSvgIcon.lightning(
color: _commandsOverlay != null color: s.isNotEmpty
? StreamChatTheme.of(context).messageInputTheme.actionButtonColor ? StreamChatTheme.of(context).colorTheme.greyGainsboro
: StreamChatTheme.of(context) : (_commandsOverlay != null
.messageInputTheme ? StreamChatTheme.of(context)
.actionButtonIdleColor, .messageInputTheme
.actionButtonColor
: StreamChatTheme.of(context)
.messageInputTheme
.actionButtonIdleColor),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tightFor( constraints: BoxConstraints.tightFor(
@@ -1711,7 +1763,9 @@ class MessageInputState extends State<MessageInput> {
if (_commandsOverlay == null) { if (_commandsOverlay == null) {
setState(() { setState(() {
_commandsOverlay = _buildCommandsOverlayEntry(); _commandsOverlay = _buildCommandsOverlayEntry();
Overlay.of(context).insert(_commandsOverlay); if (_commandsOverlay != null) {
Overlay.of(context).insert(_commandsOverlay);
}
}); });
} else { } else {
setState(() { setState(() {
@@ -1965,24 +2019,31 @@ class MessageInputState extends State<MessageInput> {
} }
Widget _buildIdleSendButton(BuildContext context) { Widget _buildIdleSendButton(BuildContext context) {
return StreamSvgIcon( return Padding(
assetName: _getIdleSendIcon(), padding: const EdgeInsets.all(8.0),
color: StreamChatTheme.of(context).messageInputTheme.sendButtonIdleColor, child: StreamSvgIcon(
assetName: _getIdleSendIcon(),
color:
StreamChatTheme.of(context).messageInputTheme.sendButtonIdleColor,
),
); );
} }
Widget _buildSendButton(BuildContext context) { Widget _buildSendButton(BuildContext context) {
return IconButton( return Padding(
onPressed: sendMessage, padding: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(0), child: IconButton(
splashRadius: 24, onPressed: sendMessage,
constraints: BoxConstraints.tightFor( padding: const EdgeInsets.all(0),
height: 24, splashRadius: 24,
width: 24, constraints: BoxConstraints.tightFor(
), height: 24,
icon: StreamSvgIcon( width: 24,
assetName: _getSendIcon(), ),
color: StreamChatTheme.of(context).messageInputTheme.sendButtonColor, icon: StreamSvgIcon(
assetName: _getSendIcon(),
color: StreamChatTheme.of(context).messageInputTheme.sendButtonColor,
),
), ),
); );
} }