lint changes message

This commit is contained in:
Deven Joshi
2021-05-05 15:33:42 +05:30
parent 5ad74b8f2b
commit bda64e8764
2 changed files with 899 additions and 878 deletions
@@ -8,37 +8,11 @@ import 'package:stream_chat_flutter/src/reaction_picker.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/utils.dart'; import 'package:stream_chat_flutter/src/utils.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'extension.dart'; /// Constructs a modal with actions for a message
import 'message_input.dart';
import 'message_widget.dart';
import 'stream_chat.dart';
import 'stream_chat_theme.dart';
class MessageActionsModal extends StatefulWidget { class MessageActionsModal extends StatefulWidget {
final Widget Function(BuildContext, Message)? editMessageInputBuilder; /// Constructor for creating a [MessageActionsModal] widget
final OnMessageTap? onThreadReplyTap;
final OnMessageTap? onReplyTap;
final Message message;
final MessageTheme messageTheme;
final bool showReactions;
final OnMessageTap? onCopyTap;
final bool showDeleteMessage;
final bool showCopyMessage;
final bool showEditMessage;
final bool showResendMessage;
final bool showReplyMessage;
final bool showThreadReplyMessage;
final bool showFlagButton;
final bool reverse;
final ShapeBorder? messageShape;
final ShapeBorder? attachmentShape;
final DisplayWidget showUserAvatar;
final BorderRadius? attachmentBorderRadiusGeometry;
/// List of custom actions
final List<MessageAction> customActions;
const MessageActionsModal({ const MessageActionsModal({
Key? key, Key? key,
required this.message, required this.message,
@@ -63,6 +37,66 @@ class MessageActionsModal extends StatefulWidget {
this.onCopyTap, this.onCopyTap,
}) : super(key: key); }) : super(key: key);
/// Builder for edit message
final Widget Function(BuildContext, Message)? editMessageInputBuilder;
/// Callback for when thread reply is tapped
final OnMessageTap? onThreadReplyTap;
/// Callback for when reply is tapped
final OnMessageTap? onReplyTap;
/// Message in focus for actions
final Message message;
/// [MessageTheme] for message
final MessageTheme messageTheme;
/// Flag for showing reactions
final bool showReactions;
/// Callback when copy is tapped
final OnMessageTap? onCopyTap;
/// Callback when delete is tapped
final bool showDeleteMessage;
/// Flag for showing copy action
final bool showCopyMessage;
/// Flag for showing edit action
final bool showEditMessage;
/// Flag for showing resend action
final bool showResendMessage;
/// Flag for showing reply action
final bool showReplyMessage;
/// Flag for showing thread reply action
final bool showThreadReplyMessage;
/// Flag for showing flag action
final bool showFlagButton;
/// Flag for reversing message
final bool reverse;
/// [ShapeBorder] to apply to the widget
final ShapeBorder? messageShape;
/// [ShapeBorder] to apply to attachment
final ShapeBorder? attachmentShape;
/// Enum for displaying user avatar
final DisplayWidget showUserAvatar;
/// [BorderRadius] for attachment border
final BorderRadius? attachmentBorderRadiusGeometry;
/// List of custom actions
final List<MessageAction> customActions;
@override @override
_MessageActionsModalState createState() => _MessageActionsModalState(); _MessageActionsModalState createState() => _MessageActionsModalState();
} }
@@ -71,9 +105,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
bool _showActions = true; bool _showActions = true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => _showMessageOptionsModal();
return _showMessageOptionsModal();
}
Widget _showMessageOptionsModal() { Widget _showMessageOptionsModal() {
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
@@ -120,10 +152,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
if (_showActions) if (_showActions)
TweenAnimationBuilder<double>( TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1), tween: Tween(begin: 0, end: 1),
duration: Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutBack, curve: Curves.easeInOutBack,
builder: (context, val, snapshot) { builder: (context, val, snapshot) => Transform.scale(
return Transform.scale(
scale: val, scale: val,
child: Center( child: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
@@ -151,15 +182,16 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
message: widget.message, message: widget.message,
), ),
), ),
SizedBox(height: 8), const SizedBox(height: 8),
IgnorePointer( IgnorePointer(
child: MessageWidget( child: MessageWidget(
key: Key('MessageWidget'), key: const Key('MessageWidget'),
reverse: widget.reverse, reverse: widget.reverse,
attachmentBorderRadiusGeometry: attachmentBorderRadiusGeometry:
widget.attachmentBorderRadiusGeometry, widget.attachmentBorderRadiusGeometry,
message: widget.message.copyWith( message: widget.message.copyWith(
text: widget.message.text!.length > 200 text: widget.message.text!.length > 200
// ignore: lines_longer_than_80_chars
? '${widget.message.text!.substring(0, 200)}...' ? '${widget.message.text!.substring(0, 200)}...'
: widget.message.text, : widget.message.text,
), ),
@@ -176,9 +208,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
textPadding: EdgeInsets.symmetric( textPadding: EdgeInsets.symmetric(
vertical: 8, vertical: 8,
horizontal: widget.message.text!.isOnlyEmoji horizontal:
? 0 widget.message.text!.isOnlyEmoji ? 0 : 16.0,
: 16.0,
), ),
showReactionPickerIndicator: showReactionPickerIndicator:
widget.showReactions && widget.showReactions &&
@@ -189,7 +220,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
attachmentShape: widget.attachmentShape, attachmentShape: widget.attachmentShape,
), ),
), ),
SizedBox(height: 8), const SizedBox(height: 8),
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: widget.reverse ? 0 : 40, left: widget.reverse ? 0 : 40,
@@ -227,12 +258,11 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
_buildFlagButton(context), _buildFlagButton(context),
if (widget.showDeleteMessage) if (widget.showDeleteMessage)
_buildDeleteButton(context), _buildDeleteButton(context),
...widget.customActions.map((action) { ...widget.customActions
return _buildCustomAction( .map((action) => _buildCustomAction(
context, context,
action, action,
); ))
})
].insertBetween( ].insertBetween(
Container( Container(
height: 1, height: 1,
@@ -250,8 +280,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
), ),
), ),
); ),
},
), ),
], ],
), ),
@@ -261,8 +290,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
InkWell _buildCustomAction( InkWell _buildCustomAction(
BuildContext context, BuildContext context,
MessageAction messageAction, MessageAction messageAction,
) { ) =>
return InkWell( InkWell(
onTap: () { onTap: () {
messageAction.onTap?.call(widget.message); messageAction.onTap?.call(widget.message);
}, },
@@ -270,14 +299,13 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
child: Row( child: Row(
children: [ children: [
messageAction.leading ?? Offstage(), messageAction.leading ?? const Offstage(),
const SizedBox(width: 16), const SizedBox(width: 16),
messageAction.title ?? Offstage(), messageAction.title ?? const Offstage(),
], ],
), ),
), ),
); );
}
void _showFlagDialog() async { void _showFlagDialog() async {
final client = StreamChat.of(context).client; final client = StreamChat.of(context).client;
@@ -290,6 +318,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
size: 24, size: 24,
), ),
question: question:
// ignore: lines_longer_than_80_chars
'Do you want to send a copy of this message to a\nmoderator for further investigation?', 'Do you want to send a copy of this message to a\nmoderator for further investigation?',
okText: 'FLAG', okText: 'FLAG',
cancelText: 'CANCEL', cancelText: 'CANCEL',
@@ -371,8 +400,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
); );
} }
Widget _buildReplyButton(BuildContext context) { Widget _buildReplyButton(BuildContext context) => InkWell(
return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
if (widget.onReplyTap != null) { if (widget.onReplyTap != null) {
@@ -395,10 +423,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
), ),
); );
}
Widget _buildFlagButton(BuildContext context) { Widget _buildFlagButton(BuildContext context) => InkWell(
return InkWell(
onTap: _showFlagDialog, onTap: _showFlagDialog,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
@@ -416,7 +442,6 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
), ),
); );
}
Widget _buildDeleteButton(BuildContext context) { Widget _buildDeleteButton(BuildContext context) {
final isDeleteFailed = final isDeleteFailed =
@@ -444,8 +469,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
); );
} }
Widget _buildCopyButton(BuildContext context) { Widget _buildCopyButton(BuildContext context) => InkWell(
return InkWell(
onTap: () async { onTap: () async {
widget.onCopyTap?.call(widget.message); widget.onCopyTap?.call(widget.message);
Navigator.pop(context); Navigator.pop(context);
@@ -467,10 +491,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
), ),
); );
}
Widget _buildEditMessage(BuildContext context) { Widget _buildEditMessage(BuildContext context) => InkWell(
return InkWell(
onTap: () async { onTap: () async {
Navigator.pop(context); Navigator.pop(context);
_showEditBottomSheet(context); _showEditBottomSheet(context);
@@ -491,7 +513,6 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
), ),
); );
}
Widget _buildResendMessage(BuildContext context) { Widget _buildResendMessage(BuildContext context) {
final isUpdateFailed = final isUpdateFailed =
@@ -533,14 +554,13 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
isScrollControlled: true, isScrollControlled: true,
backgroundColor: backgroundColor:
StreamChatTheme.of(context).messageInputTheme.inputBackground, StreamChatTheme.of(context).messageInputTheme.inputBackground,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),
topRight: Radius.circular(16), topRight: Radius.circular(16),
), ),
), ),
builder: (context) { builder: (context) => StreamChannel(
return StreamChannel(
channel: channel, channel: channel,
child: Flex( child: Flex(
direction: Axis.vertical, direction: Axis.vertical,
@@ -555,12 +575,11 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: StreamSvgIcon.edit( child: StreamSvgIcon.edit(
color: StreamChatTheme.of(context) color:
.colorTheme StreamChatTheme.of(context).colorTheme.greyGainsboro,
.greyGainsboro,
), ),
), ),
Text( const Text(
'Edit Message', 'Edit Message',
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
@@ -585,13 +604,11 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
], ],
), ),
); ),
},
); );
} }
Widget _buildThreadReplyButton(BuildContext context) { Widget _buildThreadReplyButton(BuildContext context) => InkWell(
return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
if (widget.onThreadReplyTap != null) { if (widget.onThreadReplyTap != null) {
@@ -615,4 +632,3 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
), ),
); );
} }
}
@@ -14,19 +14,18 @@ import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.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/quoted_message_widget.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/video_service.dart'; import 'package:stream_chat_flutter/src/video_service.dart';
import 'package:stream_chat_flutter/src/video_thumbnail_image.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';
import 'package:video_compress/video_compress.dart'; import 'package:video_compress/video_compress.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import '../stream_chat_flutter.dart'; /// Builder for attachment thumbnails
import 'attachment/attachment.dart';
import 'extension.dart';
import 'quoted_message_widget.dart';
import 'video_thumbnail_image.dart';
typedef AttachmentThumbnailBuilder = Widget Function( typedef AttachmentThumbnailBuilder = Widget Function(
BuildContext, BuildContext,
Attachment, Attachment,
@@ -39,16 +38,30 @@ typedef MentionTileBuilder = Widget Function(
Member member, Member member,
); );
/// Location for actions on the [MessageInput]
enum ActionsLocation { enum ActionsLocation {
/// Align to left
left, left,
/// Align to right
right, right,
/// Align to left but inside the [TextField]
leftInside, leftInside,
/// Align to right but inside the [TextField]
rightInside, rightInside,
} }
/// Default attachments for widget
enum DefaultAttachmentTypes { enum DefaultAttachmentTypes {
/// Image Attachment
image, image,
/// Video Attachment
video, video,
/// File Attachment
file, file,
} }
@@ -103,9 +116,11 @@ const _kMaxAttachmentSize = 20971520; // 20MB in Bytes
/// } /// }
/// ``` /// ```
/// ///
/// You usually put this widget in the same page of a [MessageListView] as the bottom widget. /// You usually put this widget in the same page of a [MessageListView]
/// as the bottom widget.
/// ///
/// The widget renders the ui based on the first ancestor of type [StreamChatTheme]. /// The widget renders the ui based on the first ancestor of
/// type [StreamChatTheme].
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class MessageInput extends StatefulWidget { class MessageInput extends StatefulWidget {
/// Instantiate a new MessageInput /// Instantiate a new MessageInput
@@ -213,6 +228,7 @@ class MessageInput extends StatefulWidget {
if (messageInputState == null) { if (messageInputState == null) {
throw Exception( throw Exception(
// ignore: lines_longer_than_80_chars
'You must have a MessageInput widget as ancestor of your widget tree'); 'You must have a MessageInput widget as ancestor of your widget tree');
} }
@@ -220,6 +236,7 @@ class MessageInput extends StatefulWidget {
} }
} }
/// State of [MessageInput]
class MessageInputState extends State<MessageInput> { class MessageInputState extends State<MessageInput> {
final _attachments = <String, Attachment>{}; final _attachments = <String, Attachment>{};
final List<User> _mentionedUsers = []; final List<User> _mentionedUsers = [];
@@ -313,7 +330,7 @@ class MessageInputState extends State<MessageInput> {
.greyGainsboro, .greyGainsboro,
), ),
), ),
Text( const Text(
'Reply to Message', 'Reply to Message',
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
@@ -353,23 +370,22 @@ class MessageInputState extends State<MessageInput> {
return child; return child;
} }
Flex _buildTextField(BuildContext context) { Flex _buildTextField(BuildContext context) => Flex(
return Flex(
direction: Axis.horizontal, direction: Axis.horizontal,
children: <Widget>[ children: <Widget>[
if (!_commandEnabled && widget.actionsLocation == ActionsLocation.left) if (!_commandEnabled &&
widget.actionsLocation == ActionsLocation.left)
_buildExpandActionsButton(), _buildExpandActionsButton(),
_buildTextInput(context), _buildTextInput(context),
if (!_commandEnabled && widget.actionsLocation == ActionsLocation.right) if (!_commandEnabled &&
widget.actionsLocation == ActionsLocation.right)
_buildExpandActionsButton(), _buildExpandActionsButton(),
if (widget.sendButtonLocation == SendButtonLocation.outside) if (widget.sendButtonLocation == SendButtonLocation.outside)
_animateSendButton(context), _animateSendButton(context),
], ],
); );
}
Widget _buildDmCheckbox() { Widget _buildDmCheckbox() => Row(
return Row(
children: [ children: [
Container( Container(
height: 16, height: 16,
@@ -399,8 +415,8 @@ class MessageInputState extends State<MessageInput> {
}); });
}, },
child: AnimatedCrossFade( child: AnimatedCrossFade(
duration: Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
reverseDuration: Duration(milliseconds: 300), reverseDuration: const Duration(milliseconds: 300),
crossFadeState: _sendAsDm crossFadeState: _sendAsDm
? CrossFadeState.showFirst ? CrossFadeState.showFirst
: CrossFadeState.showSecond, : CrossFadeState.showSecond,
@@ -408,7 +424,7 @@ class MessageInputState extends State<MessageInput> {
size: 16, size: 16,
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
), ),
secondChild: SizedBox( secondChild: const SizedBox(
height: 16, height: 16,
width: 16, width: 16,
), ),
@@ -431,7 +447,6 @@ class MessageInputState extends State<MessageInput> {
), ),
], ],
); );
}
Widget _animateSendButton(BuildContext context) { Widget _animateSendButton(BuildContext context) {
final sendButton = widget.activeSendButton != null final sendButton = widget.activeSendButton != null
@@ -452,8 +467,7 @@ class MessageInputState extends State<MessageInput> {
); );
} }
Widget _buildExpandActionsButton() { Widget _buildExpandActionsButton() => Padding(
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
child: AnimatedCrossFade( child: AnimatedCrossFade(
crossFadeState: _actionsShrunk crossFadeState: _actionsShrunk
@@ -473,7 +487,7 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
), ),
@@ -497,11 +511,10 @@ class MessageInputState extends State<MessageInput> {
].insertBetween(const SizedBox(width: 8)), ].insertBetween(const SizedBox(width: 8)),
), ),
), ),
duration: Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
alignment: Alignment.center, alignment: Alignment.center,
), ),
); );
}
Expanded _buildTextInput(BuildContext context) { Expanded _buildTextInput(BuildContext context) {
final theme = StreamChatTheme.of(context); final theme = StreamChatTheme.of(context);
@@ -539,7 +552,7 @@ class MessageInputState extends State<MessageInput> {
LimitedBox( LimitedBox(
maxHeight: widget.maxHeight, maxHeight: widget.maxHeight,
child: TextField( child: TextField(
key: Key('messageInputText'), key: const Key('messageInputText'),
enabled: _inputEnabled, enabled: _inputEnabled,
maxLines: null, maxLines: null,
onSubmitted: (_) => sendMessage(), onSubmitted: (_) => sendMessage(),
@@ -571,27 +584,27 @@ class MessageInputState extends State<MessageInput> {
hintStyle: theme.messageInputTheme.inputTextStyle!.copyWith( hintStyle: theme.messageInputTheme.inputTextStyle!.copyWith(
color: theme.colorTheme.grey, color: theme.colorTheme.grey,
), ),
border: OutlineInputBorder( border: const OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.transparent, color: Colors.transparent,
), ),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.transparent, color: Colors.transparent,
), ),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.transparent, color: Colors.transparent,
), ),
), ),
errorBorder: OutlineInputBorder( errorBorder: const OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.transparent, color: Colors.transparent,
), ),
), ),
disabledBorder: OutlineInputBorder( disabledBorder: const OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.transparent, color: Colors.transparent,
), ),
@@ -604,7 +617,7 @@ class MessageInputState extends State<MessageInput> {
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Container( child: Container(
constraints: BoxConstraints.tight(Size(64, 24)), constraints: BoxConstraints.tight(const Size(64, 24)),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
color: theme.colorTheme.accentBlue, color: theme.colorTheme.accentBlue,
@@ -640,8 +653,8 @@ class MessageInputState extends State<MessageInput> {
], ],
) )
: null), : null),
suffixIconConstraints: BoxConstraints.tightFor(height: 40), suffixIconConstraints: const BoxConstraints.tightFor(height: 40),
prefixIconConstraints: BoxConstraints.tightFor(height: 40), prefixIconConstraints: const BoxConstraints.tightFor(height: 40),
suffixIcon: Row( suffixIcon: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -652,7 +665,7 @@ class MessageInputState extends State<MessageInput> {
icon: StreamSvgIcon.closeSmall(), icon: StreamSvgIcon.closeSmall(),
splashRadius: 24, splashRadius: 24,
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
), ),
@@ -809,6 +822,7 @@ class MessageInputState extends State<MessageInput> {
return null; return null;
} }
// ignore: cast_nullable_to_non_nullable
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size; final size = renderBox.size;
@@ -819,7 +833,7 @@ class MessageInputState extends State<MessageInput> {
right: 0, right: 0,
child: TweenAnimationBuilder<double>( child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1), tween: Tween(begin: 0, end: 1),
duration: Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutExpo, curve: Curves.easeInOutExpo,
builder: (context, val, wid) => Transform.scale( builder: (context, val, wid) => Transform.scale(
scale: val, scale: val,
@@ -833,8 +847,8 @@ class MessageInputState extends State<MessageInput> {
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Container( child: Container(
constraints: constraints: BoxConstraints.loose(
BoxConstraints.loose(Size.fromHeight(400)), const Size.fromHeight(400)),
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.colorTheme .colorTheme
@@ -907,8 +921,11 @@ class MessageInputState extends State<MessageInput> {
.textTheme .textTheme
.body .body
.copyWith( .copyWith(
// ignore: lines_longer_than_80_chars
color: StreamChatTheme.of( color: StreamChatTheme.of(
// ignore: lines_longer_than_80_chars
context) context)
// ignore: lines_longer_than_80_chars
.colorTheme .colorTheme
.grey, .grey,
), ),
@@ -932,9 +949,8 @@ class MessageInputState extends State<MessageInput> {
} }
Widget _buildFilePickerSection() { Widget _buildFilePickerSection() {
final _attachmentContainsFile = _attachments.values.any((it) { final _attachmentContainsFile =
return it.type == 'file'; _attachments.values.any((it) => it.type == 'file');
});
Color _getIconColor(int index) { Color _getIconColor(int index) {
switch (index) { switch (index) {
@@ -973,7 +989,8 @@ class MessageInputState extends State<MessageInput> {
} }
return AnimatedContainer( return AnimatedContainer(
duration: _animateContainer ? Duration(milliseconds: 300) : Duration.zero, duration:
_animateContainer ? const Duration(milliseconds: 300) : Duration.zero,
height: _openFilePickerSection ? _filePickerSize : 0, height: _openFilePickerSection ? _filePickerSize : 0,
child: Material( child: Material(
color: StreamChatTheme.of(context).colorTheme.whiteSmoke, color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
@@ -1095,7 +1112,7 @@ class MessageInputState extends State<MessageInput> {
void _addAttachment(AssetEntity medium) async { void _addAttachment(AssetEntity medium) async {
final mediaFile = await medium.originFile.timeout( final mediaFile = await medium.originFile.timeout(
Duration(seconds: 5), const Duration(seconds: 5),
onTimeout: () => medium.originFile, onTimeout: () => medium.originFile,
); );
@@ -1116,13 +1133,14 @@ class MessageInputState extends State<MessageInput> {
if (mediaInfo.filesize! > _kMaxAttachmentSize) { if (mediaInfo.filesize! > _kMaxAttachmentSize) {
_showErrorAlert( _showErrorAlert(
// ignore: lines_longer_than_80_chars
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
); );
return; return;
} }
file = AttachmentFile( file = AttachmentFile(
name: file.name, name: file.name,
size: mediaInfo.filesize!, size: mediaInfo.filesize,
bytes: await mediaInfo.file?.readAsBytes(), bytes: await mediaInfo.file?.readAsBytes(),
path: mediaInfo.path, path: mediaInfo.path,
); );
@@ -1234,33 +1252,35 @@ class MessageInputState extends State<MessageInput> {
.then((res) => res.members); .then((res) => res.members);
} }
final members = StreamChannel.of(context).channel.state?.members.where((m) { final members = StreamChannel.of(context)
return m.user?.name.toLowerCase().contains(query) == true; .channel
}).toList() ?? .state
?.members
.where((m) => m.user?.name.toLowerCase().contains(query) == true)
.toList() ??
[]; [];
if (members.isEmpty) { if (members.isEmpty) {
return null; return null;
} }
// ignore: cast_nullable_to_non_nullable
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size; final size = renderBox.size;
return OverlayEntry( return OverlayEntry(
builder: (context) { builder: (context) => Positioned(
return Positioned(
bottom: size.height + MediaQuery.of(context).viewInsets.bottom, bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0, left: 0,
right: 0, right: 0,
child: TweenAnimationBuilder<double>( child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1), tween: Tween(begin: 0, end: 1),
duration: Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutExpo, curve: Curves.easeInOutExpo,
builder: (context, val, wid) { builder: (context, val, wid) => Transform.scale(
return Transform.scale(
scale: val, scale: val,
child: Card( child: Card(
margin: EdgeInsets.all(8), margin: const EdgeInsets.all(8),
elevation: 2, elevation: 2,
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@@ -1268,29 +1288,26 @@ class MessageInputState extends State<MessageInput> {
), ),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Container( child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(240)), constraints: BoxConstraints.loose(const Size.fromHeight(240)),
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
), ),
child: FutureBuilder<List<Member>>( child: FutureBuilder<List<Member>>(
future: queryMembers ?? Future.value(members), future: queryMembers ?? Future.value(members),
initialData: members, initialData: members,
builder: (context, snapshot) { builder: (context, snapshot) => ListView(
return ListView(
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
SizedBox( const SizedBox(
height: 8, height: 8,
), ),
...snapshot.data! ...snapshot.data!
.where((it) => it.user != null) .where((it) => it.user != null)
.map( .map(
(m) { (m) => Material(
return Material( color:
color: StreamChatTheme.of(context) StreamChatTheme.of(context).colorTheme.white,
.colorTheme
.white,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (m.user != null) { if (m.user != null) {
@@ -1303,8 +1320,8 @@ class MessageInputState extends State<MessageInput> {
textEditingController.value = textEditingController.value =
TextEditingValue( TextEditingValue(
text: rejoin + text: rejoin +
textEditingController.text textEditingController.text.substring(
.substring(textEditingController textEditingController
.selection.start), .selection.start),
selection: TextSelection.collapsed( selection: TextSelection.collapsed(
offset: rejoin.length, offset: rejoin.length,
@@ -1315,27 +1332,23 @@ class MessageInputState extends State<MessageInput> {
_mentionsOverlay = null; _mentionsOverlay = null;
}, },
child: widget.mentionsTileBuilder != null child: widget.mentionsTileBuilder != null
? widget.mentionsTileBuilder!( ? widget.mentionsTileBuilder!(context, m)
context, m)
: MentionTile(m), : MentionTile(m),
), ),
); ),
}, )
).toList(), .toList(),
SizedBox( const SizedBox(
height: 8, height: 8,
), ),
], ],
);
},
), ),
), ),
), ),
);
},
), ),
); ),
}, ),
),
); );
} }
@@ -1358,16 +1371,17 @@ class MessageInputState extends State<MessageInput> {
return null; return null;
} }
// ignore: cast_nullable_to_non_nullable
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size; final size = renderBox.size;
return OverlayEntry(builder: (context) { return OverlayEntry(
return Positioned( builder: (context) => Positioned(
bottom: size.height + MediaQuery.of(context).viewInsets.bottom, bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0, left: 0,
right: 0, right: 0,
child: Card( child: Card(
margin: EdgeInsets.all(8), margin: const EdgeInsets.all(8),
elevation: 2, elevation: 2,
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@@ -1375,9 +1389,9 @@ class MessageInputState extends State<MessageInput> {
), ),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Container( child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(200)), constraints: BoxConstraints.loose(const Size.fromHeight(200)),
decoration: BoxDecoration( decoration: BoxDecoration(
boxShadow: [ boxShadow: const [
BoxShadow( BoxShadow(
spreadRadius: -8, spreadRadius: -8,
blurRadius: 5, blurRadius: 5,
@@ -1397,7 +1411,8 @@ class MessageInputState extends State<MessageInput> {
child: Row( child: Row(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 8), padding:
const EdgeInsets.symmetric(horizontal: 8),
child: StreamSvgIcon.smile( child: StreamSvgIcon.smile(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.colorTheme .colorTheme
@@ -1423,7 +1438,9 @@ class MessageInputState extends State<MessageInput> {
final emoji = emojis.elementAt(i - 1)!; final emoji = emojis.elementAt(i - 1)!;
return ListTile( return ListTile(
title: SubstringHighlight( title: SubstringHighlight(
text: "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", text:
// ignore: lines_longer_than_80_chars
"${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
term: query, term: query,
textStyleHighlight: textStyleHighlight:
Theme.of(context).textTheme.headline6!.copyWith( Theme.of(context).textTheme.headline6!.copyWith(
@@ -1442,8 +1459,7 @@ class MessageInputState extends State<MessageInput> {
}), }),
), ),
), ),
); ));
});
} }
void _chooseEmoji(List<String> splits, Emoji emoji) { void _chooseEmoji(List<String> splits, Emoji emoji) {
@@ -1474,7 +1490,7 @@ class MessageInputState extends State<MessageInput> {
} }
Widget _buildReplyToMessage() { Widget _buildReplyToMessage() {
if (!_hasQuotedMessage) return Offstage(); if (!_hasQuotedMessage) return const Offstage();
final containsUrl = widget.quotedMessage!.attachments final containsUrl = widget.quotedMessage!.attachments
.any((element) => element.ogScrapeUrl != null) == .any((element) => element.ogScrapeUrl != null) ==
true; true;
@@ -1492,7 +1508,7 @@ class MessageInputState extends State<MessageInput> {
} }
Widget _buildAttachments() { Widget _buildAttachments() {
if (_attachments.isEmpty) return Offstage(); if (_attachments.isEmpty) return const Offstage();
final fileAttachments = _attachments.values final fileAttachments = _attachments.values
.where((it) => it.type == 'file') .where((it) => it.type == 'file')
.toList(growable: false); .toList(growable: false);
@@ -1546,7 +1562,7 @@ class MessageInputState extends State<MessageInput> {
children: <Widget>[ children: <Widget>[
AspectRatio( AspectRatio(
aspectRatio: 1, aspectRatio: 1,
child: Container( child: SizedBox(
height: 104, height: 104,
width: 104, width: 104,
child: _buildAttachment(attachment), child: _buildAttachment(attachment),
@@ -1569,8 +1585,7 @@ class MessageInputState extends State<MessageInput> {
); );
} }
Widget _buildRemoveButton(Attachment attachment) { Widget _buildRemoveButton(Attachment attachment) => SizedBox(
return Container(
height: 24, height: 24,
width: 24, width: 24,
child: RawMaterialButton( child: RawMaterialButton(
@@ -1584,7 +1599,8 @@ class MessageInputState extends State<MessageInput> {
onPressed: () { onPressed: () {
setState(() => _attachments.remove(attachment.id)); setState(() => _attachments.remove(attachment.id));
}, },
fillColor: StreamChatTheme.of(context).colorTheme.black.withOpacity(.5), fillColor:
StreamChatTheme.of(context).colorTheme.black.withOpacity(.5),
child: Center( child: Center(
child: StreamSvgIcon.close( child: StreamSvgIcon.close(
size: 24, size: 24,
@@ -1593,7 +1609,6 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
); );
}
Widget _buildAttachment(Attachment attachment) { Widget _buildAttachment(Attachment attachment) {
if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) == if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) ==
@@ -1611,24 +1626,20 @@ class MessageInputState extends State<MessageInput> {
? Image.memory( ? Image.memory(
attachment.file!.bytes!, attachment.file!.bytes!,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, _, __) { errorBuilder: (context, _, __) => Image.asset(
return Image.asset(
'images/placeholder.png', 'images/placeholder.png',
package: 'stream_chat_flutter', package: 'stream_chat_flutter',
); ),
},
) )
: CachedNetworkImage( : CachedNetworkImage(
imageUrl: attachment.imageUrl ?? imageUrl: attachment.imageUrl ??
attachment.assetUrl ?? attachment.assetUrl ??
attachment.thumbUrl!, attachment.thumbUrl!,
fit: BoxFit.cover, fit: BoxFit.cover,
errorWidget: (_, obj, trace) { errorWidget: (_, obj, trace) =>
return getFileTypeImage( getFileTypeImage(attachment.extraData['other'] as String?),
attachment.extraData['other'] as String?); progressIndicatorBuilder: (context, _, progress) =>
}, Shimmer.fromColors(
progressIndicatorBuilder: (context, _, progress) {
return Shimmer.fromColors(
baseColor: baseColor:
StreamChatTheme.of(context).colorTheme.greyGainsboro, StreamChatTheme.of(context).colorTheme.greyGainsboro,
highlightColor: highlightColor:
@@ -1638,8 +1649,7 @@ class MessageInputState extends State<MessageInput> {
fit: BoxFit.cover, fit: BoxFit.cover,
package: 'stream_chat_flutter', package: 'stream_chat_flutter',
), ),
); ),
},
); );
case 'video': case 'video':
return Stack( return Stack(
@@ -1663,7 +1673,7 @@ class MessageInputState extends State<MessageInput> {
default: default:
return Container( return Container(
color: Colors.black26, color: Colors.black26,
child: Icon(Icons.insert_drive_file), child: const Icon(Icons.insert_drive_file),
); );
} }
} }
@@ -1684,7 +1694,7 @@ class MessageInputState extends State<MessageInput> {
.actionButtonIdleColor), .actionButtonIdleColor),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
), ),
@@ -1696,7 +1706,7 @@ class MessageInputState extends State<MessageInput> {
_openFilePickerSection = false; _openFilePickerSection = false;
_filePickerSize = _kMinMediaPickerSize; _filePickerSize = _kMinMediaPickerSize;
}); });
await Future.delayed(Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
} }
if (_commandsOverlay == null) { if (_commandsOverlay == null) {
@@ -1716,8 +1726,7 @@ class MessageInputState extends State<MessageInput> {
); );
} }
Widget _buildAttachmentButton() { Widget _buildAttachmentButton() => IconButton(
return IconButton(
icon: StreamSvgIcon.attach( icon: StreamSvgIcon.attach(
color: _openFilePickerSection color: _openFilePickerSection
? StreamChatTheme.of(context).messageInputTheme.actionButtonColor ? StreamChatTheme.of(context).messageInputTheme.actionButtonColor
@@ -1726,7 +1735,7 @@ class MessageInputState extends State<MessageInput> {
.actionButtonIdleColor, .actionButtonIdleColor,
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
), ),
@@ -1750,9 +1759,9 @@ class MessageInputState extends State<MessageInput> {
} }
}, },
); );
}
/// Show the attachment modal, making the user choose where to pick a media from /// Show the attachment modal, making the user choose where to
/// pick a media from
void showAttachmentModal() { void showAttachmentModal() {
if (_focusNode.hasFocus) { if (_focusNode.hasFocus) {
_focusNode.unfocus(); _focusNode.unfocus();
@@ -1765,7 +1774,7 @@ class MessageInputState extends State<MessageInput> {
} else { } else {
showModalBottomSheet( showModalBottomSheet(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(32), topLeft: Radius.circular(32),
topRight: Radius.circular(32), topRight: Radius.circular(32),
@@ -1773,11 +1782,10 @@ class MessageInputState extends State<MessageInput> {
), ),
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
builder: (_) { builder: (_) => Column(
return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
ListTile( const ListTile(
title: Text( title: Text(
'Add a file', 'Add a file',
style: TextStyle( style: TextStyle(
@@ -1786,16 +1794,16 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
ListTile( ListTile(
leading: Icon(Icons.image), leading: const Icon(Icons.image),
title: Text('Upload a photo'), title: const Text('Upload a photo'),
onTap: () { onTap: () {
pickFile(DefaultAttachmentTypes.image); pickFile(DefaultAttachmentTypes.image);
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
ListTile( ListTile(
leading: Icon(Icons.video_library), leading: const Icon(Icons.video_library),
title: Text('Upload a video'), title: const Text('Upload a video'),
onTap: () { onTap: () {
pickFile(DefaultAttachmentTypes.video); pickFile(DefaultAttachmentTypes.video);
Navigator.pop(context); Navigator.pop(context);
@@ -1803,8 +1811,8 @@ class MessageInputState extends State<MessageInput> {
), ),
if (!kIsWeb) if (!kIsWeb)
ListTile( ListTile(
leading: Icon(Icons.camera_alt), leading: const Icon(Icons.camera_alt),
title: Text('Photo from camera'), title: const Text('Photo from camera'),
onTap: () { onTap: () {
pickFile(DefaultAttachmentTypes.image, true); pickFile(DefaultAttachmentTypes.image, true);
Navigator.pop(context); Navigator.pop(context);
@@ -1812,24 +1820,23 @@ class MessageInputState extends State<MessageInput> {
), ),
if (!kIsWeb) if (!kIsWeb)
ListTile( ListTile(
leading: Icon(Icons.videocam), leading: const Icon(Icons.videocam),
title: Text('Video from camera'), title: const Text('Video from camera'),
onTap: () { onTap: () {
pickFile(DefaultAttachmentTypes.video, true); pickFile(DefaultAttachmentTypes.video, true);
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
ListTile( ListTile(
leading: Icon(Icons.insert_drive_file), leading: const Icon(Icons.insert_drive_file),
title: Text('Upload a file'), title: const Text('Upload a file'),
onTap: () { onTap: () {
pickFile(DefaultAttachmentTypes.file); pickFile(DefaultAttachmentTypes.file);
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
], ],
); ));
});
} }
} }
@@ -1845,6 +1852,7 @@ class MessageInputState extends State<MessageInput> {
/// Pick a file from the device /// Pick a file from the device
/// If [camera] is true then the camera will open /// If [camera] is true then the camera will open
// ignore: avoid_positional_boolean_parameters
void pickFile(DefaultAttachmentTypes fileType, [bool camera = false]) async { void pickFile(DefaultAttachmentTypes fileType, [bool camera = false]) async {
setState(() => _inputEnabled = false); setState(() => _inputEnabled = false);
@@ -1920,13 +1928,14 @@ class MessageInputState extends State<MessageInput> {
if (mediaInfo.filesize! > _kMaxAttachmentSize) { if (mediaInfo.filesize! > _kMaxAttachmentSize) {
_showErrorAlert( _showErrorAlert(
// ignore: lines_longer_than_80_chars
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
); );
return; return;
} }
file = AttachmentFile( file = AttachmentFile(
name: file.name, name: file.name,
size: mediaInfo.filesize!, size: mediaInfo.filesize,
bytes: await mediaInfo.file!.readAsBytes(), bytes: await mediaInfo.file!.readAsBytes(),
path: mediaInfo.path, path: mediaInfo.path,
); );
@@ -1941,18 +1950,17 @@ class MessageInputState extends State<MessageInput> {
_attachments[attachment.id] = attachment; _attachments[attachment.id] = attachment;
setState(() { setState(() {
_attachments.update(attachment.id, (it) { _attachments.update(
return it.copyWith( attachment.id,
(it) => it.copyWith(
file: file, file: file,
extraData: {...it.extraData} extraData: {...it.extraData}
..update('file_size', ((_) => file!.size!)), ..update('file_size', ((_) => file!.size!)),
); ));
});
}); });
} }
Widget _buildIdleSendButton(BuildContext context) { Widget _buildIdleSendButton(BuildContext context) => Padding(
return Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: StreamSvgIcon( child: StreamSvgIcon(
assetName: _getIdleSendIcon(), assetName: _getIdleSendIcon(),
@@ -1960,26 +1968,24 @@ class MessageInputState extends State<MessageInput> {
StreamChatTheme.of(context).messageInputTheme.sendButtonIdleColor, StreamChatTheme.of(context).messageInputTheme.sendButtonIdleColor,
), ),
); );
}
Widget _buildSendButton(BuildContext context) { Widget _buildSendButton(BuildContext context) => Padding(
return Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: IconButton( child: IconButton(
onPressed: sendMessage, onPressed: sendMessage,
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
splashRadius: 24, splashRadius: 24,
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
), ),
icon: StreamSvgIcon( icon: StreamSvgIcon(
assetName: _getSendIcon(), assetName: _getSendIcon(),
color: StreamChatTheme.of(context).messageInputTheme.sendButtonColor, color:
StreamChatTheme.of(context).messageInputTheme.sendButtonColor,
), ),
), ),
); );
}
String _getIdleSendIcon() { String _getIdleSendIcon() {
if (_commandEnabled) { if (_commandEnabled) {
@@ -2009,7 +2015,7 @@ class MessageInputState extends State<MessageInput> {
final shouldUnfocus = _commandEnabled; final shouldUnfocus = _commandEnabled;
if (_commandEnabled) { if (_commandEnabled) {
text = '/${_chosenCommand!.name} ' + text; text = '${'/${_chosenCommand!.name} '}$text';
} }
final attachments = [..._attachments.values]; final attachments = [..._attachments.values];
@@ -2096,30 +2102,29 @@ class MessageInputState extends State<MessageInput> {
showModalBottomSheet( showModalBottomSheet(
backgroundColor: StreamChatTheme.of(context).colorTheme.white, backgroundColor: StreamChatTheme.of(context).colorTheme.white,
context: context, context: context,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),
topRight: Radius.circular(16), topRight: Radius.circular(16),
)), )),
builder: (context) { builder: (context) => Column(
return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
SizedBox( const SizedBox(
height: 26, height: 26,
), ),
StreamSvgIcon.error( StreamSvgIcon.error(
color: StreamChatTheme.of(context).colorTheme.accentRed, color: StreamChatTheme.of(context).colorTheme.accentRed,
size: 24, size: 24,
), ),
SizedBox( const SizedBox(
height: 26, height: 26,
), ),
Text( Text(
'Something went wrong', 'Something went wrong',
style: StreamChatTheme.of(context).textTheme.headlineBold, style: StreamChatTheme.of(context).textTheme.headlineBold,
), ),
SizedBox( const SizedBox(
height: 7, height: 7,
), ),
Padding( Padding(
@@ -2129,7 +2134,7 @@ class MessageInputState extends State<MessageInput> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
SizedBox( const SizedBox(
height: 36, height: 36,
), ),
Container( Container(
@@ -2158,8 +2163,7 @@ class MessageInputState extends State<MessageInput> {
], ],
), ),
], ],
); ),
},
); );
} }
@@ -2196,12 +2200,6 @@ class MessageInputState extends State<MessageInput> {
/// Represents a 2-tuple, or pair. /// Represents a 2-tuple, or pair.
class Tuple2<T1, T2> { class Tuple2<T1, T2> {
/// Returns the first item of the tuple
final T1 item1;
/// Returns the second item of the tuple
final T2 item2;
/// Creates a new tuple value with the specified items. /// Creates a new tuple value with the specified items.
const Tuple2(this.item1, this.item2); const Tuple2(this.item1, this.item2);
@@ -2214,6 +2212,12 @@ class Tuple2<T1, T2> {
return Tuple2<T1, T2>(items[0] as T1, items[1] as T2); return Tuple2<T1, T2>(items[0] as T1, items[1] as T2);
} }
/// Returns the first item of the tuple
final T1 item1;
/// Returns the second item of the tuple
final T2 item2;
/// Returns a tuple with the first item set to the specified value. /// Returns a tuple with the first item set to the specified value.
Tuple2<T1, T2> withItem1(T1 v) => Tuple2<T1, T2>(v, item2); Tuple2<T1, T2> withItem1(T1 v) => Tuple2<T1, T2>(v, item2);
@@ -2243,12 +2247,6 @@ class Tuple2<T1, T2> {
} }
class _PickerWidget extends StatefulWidget { class _PickerWidget extends StatefulWidget {
final int filePickerIndex;
final bool containsFile;
final List<String> selectedMedias;
final void Function(DefaultAttachmentTypes) onAddMoreFilesClick;
final void Function(AssetEntity) onMediaSelected;
const _PickerWidget({ const _PickerWidget({
Key? key, Key? key,
required this.filePickerIndex, required this.filePickerIndex,
@@ -2258,6 +2256,12 @@ class _PickerWidget extends StatefulWidget {
required this.onMediaSelected, required this.onMediaSelected,
}) : super(key: key); }) : super(key: key);
final int filePickerIndex;
final bool containsFile;
final List<String> selectedMedias;
final void Function(DefaultAttachmentTypes) onAddMoreFilesClick;
final void Function(AssetEntity) onMediaSelected;
@override @override
__PickerWidgetState createState() => __PickerWidgetState(); __PickerWidgetState createState() => __PickerWidgetState();
} }
@@ -2274,7 +2278,7 @@ class __PickerWidgetState extends State<_PickerWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.filePickerIndex != 0) { if (widget.filePickerIndex != 0) {
return Offstage(); return const Offstage();
} }
return FutureBuilder<bool>( return FutureBuilder<bool>(
future: requestPermission, future: requestPermission,
@@ -2290,7 +2294,7 @@ class __PickerWidgetState extends State<_PickerWidget> {
widget.onAddMoreFilesClick(DefaultAttachmentTypes.file); widget.onAddMoreFilesClick(DefaultAttachmentTypes.file);
}, },
child: Container( child: Container(
constraints: BoxConstraints.expand(), constraints: const BoxConstraints.expand(),
color: StreamChatTheme.of(context).colorTheme.whiteSmoke, color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
@@ -2326,12 +2330,13 @@ class __PickerWidgetState extends State<_PickerWidget> {
color: StreamChatTheme.of(context).colorTheme.greyGainsboro, color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
), ),
Text( Text(
// ignore: lines_longer_than_80_chars
'Please enable access to your photos \nand videos so you can share them with friends.', 'Please enable access to your photos \nand videos so you can share them with friends.',
style: StreamChatTheme.of(context).textTheme.body.copyWith( style: StreamChatTheme.of(context).textTheme.body.copyWith(
color: StreamChatTheme.of(context).colorTheme.grey), color: StreamChatTheme.of(context).colorTheme.grey),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
SizedBox(height: 6), const SizedBox(height: 6),
Center( Center(
child: Text( child: Text(
'Allow access to your gallery', 'Allow access to your gallery',