From 9b3ec65476d79de0f2ed0412e4c2038ee1dd3d1b Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 2 Jun 2026 10:04:38 -0400 Subject: [PATCH] feat(android): swipe to change Library tabs Operator: tabs in the Library view should feel swipeable, not just tappable. Replace the selectedTab Int state + when-block content with HorizontalPager whose state drives the PrimaryScrollableTabRow. Tap routes through animateScrollToPage so swipe + tap share one source of truth. Horizontal pager gestures don't conflict with the LazyVerticalGrid inside each tab (different axes) or with PullToRefreshScaffold's vertical pull (different axes). HorizontalPager renders only the current page by default; adjacent tabs remain composed during the swipe but not eager-mounted at start. --- .../minstrel/library/ui/LibraryScreen.kt | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/android/app/src/main/java/com/fabledsword/minstrel/library/ui/LibraryScreen.kt b/android/app/src/main/java/com/fabledsword/minstrel/library/ui/LibraryScreen.kt index d01236eb..c0971a5b 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/library/ui/LibraryScreen.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/library/ui/LibraryScreen.kt @@ -4,7 +4,6 @@ package com.fabledsword.minstrel.library.ui import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize @@ -12,6 +11,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -22,11 +23,10 @@ import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController @@ -69,8 +69,12 @@ fun LibraryScreen( navController: NavHostController, viewModel: LibraryViewModel = hiltViewModel(), ) { - var selectedTab by remember { mutableIntStateOf(0) } val tabs = LIBRARY_TABS + // HorizontalPager owns the active-tab state — the TabRow reads + // pagerState.currentPage and animateScrollToPage drives it on tap, + // so swipe and tap stay in sync. + val pagerState = rememberPagerState(pageCount = { tabs.size }) + val scope = rememberCoroutineScope() Scaffold( modifier = Modifier.fillMaxSize(), @@ -91,13 +95,13 @@ fun LibraryScreen( }, ) PrimaryScrollableTabRow( - selectedTabIndex = selectedTab, + selectedTabIndex = pagerState.currentPage, edgePadding = 16.dp, ) { tabs.forEachIndexed { index, label -> Tab( - selected = selectedTab == index, - onClick = { selectedTab = index }, + selected = pagerState.currentPage == index, + onClick = { scope.launch { pagerState.animateScrollToPage(index) } }, text = { Text(label) }, ) } @@ -105,8 +109,11 @@ fun LibraryScreen( } }, ) { inner -> - Box(modifier = Modifier.fillMaxSize().padding(inner)) { - when (selectedTab) { + HorizontalPager( + state = pagerState, + modifier = Modifier.fillMaxSize().padding(inner), + ) { page -> + when (page) { TAB_ARTISTS -> ArtistsTab(viewModel = viewModel, navController = navController) TAB_ALBUMS -> AlbumsTab(viewModel = viewModel, navController = navController) TAB_HISTORY -> HistoryTab(