在使用Firestore查询流从streambuilder渲染数据列表视图后,使用列表中的ScrollController滚动到列表底部的最佳方法是什么?
使用scrollcontroller.jumpto方法的最佳位置是什么?
git diff
任何人都可以提出适当的解决方案,以便在正确呈现数据后处理到页面底部的滚动。
谢谢。
答案 0 :(得分:1)
当然,滚动控制器是使聊天进入最后发送的消息的第一件事。但这不是唯一的方法。
我提供了我在创建聊天系统时发现的解决方案:
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);
},
);
}
),
在前面的代码中,所做的是反转列表,最新消息将在底部,并以降序对记录进行排序,即从最新到最近。这样,最新消息将位于列表的开头(在这种情况下位于底部),最少的消息位于列表的底部(在此情况下位于顶部)。
答案 1 :(得分:0)
scrollController.jumpTo(scrollController.position.maxScrollExtent);
只会将列表视图滚动到maxScrollValue
,即,最大可滚动一次。
您将必须使用
scrollController.jumpTo(info["challengeReplies"].length*1.0)
实现您的尝试。
信息的理由[“ challengeReplies”]。length * 1.0: Coz,JumpTo需要一个双精度值。
答案 2 :(得分:0)
这可能有点笨拙,但我找不到其他方法。
就我而言(我在 StreamBuilder 中使用了 PageView,但应该与您的类似),
我将它提取到单独的有状态小部件中,给它一个 UniqueKey()
并将它的 ScrollController
移动到它的状态,例如(请阅读代码中的注释):
//The extracted scrolling widget
class ScrollHeader extends StatefulWidget {
const ScrollHeader({
Key key,
@required this.slides,
@required this.currentSlide,
}) : super(key: key);
final List<Widget> slides;
final int currentSlide;
@override
_ScrollHeaderState createState() => _ScrollHeaderState();
}
class _ScrollHeaderState extends State<ScrollHeader> {
PageController headerController;
@override
void initState() {
super.initState();
//Assign initialPage(Or newly received page) in initState.
headerController = PageController(initialPage: widget.currentSlide);
}
@override
Widget build(BuildContext context) {
//Since PageView doesn't exist on the first render yet,
//ScrollController will throw an error, so we need to escape it using IF:
if (headerController.hasClients) {
headerController.animateToPage(
widget.currentSlide,
duration: Duration(milliseconds: 350),
curve: Curves.easeIn,
);
}
//If you don't need animation, you can just do this without IFs:
headerController.jumpToPage(index);
return PageView(
scrollDirection: Axis.horizontal,
children: widget.slides,
controller: headerController,
);
}
}
StreamBuilder
本身将为这个滚动小部件提供 initialPage
和子元素:
Widget build(BuildContext context) {
Key appbarKey = UniqueKey(); //We need Key to not destroy State between StreamBuilder's updates
StreamBuilder(
stream: appBarState.stream$, //Could be any stream(Like Firestore for example).
builder: (context, snap) {
if (snap.hasData) {
//Just generated children elements
List<Widget> slides = List<Widget>.from(
List<String>.from(appBarState.current["slides"]).map(
(e) => Center(
child: Text(
e,
style: TextStyle(
color: theme.accentColor,
fontSize: 20,
),
),
),
),
);
print(snap.data);
return ScrollHeader(
key: appbarKey,
slides: slides,
currentSlide: snap.data["currentSlide"], //This is the page that pageView will snap/animate to after receiving new data
);
}
return Container();
},
),
这允许您随时更新和动画/捕捉滚动小部件的页面,并在使用流时更新其子项。
希望有帮助,我希望有人会发布一个更好、更优雅的解决方案,但这个解决方案有效(到目前为止)。
答案 3 :(得分:0)
将 addListener 与 jumpTo 函数一起使用。你可以参考这个例子。
ScrollController _scrollController;
double _lastEndOfScroll = 0;
_scrollController.addListener(() {
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
if (maxScroll == currentScroll) {
if (!isLoading) {
_lastEndOfScroll = maxScroll;
fetchData();
}
}
});
if (!isLoading) _scrollController.jumpTo(_lastEndOfScroll);
让我知道这是否有效。