我正在尝试构建一个基本的聊天功能,其中所有用户的聊天消息都作为文档存储在“聊天”集合中。我已经成功实现了分页,以确保在用户滚动之前不会超拉数据。
但是,即使我有StreamBuilder,新的聊天文档也不会像通常那样自动出现。为什么streambuilder无法注册和显示这些新消息?
这是我的代码:
class MotivatorChat extends StatefulWidget {
@override
_MotivatorChatState createState() => _MotivatorChatState();
}
class _MotivatorChatState extends State<MotivatorChat> {
Firestore firestore = Firestore.instance;
List<DocumentSnapshot> chats = [];
bool isLoading = false;
bool hasMore = true;
int documentLimit = 10;
DocumentSnapshot lastDocument;
ScrollController _scrollController = ScrollController();
StreamController<List<DocumentSnapshot>> _controller = StreamController<List<DocumentSnapshot>>();
Stream<List<DocumentSnapshot>> get _streamController => _controller.stream;
@override
void initState() {
super.initState();
getChats();
_scrollController.addListener(() {
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
double delta = MediaQuery.of(context).size.height * 0.20;
if (maxScroll - currentScroll <= delta) {
getChats();
}
});
}
getChats() async {
if (!hasMore) {
print('No More Chats');
return;
}
if (isLoading) {
return;
}
setState(() {
isLoading = true;
});
QuerySnapshot querySnapshot;
if (lastDocument == null) {
querySnapshot = await firestore
.collection('chats')
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.getDocuments();
} else {
querySnapshot = await firestore
.collection('chats')
.orderBy('timestamp', descending: true)
.startAfterDocument(lastDocument)
.limit(documentLimit)
.getDocuments();
print(1);
}
if (querySnapshot.documents.length < documentLimit) {
hasMore = false;
}
lastDocument = querySnapshot.documents[querySnapshot.documents.length - 1];
chats.addAll(querySnapshot.documents);
_controller.sink.add(chats);
setState(() {
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Pagination with Firestore'),
),
body: Column(children: [
Expanded(
child: StreamBuilder<List<DocumentSnapshot>>(
stream: _streamController,
builder: (sContext, snapshot) {
print(snapshot.connectionState);
if (snapshot.hasData && snapshot.data.length > 0) {
return ListView.builder(
reverse: true,
controller: _scrollController,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 20),
child: Container(
height: 20,
child: Text(snapshot.data[index].data['text']),
),
);
},
);
} else {
return Center(
child: Text('No Data...'),
);
}
},
),
),
isLoading
? Container(
width: MediaQuery
.of(context)
.size
.width,
padding: EdgeInsets.all(5),
color: Colors.yellowAccent,
child: Text(
'Loading',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
)
: Container(),
]),
);
}
}
更新的StreamBuilder
StreamBuilder<List<DocumentSnapshot>>(
stream: _streamController,
builder: (sContext, snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return Text("None");
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
} else if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData && snapshot.data.length > 0) {
return ListView.builder(
reverse: true,
controller: _scrollController,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 20),
child: Container(
height: 20,
child: Text(snapshot.data[index].data['text']),
),
);
},
);
} else {
return Center(
child: Text('No Data...'),
);
}
} else {
return Text("return list");
}
},
),
答案 0 :(得分:0)
我尝试您的代码,并将文档限制设置为20,它可以正常工作。
DartPad上的示例
答案 1 :(得分:0)
如果
hasMore
为假,则流将不会接收新数据。
不要检查hasMore
// if (!hasMore) {
// print('No More Chats');
// return;
// }
并检查文件
// if (querySnapshot.documents.length < documentLimit) {
// hasMore = false;
// }
if (querySnapshot.documents.isEmpty) {
print('No More Chats');
setLoading(false);
return;
}
我在DartPad上进行测试,它每3秒发送一次数据。
答案 2 :(得分:0)
看看这个代码是否有帮助:
class _MessagesState extends State<Messages> {
ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.offset >=
(_scrollController.position.maxScrollExtent) &&
!_scrollController.position.outOfRange) {
_getChats();
}
});
}
final StreamController<List<DocumentSnapshot>> _chatController =
StreamController<List<DocumentSnapshot>>.broadcast();
List<List<DocumentSnapshot>> _allPagedResults = [<DocumentSnapshot>[]];
static const int chatLimit = 10;
DocumentSnapshot? _lastDocument;
bool _hasMoreData = true;
Stream<List<DocumentSnapshot>> listenToChatsRealTime() {
_getChats();
return _chatController.stream;
}
void _getChats() {
final CollectionReference _chatCollectionReference = FirebaseFirestore
.instance
.collection("ChatRoom")
.doc(widget.chatRoomId)
.collection("channel");
var pagechatQuery = _chatCollectionReference
.orderBy('createdAt', descending: true)
.limit(chatLimit);
if (_lastDocument != null) {
pagechatQuery = pagechatQuery.startAfterDocument(_lastDocument!);
}
if (!_hasMoreData) return;
var currentRequestIndex = _allPagedResults.length;
pagechatQuery.snapshots().listen(
(snapshot) {
if (snapshot.docs.isNotEmpty) {
var generalChats = snapshot.docs.toList();
var pageExists = currentRequestIndex < _allPagedResults.length;
if (pageExists) {
_allPagedResults[currentRequestIndex] = generalChats;
} else {
_allPagedResults.add(generalChats);
}
var allChats = _allPagedResults.fold<List<DocumentSnapshot>>(
<DocumentSnapshot>[],
(initialValue, pageItems) => initialValue..addAll(pageItems));
_chatController.add(allChats);
if (currentRequestIndex == _allPagedResults.length - 1) {
_lastDocument = snapshot.docs.last;
}
_hasMoreData = generalChats.length == chatLimit;
}
},
);
}
@override
Widget build(BuildContext context) {
return Container(
child: StreamBuilder<List<DocumentSnapshot>>(
stream: listenToChatsRealTime(),
builder: (ctx, chatSnapshot) {
if (chatSnapshot.connectionState == ConnectionState.waiting ||
chatSnapshot.connectionState == ConnectionState.none) {
return chatSnapshot.hasData
? Center(
child: CircularProgressIndicator(),
)
: Center(
child: Text("Start a conversation."),
);
} else {
if (chatSnapshot.hasData) {
final chatDocs = chatSnapshot.data!;
final user = Provider.of<User?>(context);
return ListView.builder(
controller: _scrollController,
reverse: true,
itemBuilder: (ctx, i) {
Map chatData = chatDocs[i].data() as Map;
return MessageBubble(
username: chatData['username'],
message: chatData['text'],
isMe: chatData['senderId'] == user!.uid,
key: ValueKey(chatDocs[i].id));
},
itemCount: chatDocs.length,
);
} else {
return CircularProgressIndicator();
}
}
}),
);
}
}
我提到了这个答案:Pagination in Flutter with Firebase Realtime Database