ScrollController 在 ListView.builder 完成时跳转到
ScrollController jumpto when ListView.builder complete
我正在用 flutter 创建一个与 firebase 的聊天,我希望当 listview 构建器完成时它可以转到列表的末尾(最后一条消息)。
这是我的构建方法的样子:
return Scaffold(
backgroundColor: Color(0xFFf1e4e8),
body: Stack(
children: [
Container(
padding: EdgeInsets.only(bottom: 125),
child: StreamBuilder(
stream: userBloc.chat(widget.chatID),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ListView.builder(
controller: scrollController,
physics: BouncingScrollPhysics(),
itemCount: snapshot.data.size,
itemBuilder: (context, index) {
return ChatMessage(
isUserMessage: isUserMessage(snapshot,index),
message:
snapshot.data.docs[index].data()['Message'],
timeStamp:snapshot.data.docs[index].data()['Timestamp']);
},
);
}
}),
);
正确的做法是什么?
在您的 initState() 中,您可以使用 addPostFrameCallback 注册一个回调,它会在第一帧完成渲染后被调用一次。您可以使用此回调滚动到 ListView 的底部。
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback( (_) {
_scrollToBottomOfListView();
}
}
最简单的解决方案如下:
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('<MessagesColection>')
.orderBy('<Time field>',descending: true)
.snapshots(),
builder: (context,snapshot) {
return ListView.builder(
//The reversed list will put the list backwards.
//The start of the list will start at the bottom.
reverse: true,
controller: scrollController,
itemCount: snapshot.data.size,
itemBuilder: (context, index) {
return ChatMessage(snapshot);
},
);
}
),
在前面的代码中,所做的是将列表反转,最近的消息会在底部,并且按照降序排列记录,即从最近到最不最近。这样,最新的消息将位于列表的开头(在本例中位于底部),最少的位于列表底部(在本例中位于顶部)。
将 addListener 附加到您的滚动控制器,然后相应地使用 jumpTo 函数。你可以按照这个例子
ScrollController _scrollController;
double _lastEndOfScroll = 0;
_scrollController.addListener(() {
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
if (maxScroll == currentScroll) {
_lastEndOfScroll = maxScroll;
}
});
_scrollController.jumpTo(_lastEndOfScroll);
我正在用 flutter 创建一个与 firebase 的聊天,我希望当 listview 构建器完成时它可以转到列表的末尾(最后一条消息)。
这是我的构建方法的样子:
return Scaffold(
backgroundColor: Color(0xFFf1e4e8),
body: Stack(
children: [
Container(
padding: EdgeInsets.only(bottom: 125),
child: StreamBuilder(
stream: userBloc.chat(widget.chatID),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ListView.builder(
controller: scrollController,
physics: BouncingScrollPhysics(),
itemCount: snapshot.data.size,
itemBuilder: (context, index) {
return ChatMessage(
isUserMessage: isUserMessage(snapshot,index),
message:
snapshot.data.docs[index].data()['Message'],
timeStamp:snapshot.data.docs[index].data()['Timestamp']);
},
);
}
}),
);
正确的做法是什么?
在您的 initState() 中,您可以使用 addPostFrameCallback 注册一个回调,它会在第一帧完成渲染后被调用一次。您可以使用此回调滚动到 ListView 的底部。
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback( (_) {
_scrollToBottomOfListView();
}
}
最简单的解决方案如下:
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('<MessagesColection>')
.orderBy('<Time field>',descending: true)
.snapshots(),
builder: (context,snapshot) {
return ListView.builder(
//The reversed list will put the list backwards.
//The start of the list will start at the bottom.
reverse: true,
controller: scrollController,
itemCount: snapshot.data.size,
itemBuilder: (context, index) {
return ChatMessage(snapshot);
},
);
}
),
在前面的代码中,所做的是将列表反转,最近的消息会在底部,并且按照降序排列记录,即从最近到最不最近。这样,最新的消息将位于列表的开头(在本例中位于底部),最少的位于列表底部(在本例中位于顶部)。
将 addListener 附加到您的滚动控制器,然后相应地使用 jumpTo 函数。你可以按照这个例子
ScrollController _scrollController;
double _lastEndOfScroll = 0;
_scrollController.addListener(() {
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
if (maxScroll == currentScroll) {
_lastEndOfScroll = maxScroll;
}
});
_scrollController.jumpTo(_lastEndOfScroll);