I wanna the navigation bar to be at the bottom of the page using jet compose (material 3)
How to set the bottom navigation bar position fixed in compose
Here is my sample code for the navigation bar
import androidx.compose.foundation.background
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import io.material.compose.ui.theme.Purple40
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun NavigationBarDemoSample(navController: NavController) {
var selectedItem by remember { mutableStateOf(0) }
val items = listOf("Songs", "Artists", "Playlists")
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = { Text("Navigation Bar") },
Modifier.background(Purple40),
navigationIcon = {
IconButton(onClick = { /* doSomething() */
navController.navigateUp()
}) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
},
)
},
content = {
NavigationBar {
items.forEachIndexed { index, item ->
NavigationBarItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(item) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}
}
)
}
Here is my output:
Just change your Scaffold adding the NavigationBar in the bottomBar parameter instead of the content parameter:
Scaffold(
topBar = /* .. */ ,
bottomBar = {
NavigationBar {
items.forEachIndexed { index, item ->
NavigationBarItem(
/** ..... */
)
}
}
}
)
Scaffold
Compose provides convenient layouts for combining Material Components into common screen patterns. Composables such as Scaffold provide slots for various components and other screen elements.
Screen content
Scaffold has a generic content trailing lambda slot. The lambda receives an instance of PaddingValues that should be applied to the content root — for example, via Modifier.padding — to offset the top and bottom bars, if they exist.
Read more: https://developer.android.com/jetpack/compose/layouts/material#scaffold
package compose.material.theme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.*
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun NavigationBarDemoSample() {
var selectedItem by remember { mutableStateOf(0) }
val items = listOf("Songs", "Artists", "Playlists")
Scaffold(
bottomBar = {
NavigationBar {
items.forEachIndexed { index, item ->
NavigationBarItem(
icon = {
androidx.compose.material.Icon(
Icons.Filled.Favorite,
contentDescription = null
)
},
label = { androidx.compose.material.Text(item) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}
}
) {
// Screen content
}
}
Related
I want to add Divider after title. I tried to add Divider(), but it goes to above the text.
I am using Material 3 using implementation "androidx.compose.material3:material3:1.0.1"
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.letsgetchecked.app.common.DialogOptionsData
#Composable
fun <T> DialogOptionsView(
optionData: DialogOptionsData<T>,
) {
AlertDialog(
onDismissRequest = {},
confirmButton = {},
title = {
Text(text = optionData.header)
Divider()
},
text = {
LazyColumn {
items(optionData.items) {
Text(text = "$it")
}
}
},
)
}
#Preview(showBackground = true)
#Composable
fun PreviewDialogOptions() {
val items = listOf(1, 2)
val dataItems = DialogOptionsData(header = "Header", items = items)
DialogOptionsView(dataItems)
}
Expected Output
Actual Output
It happens because the title attribute internally uses a Box as parent container.
Add a Column to achieve the expected result:
AlertDialog(
onDismissRequest = {},
confirmButton = {},
title = {
Column() {
Text(text = "header")
Divider()
}
},
If you use Dialog() composable instead of AlertDialog() you can get full width divider. Try this code
#Composable
fun <T> DialogOptionsView(optionData: DialogOptionsData<T>) {
Dialog(onDismissRequest = {}) {
Surface(shape = RoundedCornerShape(10.dp)) {
Column {
Text(
text = optionData.header,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(16.dp, 14.dp)
)
Divider()
LazyColumn(contentPadding = PaddingValues(16.dp, 10.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
items(optionData.items) {
Text(text = "$it")
}
}
}
}
}
}
and the result is
I copied a piece of code from the example in jetpack compose.link
But in Android Studio a problem arises:
I wonder where is the problem? I'm still a beginner
The following is the complete code:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.freedom.android.ui.theme.MyApplicationTheme
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { Text("Drawer content") },
topBar = {
TopAppBar(
title = { Text("Simple Scaffold Screen") },
navigationIcon = {
IconButton(
onClick = {
scope.launch { scaffoldState.drawerState.open() }
}
) {
Icon(Icons.Filled.Menu, contentDescription = "Localized description")
}
}
)
},
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Inc") },
onClick = { /* fab click handler */ }
)
},
content = { innerPadding ->
LazyColumn(contentPadding = innerPadding) {
items(count = 100) {
Box(
Modifier
.fillMaxWidth()
.height(50.dp)
.background(colors[it % colors.size])
)
}
}
}
)
}
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
MyApplicationTheme {
Greeting("Android")
}
}
I think the problem is with the colors variable, it doesn't seem to be an array, but this was copied from the official documentation, I didn't change it.
package androidx.compose.material
object MaterialTheme {
/**
* Retrieves the current [Colors] at the call site's position in the hierarchy.
*
* #sample androidx.compose.material.samples.ThemeColorSample
*/
val colors: Colors
#Composable
#ReadOnlyComposable
get() = LocalColors.current
}
If you look at the full source code of the sample the docs use, it has a top level object:
private val colors = listOf(
Color(0xFFffd7d7.toInt()),
Color(0xFFffe9d6.toInt()),
Color(0xFFfffbd0.toInt()),
Color(0xFFe3ffd9.toInt()),
Color(0xFFd0fff8.toInt())
)
So yeah, the colors that sample is referring to is a list that you can index into. If you want to also have a semi-random set of colors for your backgrounds, you can copy that list into your code as well.
I'm trying to build a Jetpack Compose app with Scaffold and a LargeTopAppBar. I currently have a very simple UI with only the LargeTopAppBar in a Scaffold, but when I run my app I see two small titles at the top of the screen.
Any ideas why this is happening or how to fix it? My activity code is as follows
#OptIn(ExperimentalMaterial3Api::class)
class MainActivity : MonetCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launchWhenCreated {
monet.awaitMonetReady()
setContent {
TVTimeTheme(monetCompat = monet) {
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val topAppBarScrollState = rememberTopAppBarScrollState()
val scrollBehavior = remember(decayAnimationSpec) {
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
decayAnimationSpec, topAppBarScrollState
)
}
Scaffold (
topBar = {
LargeTopAppBar(
title = { Text(text = "movies") },
scrollBehavior = scrollBehavior
)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding))
}
}
}
}
}
}
These helped me:
Use only material3 components (androidx.compose.material3.*) in TabBar, not material (androidx.compose.material.*) components
Remove defaultTextColor of titleLarge and bodyLarge in your typography
I ran across this issue when I set the color within the Text composable, instead of the titleContentColor associated with the TopAppBar composable.
I was doing this:
MediumTopAppBar(
title = {
Text(
text = "Example",
color = MaterialTheme.colorScheme.secondary
)
},
...
)
Instead, use the titleContentColor field on the MediumTopAppBar composable itself. That looks like this:
MediumTopAppBar(
title = {
Text(
text = "Example",
)
},
colors = TopAppBarDefaults.largeTopAppBarColors(
titleContentColor = MaterialTheme.colorScheme.secondary
)
)
Easy mistake to make but stumped me for a good 10 minutes or so!
I think it's because you have not put your navigation icon yet. Try this:
Scaffold(
topBar = {
LargeTopAppBar(
title = { Text("movies") },
navigationIcon = {
IconButton(onClick = { }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "menu icon"
)
}
},
actions = {
IconButton(onClick = { }) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "favorite icon"
)
}
},
)
},
content = {innerPadding ->
Box(modifier = Modifier.padding(innerPadding))
}
)
Source
Just make sure that whether you use import androidx.compose.material3.Text instead of import androidx.compose.material.Text
I need to make tabs with Jetpack Compose, looking like horizontal buttons. Tabs should be left aligned, and not centered. Just like in the image.
Also selected tab shouldn't show underline.
Jetpack compose has Scaffold for such case, something like this should work for you
enum class Tab {
Day,
Week,
Month,
}
#Composable
fun TestView(
) {
var selectedTab by remember { mutableStateOf(Tab.Day) }
Scaffold(topBar = {
Row(Modifier.padding(5.dp)) {
Tab.values().forEach { tab ->
BottomBarButton(
tab.name,
selected = selectedTab == tab,
onSelect = {
selectedTab = tab
},
)
}
}
}) {
when (selectedTab) {
Tab.Day -> Text("$selectedTab content")
Tab.Week -> Text("$selectedTab content")
Tab.Month -> Text("$selectedTab content")
}
}
}
#Composable
fun BottomBarButton(
text: String,
selected: Boolean,
onSelect: () -> Unit
) {
Text(
text,
modifier = Modifier
.background(
if (selected)
Color.Green
else
Color.Transparent
)
.clickable(onClick = onSelect)
.padding(10.dp)
)
}
If you need bottom bar, just replace topBar = { with bottomBar = {
See more about Scaffold
How about this one?
ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
backgroundColor = colorResource(id = R.color.white),
divider = { TabRowDefaults.Divider(color = colorResource(id = R.color.transparent)) },
edgePadding = 0.dp
) {
//draw your tab
}
Try this out
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
#Composable
fun RecentTabs(tabs: List<CustomTabs>, onSelectedTab: (CustomTabs) -> Unit) {
var selectedTab by remember { mutableStateOf("") }
#Composable
fun RecentTabItem(text: String, selectedColor: Color = Color.Green, onSelect: () -> Unit) {
val selected = text == selectedTab
Text(
text,
modifier = Modifier
.clip(CircleShape)
.background(
if (selected)
selectedColor
else
Color.Transparent
)
.clickable(
onClick = {
selectedTab = text
onSelect.invoke()
}
)
.padding(vertical = 8.dp, horizontal = 18.dp)
)
}
Row(
Modifier
.scrollable(rememberScrollState(), orientation = Orientation.Horizontal)
.padding(horizontal = 5.dp, vertical = 8.dp)) {
tabs.forEach {
RecentTabItem(text = it.name, selectedColor = it.color.toColor(Color.Magenta)) { onSelectedTab.invoke(it) }
Spacer(modifier = Modifier.width(5.dp))
}
}
}
Usage:
RecentTabs(tabs = listOf(
CustomeTabs(1,"Tab1", color = Color.Blue.toString()),
CustomeTabs(2,"Tab2", color = Color.Gray.toString()),
CustomeTabs(3,"Tab3", color = Color.Red.toString())
), onSelectedTab = {
Log.d(TAG, "RecentScreen() called ${it.toString()}")
})
I want to use Icon button in Jetpack Compose, but I couldn't understand Jetpack Compose docs. Can someone share a sample code for toggle button similar to this one?
When user clicks on a button, I want to animate it like in Instagram with a bounce animation.
You can combine IconToggleButton with transitions. Sample code (using version 1.0.0-beta05):
import android.annotation.SuppressLint
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
#SuppressLint("UnusedTransitionTargetStateParameter")
#Composable
fun FavoriteButton(
isChecked: Boolean,
onClick: () -> Unit
) {
IconToggleButton(
checked = isChecked,
onCheckedChange = { onClick() }
) {
val transition = updateTransition(isChecked, label = "Checked indicator")
val tint by transition.animateColor(
label = "Tint"
) { isChecked ->
if (isChecked) Color.Red else Color.Black
}
val size by transition.animateDp(
transitionSpec = {
if (false isTransitioningTo true) {
keyframes {
durationMillis = 250
30.dp at 0 with LinearOutSlowInEasing // for 0-15 ms
35.dp at 15 with FastOutLinearInEasing // for 15-75 ms
40.dp at 75 // ms
35.dp at 150 // ms
}
} else {
spring(stiffness = Spring.StiffnessVeryLow)
}
},
label = "Size"
) { 30.dp }
Icon(
imageVector = if (isChecked) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
contentDescription = null,
tint = tint,
modifier = Modifier.size(size)
)
}
}
#Preview("Favorite Button")
#Composable
fun FavoriteButtonPreview() {
val (isChecked, setChecked) = remember { mutableStateOf(false) }
MaterialTheme {
Surface {
FavoriteButton(
isChecked = isChecked,
onClick = { setChecked(!isChecked) }
)
}
}
}
These are the required dependencies for this sample:
dependencies {
implementation 'androidx.core:core-ktx:1.6.0-alpha01'
implementation "androidx.compose.ui:ui:1.0.0-beta05"
implementation "androidx.compose.material:material:1.0.0-beta05"
implementation "androidx.compose.ui:ui-tooling:1.0.0-beta05"
implementation 'androidx.activity:activity-compose:1.3.0-alpha06'
}
For more details about transition and keyframes and ways to customize them, see Compose's Animation documentation.
Maybe it's a good idea to use Chips instead.
Filter chips use tags or descriptive words to filter content. They can be a good alternative to toggle buttons or checkboxes.
Also it's possible to use Modifier.selectable.