首先要说的是,这与我在使用Bloc模式时正在讨论的here无关。
我有一个小部件,可在其中基于StreamBuilder在CustomListView顶部返回的项目创建具有多个SliverList的CustomListView。在将childCount设置为null的意义上,每个SliverList是无限的。这是为了延迟加载的目的。问题是,当我推入页面并从页面弹出时,所有SliverList的所有项目都将重建,这会导致延迟,尤其是当我已经很远的时候。
我认为这也许可以通过Keys解决,但这似乎与此无关吗?我认为问题在于我正在build方法中动态重建SliverLists列表(请参见_ItemsBrowserState中的build())。我能想到的解决方案是将这些小部件存储在状态内,但这似乎只是在处理症状而不是原因?我对使用AutomaticKeepAliveClientMixin的感觉相同,但是随时可以改变主意。
class ItemsBrowser extends StatefulWidget {
final RepositoryBloc repoBloc;
ItemsBrowser({Key key, @required this.repoBloc}) : super(key: key);
@override
_ItemsBrowserState createState() => _ItemsBrowserState();
}
class _ItemsBrowserState extends State<ItemsBrowser> {
ScrollController _scrollController;
ItemBrowsersBloc bloc;
List<ItemBrowserBloc> blocs = [];
int atBloc = 0;
bool _batchLoadListener(ScrollNotification scrollNotification) {
if (!(scrollNotification is ScrollUpdateNotification)) return false;
if (_scrollController.position.extentAfter > 500) return false;
if (atBloc == blocs.length) return false;
if (blocs[atBloc].isLoading.value) return false;
if (blocs[atBloc].wasLastPage) atBloc++;
if (atBloc < blocs.length) blocs[atBloc].loadNextBatch();
return false;
}
@override
void initState() {
super.initState();
bloc = ItemBrowsersBloc(widget.repoBloc);
bloc.collections.listen((collections) {
if (_scrollController.hasClients) _scrollController.jumpTo(0.0);
_disposeItemBlocs();
atBloc = 0;
blocs = [];
for (var i = 0; i < collections.length; i++) {
var itemBloc = ItemBrowserBloc(collections[i], initLoad: i == 0);
blocs.add(itemBloc);
}
});
_scrollController = ScrollController();
}
void _disposeItemBlocs() {
if (blocs != null) {
for (var b in blocs) {
b.dispose();
}
}
}
@override
void dispose() {
super.dispose();
bloc?.dispose();
_disposeItemBlocs();
}
@override
Widget build(BuildContext context) {
print('Building Item Browser');
return StreamBuilder<List<Collection>>(
stream: bloc.collections,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
List<Widget> slivers = [];
for (var i = 0; i < snapshot.data.length; i++) {
slivers.add(ItemList(blocs[i], key: UniqueKey()));
slivers.add(_buildLoadingWidget(i));
}
slivers.add(const SliverToBoxAdapter(
child: const SizedBox(
height: 90,
)));
return NotificationListener<ScrollNotification>(
onNotification: _batchLoadListener,
child: CustomScrollView(
controller: _scrollController, slivers: slivers),
);
});
}
Widget _buildLoadingWidget(int index) {
return StreamBuilder(
stream: blocs[index].isLoading,
initialData: true,
builder: (context, snapshot) {
return SliverToBoxAdapter(
child: Container(
child: snapshot.data && !blocs[index].initLoaded
? Text(
'Loading more...',
style: TextStyle(color: Colors.grey.shade400),
)
: null,
),
);
},
);
}
}
class ItemList extends StatelessWidget {
final ItemBrowserBloc bloc;
ItemList(this.bloc, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return StreamBuilder<bool>(
stream: bloc.isLoading,
initialData: true,
builder: (context, snapshot) {
var isLoading = snapshot.data;
var isInitialLoad = isLoading && !bloc.initLoaded;
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
// Index: 0 1 2 3
// Return: Header Item Item null
print('INDEX $index');
if (index == 0) return _buildHeader();
if (index > bloc.items.value.length) return null;
// var itemIndex = (index - 1) % bloc.batchSize;
var itemIndex = index - 1;
var item = bloc.items.value[itemIndex];
return InkWell(
key: ValueKey<String>(item.key),
child: ItemTile(item),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => ItemPage(item)));
},
);
}, childCount: isInitialLoad ? 0 : null),
);
});
}
Widget _buildHeader() {
return Container();
}
}
行为:我打开页面并看到第一个列表。在日志中,我看到'INDEX 0','INDEX 1',....'INDEX 8'(请参阅ItemList中的build()),因为Flutter懒惰地仅构建了前9个项目。当我向下滚动时,将构建更多项。我在“索引30”处停下来,然后点击一个项目,这会推开一个新页面。现在的问题是:页面加载需要几秒钟。日志显示'INDEX 0'...'INDEX 30',即所有项目都已重建,导致延迟。我弹出页面,再次重建0到30的所有项目,从而导致延迟。
按预期的方式,如果我向下滚动到第二个SliverList,则第一个SliverList的全部和第二个SliverList的延迟构建项都将在push / pop上重建。
预期的行为:仅应重建周围的物品。
答案 0 :(得分:0)
女士们先生们,我们找到了他:
foreach
用ValueKey替换UniqueKey(或将其删除)消除了可怕的延迟!