I'm pretty new to compose and having difficulty resizing views. I have a view where I am initially hardcoding it to some size. When clicking the view I want it to expand it to fill the width to certain extent. How is this possible with in compose?
#Composable
fun InputBar(
hint: String,
onClick: () -> Unit
) {
Card(
onClick = { /* update view to fillMaxWidth() */ },
modifier = Modifier
.width(150.dp)
.height(44.dp),
) {
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = "",
onValueChange = {
},
placeholder = { Text(hint) }
)
}
}
}
you could achieve your by using something like this:
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.math.log
private const val TAG = "InputBar"
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun InputBar(
hint: String,
onClick: () -> Unit
) {
var isFillMaxWidth by remember { mutableStateOf(false) }
val isExpandModifier = remember(isFillMaxWidth){
Modifier
.height(44.dp)
.then(
if (isFillMaxWidth) {
Log.i(TAG, "InputBar: fillMaxWidth")
Modifier.fillMaxWidth()
} else {
Log.i(TAG, "InputBar: 150")
Modifier.width(150.dp)
}
)}
Card(
modifier = isExpandModifier,
) {
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = "",
onValueChange = {
},
placeholder = { Text(hint) },
interactionSource = remember { MutableInteractionSource() }
.also { interactionSource ->
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect {
if (it is PressInteraction.Release) {
// works like onClick
isFillMaxWidth = !isFillMaxWidth
Log.i(TAG, "InputBar: onClick")
}
}
}
}
)
}
}
}
Related
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 have a custom Compose Function for WebView, in Which I want to show the Custom progress bar while loading the page but it gives this error.
Functions which invoke #Composable functions must be marked with the #Composable annotation
This is my Composable.
package com.example.devdocs
import android.graphics.Bitmap
import android.view.ViewGroup
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
#Composable
fun webView(
url: String = "www.google.com"
) {
var backEnabled by remember { mutableStateOf(false) }
var webView: WebView? = null
AndroidView(
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
settings.userAgentString
settings.domStorageEnabled
settings.setSupportZoom(true)
settings.builtInZoomControls = true
settings.displayZoomControls = false
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
backEnabled = view.canGoBack()
LoadingAnimation(
modifier = Modifier
.fillMaxWidth(),
isVisible = true
)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
LoadingAnimation(
modifier = Modifier
.fillMaxWidth(),
isVisible = false
)
}
}
webChromeClient = WebChromeClient()
loadUrl(url)
webView = this
}
}, update = {
webView = it
})
BackHandler(enabled = backEnabled) {
webView?.goBack()
}
}
"LoadingAnimation" is a custom animation class that shows the animation.
Here is Animation Class Source Code.
package com.example.devdocs
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
#Composable
fun LoadingAnimation(
modifier: Modifier = Modifier,
circleSize: Dp = 25.dp,
circleColor: Color = MaterialTheme.colorScheme.primary,
spaceBetween: Dp = 10.dp,
travelDistance: Dp = 20.dp,
isVisible: Boolean = true
) {
if (isVisible) {
val circles = listOf(
remember { Animatable(initialValue = 0f) },
remember { Animatable(initialValue = 0f) },
remember { Animatable(initialValue = 0f) }
)
circles.forEachIndexed { index, animatable ->
LaunchedEffect(key1 = animatable) {
delay(index * 100L)
animatable.animateTo(
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = 1200
0.0f at 0 with LinearOutSlowInEasing
1.0f at 300 with LinearOutSlowInEasing
0.0f at 600 with LinearOutSlowInEasing
0.0f at 1200 with LinearOutSlowInEasing
},
repeatMode = RepeatMode.Restart
)
)
}
}
val circleValues = circles.map { it.value }
val distance = with(LocalDensity.current) { travelDistance.toPx() }
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(spaceBetween)
) {
circleValues.forEach { value ->
Box(
modifier = Modifier
.size(circleSize)
.graphicsLayer {
translationY = -value * distance
}
.background(
color = circleColor,
shape = CircleShape
)
)
}
}
}
}
How can I solve this? Thanks.
Card Corner Curve Design
I want to create a design like this in jetpack compose, but I don't know how to make it.
My main concern is a blue color curve on both corners, which I don't know how to make it. Can someone help to draw me this design?
You can try this code as per your usage and implement this on your code.
package com.example.myapplication
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.MyApplicationTheme
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(20.dp)) {
Card(
elevation = 4.dp,
shape = RoundedCornerShape(20.dp)
) {
Column(modifier = Modifier.padding(10.dp)) {
Text("AB CDE", fontWeight = FontWeight.W700)
Text("+0 12345678")
Text("XYZ city.", color = Color.Gray)
}
}
Spacer(modifier = Modifier.height(30.dp))
Card(
elevation = 4.dp,
) {
Column(modifier = Modifier.padding(10.dp)) {
Text("AB CDE", fontWeight = FontWeight.W700)
Text("+0 12345678")
Text("XYZ city.", color = Color.Gray)
}
}
}
}
}
}
}
I want to change solid or gradient color to jetpack compose snack bar. Please guide me how to
change color
Here is my snack bar using material3 compose, I am looking solution to change the background color
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import compose.material.theme.ui.theme.Material3ComposeTheme
import compose.material.theme.ui.theme.Purple40
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
#OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Material3ComposeTheme {
val context = LocalContext.current
val snackState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
topBar = {
},
content = {
fun launchSnackbar(message: String, actionLabel : String?=null, duration: SnackbarDuration = SnackbarDuration.Short){
scope.launch {
snackState.showSnackbar(message = message,actionLabel=actionLabel, duration=duration)
}
}
Column(
modifier = Modifier
.padding(it)
.verticalScroll(rememberScrollState())
) {
Spacer(modifier = Modifier.height(47.dp))
Text("Snackbar", Modifier.padding(bottom = 10.dp), style = MaterialTheme.typography.labelLarge)
Button(onClick = {
// * Snackbar
launchSnackbar(message = "Hi i am snackbar message", actionLabel = "Hide", duration = SnackbarDuration.Long)
}) { Text("Snackbar",style = MaterialTheme.typography.labelLarge) }
ListDividerPadding()
Text("Toast", Modifier.padding(bottom = 10.dp), style = MaterialTheme.typography.labelLarge)
Button(onClick = {
Toast.makeText(
context,
"Hi i am toast message",
Toast.LENGTH_LONG
).show()
}) { Text("Toast",style = MaterialTheme.typography.labelLarge) }
}
}
)
Box(modifier = Modifier.fillMaxSize(), Alignment.BottomCenter){
SnackbarHost(hostState = snackState)
}
}
}
}
}
You can add SnackBar composable to SnackbarHost and change colors as
SnackbarHost(hostState = snackState) {
Snackbar(
snackbarData = it,
containerColor = Color.Green,
contentColor = Color.Red
)
}
Edit
There is no overload function that takes Brush instead of Color but you can add another Composable as with gradient color or more customization via content: #Composable () -> Unit
#Composable
fun Snackbar(
modifier: Modifier = Modifier,
action: #Composable (() -> Unit)? = null,
dismissAction: #Composable (() -> Unit)? = null,
actionOnNewLine: Boolean = false,
shape: Shape = SnackbarTokens.ContainerShape.toShape(),
containerColor: Color = SnackbarTokens.ContainerColor.toColor(),
contentColor: Color = SnackbarTokens.SupportingTextColor.toColor(),
actionContentColor: Color = SnackbarTokens.ActionLabelTextColor.toColor(),
dismissActionContentColor: Color = SnackbarTokens.IconColor.toColor(),
content: #Composable () -> Unit
)
Can be used as
Snackbar {
Row(
modifier = Modifier.background(
brush = Brush.horizontalGradient(
listOf(
Color.Red,
Color.Green,
Color.Blue
)
)
)
) {
Text("Hello World")
}
}
I am seeing a weird image flickering issue with Jetpack compose. It is a simple two card deck where the top image is animated off-screen to reveal the second card. The second card displays fine for a second and then the first image flashes on the screen. I have tried with Coil, Fresco and Glide and they all behave the same way.
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import coil.compose.rememberImagePainter
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
class MainViewModel : ViewModel() {
var images = MutableLiveData(listOf(
"https://images.pexels.com/photos/212286/pexels-photo-212286.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/163016/crash-test-collision-60-km-h-distraction-163016.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/1366944/pexels-photo-1366944.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/5878501/pexels-photo-5878501.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/3846022/pexels-photo-3846022.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
))
}
class MainActivity : ComponentActivity() {
private val model by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainScreen(model)
}
}
}
#Composable
fun MainScreen(model: MainViewModel) {
val images: List<String> by model.images.observeAsState(listOf())
Box(
modifier = Modifier.fillMaxSize()
) {
images?.take(2).reversed().forEach {
Card(url = it) {
val d = model.images.value?.toMutableList()
d?.let {
it.removeFirst()
model.images.value = it
}
}
}
}
}
#Composable
fun Card(
url: String,
advance: ()-> Unit = {},
){
val coroutineScope = rememberCoroutineScope()
var offsetX = remember(url) { Animatable(0f) }
Box(
modifier = Modifier
.offset { IntOffset(offsetX.value.roundToInt(), 0) }
.fillMaxSize()
.background(color = Color.White)
.clickable {
coroutineScope.launch {
offsetX.animateTo(
targetValue = 3000F
)
}
coroutineScope.launch {
delay(400)
advance()
}
}
) {
Image(
painter = rememberImagePainter(
data = url,
),
contentDescription = null,
modifier = Modifier
.size(400.dp, 400.dp)
)
}
}
Also threw it on a github here in case anyone wants to try it out:
https://github.com/studentjet/learncompose
The problem is this: you have two card views. After deleting the top card, compose reuses them and updates them with the new data. And while the top card loads the second image, it still shows the first one.
You could disable caching, but in that case the image would still flash because it would show an empty space first. Instead, you need to have the second card's view reused for the first one.
Especially for such cases key Composable is made for: it'll reuse content for the same key between recompositions even if order changes.
val images = remember {
mutableStateListOf(
"https://images.pexels.com/photos/212286/pexels-photo-212286.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/163016/crash-test-collision-60-km-h-distraction-163016.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/1366944/pexels-photo-1366944.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/5878501/pexels-photo-5878501.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"https://images.pexels.com/photos/3846022/pexels-photo-3846022.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
)
}
Box(
modifier = Modifier.fillMaxSize()
) {
images.take(2).reversed().forEach {
key(it) {
Card(url = it) {
images.add(
images.removeFirst()
)
}
}
}
}