add edit message feature
This commit is contained in:
@@ -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
@@ -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
|
||||
/// 
|
||||
/// 
|
||||
/// Focused state
|
||||
/// 
|
||||
/// 
|
||||
///
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user