migrate ui

This commit is contained in:
Salvatore Giordano
2021-04-30 14:58:50 +02:00
parent 2c02f0f631
commit 34888d0fff
103 changed files with 2622 additions and 2455 deletions
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
@@ -19,6 +20,7 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/video_service.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 '../stream_chat_flutter.dart';
import 'attachment/attachment.dart';
@@ -109,7 +111,7 @@ const _kMaxAttachmentSize = 20971520; // 20MB in Bytes
class MessageInput extends StatefulWidget {
/// Instantiate a new MessageInput
MessageInput({
Key key,
Key? key,
this.onMessageSent,
this.preMessageSending,
this.parentMessage,
@@ -135,20 +137,20 @@ class MessageInput extends StatefulWidget {
}) : super(key: key);
/// Message to edit
final Message editMessage;
final Message? editMessage;
/// Message to start with
final Message initialMessage;
final Message? initialMessage;
/// Function called after sending the message
final void Function(Message) onMessageSent;
final void Function(Message)? onMessageSent;
/// Function called right before sending the message
/// Use this to transform the message
final FutureOr<Message> Function(Message) preMessageSending;
final FutureOr<Message> Function(Message)? preMessageSending;
/// Parent message in case of a thread
final Message parentMessage;
final Message? parentMessage;
/// Maximum Height for the TextField to grow before it starts scrolling
final double maxHeight;
@@ -166,25 +168,25 @@ class MessageInput extends StatefulWidget {
final bool hideSendAsDm;
/// The text controller of the TextField
final TextEditingController textEditingController;
final TextEditingController? textEditingController;
/// List of action widgets
final List<Widget> actions;
final List<Widget>? actions;
/// The location of the custom actions
final ActionsLocation actionsLocation;
/// Map that defines a thumbnail builder for an attachment type
final Map<String, AttachmentThumbnailBuilder> attachmentThumbnailBuilders;
final Map<String, AttachmentThumbnailBuilder>? attachmentThumbnailBuilders;
/// The focus node associated to the TextField
final FocusNode focusNode;
final FocusNode? focusNode;
///
final Message quotedMessage;
final Message? quotedMessage;
///
final VoidCallback onQuotedMessageCleared;
final VoidCallback? onQuotedMessageCleared;
/// The location of the send button
final SendButtonLocation sendButtonLocation;
@@ -193,20 +195,20 @@ class MessageInput extends StatefulWidget {
final bool autofocus;
/// Send button widget in an idle state
final Widget idleSendButton;
final Widget? idleSendButton;
/// Send button widget in an active state
final Widget activeSendButton;
final Widget? activeSendButton;
/// Customize the tile for the mentions overlay
final MentionTileBuilder mentionsTileBuilder;
final MentionTileBuilder? mentionsTileBuilder;
@override
MessageInputState createState() => MessageInputState();
/// Use this method to get the current [StreamChatState] instance
static MessageInputState of(BuildContext context) {
MessageInputState messageInputState;
MessageInputState? messageInputState;
messageInputState = context.findAncestorStateOfType<MessageInputState>();
@@ -224,15 +226,15 @@ class MessageInputState extends State<MessageInput> {
final List<User> _mentionedUsers = [];
final _imagePicker = ImagePicker();
FocusNode _focusNode;
late final FocusNode _focusNode;
bool _inputEnabled = true;
bool _messageIsPresent = false;
bool _animateContainer = true;
bool _commandEnabled = false;
OverlayEntry _commandsOverlay, _mentionsOverlay, _emojiOverlay;
Iterable<String> _emojiNames;
OverlayEntry? _commandsOverlay, _mentionsOverlay, _emojiOverlay;
late Iterable<String> _emojiNames;
Command _chosenCommand;
Command? _chosenCommand;
bool _actionsShrunk = false;
bool _sendAsDm = false;
bool _openFilePickerSection = false;
@@ -242,7 +244,7 @@ class MessageInputState extends State<MessageInput> {
KeyboardVisibilityController();
/// The editing controller passed to the input TextField
TextEditingController textEditingController;
late final TextEditingController textEditingController;
bool get _hasQuotedMessage => widget.quotedMessage != null;
@@ -250,7 +252,8 @@ class MessageInputState extends State<MessageInput> {
void initState() {
super.initState();
_focusNode = widget.focusNode ?? FocusNode();
_emojiNames = Emoji.all().map((e) => e.name);
_emojiNames =
Emoji.all().where((it) => it.name != null).map((e) => e.name!);
if (!kIsWeb) {
_keyboardListener =
@@ -264,7 +267,7 @@ class MessageInputState extends State<MessageInput> {
textEditingController =
widget.textEditingController ?? TextEditingController();
if (widget.editMessage != null || widget.initialMessage != null) {
_parseExistingMessage(widget.editMessage ?? widget.initialMessage);
_parseExistingMessage(widget.editMessage ?? widget.initialMessage!);
}
textEditingController.addListener(() {
@@ -447,7 +450,7 @@ class MessageInputState extends State<MessageInput> {
firstChild: sendButton,
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
duration:
StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration,
StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration!,
alignment: Alignment.center,
);
}
@@ -490,9 +493,9 @@ class MessageInputState extends State<MessageInput> {
widget.editMessage == null &&
StreamChannel.of(context)
.channel
?.config
.config
?.commands
?.isNotEmpty ==
.isNotEmpty ==
true)
_buildCommandButton(),
...widget.actions ?? [],
@@ -571,7 +574,7 @@ class MessageInputState extends State<MessageInput> {
return InputDecoration(
isDense: true,
hintText: _getHint(),
hintStyle: theme.messageInputTheme.inputTextStyle.copyWith(
hintStyle: theme.messageInputTheme.inputTextStyle!.copyWith(
color: theme.colorTheme.grey,
),
border: OutlineInputBorder(
@@ -621,7 +624,7 @@ class MessageInputState extends State<MessageInput> {
size: 16.0,
),
Text(
_chosenCommand?.name?.toUpperCase() ?? '',
_chosenCommand?.name.toUpperCase() ?? '',
style: StreamChatTheme.of(context)
.textTheme
.footnoteBold
@@ -676,9 +679,9 @@ class MessageInputState extends State<MessageInput> {
).merge(passedDecoration);
}
Timer _debounce;
Timer? _debounce;
String _previousValue;
String? _previousValue;
void _onChanged(BuildContext context, String s) {
if (s == _previousValue) {
@@ -686,14 +689,14 @@ class MessageInputState extends State<MessageInput> {
}
_previousValue = s;
if (_debounce?.isActive == true) _debounce.cancel();
if (_debounce?.isActive == true) _debounce!.cancel();
_debounce = Timer(
const Duration(milliseconds: 350),
() {
if (!mounted) {
return;
}
StreamChannel.of(context).channel.keyStroke()?.catchError((e) {});
StreamChannel.of(context).channel.keyStroke().catchError((e) {});
setState(() {
_messageIsPresent = s.trim().isNotEmpty;
@@ -721,7 +724,7 @@ class MessageInputState extends State<MessageInput> {
}
String _getHint() {
if (_commandEnabled && _chosenCommand.name == 'giphy') {
if (_commandEnabled && _chosenCommand!.name == 'giphy') {
return 'Search GIFs';
}
if (_attachments.isNotEmpty) {
@@ -742,7 +745,7 @@ class MessageInputState extends State<MessageInput> {
final textToSelection = textEditingController.text
.substring(0, textEditingController.value.selection.start);
final splits = textToSelection.split(':');
final query = splits[splits.length - 2]?.toLowerCase();
final query = splits[splits.length - 2].toLowerCase();
final emoji = Emoji.byName(query);
if (textToSelection.endsWith(':') && emoji != null) {
@@ -751,7 +754,7 @@ class MessageInputState extends State<MessageInput> {
_emojiOverlay = _buildEmojiOverlay();
if (_emojiOverlay != null) {
Overlay.of(context).insert(_emojiOverlay);
Overlay.of(context)!.insert(_emojiOverlay!);
}
}
}
@@ -767,7 +770,7 @@ class MessageInputState extends State<MessageInput> {
.contains('@')) {
_mentionsOverlay = _buildMentionsOverlayEntry();
if (_mentionsOverlay != null) {
Overlay.of(context).insert(_mentionsOverlay);
Overlay.of(context)!.insert(_mentionsOverlay!);
}
}
}
@@ -778,8 +781,8 @@ class MessageInputState extends State<MessageInput> {
.channel
.config
?.commands
?.where((element) => element.name == s.substring(1))
?.toList() ??
.where((element) => element.name == s.substring(1))
.toList() ??
[];
if (matchedCommandsList.length == 1) {
@@ -789,32 +792,32 @@ class MessageInputState extends State<MessageInput> {
setState(() {
_commandEnabled = true;
});
_commandsOverlay.remove();
_commandsOverlay!.remove();
_commandsOverlay = null;
} else {
_commandsOverlay = _buildCommandsOverlayEntry();
if (_commandsOverlay != null) {
Overlay.of(context).insert(_commandsOverlay);
Overlay.of(context)!.insert(_commandsOverlay!);
}
}
}
}
OverlayEntry _buildCommandsOverlayEntry() {
OverlayEntry? _buildCommandsOverlayEntry() {
final text = textEditingController.text.trimLeft();
final commands = StreamChannel.of(context)
.channel
.config
?.commands
?.where((c) => c.name.contains(text.replaceFirst('/', '')))
?.toList() ??
.where((c) => c.name.contains(text.replaceFirst('/', '')))
.toList() ??
[];
if (commands.isEmpty) {
return null;
}
RenderBox renderBox = context.findRenderObject();
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
return OverlayEntry(builder: (context) {
@@ -951,7 +954,6 @@ class MessageInputState extends State<MessageInput> {
.colorTheme
.black
.withOpacity(0.2));
break;
case 1:
return _attachmentContainsFile
? StreamChatTheme.of(context).colorTheme.accentBlue
@@ -964,17 +966,14 @@ class MessageInputState extends State<MessageInput> {
.colorTheme
.black
.withOpacity(0.2));
break;
case 2:
return _attachmentContainsFile && _attachments.isNotEmpty
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
break;
case 3:
return _attachmentContainsFile && _attachments.isNotEmpty
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
break;
default:
return Colors.black;
}
@@ -1106,10 +1105,10 @@ class MessageInputState extends State<MessageInput> {
}
void _addAttachment(AssetEntity medium) async {
final mediaFile = await medium.originFile.timeout(
final mediaFile = await (medium.originFile.timeout(
Duration(seconds: 5),
onTimeout: () => medium.originFile,
);
) as FutureOr<File>);
var file = AttachmentFile(
path: mediaFile.path,
@@ -1117,11 +1116,12 @@ class MessageInputState extends State<MessageInput> {
bytes: mediaFile.readAsBytesSync(),
);
if (file.size > _kMaxAttachmentSize) {
if (medium?.type == AssetType.video) {
final mediaInfo = await VideoService.compressVideo(file.path);
if (file.size! > _kMaxAttachmentSize) {
if (medium.type == AssetType.video) {
final mediaInfo = await (VideoService.compressVideo(file.path)
as FutureOr<MediaInfo>);
if (mediaInfo.filesize > _kMaxAttachmentSize) {
if (mediaInfo.filesize! > _kMaxAttachmentSize) {
_showErrorAlert(
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
);
@@ -1129,8 +1129,8 @@ class MessageInputState extends State<MessageInput> {
}
file = AttachmentFile(
name: file.name,
size: mediaInfo.filesize,
bytes: await mediaInfo.file.readAsBytes(),
size: mediaInfo.filesize!,
bytes: await mediaInfo.file?.readAsBytes(),
path: mediaInfo.path,
);
} else {
@@ -1159,7 +1159,6 @@ class MessageInputState extends State<MessageInput> {
size: 24.0,
),
);
break;
case 'ban':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1169,7 +1168,6 @@ class MessageInputState extends State<MessageInput> {
color: Colors.white,
),
);
break;
case 'flag':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1179,7 +1177,6 @@ class MessageInputState extends State<MessageInput> {
color: Colors.white,
),
);
break;
case 'imgur':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1190,7 +1187,6 @@ class MessageInputState extends State<MessageInput> {
),
),
);
break;
case 'mute':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1200,7 +1196,6 @@ class MessageInputState extends State<MessageInput> {
color: Colors.white,
),
);
break;
case 'unban':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1210,7 +1205,6 @@ class MessageInputState extends State<MessageInput> {
color: Colors.white,
),
);
break;
case 'unmute':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1220,7 +1214,6 @@ class MessageInputState extends State<MessageInput> {
color: Colors.white,
),
);
break;
default:
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
@@ -1230,17 +1223,16 @@ class MessageInputState extends State<MessageInput> {
color: Colors.white,
),
);
break;
}
}
OverlayEntry _buildMentionsOverlayEntry() {
OverlayEntry? _buildMentionsOverlayEntry() {
final splits = textEditingController.text
.substring(0, textEditingController.value.selection.start)
.split('@');
final query = splits.last.toLowerCase();
Future<List<Member>> queryMembers;
Future<List<Member>>? queryMembers;
if (query.isNotEmpty) {
queryMembers = StreamChannel.of(context)
@@ -1249,16 +1241,16 @@ 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);
})?.toList() ??
final members = StreamChannel.of(context).channel.state?.members.where((m) {
return m.user?.name.toLowerCase().contains(query) == true;
}).toList() ??
[];
if (members.isEmpty) {
return null;
}
RenderBox renderBox = context.findRenderObject();
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
return OverlayEntry(
@@ -1298,7 +1290,9 @@ class MessageInputState extends State<MessageInput> {
SizedBox(
height: 8.0,
),
...snapshot.data.map(
...snapshot.data!
.where((it) => it.user != null)
.map(
(m) {
return Material(
color: StreamChatTheme.of(context)
@@ -1306,9 +1300,11 @@ class MessageInputState extends State<MessageInput> {
.white,
child: InkWell(
onTap: () {
_mentionedUsers.add(m.user);
if (m.user != null) {
_mentionedUsers.add(m.user!);
}
splits[splits.length - 1] = m.user.name;
splits[splits.length - 1] = m.user!.name;
final rejoin = splits.join('@');
textEditingController.value =
@@ -1321,12 +1317,13 @@ class MessageInputState extends State<MessageInput> {
offset: rejoin.length,
),
);
_debounce.cancel();
_debounce!.cancel();
_mentionsOverlay?.remove();
_mentionsOverlay = null;
},
child: widget.mentionsTileBuilder != null
? widget.mentionsTileBuilder(context, m)
? widget.mentionsTileBuilder!(
context, m)
: MentionTile(m),
),
);
@@ -1349,7 +1346,7 @@ class MessageInputState extends State<MessageInput> {
);
}
OverlayEntry _buildEmojiOverlay() {
OverlayEntry? _buildEmojiOverlay() {
final splits = textEditingController.text
.substring(0, textEditingController.value.selection.start)
.split(':');
@@ -1368,7 +1365,7 @@ class MessageInputState extends State<MessageInput> {
return null;
}
RenderBox renderBox = context.findRenderObject();
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
return OverlayEntry(builder: (context) {
@@ -1431,19 +1428,20 @@ class MessageInputState extends State<MessageInput> {
);
}
final emoji = emojis.elementAt(i - 1);
final emoji = emojis.elementAt(i - 1)!;
return ListTile(
title: SubstringHighlight(
text: "${emoji.char} ${emoji.name.replaceAll('_', ' ')}",
text: "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
term: query,
textStyleHighlight:
Theme.of(context).textTheme.headline6.copyWith(
Theme.of(context).textTheme.headline6!.copyWith(
fontSize: 14.5,
fontWeight: FontWeight.bold,
),
textStyle: Theme.of(context).textTheme.headline6.copyWith(
fontSize: 14.5,
),
textStyle:
Theme.of(context).textTheme.headline6!.copyWith(
fontSize: 14.5,
),
),
onTap: () {
_chooseEmoji(splits, emoji);
@@ -1457,7 +1455,7 @@ class MessageInputState extends State<MessageInput> {
}
void _chooseEmoji(List<String> splits, Emoji emoji) {
final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char;
final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char!;
textEditingController.value = TextEditingValue(
text: rejoin +
@@ -1485,8 +1483,8 @@ class MessageInputState extends State<MessageInput> {
Widget _buildReplyToMessage() {
if (!_hasQuotedMessage) return Offstage();
final containsUrl = widget.quotedMessage.attachments
?.any((element) => element.ogScrapeUrl != null) ==
final containsUrl = widget.quotedMessage!.attachments
.any((element) => element.ogScrapeUrl != null) ==
true;
return Transform(
transform: Matrix4.rotationY(pi),
@@ -1494,7 +1492,7 @@ class MessageInputState extends State<MessageInput> {
child: QuotedMessageWidget(
reverse: true,
showBorder: !containsUrl,
message: widget.quotedMessage,
message: widget.quotedMessage!,
messageTheme: StreamChatTheme.of(context).otherMessageTheme,
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
),
@@ -1525,7 +1523,7 @@ class MessageInputState extends State<MessageInput> {
borderRadius: BorderRadius.circular(10),
clipBehavior: Clip.antiAlias,
child: FileAttachment(
message: null,
message: Message(), // dummy message
attachment: e,
size: Size(
MediaQuery.of(context).size.width * 0.65,
@@ -1609,11 +1607,9 @@ class MessageInputState extends State<MessageInput> {
}
Widget _buildAttachment(Attachment attachment) {
if (attachment == null) return Offstage();
if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) ==
true) {
return widget.attachmentThumbnailBuilders[attachment.type](
return widget.attachmentThumbnailBuilders![attachment.type!]!(
context,
attachment,
);
@@ -1624,7 +1620,7 @@ class MessageInputState extends State<MessageInput> {
case 'giphy':
return attachment.file != null
? Image.memory(
attachment.file.bytes,
attachment.file!.bytes!,
fit: BoxFit.cover,
errorBuilder: (context, _, __) {
return Image.asset(
@@ -1636,10 +1632,11 @@ class MessageInputState extends State<MessageInput> {
: CachedNetworkImage(
imageUrl: attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
attachment.thumbUrl!,
fit: BoxFit.cover,
errorWidget: (_, obj, trace) {
return getFileTypeImage(attachment.extraData['other']);
return getFileTypeImage(
attachment.extraData['other'] as String?);
},
progressIndicatorBuilder: (context, _, progress) {
return Shimmer.fromColors(
@@ -1717,7 +1714,7 @@ class MessageInputState extends State<MessageInput> {
setState(() {
_commandsOverlay = _buildCommandsOverlayEntry();
if (_commandsOverlay != null) {
Overlay.of(context).insert(_commandsOverlay);
Overlay.of(context)!.insert(_commandsOverlay!);
}
});
} else {
@@ -1852,7 +1849,7 @@ class MessageInputState extends State<MessageInput> {
void addAttachment(Attachment attachment) {
setState(() {
_attachments[attachment.id] = attachment.copyWith(
uploadState: attachment.uploadState ?? UploadState.success(),
uploadState: attachment.uploadState,
);
});
}
@@ -1862,8 +1859,8 @@ class MessageInputState extends State<MessageInput> {
void pickFile(DefaultAttachmentTypes fileType, [bool camera = false]) async {
setState(() => _inputEnabled = false);
AttachmentFile file;
String attachmentType;
AttachmentFile? file;
String? attachmentType;
if (fileType == DefaultAttachmentTypes.image) {
attachmentType = 'image';
@@ -1874,7 +1871,7 @@ class MessageInputState extends State<MessageInput> {
}
if (camera) {
PickedFile pickedFile;
PickedFile? pickedFile;
if (fileType == DefaultAttachmentTypes.image) {
pickedFile = await _imagePicker.getImage(source: ImageSource.camera);
} else if (fileType == DefaultAttachmentTypes.video) {
@@ -1890,7 +1887,7 @@ class MessageInputState extends State<MessageInput> {
bytes: bytes,
);
} else {
FileType type;
late FileType type;
if (fileType == DefaultAttachmentTypes.image) {
type = FileType.image;
} else if (fileType == DefaultAttachmentTypes.video) {
@@ -1902,8 +1899,8 @@ class MessageInputState extends State<MessageInput> {
type: type,
withData: true,
);
if (res?.files?.isNotEmpty == true) {
file = res.files.single.toAttachmentFile;
if (res?.files.isNotEmpty == true) {
file = res!.files.single.toAttachmentFile;
}
}
@@ -1911,29 +1908,28 @@ class MessageInputState extends State<MessageInput> {
if (file == null) return;
final mimeType = file.name?.mimeType ?? file.path.split('/').last.mimeType;
final mimeType = file.name?.mimeType ?? file.path!.split('/').last.mimeType;
final extraDataMap = <String, dynamic>{};
final extraDataMap = <String, Object>{};
if (mimeType?.subtype != null) {
extraDataMap['mime_type'] = mimeType.subtype.toLowerCase();
extraDataMap['mime_type'] = mimeType!.subtype.toLowerCase();
}
if (file.size != null) {
extraDataMap['file_size'] = file.size;
}
extraDataMap['file_size'] = file.size!;
final attachment = Attachment(
file: file,
type: attachmentType,
extraData: extraDataMap.isNotEmpty ? extraDataMap : null,
extraData: extraDataMap,
);
if (file.size > _kMaxAttachmentSize) {
if (file.size! > _kMaxAttachmentSize) {
if (attachmentType == 'Video') {
final mediaInfo = await VideoService.compressVideo(file.path);
final mediaInfo = await (VideoService.compressVideo(file.path)
as FutureOr<MediaInfo>);
if (mediaInfo.filesize > _kMaxAttachmentSize) {
if (mediaInfo.filesize! > _kMaxAttachmentSize) {
_showErrorAlert(
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
);
@@ -1941,8 +1937,8 @@ class MessageInputState extends State<MessageInput> {
}
file = AttachmentFile(
name: file.name,
size: mediaInfo.filesize,
bytes: await mediaInfo.file.readAsBytes(),
size: mediaInfo.filesize!,
bytes: await mediaInfo.file!.readAsBytes(),
path: mediaInfo.path,
);
} else {
@@ -1959,7 +1955,8 @@ class MessageInputState extends State<MessageInput> {
_attachments.update(attachment.id, (it) {
return it.copyWith(
file: file,
extraData: {...it.extraData}..update('file_size', (_) => file.size),
extraData: {...it.extraData}
..update('file_size', ((_) => file!.size!)),
);
});
});
@@ -2023,7 +2020,7 @@ class MessageInputState extends State<MessageInput> {
final shouldUnfocus = _commandEnabled;
if (_commandEnabled) {
text = '/${_chosenCommand.name} ' + text;
text = '/${_chosenCommand!.name} ' + text;
}
final attachments = [..._attachments.values];
@@ -2031,7 +2028,7 @@ class MessageInputState extends State<MessageInput> {
textEditingController.clear();
_attachments.clear();
if (widget.onQuotedMessageCleared != null) {
widget.onQuotedMessageCleared();
widget.onQuotedMessageCleared!();
}
setState(() {
@@ -2047,7 +2044,7 @@ class MessageInputState extends State<MessageInput> {
Future sendingFuture;
Message message;
if (widget.editMessage != null) {
message = widget.editMessage.copyWith(
message = widget.editMessage!.copyWith(
text: text,
attachments: attachments,
mentionedUsers:
@@ -2066,25 +2063,25 @@ class MessageInputState extends State<MessageInput> {
if (widget.quotedMessage != null) {
message = message.copyWith(
quotedMessageId: widget.quotedMessage.id,
quotedMessageId: widget.quotedMessage!.id,
);
}
if (widget.preMessageSending != null) {
message = await widget.preMessageSending(message);
message = await widget.preMessageSending!(message);
}
final streamChannel = StreamChannel.of(context);
final channel = streamChannel.channel;
if (!channel.state.isUpToDate) {
if (!channel.state!.isUpToDate) {
await streamChannel.reloadChannel();
}
_mentionedUsers.clear();
if (widget.editMessage == null ||
widget.editMessage.status == MessageSendingStatus.failed ||
widget.editMessage.status == MessageSendingStatus.sending) {
widget.editMessage!.status == MessageSendingStatus.failed ||
widget.editMessage!.status == MessageSendingStatus.sending) {
sendingFuture = channel.sendMessage(message);
} else {
sendingFuture = channel.updateMessage(message);
@@ -2099,12 +2096,12 @@ class MessageInputState extends State<MessageInput> {
_parseExistingMessage(message);
}
if (widget.onMessageSent != null) {
widget.onMessageSent(resp.message);
widget.onMessageSent!(resp.message);
}
});
}
StreamSubscription _keyboardListener;
StreamSubscription? _keyboardListener;
void _showErrorAlert(String description) {
showModalBottomSheet(
@@ -2178,14 +2175,12 @@ class MessageInputState extends State<MessageInput> {
}
void _parseExistingMessage(Message message) {
textEditingController.text = message.text;
textEditingController.text = message.text!;
_messageIsPresent = true;
if (message.attachments != null) {
for (final attachment in message.attachments) {
_attachments[attachment.id] = attachment.copyWith(
uploadState: attachment.uploadState ?? UploadState.success(),
);
}
for (final attachment in message.attachments) {
_attachments[attachment.id] = attachment.copyWith(
uploadState: attachment.uploadState,
);
}
}
@@ -2266,12 +2261,12 @@ class _PickerWidget extends StatefulWidget {
final void Function(AssetEntity) onMediaSelected;
const _PickerWidget({
Key key,
@required this.filePickerIndex,
@required this.containsFile,
@required this.selectedMedias,
@required this.onAddMoreFilesClick,
@required this.onMediaSelected,
Key? key,
required this.filePickerIndex,
required this.containsFile,
required this.selectedMedias,
required this.onAddMoreFilesClick,
required this.onMediaSelected,
}) : super(key: key);
@override
@@ -2279,7 +2274,7 @@ class _PickerWidget extends StatefulWidget {
}
class __PickerWidgetState extends State<_PickerWidget> {
Future<bool> requestPermission;
Future<bool>? requestPermission;
@override
void initState() {
@@ -2299,7 +2294,7 @@ class __PickerWidgetState extends State<_PickerWidget> {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.data) {
if (snapshot.data!) {
if (widget.containsFile) {
return GestureDetector(
onTap: () {