mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-15 04:07:03 +05:30
android: replace ClawFab with a NavigationBar interaction
This commit is contained in:
parent
f8a32ccd2c
commit
b8a6cf90c8
5 changed files with 11 additions and 94 deletions
|
@ -10,11 +10,8 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
@ -31,7 +28,6 @@ import com.google.accompanist.insets.statusBarsPadding
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import dev.msfjarvis.claw.android.R
|
import dev.msfjarvis.claw.android.R
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.ClawAppBar
|
import dev.msfjarvis.claw.android.ui.decorations.ClawAppBar
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.ClawFab
|
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
|
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
|
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
|
||||||
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
|
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
|
||||||
|
@ -44,6 +40,7 @@ import dev.msfjarvis.claw.common.comments.HTMLConverter
|
||||||
import dev.msfjarvis.claw.common.comments.LocalHTMLConverter
|
import dev.msfjarvis.claw.common.comments.LocalHTMLConverter
|
||||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||||
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -53,14 +50,12 @@ fun LobstersApp(
|
||||||
htmlConverter: HTMLConverter,
|
htmlConverter: HTMLConverter,
|
||||||
setWebUri: (String?) -> Unit,
|
setWebUri: (String?) -> Unit,
|
||||||
) {
|
) {
|
||||||
var isFabVisible by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
val networkListState = rememberLazyListState()
|
val networkListState = rememberLazyListState()
|
||||||
val savedListState = rememberLazyListState()
|
val savedListState = rememberLazyListState()
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val postActions = rememberPostActions(urlLauncher, navController, viewModel)
|
val postActions = rememberPostActions(urlLauncher, navController, viewModel)
|
||||||
val nestedScrollConnection = rememberNestedScrollConnection { isFabVisible = it }
|
|
||||||
val currentDestination by currentNavigationDestination(navController)
|
val currentDestination by currentNavigationDestination(navController)
|
||||||
|
|
||||||
val networkPosts = viewModel.pagerFlow.collectAsLazyPagingItems()
|
val networkPosts = viewModel.pagerFlow.collectAsLazyPagingItems()
|
||||||
|
@ -81,12 +76,12 @@ fun LobstersApp(
|
||||||
label = "Hottest",
|
label = "Hottest",
|
||||||
route = Destinations.Hottest.getRoute(),
|
route = Destinations.Hottest.getRoute(),
|
||||||
icon = painterResource(R.drawable.ic_whatshot_24dp),
|
icon = painterResource(R.drawable.ic_whatshot_24dp),
|
||||||
),
|
) { coroutineScope.launch { networkListState.animateScrollToItem(index = 0) } },
|
||||||
NavigationItem(
|
NavigationItem(
|
||||||
label = "Saved",
|
label = "Saved",
|
||||||
route = Destinations.Saved.getRoute(),
|
route = Destinations.Saved.getRoute(),
|
||||||
icon = painterResource(commonR.drawable.ic_favorite_24dp),
|
icon = painterResource(commonR.drawable.ic_favorite_24dp),
|
||||||
),
|
) { coroutineScope.launch { savedListState.animateScrollToItem(index = 0) } },
|
||||||
)
|
)
|
||||||
|
|
||||||
SideEffect { systemUiController.setStatusBarColor(color = systemBarsColor) }
|
SideEffect { systemUiController.setStatusBarColor(color = systemBarsColor) }
|
||||||
|
@ -109,12 +104,6 @@ fun LobstersApp(
|
||||||
modifier = Modifier.statusBarsPadding(),
|
modifier = Modifier.statusBarsPadding(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
|
||||||
ClawFab(
|
|
||||||
isFabVisible = isFabVisible && currentDestination == Destinations.Hottest.getRoute(),
|
|
||||||
listState = networkListState,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
ClawNavigationBar(
|
ClawNavigationBar(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
@ -136,7 +125,6 @@ fun LobstersApp(
|
||||||
isPostSaved = viewModel::isPostSaved,
|
isPostSaved = viewModel::isPostSaved,
|
||||||
reloadPosts = viewModel::reloadPosts,
|
reloadPosts = viewModel::reloadPosts,
|
||||||
postActions = postActions,
|
postActions = postActions,
|
||||||
modifier = Modifier.nestedScroll(nestedScrollConnection),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(Destinations.Saved.getRoute()) {
|
composable(Destinations.Saved.getRoute()) {
|
||||||
|
@ -146,9 +134,7 @@ fun LobstersApp(
|
||||||
listState = savedListState,
|
listState = savedListState,
|
||||||
isSaved = viewModel::isPostSaved,
|
isSaved = viewModel::isPostSaved,
|
||||||
postActions = postActions,
|
postActions = postActions,
|
||||||
modifier =
|
modifier = Modifier.padding(bottom = paddingValues.calculateBottomPadding()),
|
||||||
Modifier.nestedScroll(nestedScrollConnection)
|
|
||||||
.padding(bottom = paddingValues.calculateBottomPadding()),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(Destinations.Comments.getRoute("{postId}")) { backStackEntry ->
|
composable(Destinations.Comments.getRoute("{postId}")) { backStackEntry ->
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
package dev.msfjarvis.claw.android.ui.decorations
|
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
|
||||||
import androidx.compose.material3.FloatingActionButton
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import dev.msfjarvis.claw.android.R
|
|
||||||
import dev.msfjarvis.claw.android.ui.slideInAnimation
|
|
||||||
import dev.msfjarvis.claw.android.ui.slideOutAnimation
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
|
||||||
@Composable
|
|
||||||
fun ClawFab(
|
|
||||||
isFabVisible: Boolean,
|
|
||||||
listState: LazyListState,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = isFabVisible,
|
|
||||||
enter = slideInAnimation(),
|
|
||||||
exit = slideOutAnimation(),
|
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
FloatingActionButton(onClick = { coroutineScope.launch { listState.animateScrollToItem(0) } }) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(R.drawable.ic_arrow_upward_24dp),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -33,7 +33,10 @@ fun ClawNavigationBar(
|
||||||
label = { Text(text = navItem.label) },
|
label = { Text(text = navItem.label) },
|
||||||
selected = navController.currentDestination?.route == navItem.route,
|
selected = navController.currentDestination?.route == navItem.route,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (navController.currentDestination?.route == navItem.route) return@NavigationBarItem
|
if (navController.currentDestination?.route == navItem.route) {
|
||||||
|
navItem.listStateResetCallback()
|
||||||
|
return@NavigationBarItem
|
||||||
|
}
|
||||||
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
|
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
|
||||||
if (navItem.route != Destinations.startDestination.getRoute()) {
|
if (navItem.route != Destinations.startDestination.getRoute()) {
|
||||||
navController.navigate(navItem.route)
|
navController.navigate(navItem.route)
|
||||||
|
@ -49,4 +52,5 @@ class NavigationItem(
|
||||||
val label: String,
|
val label: String,
|
||||||
val route: String,
|
val route: String,
|
||||||
val icon: Painter,
|
val icon: Painter,
|
||||||
|
val listStateResetCallback: () -> Unit,
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,11 +18,8 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.compositeOver
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Destinations
|
import dev.msfjarvis.claw.android.ui.navigation.Destinations
|
||||||
|
@ -108,28 +105,6 @@ fun rememberPostActions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val ScrollDelta = 50
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun rememberNestedScrollConnection(setVisibility: (Boolean) -> Unit): NestedScrollConnection {
|
|
||||||
return remember {
|
|
||||||
object : NestedScrollConnection {
|
|
||||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
|
||||||
val delta = available.y
|
|
||||||
|
|
||||||
if (delta > ScrollDelta) {
|
|
||||||
setVisibility(true)
|
|
||||||
} else if (delta < -ScrollDelta) {
|
|
||||||
setVisibility(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We didn't consume any offset here so return Offset.Zero
|
|
||||||
return Offset.Zero
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the [ColorScheme.surface] color with an alpha of the [ColorScheme.primary] color overlaid
|
* Returns the [ColorScheme.surface] color with an alpha of the [ColorScheme.primary] color overlaid
|
||||||
* on top of it. Computes the surface tonal color at different elevation levels e.g. surface1
|
* on top of it. Computes the surface tonal color at different elevation levels e.g. surface1
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
|
||||||
</vector>
|
|
Loading…
Add table
Add a link
Reference in a new issue