It's not recognizing the parameter and invocations related to the drawer
#Composable
fun PantallaPrincipal(userViewModel: UserViewModel) {
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(initialValue = DrawerValue.Closed))
val scope= rememberCoroutineScope()
Scaffold(scaffoldState = scaffoldState,
topBar= { TopBar(scope,scaffoldState=scaffoldState)},
bottomBar= { BottomBar(navController=navController)},
drawerContent = { Drawer(scope=scope,scaffoldState=scaffoldState,navController=navController)},
floatingActionButton = {FloatingBottom(navController=navController)}
){
Navigation(navController = navController, userViewModel = userViewModel)
}
}
#Composable
fun DrawerItem (item: navigationDrawer, selected:Boolean, onclick:(navigationDrawer)->Unit) {
val itemBackground=if(selected) Color.Gray else Color.Transparent
Row(modifier = Modifier
.fillMaxWidth()
.height(45.dp)
.background(itemBackground)
.padding(10.dp)
.clickable { onclick(item) },
verticalAlignment = Alignment.CenterVertically
) {
Image(
imageVector = item.icono,
contentDescription = item.texto,
colorFilter = ColorFilter.tint(Color.Black),
modifier = Modifier
.height(35.dp)
.width(35.dp)
)
Spacer(modifier = Modifier.width(7.dp))
Text(text = item.texto,
fontSize = 18.sp,
color = Color.Black
)
}
}
#Composable
fun Drawer(scope: CoroutineScope, scaffoldState: ScaffoldState, navController: NavHostController) {
val listItems = listOf<navigationDrawer>(navigationDrawer.Inicio, navigationDrawer.Favoritos, navigationDrawer.Perfil)
val rutaActual = navController.currentBackStackEntry?.destination?.route
Column(modifier = Modifier
.background(Color.White)
){
Image(imageVector = Icons.Default.Face,
contentDescription = "logo",
modifier = Modifier
.background(Color.White)
.height(100.dp)
.fillMaxWidth()
.padding(10.dp)
)
Spacer(modifier = Modifier
.fillMaxWidth()
.height(5.dp)
)
listItems.forEach{
DrawerItem(item = it,
selected = (rutaActual==it.ruta),
onclick = {navController.navigate(it.ruta)
scope.launch {scaffoldState.drawerState.close()}
})
}
}
}
Important: You must use scaffold as ModalNavigationDrawer content.
Material 3 Example - Navigation Drawer with scaffold in compose
val scope = rememberCoroutineScope()
val drawerState = rememberDrawerState(DrawerValue.Closed)
val items = listOf(Icons.Default.Close, Icons.Default.Clear, Icons.Default.Call)
val selectedItem = remember { mutableStateOf(items[0]) }
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
items.forEach { item ->
NavigationDrawerItem(
icon = { Icon(item, contentDescription = null) },
label = { Text(item.name) },
selected = item == selectedItem.value,
onClick = {
scope.launch { drawerState.close() }
selectedItem.value = item
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
},
content = {
Scaffold(
topBar = { TopBar(preesOnDrawerIcon = { scope.launch { drawerState.open() } }) },
bottomBar = {},
snackbarHost = {},
content = {},
)
}
)
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun TopBar(pressOnDrawer: () -> Unit){
...
}
In Material 3 it's called ModalNavigationDrawer. You can use it using a ModalDrawerSheet and ModalDrawerItems like in this example taken from the docs:
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
items.forEach { item ->
NavigationDrawerItem(
icon = { Icon(item, contentDescription = null) },
label = { Text(item.name) },
selected = item == selectedItem.value,
onClick = {
scope.launch { drawerState.close() }
selectedItem.value = item
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
},
Related
In my application I'm using FirebaseAuth as the logging system. In HomeScreen I'm displaying currently reading books based on User's firestore database. The user can update this list by adding the book to firestore database in DetailsScreen. The problem is that even though the database is being updated, it is being shown on the UI only if the user sign out and then log in again, it is not being shown while the user is logged in. How can i fix this? I'm providing necessarry code down below. I know, that's a lot but I simply can't find the issue.
HomeScreen
#Composable
fun HomeScreen(
navController: NavController,
viewModel: HomeViewModel = hiltViewModel(),
commonViewModel: CommonViewModel
) {
val currentUser = FirebaseAuth.getInstance().currentUser
val context = LocalContext.current
val userBooks = viewModel.booksFromFB.value.filter {
it.userId == currentUser?.uid.toString()
}
Log.d("Test", "${userBooks}")
if (viewModel.isLoading.value) {
CircularProgressIndicator()
} else {
Box(
modifier = Modifier
.fillMaxSize()
.background(gradient(colors = listOf(AppColors.mBackground, AppColors.mBackgroundSec)))
) {
Log.d("Test", currentUser?.uid.toString())
Column(
modifier = Modifier.padding(start = 8.dp, end = 8.dp, top = 16.dp, bottom = 16.dp)
) {
Header(nick = currentUser?.email?.split('#')?.first().toString()) {
navController.navigate(Screen.Profile.route)
}
Spacer(modifier = Modifier.height(20.dp))
CurrentlyReadingSection(
context = context,
navController = navController,
userBooks = userBooks,
commonViewModel = commonViewModel
)
CurrentlyReadingSection
#Composable
fun CurrentlyReadingSection(
userBooks: List<BookFB>,
context: Context = LocalContext.current,
commonViewModel: CommonViewModel,
navController: NavController
) {
Column(
modifier = Modifier.padding()
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Currently reading",
color = AppColors.mTextWhite,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Bold,
fontSize = 30.sp,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(10.dp))
LazyRow() {
items(userBooks) { book ->
BookRow(
modifier = Modifier.padding(4.dp),
onItemClicked = { }
) {
val isExpanded = remember {
mutableStateOf(false)
}
val isRead = rememberSaveable {
mutableStateOf(false)
}
Box(
modifier = Modifier
.fillMaxSize()
.background(AppColors.mBackgroundSec),
contentAlignment = BottomCenter
) {
Row {
AnimatedVisibility(visible = isExpanded.value) {
Box(
modifier = Modifier.fillMaxSize()
.clickable { isExpanded.value = !isExpanded.value },
contentAlignment = Center
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyButton(
modifier = Modifier
.fillMaxWidth(0.7f)
.fillMaxHeight(0.2f)
.clip(RoundedCornerShape(12.dp)),
text = "READ",
fontSize = 12,
contentPadding = 8
) {
isRead.value = !isRead.value
isExpanded.value = !isExpanded.value
}
Spacer(modifier = Modifier.height(4.dp))
MyButton(
modifier = Modifier
.fillMaxWidth(0.7f)
.fillMaxHeight(0.24f)
.clip(RoundedCornerShape(12.dp)),
text = "RATE",
fontSize = 12,
contentPadding = 8
) {
commonViewModel.currentBook.value = book
navController.navigate(Screen.Rate.route)
}
}
}
}
AsyncImage(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(12.dp))
.clickable { isExpanded.value = !isExpanded.value },
model = ImageRequest.Builder(context)
.data(
if (isValid(book.image))
book.image
else
com.example.read.R.drawable.imagenotfound
)
.crossfade(true)
.build(),
contentScale = ContentScale.FillBounds,
contentDescription = "Book image"
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.1f)
.background(if (isRead.value) AppColors.mGreen else AppColors.mRed)
)
}
}
}
}
}
}
BookRow - for CurrentlyReadingSection
#Composable
fun BookRow(
modifier: Modifier = Modifier,
book: BookFB = BookFB(),
category: MyCategory = MyCategory("", ""),
shapeDp: Int = 12,
heightSize: Int = 170,
widthSize: Int = 120,
isForYou: Boolean = false,
isYourCollection: Boolean = false,
onItemClicked: () -> Unit = {},
content: #Composable() () -> Unit,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = if (isYourCollection) Modifier.widthIn(max = widthSize.dp) else Modifier.fillMaxWidth()
) {
Surface(
shape = RoundedCornerShape(shapeDp.dp),
elevation = 4.dp,
modifier = modifier.border(2.dp, color = Color.Black, shape = RoundedCornerShape(shapeDp.dp))
) {
Box(
modifier = Modifier
.clickable {
onItemClicked()
}
.height(heightSize.dp)
.width(widthSize.dp)
.background(AppColors.mBackgroundSec))
{
content()
}
Adding and getting books from firestore database in repository
class FirebaseRepositoryImpl #Inject constructor(
private val queryBook: CollectionReference
): FirebaseRepository {
override suspend fun addToFirebase(book: BookFB) {
if (book.toString().isNotEmpty()) {
queryBook.add(book)
.addOnSuccessListener { documentRef ->
val documentId = documentRef.id
queryBook.document(documentId)
.update(hashMapOf("id" to documentId) as Map<String, Any>)
.addOnFailureListener {
Log.w("Error", "AddToFirebase: Failed updating doc", it)
}
}
}
}
override suspend fun getBooksFromFB(): Resource<List<BookFB>> {
return try {
Resource.Loading(true)
val response = queryBook.get().await().documents.map { documentSnapshot ->
documentSnapshot.toObject(BookFB::class.java)!!
}
if (response.isNotEmpty())
Resource.Loading(false)
Resource.Success(response)
} catch (exception: FirebaseFirestoreException) {
Resource.Error(exception.message.toString())
}
}
}
Getting the books in ViewModel
#HiltViewModel
class HomeViewModel #Inject constructor(
private val repository: FirebaseRepository
): ViewModel() {
private val _booksFromFB = mutableStateOf(listOf<BookFB>())
val booksFromFB = _booksFromFB
private val _isLoading = mutableStateOf(true)
val isLoading = _isLoading
init {
getBooksFromFB()
}
fun getBooksFromFB() {
viewModelScope.launch {
_isLoading.value = true
_booksFromFB.value = repository.getBooksFromFB().data!!
if (_booksFromFB.value.isNotEmpty())
_isLoading.value = false
}
}
}
I have basic Login screen in my app, which looks like this:
LoginScreen.kt:
#Composable
fun LoginScreen(navController: NavController, viewModel: LoginViewModel) {
val userEmail = viewModel.userEmail.collectAsState()
val userPassword = viewModel.userPassword.collectAsState()
val isLoginPending = viewModel.isLoginPending.collectAsState()
val isLoginButtonEnabled = viewModel.isLoginButtonEnabled.collectAsState()
val scaffoldState = rememberScaffoldState()
if (viewModel.getOnboardingStatus() == OnboardingStatus.NOT_COMPLETED) {
navController.navigate(Screen.Onboarding.route)
}
LaunchedEffect(key1 = true) {
viewModel.loginError.collectLatest {
it?.let {
scaffoldState.snackbarHostState.showSnackbar(message = it)
}
}
}
Scaffold(
modifier = Modifier
.fillMaxSize()
.recomposeHighlighter(),
scaffoldState = scaffoldState
) {
Box(modifier = Modifier
.fillMaxSize()
.recomposeHighlighter()) {
if (isLoginPending.value) {
ProgressBar(
modifier = Modifier
.align(Alignment.Center)
.recomposeHighlighter(),
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center)
.recomposeHighlighter(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Login",
fontWeight = FontWeight.Bold,
fontSize = 36.sp
)
Spacer(modifier = Modifier
.height(64.dp)
.recomposeHighlighter())
UserInputTextField(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp)
.recomposeHighlighter(),
label = "Email",
inputState = userEmail.value,
onValueChange = { viewModel.onEvent(LoginEvent.EnteredEmail(it)) },
isLoginPending = isLoginPending.value
)
Spacer(modifier = Modifier
.height(8.dp)
.recomposeHighlighter())
UserInputTextField(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp)
.recomposeHighlighter(),
label = "Password",
inputState = userPassword.value,
onValueChange = { viewModel.onEvent(LoginEvent.EnteredPassword(it)) },
isLoginPending = isLoginPending.value,
keyboard = KeyboardOptions(keyboardType = KeyboardType.Password),
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier
.height(32.dp)
.recomposeHighlighter())
Button(
modifier = Modifier
.fillMaxWidth()
.padding(
start = 16.dp,
end = 16.dp
)
.recomposeHighlighter(),
enabled = isLoginButtonEnabled.value,
onClick = {
viewModel.onEvent(LoginEvent.Login)
}
) {
Text(text = "Login")
}
}
ClickableButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 32.dp)
.recomposeHighlighter(),
text = "No account? Click to register",
onTextClicked = { navController.navigate(Screen.Register.route) }
)
}
}
}
#Composable
private fun UserInputTextField(
modifier: Modifier,
label: String,
inputState: String,
onValueChange: (String) -> Unit,
isLoginPending: Boolean,
keyboard: KeyboardOptions? = null,
visualTransformation: VisualTransformation? = null
) {
OutlinedTextField(
value = inputState,
onValueChange = { onValueChange(it) },
label = { Text(text = label) },
placeholder = { Text(text = label) },
singleLine = true,
enabled = !isLoginPending,
modifier = modifier,
keyboardOptions = keyboard?.let { keyboard } ?: KeyboardOptions.Default,
visualTransformation = visualTransformation?.let { visualTransformation } ?: VisualTransformation.None
)
}
And I am using it with view model to decide if login button should be enabled, to login user etc.
LoginViewModel:
#HiltViewModel
class LoginViewModel #Inject constructor(private val repository: LoginRepository): ViewModel() {
private val _userEmail = MutableStateFlow<String>(value = String())
val userEmail: StateFlow<String> = _userEmail.asStateFlow()
private val _userPassword = MutableStateFlow<String>(value = String())
val userPassword: StateFlow<String> = _userPassword.asStateFlow()
private val _isLoginPending = MutableStateFlow<Boolean>(value = false)
val isLoginPending: StateFlow<Boolean> = _isLoginPending.asStateFlow()
private val _loginError = MutableSharedFlow<String?>()
val loginError: SharedFlow<String?> = _loginError.asSharedFlow()
private val _isLoginButtonEnabled = MutableStateFlow<Boolean>(value = false)
val isLoginButtonEnabled: StateFlow<Boolean> = _isLoginButtonEnabled.asStateFlow()
fun getOnboardingStatus(): OnboardingStatus {
return repository.isOnboardingCompleted()
}
fun onEvent(event: LoginEvent) {
when (event) {
is LoginEvent.EnteredEmail -> {
_userEmail.value = event.email
_isLoginButtonEnabled.value = _userEmail.value.isNotEmpty() && _userPassword.value.isNotEmpty()
}
is LoginEvent.EnteredPassword -> {
_userPassword.value = event.password
_isLoginButtonEnabled.value = _userEmail.value.isNotEmpty() && _userPassword.value.isNotEmpty()
}
is LoginEvent.Login -> {
viewModelScope.launch {
_isLoginPending.value = true
_isLoginButtonEnabled.value = false
val result = repository.loginUser(_userEmail.value, _userPassword.value)
when (result) {
is AuthRequestState.Success -> { /* nothing to do */ }
is AuthRequestState.Fail -> {
_loginError.emit(result.msg)
}
}
_isLoginPending.value = false
_isLoginButtonEnabled.value = true
}
}
}
}
}
However the problem is that when I am typing something in TextFields in layout inspector I see that other elements of UI (like Button and ClickableButton) are recomposed:
How to avoid such behavior?
I'm trying to hide a reusable floating action button (on click - then show it again when the keyboard is not visible) and request focus for a text field I made but I'm not sure what I'm doing wrong. I used focusRequester.requestFocus() and AnimatedVisibility but nothing happens.
#file:OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MySearchViewDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen(showSearchButton = true)
}
}
}
}
sealed interface Shoes { ... }
// Reusable component
#Composable
fun MyReusableScaffold(scaffoldTitle: String,
scaffoldFab: #Composable () -> Unit,
scaffoldContent: #Composable (contentPadding: PaddingValues) -> Unit) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
LargeTopAppBar(
title = { Text(text = scaffoldTitle) },
scrollBehavior = scrollBehavior
)
},
floatingActionButton = scaffoldFab,
content = { contentPadding ->
scaffoldContent(contentPadding = contentPadding)
}
)
}
// Reusable component
#Composable
fun MyExtendedFAB(expandedFab: Boolean,
onFabClick: () -> Unit
) {
ExtendedFloatingActionButton(
text = { Text(text = "Search") },
icon = { Icon(Icons.Rounded.Search, "") },
onClick = onFabClick,
expanded = expandedFab
)
}
// Reusable component
#Composable
fun <T> MyLazyColumn(modifier: Modifier,
lazyItems: Array<T>,
onClickItem: (T) -> Unit,
item: #Composable RowScope.(item: T) -> Unit,
listState: LazyListState
) {
var mText by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
Row {
LazyColumn(
modifier = modifier,
state = listState
) {
item {
if (showNotice){
Text(
text = textNotice,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
fontStyle = FontStyle.Italic,
style = MaterialTheme.typography.bodyLarge
)
}
}
item {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
value = mText,
onValueChange = {
mText = it
},
label = {
Text(text = "Search")
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(
onSearch = {
keyboardController?.hide()
focusManager.clearFocus()
}),
)
}
items(lazyItems) { choice ->
Row(modifier = Modifier
.fillMaxWidth()
.clickable {
onClickItem(choice)
}) {
item(choice)
}
}
}
}
}
#Composable
fun MainScreen(
showSearchButton: Boolean
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val listState = rememberLazyListState()
val expandedFab by remember {
derivedStateOf {
listState.firstVisibleItemIndex == 0
}
}
val focusRequester = remember { FocusRequester() }
MyReusableScaffold(
scaffoldTitle = "Shoes",
scaffoldIcon = {},
scaffoldFab = {
AnimatedVisibility(visible = showSearchButton) {
MyExtendedFAB(expandedFab) {
focusRequester.requestFocus()
}
}
},
scaffoldContent = { HomeScreenContent(listState = listState, contentPadding = it) }
)
}
#Composable
fun HomeScreenContent(
modifier: Modifier = Modifier,
listState: LazyListState,
contentPadding: PaddingValues = PaddingValues()
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val shoeItems = remember { arrayOf( ... ) }
MyLazyColumn(
modifier = Modifier.padding(contentPadding),
lazyItems = shoeItems,
onClickItem = {},
item = { ReusableTitleSubtitle(it) },
listState = listState
)
}
}
I am using Jetpack Compose for developing one app which contains multiple screens and we can navigate to them using Navigation Drawer.
How can I put my navigation drawer below TopAppBar ( or Toolbar). Below is the gif for the issue:
Below is the composable function for my screen:
fun Home(navController: NavHostController) {
val scaffoldState = rememberScaffoldState()
val coroutineScope = rememberCoroutineScope()
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
buildAnnotatedString {
append("Hello World")
pushStyle(SpanStyle(fontWeight = FontWeight.Medium, fontSize = 12.sp))
append("\nhelloworld#gmail.com")
pushStyle(SpanStyle(fontWeight = FontWeight.Light, fontSize = 10.sp))
}
)
},
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(
imageVector = Icons.Outlined.Menu,
contentDescription = null
)
}
}
)
},
drawerContent = {},
scaffoldState = scaffoldState,
drawerContentColor = MaterialTheme.colors.onBackground,
content = {
HomeScreenContent(scaffoldState, navController)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(text = "Compose") },
onClick = { navController.navigate("CreateMsg") },
modifier = Modifier.padding(0.dp),
icon = {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = null,
tint = Color.White,
)
},
shape = CircleShape,
backgroundColor = MaterialTheme.colors.primary
)
}
)
}
If you want it under your TopAppbar put TopAppbar and Scaffold inside a column as
Column(modifier=Modifier.fillMaxSize()){
TopAppbar()
Scaffold()
}
If you want your NavigationDrawer below TopAppbar put them inside a Box with
Box(modifier=Modifier.fillMaxSize()){
Scaffold()
TopAppbar()
}
And give your HomeContent topPadding as big as your TopAAppbar's height.
First one
#Composable
private fun MyComposable() {
val scaffoldState = rememberScaffoldState()
Column(modifier = Modifier.fillMaxSize()) {
val coroutineScope = rememberCoroutineScope()
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
buildAnnotatedString {
append("Hello World")
pushStyle(SpanStyle(fontWeight = FontWeight.Medium, fontSize = 12.sp))
append("\nhelloworld#gmail.com")
pushStyle(SpanStyle(fontWeight = FontWeight.Light, fontSize = 10.sp))
}
)
},
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(
imageVector = Icons.Outlined.Menu,
contentDescription = null
)
}
}
)
Scaffold(
modifier = Modifier
.fillMaxSize(),
drawerContent = {},
scaffoldState = scaffoldState,
drawerContentColor = MaterialTheme.colors.onBackground,
content = {
HomeScreenContent(scaffoldState)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(text = "Compose") },
onClick = { },
modifier = Modifier.padding(0.dp),
icon = {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = null,
tint = Color.White,
)
},
shape = CircleShape,
backgroundColor = MaterialTheme.colors.primary
)
}
)
}
}
#Composable
fun HomeScreenContent(scaffoldState: ScaffoldState) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Red)
) {
Text("HOME CONTENT", fontSize = 30.sp, color = Color.White)
}
}
Second one
#Composable
private fun MyComposable() {
val scaffoldState = rememberScaffoldState()
Box(modifier = Modifier.fillMaxSize()) {
val coroutineScope = rememberCoroutineScope()
Scaffold(
modifier = Modifier
.fillMaxSize(),
drawerContent = {},
scaffoldState = scaffoldState,
drawerContentColor = MaterialTheme.colors.onBackground,
content = {
HomeScreenContent(scaffoldState, 56.dp)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(text = "Compose") },
onClick = { },
modifier = Modifier.padding(0.dp),
icon = {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = null,
tint = Color.White,
)
},
shape = CircleShape,
backgroundColor = MaterialTheme.colors.primary
)
}
)
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
buildAnnotatedString {
append("Hello World")
pushStyle(SpanStyle(fontWeight = FontWeight.Medium, fontSize = 12.sp))
append("\nhelloworld#gmail.com")
pushStyle(SpanStyle(fontWeight = FontWeight.Light, fontSize = 10.sp))
}
)
},
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(
imageVector = Icons.Outlined.Menu,
contentDescription = null
)
}
}
)
}
}
#Composable
fun HomeScreenContent(scaffoldState: ScaffoldState, topPadding:Dp) {
Column(
modifier = Modifier
.padding(top= topPadding)
.fillMaxSize()
.background(Color.Red)
) {
Text("HOME CONTENT", fontSize = 30.sp, color = Color.White)
}
}
56.dp is default size for TopAppBar on my device. If you have custom height you need to get it via Modifier.onSizeChanged{} Modifier that set to TopAppBar.
This is my Modal drawer when open
Code to implement it
#ExperimentalMaterialApi
#Composable
private fun TutorialContent() {
ModalDrawerComponent()
}
#ExperimentalMaterialApi
#Composable
private fun ModalDrawerComponent() {
val drawerState = rememberDrawerState(DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
val openDrawer: () -> Unit = { coroutineScope.launch { drawerState.open() } }
val closeDrawer: () -> Unit = { coroutineScope.launch { drawerState.close() } }
var selectedIndex by remember { mutableStateOf(0) }
ModalDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerContentHeader()
Divider()
ModelDrawerContentBody(
selectedIndex,
onSelected = {
selectedIndex = it
},
closeDrawer = closeDrawer
)
},
content = {
Column(modifier = Modifier.fillMaxSize()) {
ModalDrawerTopAppBar(openDrawer)
ModalContent(openDrawer)
}
}
)
}
#Composable
fun ModalDrawerTopAppBar(openDrawer: () -> Unit) {
TopAppBar(
title = {
Text("ModalDrawer")
},
navigationIcon = {
IconButton(onClick = openDrawer) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = null
)
}
},
actions = {}
)
}
#Composable
fun ModalDrawerContentHeader() {
Column(
modifier = Modifier
.fillMaxWidth()
.height(180.dp)
.padding(20.dp)
) {
Image(
modifier = Modifier
.size(60.dp)
.clip(CircleShape),
painter = painterResource(id = R.drawable.avatar_1_raster),
contentDescription = null
)
Spacer(modifier = Modifier.weight(1f))
Text(text = "Android", fontWeight = FontWeight.Bold, fontSize = 22.sp)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(text = "android#android.com")
Spacer(modifier = Modifier.weight(1f))
Icon(imageVector = Icons.Filled.ArrowDropDown, contentDescription = null)
}
}
}
}
#Composable
fun ModelDrawerContentBody(
selectedIndex: Int,
onSelected: (Int) -> Unit,
closeDrawer: () -> Unit
) {
Column(modifier = Modifier.fillMaxWidth()) {
modalDrawerList.forEachIndexed { index, pair ->
val label = pair.first
val imageVector = pair.second
DrawerButton(
icon = imageVector,
label = label,
isSelected = selectedIndex == index,
action = {
onSelected(index)
}
)
}
}
}
#ExperimentalMaterialApi
#Composable
fun ModalContent(openDrawer: () -> Unit) {
LazyColumn {
items(userList) { item: String ->
ListItem(
modifier = Modifier.clickable {
openDrawer()
},
icon = {
Image(
modifier = Modifier
.size(40.dp)
.clip(CircleShape),
painter = painterResource(id = R.drawable.avatar_1_raster),
contentDescription = null
)
},
secondaryText = {
Text(text = "Secondary text")
}
) {
Text(text = item, fontSize = 18.sp)
}
}
}
}
val modalDrawerList = listOf(
Pair("My Files", Icons.Filled.Folder),
Pair("Shared with Me", Icons.Filled.People),
Pair("Starred", Icons.Filled.Star),
Pair("Recent", Icons.Filled.AccessTime),
Pair("Offline", Icons.Filled.OfflineShare),
Pair("Uploads", Icons.Filled.Upload),
Pair("Backups", Icons.Filled.CloudUpload),
)
What i actually desire is to have transparent status over the drawer as in the image below