如何在 FutureBuilder 项目的 Flutter ListView 中向上滚动时修复 'jumping to top of each item'?
How to fix 'jumping to top of each item' while scrolling up in Flutter ListView of FutureBuilder items?
我刚开始通过课程学习 Flutter,其中一个项目是编写 Hacker News reader 应用程序。需要两个 HTTP 请求,一个用于获取 id 的顶部列表,另一个用于使用其 id 的每篇文章。
与 Google 的 The Boring Show 版本相反,我需要能够在用户向下滚动到热门文章总数时请求每篇文章(~500)。我还想尝试一种比我的课程中使用的许多流、转换器和变通方法更简单的方法。
我正在使用 StreamBuilder
通过 bloc
和 FutureBuilder
从流中获取文章中 ListView.builder
的每一行的顶部 ID从 bloc
中获取。数据是从 HTTP 请求或缓存(sqflite 数据库)中提取的。
它在向下滚动时完美运行,但在向上滚动时,它会跳到每个项目的顶部,除了前 4 个项目(因为它们已经在那个时候构建)。
几天来我尝试了不同的结构和模式,每当我在 ListView.builder
中使用 FutureBuilder
时,它的行为总是相同的。
在模拟器和我的 Pixel 3 XL 上以调试和发布模式试过了。
Boring Show 将有限数量的文章从 StreamBuilder
发送到 ListView.builder
的方法有效,因为 FutureBuilder
不是需要,但无法获取更多文章。
顺便说一下,据我所知,即使每个构建的 ConnectionState
设置为 waiting
,向上滚动时也不会重新获取项目。
我已经在 YouTube 上添加了有关该问题的视频。希望它有效:
YouTube Video of ListView issue while scrolling up
这里是显示列表的class。我组合了小部件以使其紧凑:
class TopList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final StoriesBloc bloc = StoriesProvider.of(context);
return StreamBuilder<List<int>>(
stream: bloc.topList,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemBuilder: (context, int index) {
return FutureBuilder<ItemModel>(
key: Key(snapshot.data[index].toString()),
future: bloc.getNewsItem(snapshot.data[index]),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
print('${snapshot.connectionState} for $index');
return Text('Loading...');
case ConnectionState.done:
return buildItem(snapshot.data, index);
}
});
},
)
: Center(
child: CircularProgressIndicator(),
);
});
}
Widget buildItem(ItemModel item, int index) {
print('Building row $index');
return ListTile(
key: Key(index.toString()),
leading: CircleAvatar(
child: Text('${index + 1}', style: TextStyle(fontSize: 14)),
radius: 16,
),
title: Text(item.title),
subtitle: Text('${item.score}'),
trailing: Column(
children: <Widget>[
Icon(Icons.comment),
Text('${item.descendants}'),
],
),
);
}
}
来自 flutter doctor
:
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel dev, v1.5.8, on Microsoft Windows [Version 10.0.17763.437], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[√] Android Studio (version 3.4)
[√] Android Studio (version 3.5)
[√] VS Code (version 1.32.3)
[√] Connected device (1 available)
• No issues found!
我希望上下滚动能够流畅,并且 'Loading...' 文本会显示正在加载的项目。
这里是向下滚动而不是向上滚动时活动的打印输出:
I/flutter (13405): ConnectionState.waiting for 17
I/flutter (13405): Building row 17
I/flutter (13405): ConnectionState.waiting for 18
I/flutter (13405): Building row 18
I/flutter (13405): ConnectionState.waiting for 19
I/flutter (13405): Building row 19
I/flutter (13405): ConnectionState.waiting for 20
I/flutter (13405): Building row 20
I/flutter (13405): ConnectionState.waiting for 21
I/flutter (13405): Building row 21
I/flutter (13405): ConnectionState.waiting for 22
I/flutter (13405): Building row 22
I/flutter (13405): ConnectionState.waiting for 23
I/flutter (13405): Building row 23
I/flutter (13405): ConnectionState.waiting for 6
I/flutter (13405): Building row 6
I/flutter (13405): ConnectionState.waiting for 5
I/flutter (13405): Building row 5
I/flutter (13405): ConnectionState.waiting for 4
I/flutter (13405): Building row 4
I/flutter (13405): ConnectionState.waiting for 3
I/flutter (13405): Building row 3
I/flutter (13405): ConnectionState.waiting for 2
I/flutter (13405): Building row 2
I/flutter (13405): ConnectionState.waiting for 1
I/flutter (13405): Building row 1
I/flutter (13405): ConnectionState.waiting for 0
I/flutter (13405): Building row 0
问题可能很明显,但我想不通。
已修复!
好吧,从 flutter Discord 组的 'buy dip kek' 那里得到了一个建议,即在加载每个 FutureBuilder 的未来时放置完全相同的小部件,一个 ListTile 和虚拟数据。这解决了问题。
我刚开始通过课程学习 Flutter,其中一个项目是编写 Hacker News reader 应用程序。需要两个 HTTP 请求,一个用于获取 id 的顶部列表,另一个用于使用其 id 的每篇文章。
与 Google 的 The Boring Show 版本相反,我需要能够在用户向下滚动到热门文章总数时请求每篇文章(~500)。我还想尝试一种比我的课程中使用的许多流、转换器和变通方法更简单的方法。
我正在使用 StreamBuilder
通过 bloc
和 FutureBuilder
从流中获取文章中 ListView.builder
的每一行的顶部 ID从 bloc
中获取。数据是从 HTTP 请求或缓存(sqflite 数据库)中提取的。
它在向下滚动时完美运行,但在向上滚动时,它会跳到每个项目的顶部,除了前 4 个项目(因为它们已经在那个时候构建)。
几天来我尝试了不同的结构和模式,每当我在 ListView.builder
中使用 FutureBuilder
时,它的行为总是相同的。
在模拟器和我的 Pixel 3 XL 上以调试和发布模式试过了。
Boring Show 将有限数量的文章从 StreamBuilder
发送到 ListView.builder
的方法有效,因为 FutureBuilder
不是需要,但无法获取更多文章。
顺便说一下,据我所知,即使每个构建的 ConnectionState
设置为 waiting
,向上滚动时也不会重新获取项目。
我已经在 YouTube 上添加了有关该问题的视频。希望它有效:
YouTube Video of ListView issue while scrolling up
这里是显示列表的class。我组合了小部件以使其紧凑:
class TopList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final StoriesBloc bloc = StoriesProvider.of(context);
return StreamBuilder<List<int>>(
stream: bloc.topList,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemBuilder: (context, int index) {
return FutureBuilder<ItemModel>(
key: Key(snapshot.data[index].toString()),
future: bloc.getNewsItem(snapshot.data[index]),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
print('${snapshot.connectionState} for $index');
return Text('Loading...');
case ConnectionState.done:
return buildItem(snapshot.data, index);
}
});
},
)
: Center(
child: CircularProgressIndicator(),
);
});
}
Widget buildItem(ItemModel item, int index) {
print('Building row $index');
return ListTile(
key: Key(index.toString()),
leading: CircleAvatar(
child: Text('${index + 1}', style: TextStyle(fontSize: 14)),
radius: 16,
),
title: Text(item.title),
subtitle: Text('${item.score}'),
trailing: Column(
children: <Widget>[
Icon(Icons.comment),
Text('${item.descendants}'),
],
),
);
}
}
来自 flutter doctor
:
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel dev, v1.5.8, on Microsoft Windows [Version 10.0.17763.437], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[√] Android Studio (version 3.4)
[√] Android Studio (version 3.5)
[√] VS Code (version 1.32.3)
[√] Connected device (1 available)
• No issues found!
我希望上下滚动能够流畅,并且 'Loading...' 文本会显示正在加载的项目。
这里是向下滚动而不是向上滚动时活动的打印输出:
I/flutter (13405): ConnectionState.waiting for 17
I/flutter (13405): Building row 17
I/flutter (13405): ConnectionState.waiting for 18
I/flutter (13405): Building row 18
I/flutter (13405): ConnectionState.waiting for 19
I/flutter (13405): Building row 19
I/flutter (13405): ConnectionState.waiting for 20
I/flutter (13405): Building row 20
I/flutter (13405): ConnectionState.waiting for 21
I/flutter (13405): Building row 21
I/flutter (13405): ConnectionState.waiting for 22
I/flutter (13405): Building row 22
I/flutter (13405): ConnectionState.waiting for 23
I/flutter (13405): Building row 23
I/flutter (13405): ConnectionState.waiting for 6
I/flutter (13405): Building row 6
I/flutter (13405): ConnectionState.waiting for 5
I/flutter (13405): Building row 5
I/flutter (13405): ConnectionState.waiting for 4
I/flutter (13405): Building row 4
I/flutter (13405): ConnectionState.waiting for 3
I/flutter (13405): Building row 3
I/flutter (13405): ConnectionState.waiting for 2
I/flutter (13405): Building row 2
I/flutter (13405): ConnectionState.waiting for 1
I/flutter (13405): Building row 1
I/flutter (13405): ConnectionState.waiting for 0
I/flutter (13405): Building row 0
问题可能很明显,但我想不通。
已修复!
好吧,从 flutter Discord 组的 'buy dip kek' 那里得到了一个建议,即在加载每个 FutureBuilder 的未来时放置完全相同的小部件,一个 ListTile 和虚拟数据。这解决了问题。