add edit message feature

This commit is contained in:
Salvatore Giordano
2020-03-03 11:03:36 +01:00
parent d97a67d523
commit f3a6ba68a4
4 changed files with 227 additions and 48 deletions
+7 -7
View File
@@ -3,13 +3,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() async {
final client = Client(
'b67pax5b2wdq',
's2dxdhpxd94g',
logLevel: Level.INFO,
);
await client.setUser(
User(id: 'falling-mountain-7'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E',
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNDE5MjI5YTMtODRhMC00MDZiLTkzNzEtN2NlOWE0ZTBhNjc2In0.J3SjGH4e4v7b3cg5EgWkljTxXj_HeHpCWn5ujEVv_H8',
);
runApp(MyApp(client));
@@ -38,11 +38,11 @@ class ChannelListPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
// filter: {
// 'members': {
// '\$in': [StreamChat.of(context).user.id],
// }
// },
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
+112 -39
View File
@@ -8,10 +8,13 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import '../stream_chat_flutter.dart';
import 'stream_channel.dart';
/// Inactive state
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input_paint.png)
/// Focused state
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input2.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input2_paint.png)
///
@@ -55,8 +58,12 @@ class MessageInput extends StatefulWidget {
Key key,
this.onMessageSent,
this.parentMessage,
this.editMessage,
}) : super(key: key);
/// Message to edit
final Message editMessage;
/// Function called after sending the message
final void Function(Message) onMessageSent;
@@ -68,11 +75,13 @@ class MessageInput extends StatefulWidget {
}
class _MessageInputState extends State<MessageInput> {
final _textController = TextEditingController();
TextEditingController _textController;
bool _messageIsPresent = false;
bool _typingStarted = false;
final List<_SendingAttachment> _attachments = [];
final _focusNode = FocusNode();
@override
Widget build(BuildContext context) {
return SafeArea(
@@ -87,9 +96,7 @@ class _MessageInputState extends State<MessageInput> {
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
gradient: _typingStarted
? StreamChatTheme.of(context).channelTheme.inputGradient
: null,
gradient: _getGradient(context),
),
child: Container(
decoration: BoxDecoration(
@@ -127,6 +134,7 @@ class _MessageInputState extends State<MessageInput> {
_sendMessage(context);
},
controller: _textController,
focusNode: _focusNode,
onChanged: (s) {
StreamChannel.of(context).channel.keyStroke();
setState(() {
@@ -167,6 +175,19 @@ class _MessageInputState extends State<MessageInput> {
);
}
Gradient _getGradient(BuildContext context) {
if (_typingStarted) {
if (widget.editMessage == null) {
return StreamChatTheme.of(context).channelTheme.inputGradient;
}
return LinearGradient(
colors: [Colors.lightGreen, Colors.green],
);
} else {
return null;
}
}
Widget _buildAttachments() {
return Wrap(
direction: Axis.horizontal,
@@ -234,10 +255,15 @@ class _MessageInputState extends State<MessageInput> {
Widget _buildAttachment(_SendingAttachment attachment) {
switch (attachment.type) {
case FileType.IMAGE:
return Image.file(
attachment.file,
fit: BoxFit.cover,
);
return attachment.file != null
? Image.file(
attachment.file,
fit: BoxFit.cover,
)
: Image.network(
attachment.url,
fit: BoxFit.cover,
);
break;
case FileType.VIDEO:
return Container(
@@ -414,50 +440,97 @@ class _MessageInputState extends State<MessageInput> {
});
FocusScope.of(context).unfocus();
StreamChannel.of(context)
.channel
.sendMessage(
Message(
parentId: widget.parentMessage?.id,
text: text,
attachments: attachments.map((attachment) {
String type;
switch (attachment.type) {
case FileType.IMAGE:
type = 'image';
break;
case FileType.VIDEO:
type = 'video';
break;
default:
type = 'file';
}
return Attachment(
imageUrl:
attachment.type == FileType.IMAGE ? attachment.url : null,
assetUrl: attachment.url,
type: type,
);
}).toList(),
),
)
.then((_) {
if (widget.onMessageSent != null) {
widget.onMessageSent(Message(text: text));
if (widget.editMessage != null) {
final message = widget.editMessage.copyWith(
parentId: widget.parentMessage?.id,
text: text,
attachments: _getAttachments(attachments).toList(),
);
StreamChat.of(context).client.updateMessage(message).then((_) {
if (widget.onMessageSent != null) {
widget.onMessageSent(message);
}
});
} else {
final message = Message(
parentId: widget.parentMessage?.id,
text: text,
attachments: _getAttachments(attachments).toList(),
);
StreamChannel.of(context).channel.sendMessage(message).then((_) {
if (widget.onMessageSent != null) {
widget.onMessageSent(message);
}
});
}
}
Iterable<Attachment> _getAttachments(List<_SendingAttachment> attachments) {
return attachments.map((attachment) {
String type;
switch (attachment.type) {
case FileType.IMAGE:
type = 'image';
break;
case FileType.VIDEO:
type = 'video';
break;
default:
type = 'file';
}
return Attachment(
imageUrl: attachment.type == FileType.IMAGE ? attachment.url : null,
assetUrl: attachment.url,
type: type,
);
});
}
@override
void initState() {
super.initState();
if (widget.editMessage != null) {
_textController = TextEditingController(text: widget.editMessage.text);
_typingStarted = true;
_messageIsPresent = true;
widget.editMessage.attachments.forEach((attachment) {
if (attachment.type == 'image') {
_attachments.add(_SendingAttachment(
type: FileType.IMAGE,
url: attachment.imageUrl,
uploaded: true,
));
}
});
} else {
_textController = TextEditingController();
}
}
bool _focused = false;
@override
void didChangeDependencies() {
if (widget.editMessage != null && !_focused) {
FocusScope.of(context).requestFocus(_focusNode);
_focused = true;
}
super.didChangeDependencies();
}
}
class _SendingAttachment {
final File file;
final FileType type;
String url;
bool uploaded = false;
bool uploaded;
_SendingAttachment({
this.url,
this.file,
this.type,
this.uploaded = false,
});
}
+107 -1
View File
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/message_input.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/stream_channel.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
@@ -34,8 +35,12 @@ class MessageWidget extends StatefulWidget {
@required this.nextMessage,
this.onThreadTap,
this.isParent = false,
this.onMessageEdit,
}) : super(key: key);
/// Function called when editing a message
final Function(Message) onMessageEdit;
/// This message
final Message message;
@@ -399,7 +404,7 @@ class _MessageWidgetState extends State<MessageWidget>
),
),
context: context,
builder: (context) {
builder: (_) {
final fontSize = 30.0;
final textStyle = TextStyle(
fontSize: fontSize,
@@ -478,6 +483,22 @@ class _MessageWidgetState extends State<MessageWidget>
},
)
: SizedBox(),
isMyMessage
? FlatButton(
child: Padding(
padding: const EdgeInsets.all(28.0),
child: Text(
'Edit message',
style: Theme.of(context).textTheme.headline,
),
),
onPressed: () async {
await Navigator.pop(context);
_showEditBottomSheet(context);
},
)
: SizedBox(),
streamChannel.channel.config.replies
? FlatButton(
child: Padding(
@@ -499,6 +520,91 @@ class _MessageWidgetState extends State<MessageWidget>
});
}
void _showEditBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
elevation: 2,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
),
),
builder: (context) {
return Flex(
direction: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: 16.0,
left: 16.0,
right: 16.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Edit message',
style: Theme.of(context).textTheme.title,
),
Container(
height: 30,
padding: const EdgeInsets.all(2.0),
child: AspectRatio(
aspectRatio: 1,
child: RawMaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
elevation: 0,
highlightElevation: 0,
focusElevation: 0,
disabledElevation: 0,
hoverElevation: 0,
onPressed: () {
Navigator.of(context).pop();
},
fillColor: Colors.black.withOpacity(.1),
padding: EdgeInsets.all(4),
child: Icon(
Icons.close,
size: 15,
color: Colors.black,
),
),
),
),
],
),
),
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: MessageInput(
editMessage: widget.message,
parentMessage: widget.isParent
? StreamChannel.of(context)
.channel
.state
.messages
.firstWhere(
(message) => message.id == widget.message.parentId)
: null,
onMessageSent: (_) {
Navigator.pop(context);
},
),
),
],
);
},
);
}
Widget _buildReactions(bool isMyMessage) {
return GestureDetector(
onTap: () {
+1 -1
View File
@@ -20,7 +20,7 @@ dependencies:
chewie: ^0.9.8+1
file_picker: ^1.4.3+2
image_picker: ^0.6.3+4
stream_chat: ^0.1.12
stream_chat: ^0.1.13
dev_dependencies:
pedantic: ^1.8.0+1