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 {
|
void main() async {
|
||||||
final client = Client(
|
final client = Client(
|
||||||
'b67pax5b2wdq',
|
's2dxdhpxd94g',
|
||||||
logLevel: Level.INFO,
|
logLevel: Level.INFO,
|
||||||
);
|
);
|
||||||
|
|
||||||
await client.setUser(
|
await client.setUser(
|
||||||
User(id: 'falling-mountain-7'),
|
User(id: 'falling-mountain-7'),
|
||||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E',
|
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNDE5MjI5YTMtODRhMC00MDZiLTkzNzEtN2NlOWE0ZTBhNjc2In0.J3SjGH4e4v7b3cg5EgWkljTxXj_HeHpCWn5ujEVv_H8',
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(MyApp(client));
|
runApp(MyApp(client));
|
||||||
@@ -38,11 +38,11 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ChannelListView(
|
body: ChannelListView(
|
||||||
filter: {
|
// filter: {
|
||||||
'members': {
|
// 'members': {
|
||||||
'\$in': [StreamChat.of(context).user.id],
|
// '\$in': [StreamChat.of(context).user.id],
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
sort: [SortOption('last_message_at')],
|
sort: [SortOption('last_message_at')],
|
||||||
pagination: PaginationParams(
|
pagination: PaginationParams(
|
||||||
limit: 20,
|
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/message_list_view.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
|
|
||||||
|
import '../stream_chat_flutter.dart';
|
||||||
import 'stream_channel.dart';
|
import 'stream_channel.dart';
|
||||||
|
|
||||||
|
/// Inactive state
|
||||||
/// 
|
/// 
|
||||||
/// 
|
/// 
|
||||||
|
/// Focused state
|
||||||
/// 
|
/// 
|
||||||
/// 
|
/// 
|
||||||
///
|
///
|
||||||
@@ -55,8 +58,12 @@ class MessageInput extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.onMessageSent,
|
this.onMessageSent,
|
||||||
this.parentMessage,
|
this.parentMessage,
|
||||||
|
this.editMessage,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// Message to edit
|
||||||
|
final Message editMessage;
|
||||||
|
|
||||||
/// Function called after sending the message
|
/// Function called after sending the message
|
||||||
final void Function(Message) onMessageSent;
|
final void Function(Message) onMessageSent;
|
||||||
|
|
||||||
@@ -68,11 +75,13 @@ class MessageInput extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MessageInputState extends State<MessageInput> {
|
class _MessageInputState extends State<MessageInput> {
|
||||||
final _textController = TextEditingController();
|
TextEditingController _textController;
|
||||||
bool _messageIsPresent = false;
|
bool _messageIsPresent = false;
|
||||||
bool _typingStarted = false;
|
bool _typingStarted = false;
|
||||||
final List<_SendingAttachment> _attachments = [];
|
final List<_SendingAttachment> _attachments = [];
|
||||||
|
|
||||||
|
final _focusNode = FocusNode();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
@@ -87,9 +96,7 @@ class _MessageInputState extends State<MessageInput> {
|
|||||||
padding: EdgeInsets.all(2),
|
padding: EdgeInsets.all(2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
gradient: _typingStarted
|
gradient: _getGradient(context),
|
||||||
? StreamChatTheme.of(context).channelTheme.inputGradient
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -127,6 +134,7 @@ class _MessageInputState extends State<MessageInput> {
|
|||||||
_sendMessage(context);
|
_sendMessage(context);
|
||||||
},
|
},
|
||||||
controller: _textController,
|
controller: _textController,
|
||||||
|
focusNode: _focusNode,
|
||||||
onChanged: (s) {
|
onChanged: (s) {
|
||||||
StreamChannel.of(context).channel.keyStroke();
|
StreamChannel.of(context).channel.keyStroke();
|
||||||
setState(() {
|
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() {
|
Widget _buildAttachments() {
|
||||||
return Wrap(
|
return Wrap(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
@@ -234,10 +255,15 @@ class _MessageInputState extends State<MessageInput> {
|
|||||||
Widget _buildAttachment(_SendingAttachment attachment) {
|
Widget _buildAttachment(_SendingAttachment attachment) {
|
||||||
switch (attachment.type) {
|
switch (attachment.type) {
|
||||||
case FileType.IMAGE:
|
case FileType.IMAGE:
|
||||||
return Image.file(
|
return attachment.file != null
|
||||||
attachment.file,
|
? Image.file(
|
||||||
fit: BoxFit.cover,
|
attachment.file,
|
||||||
);
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: Image.network(
|
||||||
|
attachment.url,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case FileType.VIDEO:
|
case FileType.VIDEO:
|
||||||
return Container(
|
return Container(
|
||||||
@@ -414,50 +440,97 @@ class _MessageInputState extends State<MessageInput> {
|
|||||||
});
|
});
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
StreamChannel.of(context)
|
if (widget.editMessage != null) {
|
||||||
.channel
|
final message = widget.editMessage.copyWith(
|
||||||
.sendMessage(
|
parentId: widget.parentMessage?.id,
|
||||||
Message(
|
text: text,
|
||||||
parentId: widget.parentMessage?.id,
|
attachments: _getAttachments(attachments).toList(),
|
||||||
text: text,
|
);
|
||||||
attachments: attachments.map((attachment) {
|
StreamChat.of(context).client.updateMessage(message).then((_) {
|
||||||
String type;
|
if (widget.onMessageSent != null) {
|
||||||
switch (attachment.type) {
|
widget.onMessageSent(message);
|
||||||
case FileType.IMAGE:
|
}
|
||||||
type = 'image';
|
});
|
||||||
break;
|
} else {
|
||||||
case FileType.VIDEO:
|
final message = Message(
|
||||||
type = 'video';
|
parentId: widget.parentMessage?.id,
|
||||||
break;
|
text: text,
|
||||||
default:
|
attachments: _getAttachments(attachments).toList(),
|
||||||
type = 'file';
|
);
|
||||||
}
|
StreamChannel.of(context).channel.sendMessage(message).then((_) {
|
||||||
return Attachment(
|
if (widget.onMessageSent != null) {
|
||||||
imageUrl:
|
widget.onMessageSent(message);
|
||||||
attachment.type == FileType.IMAGE ? attachment.url : null,
|
}
|
||||||
assetUrl: attachment.url,
|
});
|
||||||
type: type,
|
}
|
||||||
);
|
}
|
||||||
}).toList(),
|
|
||||||
),
|
Iterable<Attachment> _getAttachments(List<_SendingAttachment> attachments) {
|
||||||
)
|
return attachments.map((attachment) {
|
||||||
.then((_) {
|
String type;
|
||||||
if (widget.onMessageSent != null) {
|
switch (attachment.type) {
|
||||||
widget.onMessageSent(Message(text: text));
|
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 {
|
class _SendingAttachment {
|
||||||
final File file;
|
final File file;
|
||||||
final FileType type;
|
final FileType type;
|
||||||
String url;
|
String url;
|
||||||
bool uploaded = false;
|
bool uploaded;
|
||||||
|
|
||||||
_SendingAttachment({
|
_SendingAttachment({
|
||||||
this.url,
|
this.url,
|
||||||
this.file,
|
this.file,
|
||||||
this.type,
|
this.type,
|
||||||
|
this.uploaded = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+107
-1
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:stream_chat/stream_chat.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/message_list_view.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_channel.dart';
|
import 'package:stream_chat_flutter/src/stream_channel.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
@@ -34,8 +35,12 @@ class MessageWidget extends StatefulWidget {
|
|||||||
@required this.nextMessage,
|
@required this.nextMessage,
|
||||||
this.onThreadTap,
|
this.onThreadTap,
|
||||||
this.isParent = false,
|
this.isParent = false,
|
||||||
|
this.onMessageEdit,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// Function called when editing a message
|
||||||
|
final Function(Message) onMessageEdit;
|
||||||
|
|
||||||
/// This message
|
/// This message
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
@@ -399,7 +404,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (_) {
|
||||||
final fontSize = 30.0;
|
final fontSize = 30.0;
|
||||||
final textStyle = TextStyle(
|
final textStyle = TextStyle(
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
@@ -478,6 +483,22 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
: SizedBox(),
|
: 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
|
streamChannel.channel.config.replies
|
||||||
? FlatButton(
|
? FlatButton(
|
||||||
child: Padding(
|
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) {
|
Widget _buildReactions(bool isMyMessage) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ dependencies:
|
|||||||
chewie: ^0.9.8+1
|
chewie: ^0.9.8+1
|
||||||
file_picker: ^1.4.3+2
|
file_picker: ^1.4.3+2
|
||||||
image_picker: ^0.6.3+4
|
image_picker: ^0.6.3+4
|
||||||
stream_chat: ^0.1.12
|
stream_chat: ^0.1.13
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
pedantic: ^1.8.0+1
|
pedantic: ^1.8.0+1
|
||||||
|
|||||||
Reference in New Issue
Block a user