Add drawer menu.

This commit is contained in:
Thomas Andres Gomez 2021-05-17 17:24:50 +02:00
parent 01d635ca1a
commit bb9e1b36f7
3 changed files with 204 additions and 61 deletions

View file

@ -2,19 +2,28 @@ package com.pixelized.biblib.ui.composable.screen
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.sharp.ArrowBack import androidx.compose.material.icons.sharp.ArrowBack
import androidx.compose.material.icons.sharp.LocalLibrary import androidx.compose.material.icons.sharp.Menu
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import com.pixelized.biblib.BuildConfig
import com.pixelized.biblib.R import com.pixelized.biblib.R
import com.pixelized.biblib.ui.composable.pages.DetailPageComposable import com.pixelized.biblib.ui.composable.pages.DetailPageComposable
import com.pixelized.biblib.ui.composable.pages.HomePageComposable import com.pixelized.biblib.ui.composable.pages.HomePageComposable
@ -26,6 +35,8 @@ import com.pixelized.biblib.ui.viewmodel.book.IBooksViewModel
import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel
import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
import kotlinx.coroutines.launch
import java.util.*
@OptIn(ExperimentalAnimationApi::class) @OptIn(ExperimentalAnimationApi::class)
@ -34,20 +45,39 @@ fun MainScreenComposable(
navigationViewModel: INavigationViewModel = viewModel<NavigationViewModel>(), navigationViewModel: INavigationViewModel = viewModel<NavigationViewModel>(),
booksViewModel: IBooksViewModel = viewModel<BooksViewModel>(), booksViewModel: IBooksViewModel = viewModel<BooksViewModel>(),
) { ) {
val lazyBooks: LazyPagingItems<BookThumbnailUio> = // navigation
booksViewModel.books.collectAsLazyPagingItems()
val lazyListState = rememberLazyListState()
val scrollableState = rememberScrollState()
val page by navigationViewModel.page.observeAsState() val page by navigationViewModel.page.observeAsState()
// coroutine
val coroutineScope = rememberCoroutineScope()
// scaffold & toolbar
val canNavigateBack = page !is Page.HomePage
val scaffoldState = rememberScaffoldState()
LaunchedEffect(key1 = "MainScreen", block = { LaunchedEffect(key1 = "MainScreen", block = {
navigationViewModel.navigateTo(Page.HomePage, true) navigationViewModel.navigateTo(Page.HomePage, true)
}) })
Scaffold( Scaffold(
topBar = { ToolbarComposable(navigationViewModel) }, scaffoldState = scaffoldState,
drawerContent = {} topBar = {
ToolbarComposable(
canNavigateBack,
onBackPress = {
navigationViewModel.navigateBack()
},
onDrawerPress = {
coroutineScope.launch { scaffoldState.drawerState.open() }
}
)
},
drawerContent = { DrawerContentComposable() }
) { ) {
Box {
val lazyBooks: LazyPagingItems<BookThumbnailUio> =
booksViewModel.books.collectAsLazyPagingItems()
val lazyListState = rememberLazyListState()
val scrollableState = rememberScrollState()
AnimatedVisibility( AnimatedVisibility(
visible = page is Page.HomePage, visible = page is Page.HomePage,
initiallyVisible = true, initiallyVisible = true,
@ -67,6 +97,8 @@ fun MainScreenComposable(
lazyBooks lazyBooks
) )
} }
}
Box {
AnimatedVisibility( AnimatedVisibility(
visible = page is Page.Detail, visible = page is Page.Detail,
initiallyVisible = false, initiallyVisible = false,
@ -85,45 +117,153 @@ fun MainScreenComposable(
currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) } currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) }
} }
} }
}
} }
@Composable @Composable
fun ToolbarComposable(navigationViewModel: INavigationViewModel) { fun ToolbarComposable(
canNavigateBack: Boolean,
onBackPress: () -> Unit,
onDrawerPress: () -> Unit,
) {
TopAppBar( TopAppBar(
title = { Text(stringResource(id = R.string.app_name)) }, title = {
navigationIcon = { NavigationIcon(navigationViewModel) } Text(stringResource(id = R.string.app_name))
) },
} navigationIcon = {
NavigationIcon(
@Composable canNavigateBack,
fun NavigationIcon(navigationViewModel: INavigationViewModel) { onBackPress,
val page: Page? by navigationViewModel.page.observeAsState() onDrawerPress,
Crossfade(targetState = page) {
when (it) {
is Page.HomePage -> IconButton(onClick = {}) {
Icon(
imageVector = Icons.Sharp.LocalLibrary,
contentDescription = ""
) )
} }
else -> IconButton(onClick = { )
navigationViewModel.navigateBack() }
}) {
@Composable
fun NavigationIcon(
canNavigateBack: Boolean,
onBackPress: () -> Unit,
onDrawerPress: () -> Unit,
) {
Crossfade(targetState = canNavigateBack) {
if (it) {
IconButton(onClick = onBackPress) {
Icon( Icon(
imageVector = Icons.Sharp.ArrowBack, imageVector = Icons.Sharp.ArrowBack,
contentDescription = "back" contentDescription = "back"
) )
} }
} else {
IconButton(onClick = onDrawerPress) {
Icon(
imageVector = Icons.Sharp.Menu,
contentDescription = "drawer"
)
} }
} }
}
}
@Composable
fun DrawerContentComposable() {
val typography = MaterialTheme.typography
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
Card(
shape = CutCornerShape(bottomEnd = 16.dp),
elevation = 8.dp
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
style = typography.body1,
text = "R. Daneel Olivaw"
)
Text(
style = typography.caption,
text = "r.daneel.olivaw@biblib.com"
)
}
}
Spacer(modifier = Modifier.height(8.dp))
DrawerLink(
text = "Nouveautés",
imageVector = Icons.Default.NewReleases,
onClick = {},
)
DrawerLink(
text = "Livres",
imageVector = Icons.Default.AutoStories,
onClick = {},
)
DrawerLink(
text = "Séries",
imageVector = Icons.Default.AutoAwesomeMotion,
onClick = {},
)
DrawerLink(
text = "Auteurs",
imageVector = Icons.Default.SupervisorAccount,
onClick = {},
)
DrawerLink(
text = "Étiquettes",
imageVector = Icons.Default.LocalOffer,
onClick = {},
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.align(Alignment.End)
.padding(16.dp),
style = typography.caption,
text = stringResource(
R.string.app_version,
BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()),
BuildConfig.VERSION_NAME,
BuildConfig.VERSION_CODE
)
)
}
}
@Composable
fun DrawerLink(text: String, imageVector: ImageVector, onClick: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.clickable { onClick() }) {
Icon(
modifier = Modifier.padding(horizontal = 16.dp),
imageVector = imageVector,
contentDescription = ""
)
Text(
text = text,
)
Spacer(modifier = Modifier.weight(1f))
Icon(
modifier = Modifier.padding(end = 8.dp),
imageVector = Icons.Default.NavigateNext,
contentDescription = ""
)
}
} }
@Preview @Preview
@Composable @Composable
fun ToolbarComposableDarkPreview() { fun ToolbarComposableDarkPreview() {
BibLibTheme(darkTheme = false) { BibLibTheme(darkTheme = false) {
ToolbarComposable(navigationViewModel = INavigationViewModel.Mock()) ToolbarComposable(false, {}, {})
} }
} }
@ -131,7 +271,7 @@ fun ToolbarComposableDarkPreview() {
@Composable @Composable
fun ToolbarComposableLightPreview() { fun ToolbarComposableLightPreview() {
BibLibTheme(darkTheme = true) { BibLibTheme(darkTheme = true) {
ToolbarComposable(navigationViewModel = INavigationViewModel.Mock()) ToolbarComposable(true, {}, {})
} }
} }

View file

@ -2,7 +2,6 @@ package com.pixelized.biblib.ui.viewmodel.navigation
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.pixelized.biblib.ui.data.BookUio
interface INavigationViewModel { interface INavigationViewModel {
val screen: LiveData<Navigable.Screen> val screen: LiveData<Navigable.Screen>
@ -10,6 +9,7 @@ interface INavigationViewModel {
fun navigateTo(navigable: Navigable, addToBackStack: Boolean = false): Boolean fun navigateTo(navigable: Navigable, addToBackStack: Boolean = false): Boolean
fun navigateBack(): Boolean fun navigateBack(): Boolean
fun canNavigateBack(): Boolean
sealed class Navigable { sealed class Navigable {
sealed class Screen : Navigable() { sealed class Screen : Navigable() {
@ -33,5 +33,6 @@ interface INavigationViewModel {
override fun navigateTo(navigable: Navigable, addToBackStack: Boolean): Boolean = false override fun navigateTo(navigable: Navigable, addToBackStack: Boolean): Boolean = false
override fun navigateBack(): Boolean = false override fun navigateBack(): Boolean = false
override fun canNavigateBack(): Boolean = false
} }
} }

View file

@ -43,6 +43,8 @@ class NavigationViewModel : ViewModel(), INavigationViewModel {
} }
} }
override fun canNavigateBack(): Boolean = stack.any { it != null }
private fun popBackStack(): Navigable? { private fun popBackStack(): Navigable? {
return if (stack.isEmpty()) { return if (stack.isEmpty()) {
null null