firebase - Stream 内的 Flutter ListView.Builder 不更新
问题描述
抱歉我的英语不好,我正在用颤振和 firebase 实时数据库做一个聊天应用程序。我正在尝试进行分页。首先,我加载最后 20 条聊天消息,当用户滚动到聊天顶部时,我想再加载 20 条消息。数据恢复正确,但列表视图没有更新。这是我的代码:
class _ChatState extends State<Chat> {
TextEditingController messageEditingController = new TextEditingController();
ScrollController _scrollController = ScrollController();
bool _showKeyboard = true;
Stream _chats;
List item = [];
var _firebaseRef = FirebaseDatabase().reference().child('chatRoom');
sendMessage() {
if (messageEditingController.text.isNotEmpty) {
_firebaseRef.child(widget.chatRoomId).child("chats").push().set({
"sendBy": widget.user.uid,
"message": messageEditingController.text,
"time": DateTime.now().millisecondsSinceEpoch
});
}
setState(() {
messageEditingController.text = "";
});
}
@override
void initState() {
setState(() {
_chats = _firebaseRef
.child(widget.chatRoomId)
.child("chats")
.limitToLast(20)
.onValue;
});
_scrollController.addListener(() {
if (_scrollController.position.atEdge) {
if (_scrollController.position.pixels == 0) {
} else {
_requestMoreMessages();
}
}
});
super.initState();
}
_requestMoreMessages() async {
List _moreItems =[];
_firebaseRef
.child(widget.chatRoomId)
.child("chats")
.orderByChild("time")
.endAt(item.elementAt(19)["time"])
.limitToLast(20)
.once()
.then((snapshot) {
Map<dynamic, dynamic> map = snapshot.value;
map.forEach((key, data) {
print(data["message"]);
_moreItems.add({
"key": key,
"message": data['message'],
"sendBy": data['sendBy'],
"time": data['time']
});
});
setState(() {
item.addAll(_moreItems);
});
});
}
Widget chatMessages() {
return StreamBuilder(
stream:_chats,
builder: (context, snapshot) {
if (snapshot.hasData &&
!snapshot.hasError &&
snapshot.data.snapshot.value != null) {
Map data = snapshot.data.snapshot.value;
item = [];
data.forEach((index, data) => item.add({
"key": index,
"message": data['message'],
"sendBy": data['sendBy'],
"time": data['time']
}));
item.sort((b, a) => a["time"].compareTo(b["time"]));
return ListView.builder(
reverse: true,
controller: _scrollController,
itemCount: item.length,
itemBuilder: (context, index) {
return MessageTile(
message: item[index]['message'],
sendByMe: widget.user.uid == item[index]['sendBy'],
time: item[index]['time']);
});
} else {
return Container();
}
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context, widget.userName, widget.urlFoto, context),
body: Column(
children: <Widget>[
Flexible(
child: chatMessages(),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black26),
borderRadius: BorderRadius.circular(50),
color: Colors.black26),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: !_showKeyboard == true
? Icon(LineIcons.keyboard_o)
: Icon(LineIcons.smile_o),
onPressed: () {
if (MediaQuery.of(context).viewInsets.bottom ==
0) {
SystemChannels.textInput
.invokeMethod('TextInput.show');
}
if (mounted) {
setState(() {
_showKeyboard = !_showKeyboard;
});
}
},
),
SizedBox(
width: 230,
child: TextField(
onTap: () {
if (mounted) {
setState(() {
_showKeyboard = true;
});
}
},
controller: messageEditingController,
style: simpleTextStyle(),
textInputAction: TextInputAction.none,
decoration: InputDecoration(
hintText: "Escribe un mensaje",
hintStyle: TextStyle(
color: Colors.black,
fontSize: 16,
),
),
),
),
],
),
),
ClipOval(
child: Material(
color: Theme.of(context).primaryColorDark,
child: InkWell(
splashColor: Theme.of(context).accentColor,
child: IconButton(
color: Colors.white,
onPressed: () {
sendMessage();
// addMessage();
},
icon: Icon(LineIcons.paper_plane),
),
)),
),
],
),
),
)
],
));
}
}
解决方案
感谢 Shubham Gupta 的帮助,这是对我有用的代码:
class _ChatState extends State<Chat> {
TextEditingController messageEditingController = new TextEditingController();
ScrollController _scrollController = ScrollController();
int numeroDocumentos = 0;
bool _showKeyboard = true;
Stream _chats;
List item = [];
List _moreItems = [];
bool primeraVez = true;
var _firebaseRef = FirebaseDatabase().reference().child('chatRoom');
sendMessage() {
if (messageEditingController.text.isNotEmpty) {
_firebaseRef.child(widget.chatRoomId).child("chats").push().set({
"sendBy": widget.user.uid,
"message": messageEditingController.text,
"time": DateTime.now().millisecondsSinceEpoch
});
}
setState(() {
messageEditingController.text = "";
});
}
@override
void initState() {
item = [];
setState(() {
_chats = _firebaseRef
.child(widget.chatRoomId)
.child("chats")
.limitToLast(20)
.onValue;
});
_scrollController.addListener(() {
if (_scrollController.position.atEdge) {
if (_scrollController.position.pixels == 0) {
} else {
_requestMoreMessages();
}
}
});
super.initState();
}
_requestMoreMessages() async {
_firebaseRef
.child(widget.chatRoomId)
.child("chats")
.orderByChild("time")
.endAt(item.elementAt(numeroDocumentos - 1)["time"] - 1)
.limitToLast(20)
.once()
.then((snapshot) {
Map<dynamic, dynamic> map = snapshot.value;
map.forEach((key, data) {
print(data["message"]);
_moreItems.add({
"key": key,
"message": data['message'],
"sendBy": data['sendBy'],
"time": data['time']
});
});
}).then((value) {
setState(() {});
});
}
Widget chatMessages() {
return StreamBuilder(
stream: _chats,
builder: (context, snapshot) {
if (snapshot.hasData &&
!snapshot.hasError &&
snapshot.data.snapshot.value != null) {
item = [];
Map data = snapshot.data.snapshot.value;
data.forEach((index, data) => item.add({
"key": index,
"message": data['message'],
"sendBy": data['sendBy'],
"time": data['time']
}));
item = List.from(item)..addAll(_moreItems);
numeroDocumentos = item.length;
item.sort((b, a) => a["time"].compareTo(b["time"]));
return ListView.builder(
reverse: true,
controller: _scrollController,
itemCount: item.length,
itemBuilder: (context, index) {
return MessageTile(
message: item[index]['message'],
sendByMe: widget.user.uid == item[index]['sendBy'],
time: item[index]['time']);
});
} else {
return Container();
}
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context, widget.userName, widget.urlFoto, context),
body: Column(
children: <Widget>[
Flexible(
child: chatMessages(),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black26),
borderRadius: BorderRadius.circular(50),
color: Colors.black26),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: !_showKeyboard == true
? Icon(LineIcons.keyboard_o)
: Icon(LineIcons.smile_o),
onPressed: () {
if (MediaQuery
.of(context)
.viewInsets
.bottom ==
0) {
SystemChannels.textInput
.invokeMethod('TextInput.show');
}
if (mounted) {
setState(() {
_showKeyboard = !_showKeyboard;
});
}
},
),
SizedBox(
width: 230,
child: TextField(
onTap: () {
if (mounted) {
setState(() {
_showKeyboard = true;
});
}
},
controller: messageEditingController,
style: simpleTextStyle(),
textInputAction: TextInputAction.none,
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
hintText: "Escribe un mensaje",
hintStyle: TextStyle(
color: Colors.black,
fontSize: 16,
),
),
),
),
],
),
),
ClipOval(
child: Material(
color: Theme
.of(context)
.primaryColorDark,
child: InkWell(
splashColor: Theme
.of(context)
.accentColor,
child: IconButton(
color: Colors.white,
onPressed: () {
sendMessage();
// addMessage();
},
icon: Icon(LineIcons.paper_plane),
),
)),
),
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: _showKeyboard == false ? buildSticker() : Container(),
),
],
));
}
Widget buildSticker() {
SystemChannels.textInput.invokeMethod('TextInput.hide');
return EmojiPicker(
rows: 4,
columns: 8,
buttonMode: ButtonMode.MATERIAL,
numRecommended: 21,
onEmojiSelected: (emoji, category) {
messageEditingController.text =
messageEditingController.text + emoji.emoji;
messageEditingController.selection = TextSelection.fromPosition(
TextPosition(offset: messageEditingController.text.length));
},
);
}
}
推荐阅读
- latex-environment - 即使安装后也找不到“microtype.sty”
- c# - 如何获取具有特定名称的所有节点
- python - 如何使用 Selenium 和 Python 单击启用了 javascript 的锚元素
- ms-access - 使用 excel 访问链接表的元数据
- c# - 如何使用 foreach 将输入值存储在数组中?
- javascript - 为不同的模型分配相同的值,它们一起工作
- android - 我不断收到错误消息“指定的孩子已经有父母。您必须先在孩子的父母上调用 removeView() (Android)
- web - 有什么办法,我可以使用我的 32 位操作系统创建网站吗?大多数软件不支持我的笔记本电脑
- c# - System.Data.dll 中的不受控类型异常“System.IndexOutOfRangeException”超出矩阵范围的索引
- javascript - 嵌套对象方法的挫折 - Javascript