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