perf(search): reduce list rendering and provider rebuilds

This commit is contained in:
zarzet
2026-09-26 23:41:47 +07:00
parent e0093841bc
commit abfd8df5fb
6 changed files with 223 additions and 50 deletions
+26 -22
View File
@@ -765,22 +765,25 @@ class _HomeTabState extends ConsumerState<HomeTab>
),
);
final hasExploreContent = ref.watch(
exploreProvider.select((s) => s.sections.isNotEmpty),
);
final exploreLoading = ref.watch(
exploreProvider.select((s) => s.isLoading),
);
final hasHomeFeedExtension = ref.watch(
extensionProvider.select(
(s) => s.extensions.any((e) => e.enabled && e.hasHomeFeed),
),
);
final homeFeedDisabled = ref.watch(
settingsProvider.select(
(s) => s.homeFeedProvider == AppSettings.homeFeedProviderOff,
),
);
final hasExploreContent =
_showsHome &&
ref.watch(exploreProvider.select((s) => s.sections.isNotEmpty));
final exploreLoading =
_showsHome && ref.watch(exploreProvider.select((s) => s.isLoading));
final hasHomeFeedExtension =
_showsHome &&
ref.watch(
extensionProvider.select(
(s) => s.extensions.any((e) => e.enabled && e.hasHomeFeed),
),
);
final homeFeedDisabled =
_showsHome &&
ref.watch(
settingsProvider.select(
(s) => s.homeFeedProvider == AppSettings.homeFeedProviderOff,
),
);
final colorScheme = Theme.of(context).colorScheme;
final searchText = _showsSearch ? _urlController.text.trim() : '';
@@ -804,9 +807,11 @@ class _HomeTabState extends ConsumerState<HomeTab>
_showsSearch &&
ref.watch(trackProvider.select((s) => s.isShowingRecentAccess));
final screenHeight = MediaQuery.sizeOf(context).height;
final hasHistoryItems = ref.watch(
_homeHistoryPreviewProvider.select((items) => items.isNotEmpty),
);
final hasHistoryItems =
_showsHome &&
ref.watch(
_homeHistoryPreviewProvider.select((items) => items.isNotEmpty),
);
final recentModeRequested =
widget.mode == HomeTabMode.search ||
@@ -982,9 +987,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
final recentAccessView = ref.watch(
recentAccessViewProvider,
);
return SliverToBoxAdapter(
child: _buildRecentAccess(recentAccessView, colorScheme),
);
return _buildRecentAccess(recentAccessView, colorScheme);
},
),
@@ -1113,6 +1116,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
return SliverMainAxisGroup(
slivers: _buildSearchResults(
resultsRef: ref,
tracks: tracks,
isLoading: isLoading,
error: error,
+32 -9
View File
@@ -2,12 +2,43 @@ part of 'home_tab.dart';
extension _HomeTabRecentUI on _HomeTabState {
Widget _buildRecentAccess(_RecentAccessView view, ColorScheme colorScheme) {
return SliverMainAxisGroup(
slivers: [
SliverToBoxAdapter(child: _buildRecentAccessHeader(view, colorScheme)),
if (view.uniqueItems.isNotEmpty)
SliverPadding(
padding:
const EdgeInsets.fromLTRB(16, 0, 16, 8) +
EdgeInsets.symmetric(horizontal: wideListInset(context)),
sliver: SliverList.builder(
itemCount: view.uniqueItems.length,
itemBuilder: (context, index) {
final item = view.uniqueItems[index];
return KeyedSubtree(
key: ValueKey('${item.type.name}:${item.id}'),
child: _buildRecentAccessItem(
item,
colorScheme,
view.downloadFilePathByRecentKey,
),
);
},
),
),
],
);
}
Widget _buildRecentAccessHeader(
_RecentAccessView view,
ColorScheme colorScheme,
) {
final uniqueItems = view.uniqueItems;
final hasHiddenDownloads = view.hasHiddenDownloads;
return Padding(
padding:
const EdgeInsets.fromLTRB(16, 8, 16, 8) +
EdgeInsets.fromLTRB(16, 8, 16, uniqueItems.isEmpty ? 8 : 0) +
EdgeInsets.symmetric(horizontal: wideListInset(context)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -99,14 +130,6 @@ extension _HomeTabRecentUI on _HomeTabState {
],
),
),
)
else
...uniqueItems.map(
(item) => _buildRecentAccessItem(
item,
colorScheme,
view.downloadFilePathByRecentKey,
),
),
],
),
+3 -1
View File
@@ -299,6 +299,7 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
}
List<Widget> _buildSearchResults({
required WidgetRef resultsRef,
required List<Track> tracks,
required bool isLoading,
required String? error,
@@ -418,7 +419,7 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
if (sortedTracks.isNotEmpty) {
_historySnapshot.update(sortedTracks);
final historyLookups = _historySnapshot.lookups;
final existingHistoryKeys = ref.watch(
final existingHistoryKeys = resultsRef.watch(
downloadHistoryVisibleBatchExistsProvider(_historySnapshot.request),
);
slivers.addAll(
@@ -540,6 +541,7 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: MornyeGlassPanel.overlay(
blurEnabled: false,
radius: 24,
firstInGroup: isFirst,
lastInGroup: isLast,
+2 -3
View File
@@ -219,9 +219,9 @@ class _TrackItemWithStatus extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final colorScheme = Theme.of(context).colorScheme;
final queueItem = ref.watch(
final isQueued = ref.watch(
downloadQueueLookupProvider.select(
(lookup) => lookup.byTrackId[track.id],
(lookup) => lookup.byTrackId.containsKey(track.id),
),
);
@@ -249,7 +249,6 @@ class _TrackItemWithStatus extends ConsumerWidget {
thumbHeight = thumbSize.$2;
}
final isQueued = queueItem != null;
final hasCover = track.coverUrl?.isNotEmpty == true;
return Column(
+6
View File
@@ -131,6 +131,7 @@ class MornyeGlassPanel extends ConsumerWidget {
this.strongTint = false,
this.tintOpacity,
this.liquidGlass = false,
this.blurEnabled = true,
});
/// Shared glass material for confirmation dialogs and floating sheets.
@@ -142,6 +143,7 @@ class MornyeGlassPanel extends ConsumerWidget {
this.lastInGroup = true,
this.tintOpacity = 0.78,
this.liquidGlass = false,
this.blurEnabled = true,
}) : strongTint = false;
final Widget child;
@@ -152,9 +154,13 @@ class MornyeGlassPanel extends ConsumerWidget {
final double? tintOpacity;
final bool liquidGlass;
/// Disable backdrop sampling for surfaces that scroll over a plain page.
final bool blurEnabled;
@override
Widget build(BuildContext context, WidgetRef ref) {
final blur =
blurEnabled &&
!MediaQuery.highContrastOf(context) &&
(!ref.watch(lowEndDeviceProvider) ||
ref.watch(backdropBlurEnabledProvider));