This帖子描述了一个非常相似的问题,但是那里的答案并不能解决所有问题:
我有一个可能很长的列表,用户可以在其中添加新项目(一次打开)。添加后/添加后,列表应滚动到末尾。
(顺便说一句,reverse: true
不是一个选择)
在阅读了另一篇文章之后,我了解使用SchedulerBinding.instance.addPostFrameCallback((_) => scrollToEnd());
可以欺骗新列表maxScrollExtent
是正确的。
但是它不能可靠地工作: 当已经滚动到列表的末尾或接近末尾时,一切正常。 但是,当添加新项目时将列表滚动到其开头(或从末尾某种方式)时,该列表将被滚动,但是滚动位置恰好是一项-最新项。
我认为这可能与ListView.builder
并没有使所有孩子都活着-而是如何解决有关?
哦,还有一个额外的问题:刚刚发现了另一个非常奇怪的行为:在添加了两个项目之后,最后一个项目有点看不见了,但是列表不可滚动-这很奇怪。但是,甚至更奇怪的是,在下一个添加项上单击,列表会滚动这一点-但从未创建新项!?
下面是一个完整的示例:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
runApp(MyList());
}
class MyList extends StatefulWidget {
MyList({Key key}) : super(key: key);
@override
_MyListState createState() => _MyListState();
}
var items = List<String>.generate(8, (i) => "Item $i");
class _MyListState extends State<MyList> {
static ScrollController _scrollController = ScrollController();
void add() {
setState(() {
items.add("new Item ${items.length}");
print(items.length);
});
SchedulerBinding.instance.addPostFrameCallback((_) => scrollToEnd());
}
void scrollToEnd() {
_scrollController.animateTo(_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 350), curve: Curves.easeOut);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "List",
home: Scaffold(
appBar: AppBar(
title: Text("List"),
),
body: ListView.builder(
controller: _scrollController,
itemCount: items.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
},
),
bottomSheet: Container(
decoration: BoxDecoration(
border:
Border(top: BorderSide(color: Colors.black, width: 1))),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () {
add();
},
child: Icon(Icons.add),
)
],
))),
);
}
}
答案 0 :(得分:1)
我将滚动条与maxScrollExtent
结合到Scrollable.ensureVisible
,并且它们每个都修复了另一个缺陷。
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyList extends StatefulWidget {
MyList({Key key}) : super(key: key);
@override
_MyListState createState() => _MyListState();
}
class _MyListState extends State<MyList> {
final ScrollController _scrollController = ScrollController();
final lastKey = GlobalKey();
List<String> items;
@override
void initState() {
super.initState();
items = List<String>.generate(8, (i) => "Item $i");
}
void add() {
setState(() {
items.add("new Item ${items.length}");
});
SchedulerBinding.instance.addPostFrameCallback((_) => scrollToEnd());
}
void scrollToEnd() async {
await _scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 350),
curve: Curves.easeOut);
Scrollable.ensureVisible(lastKey.currentContext);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "List",
home: Scaffold(
body: ListView.builder(
controller: _scrollController,
itemCount: items.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
key: index == items.length - 1 ? lastKey : null,
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
add();
},
child: Icon(Icons.add),
),
));
}
}
Scrollable.ensureVisible
本身无法提供可见性(如果尚未创建该项目),但是当该项目非常接近时可以应对。
答案 1 :(得分:1)
指定太大的 scrollPosition 不会出错,然后 ScrollController 会自动滚动到最终最大值。我定义了一个 _scrollController 并执行以下命令:
_scrollController.animateTo(
_scrollController.position.maxScrollExtent + 200,
duration: const Duration(milliseconds: 350),
curve: Curves.easeOut);
或
_scrollController.jumpTo(_scrollController.position.maxScrollExtent + 200);