【发布时间】:2019-08-20 19:40:57
【问题描述】:
首先想说这与here 正在讨论的内容无关,因为我使用的是 Bloc 模式。
我有一个小部件,在该小部件中,我根据 StreamBuilder 在 CustomListView 顶部返回的项目创建了一个带有多个 SliverLists 的 CustomListView。在 childCount 设置为 null 的意义上,每个 SliverList 都是无限的。这是为了延迟加载。问题是当我推入和从页面弹出时,所有 SliverLists 的所有项目都被重建,这会导致延迟,尤其是当我已经在列表中很远的时候。
我认为也许这可以通过 Keys 解决,但这似乎与此无关?我认为问题在于我正在构建方法中动态重建 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 个项目。当我向下滚动时,会构建更多项目。我停在“INDEX 30”并点击一个项目,这会推动一个新页面。现在的问题:页面加载需要一秒钟。日志显示'INDEX 0' ...'INDEX 30',即所有项目都被重建,导致延迟。我弹出页面,再次重新构建从 0 到 30 的所有项目,导致延迟。
如预期的那样,如果我向下滚动到第二个 SliverList,则整个第一个 SliverList 和第二个 SliverList 的延迟构建项都会在 push/pop 时重建。
预期行为:应该只重建周围的项目。
【问题讨论】:
-
这并没有错。 “导致延迟”是什么意思?
-
重建项目时它会挂起一秒钟。就像,它滞后,冻结。
-
我在您的构建方法中没有看到任何令人惊讶的地方。这可能是您的 BLoC 问题。你介意分享一下吗?
-
当然!感谢您的时间。 pastebin.com/mKKx8EFd
-
@RémiRousselet 不想打扰你,但你有没有偶然看到这个?
标签: flutter