我有一个客户的ListView,用户可以检查/取消选中每个客户以将其标记为“收藏夹”。但是,每次点击ListItem时,ListView都会跳回到屏幕顶部。
如何防止这种情况发生?我只希望Checkbox刷新,而不是整个屏幕刷新,并防止每次屏幕跳转到顶部。
@override
Widget build(BuildContext context) {
customers = getAllCustomers();
return Scaffold(
appBar: _getAppBar(),
body: customers != null
? FutureBuilder(
future: getFavouriteCustomers(), // contains IDs of favourite customers
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
List<String> favouriteCustomersList = snapshot.data;
return ListView.builder(
itemCount: customers?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
customer c = customers?.elementAt(index);
if (favouriteCustomersList.contains(c.id)) {
c.isSelected = true;
}
return ListTile(
title: Text(c.name),
trailing: Checkbox(
value: c.isFavourite,
onChanged: (newValue) {}),
onTap: () {
if (c.isSelected) {
setState(() {
c.setFavourite(false);
});
} else {
setState(() {
c.setFavourite(true);
}
}
},
);
});
}
} else {
return CircularProgressIndicator();
}
})
: Center(
child: CircularProgressIndicator(),
);
);
}
答案 0 :(得分:0)
确保您在 StatefulWidget 中执行此操作。
在状态类(不是小部件类)中创建一个 ScrollController
作为字段:
_controller = ScrollController(keepScrollOffset: true);
这样控制器将保留在状态中,而不是在重建时重新创建。
controller: _controller,
@override
void dispose() {
_controller.dispose();
super.dispose();
}
答案 1 :(得分:0)
不确定您是否已经找到答案,但我通常通过使用两个 StatefulWidget 拆分构建方法来解决问题。这里发生的事情是,每次调用 setState()
时,都会重建整个小部件,因为整个 State 类的状态都发生了变化。这意味着 build 方法被调用,强制重新创建脚手架及其中的所有小部件树。由于小部件被重建,它会假定它的原始位置偏移量为 0。我建议创建另一个有状态的小部件来返回 ListTile
对象。这将破坏构建方法,并且在每个 ListTile
上执行的操作是在它自己的 build
方法中执行的。这不会强制您重新创建 Scaffold。
我没有您的完整代码,因此无法建议完整的工作解决方案,但以下内容可能会有所帮助。
class SomeStatefulWidget extends StatefulWidget {
@override
_SomeStatefulWidgetState createState() => _SomeStatefulWidgetState();
}
class SomeStatefulWidgetState extends State<SomeStatefulWidget> {
@override
Widget build(BuildContext context) {
customers = getAllCustomers();
return Scaffold(
appBar: _getAppBar(),
body: customers != null
? FutureBuilder(
future: getFavouriteCustomers(), // contains IDs of favourite customers
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
List<String> favouriteCustomersList = snapshot.data;
return ListView.builder(
itemCount: customers?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
customer c = customers?.elementAt(index);
if (favouriteCustomersList.contains(c.id)) {
c.isSelected = true;
}
return MyListTile(c);
});
}
} else {
return CircularProgressIndicator();
}
})
: Center(
child: CircularProgressIndicator(),
);
);
}
class MyListTile extends StatefulWidget {
customer c;
@override
_MyListTileState createState(this.c) => _MyListTileState();
}
class _MyListTileState extends State<MyListTile> {
@override
Widget build(BuildContext context) {
var c = widget.c;
return ListTile(
title: Text(c.name),
trailing: Checkbox(
value: c.isFavourite,
onChanged: (newValue) {}),
onTap: () {
if (c.isSelected) {
setState(() {
c.setFavourite(false);
});
} else {
setState(() {
c.setFavourite(true);
}
}
},
);
}
}
通过将小部件一分为二,setState()
现在将仅通过调用 MyListTile
类的 build
方法构建 MyListTileState
对象。您有效地将 ListTile
包装到您自己的名为 MyListTile
的有状态类中。
我希望这对你有用。如前所述,我没有你的完整代码,因此制作了一些包装类来使这个例子清晰。此外,我还没有编译这段代码并在此处输入所有内容,因此可能存在一些编译时错误。但这解释了跳到顶部的概念、原因和解决方案。
答案 2 :(得分:0)
您可以尝试将 listTile 包装在 StreamBuilder 中,而不是使用 setState() 来更新 UI,并为其提供每次点击复选框时都会发送数据的流。一旦 StreamBuilder 从流中接收到数据,它只会更新单元格,而不是整个列表,最终您将能够避免列表的顶部滚动。