在上一篇文章中,我们完成了聊天列表的用户界面与功能代码。在用户添加完会话后,聊天列表会增加对应的会话项,通过点击会话项,可以进入聊天屏幕。在这一篇文章中,我们主要是修改lib/chat_screen.dart
的代码,也就是最早的聊天屏幕。
首先打开lib/chat_screen.dart
,在ChatScreen
中添加五个参数,messages
是会话对应的消息列表,myName
和sheName
是会话双方的名称,myPhone
和shePhone
则是双方的手机号码,也是唯一标识符。我们需要这些参数来将发送的消息写入到双方的数据节点。
class ChatScreen extends StatefulWidget {
ChatScreen(
{this.messages, this.myName, this.sheName, this.myPhone, this.shePhone});
final String messages;
final String myName;
final String sheName;
final String myPhone;
final String shePhone;
@override
State createState() => new ChatScreenState(messages);
}
接下来在ChatScreenState
中添加两个数据库连接,chatsReference
连接会话列表,messagesReference
连接具体每一个会话中的信息。还有一个ScaffoldState
类型的GlobalKey
变量,是用来控制聊天屏幕的脚手架(Scaffold
)的状态,比如在底部显示提示信息。
class ChatScreenState extends State<ChatScreen> {
ChatScreenState(this._messages);
final String _messages;
static final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final TextEditingController _textController = new TextEditingController();
final chatsReference = FirebaseDatabase.instance.reference().child('chats');
final messagesReference = FirebaseDatabase.instance.reference().child('messages');
bool _isComposing = false;
//...
}
我们再修改一下ChatScreenState
中的_sendMessage
方法,会话的一方只要发送了一条消息,就会更新双方的聊天列表中,显示最新的消息内容与消息发送的时间。这里保存的最新时间,是聊天列表排序的依据,这样聊天列表就会以消息的更新时间排序。
class ChatScreenState extends State<ChatScreen> {
//...
void _sendMessage({String text, String imageUrl}) {
String time = new DateTime.now().toString();
messagesReference.child(_messages).push().set({
'text': text,
'imageUrl': imageUrl,
'senderName': widget.myName,
'timestamp': time
});
chatsReference
.child('${widget.shePhone}/${widget.myPhone}/lastMessage')
.set(text);
chatsReference
.child('${widget.shePhone}/${widget.myPhone}/timestamp')
.set(time);
chatsReference
.child('${widget.myPhone}/${widget.shePhone}/lastMessage')
.set(text);
chatsReference
.child('${widget.myPhone}/${widget.shePhone}/timestamp')
.set(time);
}
//...
}
在聊天屏幕的右上方,我们需要增加一个弹出菜单,使用PopupMenuButton
控件,在标题栏右方增加一个按钮,点击即可弹出选项菜单,更多关于弹出菜单按钮的内容,可以查看《Flutter进阶—质感设计之弹出菜单》。当用户点击删除选项时,会话双方的activate
都将被设置成false
,表示这个会话已经无效。
class ChatScreenState extends State<ChatScreen> {
//...
@override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text(widget.sheName),
centerTitle: true,
elevation: 1.0,
actions: <Widget>[
new PopupMenuButton<String>(
onSelected: (String value) {
if (value == "delete") {
chatsReference
.child('${widget.shePhone}/${widget.myPhone}/activate')
.set("false");
chatsReference
.child('${widget.myPhone}/${widget.shePhone}/activate')
.set("false");
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text("删除成功!"),
));
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
new PopupMenuItem<String>(
value: "delete", child: new Text('删除会话')),
])
]),
body: new Stack(children: <Widget>[
//...
]),
);
}
}
当会话已经被被设置为无效状态时,应用程序不会自动返回到聊天列表屏幕,因此当用户再次发送消息时,我们应该提示用户该会话已经被删除。修改一下ChatScreenState
中的_handleSubmitted
方法,在发送消息之前先判断当前会话是否有效,有效时调用_sendMessage
方法发送消息,无效则使用脚手架来显示提示信息。
class ChatScreenState extends State<ChatScreen> {
//...
Future _handleSubmitted(String text) async {
chatsReference
.child('${widget.myPhone}/${widget.shePhone}/activate')
.onValue
.listen((Event event) {
if (event.snapshot.value == "false") {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text("会话已经被删除了哦!"),
));
} else {
if (text.trim() == "") return;
_textController.clear();
_isComposing = false;
_sendMessage(text: text);
}
});
}
//...
}
关于聊天屏幕的更多样式修改,可以查看chat_screen.dart文件了解详情。现在我们打开group_chat_list_body.dart
文件,修改一下_GroupChatListBodyState
的build
方法,当会话为无效时,不显示该会话。
class _GroupChatListBodyState extends State<GroupChatListBody> {
//...
@override
Widget build(BuildContext context) {
return new FirebaseAnimatedList(
//...
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation) {
return new SizeTransition(
sizeFactor: animation,
child: snapshot.value["activate"] == "false"
? null
: new GroupChatListBodyItem(
//...
),
);
},
);
}
}