improve initial item scroll for list/grid

This commit is contained in:
Fabian Freund
2026-02-01 10:50:11 +01:00
parent 6d2f9ff837
commit 7ed6bbf9a9
2 changed files with 66 additions and 38 deletions
@@ -187,37 +187,49 @@ class _TabGridView extends HookConsumerWidget {
[screenWidth, crossAxisCount],
);
final lastScroll = useRef<String?>(null);
final didInitialScroll = useRef(false);
useEffect(() {
if (didInitialScroll.value) return null;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (scrollController.hasClients) {
if (lastScroll.value != activeTab) {
final index = filteredTabEntities.value.indexWhere(
(entity) => entity.tabId == activeTab,
);
if (scrollController.hasClients && activeTab != null) {
final index = filteredTabEntities.value.indexWhere(
(entity) => entity.tabId == activeTab,
);
if (index > -1) {
final offset = (index ~/ 2) * itemSize.height;
if (index > -1) {
final row = index ~/ crossAxisCount;
final viewportStart = scrollController.offset;
final viewportEnd =
viewportStart + scrollController.position.viewportDimension;
final tabStart = row * itemSize.height;
final tabEnd = tabStart + itemSize.height;
if (offset != scrollController.offset) {
lastScroll.value = activeTab;
final isVisible =
tabStart >= viewportStart && tabEnd <= viewportEnd;
unawaited(
scrollController.animateTo(
offset,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
}
if (!isVisible) {
unawaited(
scrollController.animateTo(
tabStart,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
didInitialScroll.value = true;
} else {
didInitialScroll.value = true;
}
} else {
didInitialScroll.value = true;
}
}
});
return null;
}, [filteredTabEntities, activeTab]);
}, [activeTab]);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
@@ -175,37 +175,53 @@ class _TabListView extends HookConsumerWidget {
final activeTab = ref.watch(selectedTabProvider);
final lastScroll = useRef<String?>(null);
final didInitialScroll = useRef(false);
useEffect(() {
if (didInitialScroll.value) return null;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (scrollController.hasClients) {
if (lastScroll.value != activeTab) {
final index = filteredTabEntities.value.indexWhere(
(entity) => entity.tabId == activeTab,
);
if (scrollController.hasClients && activeTab != null) {
final index = filteredTabEntities.value.indexWhere(
(entity) => entity.tabId == activeTab,
);
if (index > -1) {
final offset = index * _itemHeight;
if (index > -1) {
final viewportStart = scrollController.offset;
final viewportEnd =
viewportStart + scrollController.position.viewportDimension;
final tabStart = index * _itemHeight;
final tabEnd = tabStart + _itemHeight;
if (offset != scrollController.offset) {
lastScroll.value = activeTab;
final isVisible =
tabStart >= viewportStart && tabEnd <= viewportEnd;
unawaited(
scrollController.animateTo(
offset,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
}
if (!isVisible) {
final targetOffset = (tabStart - _itemHeight).clamp(
0.0,
scrollController.position.maxScrollExtent,
);
unawaited(
scrollController.animateTo(
targetOffset,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
didInitialScroll.value = true;
} else {
didInitialScroll.value = true;
}
} else {
didInitialScroll.value = true;
}
}
});
return null;
}, [filteredTabEntities, activeTab]);
}, [activeTab]);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),