I am practicing JetPack compose, but my text won't align in the center for some reason within the 2 surfaces. Any tips why?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SimpleLayoutsTheme {
// A surface container using the 'background' color from the theme
MainScreen("Hello World!")
}
}
}
}
#Composable
fun MainScreen(message: String) {
Surface {
Column {
Surface(modifier = Modifier
.width(500.dp)
.height(250.dp)) {
Text(text = message, Modifier.align(Alignment.CenterHorizontally))
}
Surface(modifier = Modifier
.width(500.dp)
.height(250.dp)) {
Text(text = message, Modifier.align(Alignment.CenterHorizontally))
}
}
}
}
I tried adding .FillMaxWidth() as a modifier to text, but it didn't help.
You can not align directly on a surface. You have to wrap your content with Column, Row, Box, etc.
You can change your code like the following
#Composable
fun MainScreen(message: String) {
Surface {
Column {
Surface(
modifier = Modifier
.width(500.dp)
.height(250.dp),
color = Color.Green
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = message)
}
}
Surface(
modifier = Modifier
.width(500.dp)
.height(250.dp),
color = Color.Blue
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = message
)
}
}
}
}
}
Related
I have simple viewpager and toolbar in my code :
#Composable
fun DefaultAppBar(
mainViewModel: MainViewModel
) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl ) {
TopAppBar(
backgroundColor = Color.White
,
title = {
Text(text = "",
modifier = Modifier
.clickable { }
// margin
.padding(start = 160.dp)
)
},
actions = {
if(mainViewModel.searchWidgetVisibility.value) {
IconButton(
onClick = { },
modifier = Modifier,
) {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = "Search Icon",
tint = Color.Black,
modifier = Modifier,
)
}
}
}
)
}
}
As you can see above, I have an search icon which I want it to be visible whenever I enter SecondScreen in my viewpager .
MainViewModel.kt :
class MainViewModel:ViewModel() {
private val _searchWidgetVisibility: MutableState<Boolean> = mutableStateOf(value = false)
val searchWidgetVisibility: MutableState<Boolean> = _searchWidgetVisibility
fun updateSearchWidgetVisibility(newValue:Boolean) {
_searchWidgetVisibility.value = newValue
}
}
The searchWidgetVisibility controls the visibility of my search icon.
These are my pages in my viewpager :
#Composable
fun SecondScreen(
mainViewModel: MainViewModel
) {
mainViewModel.updateSearchWidgetVisibility(true)
Box(
modifier = Modifier
.fillMaxSize()
.background(color = C2)
,
contentAlignment = Alignment.Center
) {
Text(text = "second screen")
}
}
#Composable
fun FirstScreen(
mainViewModel: MainViewModel
) {
mainViewModel.updateSearchWidgetVisibility(false)
Box(
modifier = Modifier
.fillMaxSize()
.background(color = C2)
,
contentAlignment = Alignment.Center
) {
Text(text = "first screen")
}
}
But that does not work. when my search icon become visible for the first time,
when I navigate to the FirstScreen, It does not become invisible.
what is the problem here?
You need to place your mainViewModel.updateSearchWidgetVisibility(false) to not be called in your composition, Compose code needs to be side effect free. Because Composables can run at any time, in parallel or not at all. The position of where you are running that line of code, is not guaranteed to be executed at all.
More info here: https://developer.android.com/jetpack/compose/mental-model#parallel
If you need those functions to run when that composable is added to the composition, you should use LaunchedEffect to do so.
#Composable
fun FirstScreen(
mainViewModel: MainViewModel
) {
LaunchedEffect(mainViewModel) {
mainViewModel.updateSearchWidgetVisibility(false)
}
Box(
modifier = Modifier
.fillMaxSize()
.background(color = C2)
,
contentAlignment = Alignment.Center
) {
Text(text = "first screen")
}
}
I am trying to learn jetpack compose and this is the code I am trying out
My Activity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
UserList()
// MessageCard()
}
}
}
My Data class
data class User(val id: Int)
val users = listOf(User(1), User(2), User(3), User(4), User(5), User(5))
#Composable
fun UserList( ) {
LazyColumn {
items(users){
user ->MessageCard()
}
}
}
#Composable
fun MessageCard() {
val context = LocalContext.current
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.clickable {
Toast.makeText(context, R.string.app_name,Toast.LENGTH_SHORT)
},
backgroundColor = MaterialTheme.colors.surface.copy(alpha = .5f)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "Title",
modifier = Modifier
.padding(horizontal = 15.dp)
.fillMaxWidth(),
style = MaterialTheme.typography.h6,
)
Text(
text = "Sub title", modifier = Modifier
.padding(horizontal = 15.dp)
.fillMaxWidth(),
style = MaterialTheme.typography.subtitle1
)
}
}
}
}
The Toast message does not appear when I click a MessageCard
Any help is greatly appreciated.
You forgot to call Toast's show()
Toast.makeText(context, R.string.app_name,Toast.LENGTH_SHORT).show()
New to Compose and struggling hard with more complex state cases.
I cant seem to change the text dynamically in these cards, only set the text manually. Each button press should change the text in a box, starting left to right, leaving the next boxes empty.
What is wrong here?
UI:
val viewModel = HomeViewModel()
val guessArray = viewModel.guessArray
#Composable
fun HomeScreen() {
Column(
modifier = Modifier
.fillMaxWidth()
) {
WordGrid()
Keyboard()
}
}
#Composable
fun WordGrid() {
CardRow()
}
#Composable
fun Keyboard() {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
MyKeyboardButton(text = "A", 35)
MyKeyboardButton(text = "B", 35)
}
}
#Composable
fun MyCard(text: String) {
Card(
modifier = Modifier
.padding(4.dp, 8.dp)
.height(55.dp)
.aspectRatio(1f),
backgroundColor = Color.White,
border = BorderStroke(2.dp, Color.Black),
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = text,
fontSize = 20.sp,
)
}
}
}
#Composable
fun CardRow() {
guessArray.forEach { rowCards ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
rowCards.forEach {
MyCard(it)
println(it)
}
}
}
}
#Composable
fun MyKeyboardButton(text: String, width: Int) {
Button(
onClick = {
guessArray[viewModel.currentRow][viewModel.column] = text
viewModel.column += 1
},
modifier = Modifier
.width(width.dp)
.height(60.dp)
.padding(0.dp, 2.dp)
) {
Text(text = text, textAlign = TextAlign.Center)
}
}
ViewModel:
class HomeViewModel : ViewModel() {
var currentRow = 0
var guessArray = Array(5) { Array(6) { "" }.toMutableList() }
var column = 0
}
The grid is created, but the text is never changed.
To make Compose view recompose with the new value, a mutable state should be used.
You're already using it with remember in your composable, but it also needs to be used in your view model for properties, which should trigger recomposition.
class HomeViewModel : ViewModel() {
var currentRow = 0
val guessArray = List(5) { List(6) { "" }.toMutableStateList() }
var column = 0
}
I am wanting to build the following layout in Compose:
I have written the following code for this:
#Composable
fun MainScreenContent(){
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier
.wrapContentWidth(Alignment.CenterHorizontally)
.wrapContentHeight(Alignment.CenterVertically)
) {
Row() {
Column(Modifier.weight(0.3F)) {
Greeting("Android")
Spacer(Modifier.padding(16.dp))
Greeting("User")
}
Surface(modifier = Modifier.weight(0.4F),
color = MaterialTheme.colors.primaryVariant,
){
Spacer(Modifier.padding(16.dp))
}
Column(Modifier.weight(0.3F)) {
Greeting("Android")
Spacer(Modifier.padding(16.dp))
Greeting("User")
}
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!",
Modifier
.padding(16.dp), fontSize = 30.sp)
}
But with this, I am getting the following output:
How can I achieve the objective?
Set the parent Surface's height to IntrinsicSize.Max, and add fillMaxHeight() to the inner Surface's modifier.
#Composable
fun MainScreenContent() {
Surface(
...,
modifier = Modifier
.wrapContentWidth(Alignment.CenterHorizontally)
.height(IntrinsicSize.Max),
) {
Row {
...
Surface(
modifier = Modifier
.weight(0.4F)
.fillMaxHeight(),
color = MaterialTheme.colors.primaryVariant,
) {
Spacer(Modifier.padding(16.dp))
}
...
}
}
}
As can be seen in gif
when Column that contains of Text, Spacer, and LazyRowForIndexed is touched ripple is not propagating with circular motion. And it gets touched effect even when horizontal list is touched.
#Composable
fun Chip(modifier: Modifier = Modifier, text: String) {
Card(
modifier = modifier,
border = BorderStroke(color = Color.Black, width = Dp.Hairline),
shape = RoundedCornerShape(8.dp)
) {
Row(
modifier = Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier.preferredSize(16.dp, 16.dp)
.background(color = MaterialTheme.colors.secondary)
)
Spacer(Modifier.preferredWidth(4.dp))
Text(text = text)
}
}
}
#Composable
fun TutorialSectionCard(model: TutorialSectionModel) {
Column(
modifier = Modifier
.padding(top = 8.dp)
.clickable(onClick = { /* Ignoring onClick */ })
.padding(16.dp)
) {
Text(text = model.title, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h6)
Spacer(Modifier.preferredHeight(8.dp))
Providers(AmbientContentAlpha provides ContentAlpha.medium) {
Text(model.description, style = MaterialTheme.typography.body2)
}
Spacer(Modifier.preferredHeight(16.dp))
LazyRowForIndexed(items = model.tags) { _: Int, item: String ->
Chip(text = item)
Spacer(Modifier.preferredWidth(4.dp))
}
}
}
#Preview
#Composable
fun TutorialSectionCardPreview() {
val model = TutorialSectionModel(
clazz = MainActivity::class.java,
title = "1-1 Column/Row Basics",
description = "Create Rows and Columns that adds elements in vertical order",
tags = listOf("Jetpack", "Compose", "Rows", "Columns", "Layouts", "Text", "Modifier")
)
Column {
TutorialSectionCard(model)
TutorialSectionCard(model)
TutorialSectionCard(model)
}
}
What should be done to have circular effect, but not when list itself or an item from list is touched, or scrolled?
You have to apply a Theme to your composable, which in turn provides a default ripple factory, or you have to set the ripple explicitly:
#Preview
#Composable
fun TutorialSectionCardPreview() {
MaterialTheme() {
Column {
TutorialSectionCard
...
}
}
}
or
Column(
modifier = Modifier
.padding(top = 8.dp)
.clickable(
onClick = { /* Ignoring onClick */ },
indication = rememberRipple(bounded = true)
)
.padding(16.dp)
) {
// content
}
(As of compose version 1.0.0-alpha09 there seems to be no way to prevent the ripple from showing when content is scrolled)
I'm using this approach:
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(
color = Color.Black
),
onClick = {
}
)
I also figured out how to keep ripple only for the card not the scrollable list that contains tags. To prevent ripple only move through cards use a Box which places it's children as a stack, and add clickable to section that contains header and text.
#Composable
fun TutorialSectionCard(
model: TutorialSectionModel,
onClick: ((TutorialSectionModel) -> Unit)? = null
) {
Card(
modifier = Modifier.padding(vertical = 3.dp, horizontal = 8.dp),
elevation = 1.dp,
shape = RoundedCornerShape(8.dp)
) {
Box(
contentAlignment = Alignment.BottomStart
) {
TutorialContentComponent(onClick, model)
TutorialTagsComponent(model)
}
}
}
#Composable
private fun TutorialContentComponent(
onClick: ((TutorialSectionModel) -> Unit)?,
model: TutorialSectionModel
) {
Column(Modifier
.clickable(
onClick = { onClick?.invoke(model) }
)
.padding(16.dp)
) {
Text(
text = model.title,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h6
)
// Vertical spacing
Spacer(Modifier.height(8.dp))
// Description text
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(model.description, style = MaterialTheme.typography.body2)
}
// Vertical spacing
Spacer(Modifier.height(36.dp))
}
}
#Composable
private fun TutorialTagsComponent(model: TutorialSectionModel) {
Column(Modifier.padding(12.dp)) {
// Horizontal list for tags
LazyRow(content = {
items(model.tags) { tag ->
TutorialChip(text = tag)
Spacer(Modifier.width(8.dp))
}
})
}
}