Flutter实战一Flutter聊天应用(十七)
在正文开始之前,我们先组织一下数据,所有Firebase实时数据库的数据都被存储为Json对象。我们可以将该数据库视为云托管Json树,该数据库与SQL数据库不同,没有任何表格或记录。当我们将数据添加至Json树时,它变为现有Json结构中的一个节点。 虽然Firebase实时数据库允许嵌套数据的深度多达32层,然而,当我们提取数据库中某个位置的数据时,也会检索所有子节点。另外,当我们向某用户授予数据库中某个节点的读写访问权时,也会将该节点下所有数据的访问权授予该用户。 在Firebase实时数据库中,数据结构最佳做法是平展数据结构,数据被拆分到不同路径(又称反规范化),可以根据需要通过不同调用有效地下载。即使列表含有成千上万条记录,也可单独提取和显示,从而确保UI 的及时响应和速度。 {
"users": { "1380001": { "email": "xingmi@163.com","name": "小明","password": "123456","phone": "1380001" },"1380002": { "..": ".." },"1380003": { "..": ".." } },"chats": { "1380001": { "1380002": { "name": "小红","phone": "1380002","messages": "13800011380002","lastMessage": "电路好像出问题了!","timestamp": 1459361875666,"activate": "true" },"1380003": { "name": "小刚","phone": "1380003","messages": "13800011380003","lastMessage": "发现问题,有人把地线和火线接反","activate": "false" } },"messages": { "13800011380002": { "Ph6dARrtdEAUY5PDL2gt": { "name": "小红","message": "电路好像出问题了!" },"x415NpxFZM2CJiBRMCcL": { "..": ".." } },"13800011380003": { "..": ".." },"13800031380002": { "..": ".." } } }
在我们的应用程序中,用户个人资料位于“/users”路径中。现在有“/users/1380001”这个账户,它在“/chats/1380001”下检索所有的会话,再通过“/chats/1380001/messages”的值,在“/messages”中读取这个会话的所有聊天消息。 在上一篇文章中,我们实现了聊天列表屏幕的基本UI,在这一篇文本中,会具体实现添加会话的功能。具体实现则是当用户点击右下方的添加按钮时,会进入到添加会话屏幕,因此我们需要在 import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'dart:async';
import 'prompt_wait.dart';
class AddSession extends StatefulWidget {
AddSession(this.myPhone);
final String myPhone;
@override
State createState() => new _AddSessionState(myPhone);
}
class _AddSessionState extends State<AddSession> {
_AddSessionState(this._myPhone);
final String _myPhone;
@override
Widget build(BuildContext context) {
return new SimpleDialog(title: new Text("添加会话"),children: <Widget>[
new Text("这里用来放一些相关控件");
]);
}
}
在添加完 //...
import 'add_session.dart';
//...
class _GroupChatListState extends State<GroupChatList> {
//...
void _floatingButtonCallback() {
showDialog<Null>(
context: context,barrierDismissible: false,child: new AddSession(phone));
}
//...
Widget build(BuildContext context) {
//...
return new Scaffold(
//...
floatingActionButton: new FloatingActionButton(
backgroundColor: Colors.orange[800],elevation: 0.0,onPressed: _floatingButtonCallback,child: new Icon(Icons.person_add)));
}
}
回到 class _AddSessionState extends State<AddSession> {
//...
final usersReference = FirebaseDatabase.instance.reference().child('users');
String _searchUsername = "";
String _searchPhone = "";
//...
Future<int> _findUser(String phone) async {
return await usersReference
.child(phone)
.once()
.then((DataSnapshot onValue) {
if (onValue.value != null) {
_searchUsername = onValue.value["name"];
_searchPhone = onValue.value["phone"];
return 1;
} else {
return 0;
}
});
}
//...
}
我们还需要一个处理用户点击搜索按钮的事件,在 class _AddSessionState extends State<AddSession> {
//...
final TextEditingController _phoneController = new TextEditingController();
//...
void _handleFind() {
FocusScope.of(context).requestFocus(new FocusNode());
if (_phoneController.text.isEmpty) {
showMessage(context,"手机号码不能为空!");
return;
} else if (_phoneController.text.trim() == widget.myPhone) {
showMessage(context,"这是你的手机号码哦!");
return;
} else if (_phoneController.text.trim().length < 7 ||
_phoneController.text.trim().length > 12) {
showMessage(context,"手机号码的格式不正确!");
return;
}
showDialog<int>(
context: context,child: new ShowAwait(_findUser(_phoneController.text)))
.then((int onValue) {
if (onValue == 0) {
showMessage(context,"该用户不存在!");
} else if (onValue == 1) {
setState(() {});
}
});
}
//...
}
现在我们在 class _AddSessionState extends State<AddSession> {
//...
@override
Widget build(BuildContext context) {
return new SimpleDialog(title: new Text("添加会话"),children: <Widget>[
new Container(
margin: const EdgeInsets.symmetric(horizontal: 23.0),child: new Row(
children: <Widget>[
new Flexible(
child: new TextField(
controller: _phoneController,keyboardType: TextInputType.phone,decoration:
new InputDecoration.collapsed(hintText: '点击此处输入手机号码'),)),new IconButton(
icon: new Icon(Icons.search),onPressed: () {
_handleFind();
}),],]);
}
}
上面的代码中,如果手机号码为真实账户,我们会保存账户信息,所以我们还需要将搜索到的账户信息展示给用户。在 class _AddSessionState extends State<AddSession> {
//...
@override
Widget build(BuildContext context) {
return new SimpleDialog(title: new Text("添加会话"),children: <Widget>[
//...
_searchUsername == ""
? new Text("")
: new Container(
margin: const EdgeInsets.symmetric(horizontal: 23.0),child: new Row(
children: <Widget>[
new CircleAvatar(
child: new Text(_searchUsername[0]),backgroundColor: Theme.of(context).buttonColor),new Flexible(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[
new Text(
" " + _searchUsername,textScaleFactor: 1.2,overflow: TextOverflow.ellipsis,),new Text(" " + _searchPhone)
],))
],]);
}
}
我们在 class _AddSessionState extends State<AddSession> {
//...
final chatsReference = FirebaseDatabase.instance.reference().child('chats');
//...
Future<int> _addSession() async {
return await chatsReference
.child('$_myPhone/$_searchPhone')
.once()
.then((DataSnapshot onValue) {
if (onValue.value == null) {
chatsReference.child('$_myPhone/$_searchPhone').set({
"name": _searchUsername,"phone": _searchPhone,"messages": "$_myPhone$_searchPhone","lastMessage": "一起来聊天吧!","activate": "true"
});
return 1;
} else {
if (onValue.value["activate"] == true) {
print("跳转到对应的聊天窗口");
return 0;
} else {
print("移除以前的记录,创建一条新记录");
return 2;
}
}
});
}
//...
}
现在添加一个处理用户点击添加按钮的事件,在 class _AddSessionState extends State<AddSession> {
//...
void _handleAppend() {
showDialog<int>(
context: context,barrierDismissible: false,child: new ShowAwait(_addSession())).then((int onValue) {
if (onValue == 1) {
print("会话创建成功,返回到聊天列表屏幕");
}
});
}
//...
}
最后,我们在 class _AddSessionState extends State<AddSession> {
//...
@override
Widget build(BuildContext context) {
return new SimpleDialog(title: new Text("添加会话"),children: <Widget>[
//...
new Container(
margin: const EdgeInsets.only(top: 18.0),child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: <Widget>[
new RaisedButton(
elevation: 0.0,onPressed: () {
Navigator.of(context).pop();
},colorBrightness: Brightness.dark,child: const Text('取消'),new RaisedButton(
elevation: 0.0,onPressed: _searchUsername == "" ? null : _handleAppend,colorBrightness:
_searchUsername == "" ? Brightness.light : Brightness.dark,child: const Text('添加'),))
]);
}
}
大家可以在GitHub上直接查看add_session.dart文件的代码。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |