Skip to content

Commit

Permalink
feat: bottom sheet navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
adrielcafe committed Aug 27, 2021
1 parent b8f4bfc commit 51803a0
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ See the [project website](https://voyager.adriel.cafe) for documentation and API

### Features
- Create scalable Single-Activity apps powered by a [pragmatic API](https://voyager.adriel.cafe/navigation/fundamentals)
- [BottomSheet navigation](https://voyager.adriel.cafe/navigation/bottomsheet-navigation)
- [Tab navigation](https://voyager.adriel.cafe/navigation/tab-navigation) like [Youtube app](https://play.google.com/store/apps/details?id=com.google.android.youtube)
- [Nested navigation](https://voyager.adriel.cafe/navigation/nested-navigation) if you need to manage multiple stacks
- [State restoration](https://voyager.adriel.cafe/state-restoration) after Activity recreation
Expand Down
1 change: 1 addition & 0 deletions sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ android {
dependencies {
implementation projects.voyagerNavigator
implementation projects.voyagerTabNavigator
implementation projects.voyagerBottomSheetNavigator
implementation projects.voyagerTransitions
implementation projects.voyagerAndroidx

Expand Down
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<activity android:name=".tabNavigation.TabNavigationActivity"/>
<activity android:name=".nestedNavigation.NestedNavigationActivity"/>
<activity android:name=".androidNavigation.AndroidNavigationActivity"/>
<activity android:name=".bottomSheetNavigation.BottomSheetNavigationActivity"/>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.sample.androidNavigation.AndroidNavigationActivity
import cafe.adriel.voyager.sample.basicNavigation.BasicNavigationActivity
import cafe.adriel.voyager.sample.bottomSheetNavigation.BottomSheetNavigationActivity
import cafe.adriel.voyager.sample.nestedNavigation.NestedNavigationActivity
import cafe.adriel.voyager.sample.stateStack.StateStackActivity
import cafe.adriel.voyager.sample.tabNavigation.TabNavigationActivity
Expand Down Expand Up @@ -50,9 +51,11 @@ class SampleActivity : ComponentActivity() {
Spacer(modifier = Modifier.height(24.dp))
StartSampleButton<TabNavigationActivity>("Tab Navigation")
Spacer(modifier = Modifier.height(24.dp))
StartSampleButton<NestedNavigationActivity>("Nested Navigation")
StartSampleButton<BottomSheetNavigationActivity>("BottomSheet Navigation")
Spacer(modifier = Modifier.height(24.dp))
StartSampleButton<AndroidNavigationActivity>("Android Navigation")
Spacer(modifier = Modifier.height(24.dp))
StartSampleButton<NestedNavigationActivity>("Nested Navigation")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ data class BasicNavigationScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.run {
if (wrapContent) wrapContentHeight()
if (wrapContent) padding(vertical = 16.dp).wrapContentHeight()
else fillMaxSize()
}
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cafe.adriel.voyager.sample.bottomSheetNavigation

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import cafe.adriel.voyager.sample.basicNavigation.BasicNavigationScreen

class BackScreen : Screen {

@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current

Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
Button(
onClick = { bottomSheetNavigator.show(BasicNavigationScreen(index = 0, wrapContent = true)) }
) {
Text(text = "Show BottomSheet")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cafe.adriel.voyager.sample.bottomSheetNavigation

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.ExperimentalMaterialApi
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator

class BottomSheetNavigationActivity : ComponentActivity() {

@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
BottomSheetNavigator {
Navigator(BackScreen())
}
}
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ include(
':voyager-core',
':voyager-navigator',
':voyager-tab-navigator',
':voyager-bottom-sheet-navigator',
':voyager-transitions',
':voyager-androidx'
)
Expand Down
1 change: 1 addition & 0 deletions voyager-bottom-sheet-navigator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
25 changes: 25 additions & 0 deletions voyager-bottom-sheet-navigator/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apply plugin: "com.android.library"
apply from: "../android-module.gradle"

android {
defaultConfig {
consumerProguardFiles "consumer-rules.pro"
}
kotlinOptions {
freeCompilerArgs += '-Xexplicit-api=strict'
}
}

dependencies {
api projects.voyagerCore
implementation projects.voyagerNavigator

implementation libs.compose.runtimeSaveable
implementation libs.compose.material
implementation libs.compose.activity

testRuntimeOnly libs.junit.engine
testImplementation libs.junit.api
}

apply plugin: "com.vanniktech.maven.publish"
Empty file.
3 changes: 3 additions & 0 deletions voyager-bottom-sheet-navigator/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=VoyagerBottomSheetNavigator
POM_ARTIFACT_ID=voyager-bottom-sheet-navigator
POM_PACKAGING=aar
2 changes: 2 additions & 0 deletions voyager-bottom-sheet-navigator/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="cafe.adriel.voyager.navigator.bottomSheet"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package cafe.adriel.voyager.navigator.bottomSheet

import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetDefaults
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.contentColorFor
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.stack.Stack
import cafe.adriel.voyager.navigator.CurrentScreen
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.internal.BottomSheetNavigatorBackHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

public typealias BottomSheetNavigatorContent = @Composable (bottomSheetNavigator: BottomSheetNavigator) -> Unit

public val LocalBottomSheetNavigator: ProvidableCompositionLocal<BottomSheetNavigator> =
staticCompositionLocalOf { error("BottomSheetNavigator not initialized") }

@ExperimentalMaterialApi
@Composable
public fun BottomSheetNavigator(
modifier: Modifier = Modifier,
hideOnBackPress: Boolean = true,
scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
sheetShape: Shape = MaterialTheme.shapes.large,
sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
sheetBackgroundColor: Color = MaterialTheme.colors.surface,
sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
sheetContent: BottomSheetNavigatorContent = { CurrentScreen() },
content: BottomSheetNavigatorContent
) {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()

Navigator(HiddenBottomSheetScreen, onBackPressed = null) { navigator ->
val bottomSheetNavigator = remember(navigator, sheetState, coroutineScope) {
BottomSheetNavigator(navigator, sheetState, coroutineScope)
}

CompositionLocalProvider(
LocalBottomSheetNavigator provides bottomSheetNavigator
) {
ModalBottomSheetLayout(
modifier = modifier,
scrimColor = scrimColor,
sheetState = sheetState,
sheetShape = sheetShape,
sheetElevation = sheetElevation,
sheetBackgroundColor = sheetBackgroundColor,
sheetContentColor = sheetContentColor,
sheetContent = {
BottomSheetNavigatorBackHandler(bottomSheetNavigator, sheetState, hideOnBackPress)
sheetContent(bottomSheetNavigator)
},
content = {
content(bottomSheetNavigator)
}
)
}
}
}

@OptIn(ExperimentalMaterialApi::class)
public class BottomSheetNavigator internal constructor(
navigator: Navigator,
private val sheetState: ModalBottomSheetState,
private val coroutineScope: CoroutineScope
) : Stack<Screen> by navigator {

public val isVisible: Boolean
get() = sheetState.isVisible

public fun show(screen: Screen) {
coroutineScope.launch {
replaceAll(screen)
sheetState.show()
}
}

public fun hide() {
coroutineScope.launch {
sheetState.hide()
replaceAll(HiddenBottomSheetScreen)
}
}
}

private object HiddenBottomSheetScreen : Screen {

@Composable
override fun Content() {
Spacer(modifier = Modifier.height(1.dp))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cafe.adriel.voyager.navigator.bottomSheet.internal

import androidx.activity.compose.BackHandler
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator

@ExperimentalMaterialApi
@Composable
internal fun BottomSheetNavigatorBackHandler(
navigator: BottomSheetNavigator,
sheetState: ModalBottomSheetState,
hideOnBackPress: Boolean
) {
BackHandler(
enabled = sheetState.isVisible,
onBack = {
if (navigator.pop().not() && hideOnBackPress) {
navigator.hide()
}
}
)
}

0 comments on commit 51803a0

Please sign in to comment.