I want to select all the characters in the text field when the text field is shown on the screen. When passing the same index like TextRange(2,2) it works fine but when using a different index like TextRange(0, name.length)), the selection is not working. I have mentioned the function below
#Composable
fun TestFunction(name: String) {
var fieldValue by remember {
mutableStateOf(TextFieldValue(name, selection = TextRange(0, name.length)))
}
val focusRequester by remember {
mutableStateOf(FocusRequester())
}
DisposableEffect(Unit) {
focusRequester.requestFocus()
onDispose { }
}
BasicTextField(
value = fieldValue,
onValueChange = { fieldValue = it },
modifier = Modifier.focusRequester(focusRequester),
)
}
Selection color depends on primary and background colors from your theme. You can see it in the source code of rememberTextSelectionColors which is used internally.
So if you can't see selection, that's probably because of your theme.
Check out how you can override this value without modifying your theme in this answer. To make it work for whole app you can embed this CompositionLocalProvider to your theme, like this:
#Composable
fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkThemeColors
} else {
LightThemeColors
}
val customTextSelectionColors = TextSelectionColors(
handleColor = Color.Red,
backgroundColor = Color.Red.copy(alpha = 0.4f)
)
MaterialTheme(
colors = colors,
typography = typography,
shapes = shapes,
) {
CompositionLocalProvider(
LocalTextSelectionColors provides customTextSelectionColors,
content = content,
)
}
}
Related
I have a Text and I want to draw a circle that should have a color that exists inside my resource file.
Text(
modifier = Modifier.align(Alignment.TopEnd).drawBehind {
drawCircle(
color = colorResource(R.color.primary),
radius = 96.00f
)
},
text = "X"
)
But I get:
#Composable invocations can only happen from the context of a #Composable function
If I use color = Color.Red, it works fine. So I don't want to use any of those colors, I want to use mine. So I thought to make a conversion, but I cannot find any solution. How can I convert that?
You need to call Composable functions from a scope that has #Composable annotation.
Box() {
val color = colorResource(R.color.black)
Text(
modifier = Modifier.align(Alignment.TopEnd).drawBehind {
drawCircle(
color = color,
radius = 96.00f
)
},
text = "X"
)
}
colorResource is a #Composable function
#Composable
#ReadOnlyComposable
fun colorResource(#ColorRes id: Int): Color {
val context = LocalContext.current
return if (Build.VERSION.SDK_INT >= 23) {
ColorResourceHelper.getColor(context, id)
} else {
#Suppress("DEPRECATION")
Color(context.resources.getColor(id))
}
}
but lambda of Modifier.drawBehind isn't
fun Modifier.drawBehind(
onDraw: DrawScope.() -> Unit
) = this.then(
DrawBackgroundModifier(
onDraw = onDraw,
inspectorInfo = debugInspectorInfo {
name = "drawBehind"
properties["onDraw"] = onDraw
}
)
)
You can check this answer for difference between function, lambdas or params with #Composable annotation or the ones without
How can I change the typography of a text with animation in jetpack compose?
try this code. i think it works
val rem = remember {mutableStateOf(true)}
Button(onClick = {rem.value = !rem.value})
{(text = "animate text!")}
val animRem = animateFloatAsState(targetValue = if (rem.value) 1f else .1f)
val decoratedLabel: #Composable (() -> Unit) =
#Composable {
val labelAnimatedStyle = lerp(
MaterialTheme.typography.subtitle1,
MaterialTheme.typography.caption,
animRem.value
)
Decoration2(
contentColor = TextFieldDefaults.textFieldColors()
.labelColor(
true,
// if label is used as a placeholder (aka not as a small header
// at the top), we don't use an error color
rem.value,
remember { MutableInteractionSource() }
).value,
typography = labelAnimatedStyle,
content = { Text(text = "label") }
)
}
decoratedLabel()
you should duplicate this function. because it is an internal function and you can't use it in your code
#Composable fun Decoration(
contentColor: Color,
typography: TextStyle? = null,
contentAlpha: Float? = null,
content: #Composable () -> Unit) {
val colorAndEmphasis: #Composable () -> Unit = #Composable {
CompositionLocalProvider(LocalContentColor provides contentColor) {
if (contentAlpha != null) {
CompositionLocalProvider(
LocalContentAlpha provides contentAlpha,
content = content
)
} else {
CompositionLocalProvider(
LocalContentAlpha provides contentColor.alpha,
content = content
)
}
}
}
if (typography != null) ProvideTextStyle(typography, colorAndEmphasis) else colorAndEmphasis()}
I have a string that contains html, how can I display this in a Jetpack compose Text?
In a TextView I would use a Spanned and do something like:
TextView.setText(Html.fromHtml("<p>something", HtmlCompat.FROM_HTML_MODE_LEGACY)
How can I do this with Text from Jetpack compose?
Same answer as Yhondri, but using HtmlCompat if you are targeting api >24:
#Composable
fun Html(text: String) {
AndroidView(factory = { context ->
TextView(context).apply {
setText(HtmlCompat.fromHtml(text, HtmlCompat.FROM_HTML_MODE_LEGACY))
}
})
}
I have done it this way instead of using TextView in AndroidView and it seems to work quite well for me. The below composable also wraps up the text and expands when you click on it.
#Composable
fun ExpandingText(
description: String,
modifier: Modifier = Modifier,
textStyle: TextStyle = MaterialTheme.typography.body2,
expandable: Boolean = true,
collapsedMaxLines: Int = 3,
expandedMaxLines: Int = Int.MAX_VALUE,
) {
val text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY)
} else {
HtmlCompat.fromHtml(description, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
var canTextExpand by remember(text) { mutableStateOf(true) }
var expanded by remember { mutableStateOf(false) }
val interactionSource = remember { MutableInteractionSource() }
Text(
text = text.toString(),
style = textStyle,
overflow = TextOverflow.Ellipsis,
maxLines = if (expanded) expandedMaxLines else collapsedMaxLines,
modifier = Modifier
.clickable(
enabled = expandable && canTextExpand,
onClick = { expanded = !expanded },
indication = rememberRipple(bounded = true),
interactionSource = interactionSource,
)
.animateContentSize(animationSpec = spring())
.then(modifier),
onTextLayout = {
if (!expanded) {
canTextExpand = it.hasVisualOverflow
}
}
)
}
Unfortunately, Jetpack compose does NOT support HTML yet...
So, what you could do is:
Option 1: Create your own HTML parser
Jetpack compose supports basic styling such as Bold, color, font etc.. So what you can do is loop through the original HTML text and apply text style manually.
Option 2: Integrate the old TextView into your Jetpack compose.
Please read: Adopting Compose in your app
Thanks.
You can integrate the old TextView into your Jetpack compose like follows:
AndroidView(factory = { context ->
TextView(context).apply {
text = Html.fromHtml(your_html)
}
})
More info: https://foso.github.io/Jetpack-Compose-Playground/viewinterop/androidview/
you can use the code below:
#Composable
private fun TextHtml() {
Text(text = buildAnnotatedString {
withStyle(style = SpanStyle(color = Gray600)) {
append("normal text")
}
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold,color = Gray700)) {
append("bold text ")
}
})
}
use withStyle to apply the html tags and use append() inside it to add the string
Android Jetpack Compose Colors class contains a set of color types a material themed app can be implemented with. In case my app theme requires some extra color types, how can I add these extra colors so they would be available via MaterialTheme object?
Just a small change to Valeriy Katkov's answer
In some versions of the android studio, the following code will not work
#Composable
val Colors.myExtraColor: Color
get() = if (isLight) Color.Red else Color.Green
#Composable
fun ExtraColorExample() {
Text(
text = "test",
color = MaterialTheme.colors.myExtraColor // <-- the newly added color
)
}
It will show an error
This annotation is not applicable to target 'top-level property
without backing field or delegate'
To fix this either write it like this
#get:Composable
val Colors.myExtraColor: Color
get() = if (isLight) Color.Red else Color.Green
Or
val Colors.myExtraColor: Color
#Composable
get() = if (isLight) Color.Red else Color.Green
The version I found this error in
Android Studio Arctic Fox | 2020.3.1 Canary 12
Build #AI-203.7148.57.2031.7226969, built on March 23, 2021
Runtime version: 11.0.8+10-b944.6842174 amd64
VM: OpenJDK 64-Bit Server VM by N/A
Windows 10 10.0
There are basically two approaches to this:
The naive way is to just create extension properties tot hard-code the colors. If you don't want to support multiple themes or light/dark mode this works just fine.
If you want to integrate your colors properly to the theme, you can read my article on it, as it's a bit long to inline here: https://gustav-karlsson.medium.com/extending-the-jetpack-compose-material-theme-with-more-colors-e1b849390d50
But in short, the steps are:
Create your own MyColors class that holds a reference to Colors as well as your new colors.
Create a CompositionLocal that holds a MyColor instance.
Create your theme and wrap the MaterialTheme in a CompositionLocalProvider where the CompositionLocal provides MyColors. Make sure to also assign the Colors instance from MyColors to the MaterialTheme.
Create an extension property on MaterialTheme that refers to the CompositionLocal holding MyColors. This is how you refer to your new colors.
This will allow you to introduce new colors to the theme that will update dynamically as the theme changes.
Extending Colors class
You can easily add an extension property to the Colors class so it would be available via any Colors object across your app.
#Composable
val Colors.myExtraColor: Color
get() = if (isLight) Color.Red else Color.Green
#Composable
fun ExtraColorExample() {
Text(
text = "test",
color = MaterialTheme.colors.myExtraColor // <-- the newly added color
)
}
There's an example in the compose documentation as well, see Extending Material colors.
Specifying a content alpha
In case the color you're missing differs from an existing one only by it's alpha and the purpose of the color is to change a content priority, there's no need to add an additional color to the theme. You can specify a content alpha for a hierarchy by providing a value for LocalContentAlpha.
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.medium,
LocalContentColor provides MaterialTheme.colors.onSurface
) {
// this text is displayed using ContentAlpha.medium
// and MaterialTheme.colors.onSurface color
Text("Hello world!")
}
See Content Alpha documentation for more details. There's also Content Alpha section in Jetpack Compose Theming codelab.
For Material3, I had to extend the androidx.compose.material3.ColorScheme object, since MaterialTheme.colors does not exist.
val ColorScheme.successContainer: Color #Composable
get() = if (!isSystemInDarkTheme()) Color(0xFFd6ffe0) else Color(0xFF269300)
Text(
text = "Hello World",
modifier = Modifier.background(color = MaterialTheme.colorScheme.successContainer)
)
"CompositionLocal" answer is right, but maybe not quite easy to understand.
Android Developers on youtube send a video: "Compose by example".
https://www.youtube.com/watch?v=DDd6IOlH3io
on 6:28/22:07 Introduced this usage.
Here is my sample code to learn this part, for someone to copy:
Define the theme:
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
val yellow200 = Color(0xffffeb46)
val yellow400 = Color(0xffffc000)
val yellow500 = Color(0xffffde03)
val yellowDarkPrimary = Color(0xff242316)
val yellowLightPrimary = Color(0xffd4d3f6)
class RabbitColorPalette(
val raFirstColor: Color,
val raSecColor: Color,
val raOnFirstColor: Color,
val raOnSecColor: Color
)
val rabbitPurple = RabbitColorPalette(Purple200,Purple500,Purple700,Teal200)
val rabbitYellow = RabbitColorPalette(yellow200,yellow400,yellow500,yellowDarkPrimary)
val rabbitColors = compositionLocalOf<RabbitColorPalette>{
rabbitPurple
}
#Composable
fun RabbitThemePink(
content: #Composable() () -> Unit
) {
val colors = rabbitPurple
CompositionLocalProvider(rabbitColors provides colors){
MaterialTheme(
typography = Typography,
shapes = Shapes,
content = content
)
}
}
object RabbitTheme{
val colors: RabbitColorPalette
#Composable get() = rabbitColors.current
}
#Composable
fun RabbitThemeYellow(
content: #Composable() () -> Unit
) {
val colors = rabbitYellow
CompositionLocalProvider(rabbitColors provides colors){
MaterialTheme(
typography = Typography,
shapes = Shapes,
content = content
)
}
}
usage
class RabbitThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RabbitThemePink {
SampleLayout()
}
}
}
#Composable
fun SampleLayout() {
Column {
Text(text = "sample", modifier = Modifier.background(color = RabbitTheme.colors.raFirstColor))
Text(text = "des", modifier = Modifier.background(color = RabbitTheme.colors.raSecColor))
}
}
#Preview
#Composable
fun ConstraintLayoutContentPinkPreview() {
RabbitThemePink {
SampleLayout()
}
}
#Preview
#Composable
fun ConstraintLayoutContentYellowPreview() {
RabbitThemeYellow {
SampleLayout()
}
}
}
In my case, the theme component gets its parameters from the saved settings, i.e. The app theme is independent of the device theme. For this reason, the current answers don't work for me.
My implementation is relevant for Material 3 and directly depends on the passed parameters.
Color.kt
package com.my.app.theme
import androidx.compose.ui.graphics.Color
val SuccessLightColor = Color(0xFF436915)
val SuccessLightContainerColor = Color(0xFFC2F18D)
val SuccessDarkColor = Color(0xFFA7D474)
val SuccessDarkContainerColor = Color(0xFF2D5000)
Theme.kt
package com.my.app.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme()
private val LightColorScheme = lightColorScheme()
var successColorSchemeColor by mutableStateOf(SuccessLightColor)
var successContainerColorSchemeColor by mutableStateOf(SuccessLightColor)
#Suppress("unused")
var ColorScheme.success: Color
get() = successColorSchemeColor
set(value) {
successColorSchemeColor = value
}
#Suppress("unused")
var ColorScheme.successContainer: Color
get() = successContainerColorSchemeColor
set(value) {
successContainerColorSchemeColor = value
}
#Composable
fun Theme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: #Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
colorScheme.success = if (darkTheme) {
SuccessDarkColor
} else {
SuccessLightColor
}
colorScheme.successContainer = if (darkTheme) {
SuccessDarkContainerColor
} else {
SuccessLightContainerColor
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
Use case:
Box(
modifier = Modifier
.size(128.dp)
.background(
color = MaterialTheme.colorScheme.successContainer
)
)
Is there any way to use android:autoLink feature on JetPack Compose Text?
I know, that it is maybe not "declarative way" for using this feature in one simple tag/modifier, but maybe there is some easy way for this?
For styling text I can use this way
val apiString = AnnotatedString.Builder("API provided by")
apiString.pushStyle(
style = SpanStyle(
color = Color.Companion.Blue,
textDecoration = TextDecoration.Underline
)
)
apiString.append("https://example.com")
Text(text = apiString.toAnnotatedString())
But, how can I manage clicks here? And would be great, if I programatically say what behaviour I expect from the system (email, phone, web, etc). Like it. works with TextView.
Thank you
We can achieve Linkify kind of TextView in Android Compose like this example below,
#Composable
fun LinkifySample() {
val uriHandler = UriHandlerAmbient.current
val layoutResult = remember {
mutableStateOf<TextLayoutResult?>(null)
}
val text = "API provided by"
val annotatedString = annotatedString {
pushStyle(
style = SpanStyle(
color = Color.Companion.Blue,
textDecoration = TextDecoration.Underline
)
)
append(text)
addStringAnnotation(
tag = "URL",
annotation = "https://example.com",
start = 0,
end = text.length
)
}
Text(
fontSize = 16.sp,
text = annotatedString, modifier = Modifier.tapGestureFilter { offsetPosition ->
layoutResult.value?.let {
val position = it.getOffsetForPosition(offsetPosition)
annotatedString.getStringAnnotations(position, position).firstOrNull()
?.let { result ->
if (result.tag == "URL") {
uriHandler.openUri(result.item)
}
}
}
},
onTextLayout = { layoutResult.value = it }
)
}
In the above example, we can see we give the text and also we use addStringAnnotation to set the tag. And using tapGestureFilter, we can get the clicked annotation.
Finally using UriHandlerAmbient.current we can open the link like email, phone, or web.
Reference : https://www.hellsoft.se/rendering-markdown-with-jetpack-compose/
The most important part of jetpack compose is the compatibility with native android components.
Create a component that use TextView and use it:
#Composable
fun DefaultLinkifyText(modifier: Modifier = Modifier, text: String?) {
val context = LocalContext.current
val customLinkifyTextView = remember {
TextView(context)
}
AndroidView(modifier = modifier, factory = { customLinkifyTextView }) { textView ->
textView.text = text ?: ""
LinkifyCompat.addLinks(textView, Linkify.ALL)
Linkify.addLinks(textView, Patterns.PHONE,"tel:",
Linkify.sPhoneNumberMatchFilter, Linkify.sPhoneNumberTransformFilter)
textView.movementMethod = LinkMovementMethod.getInstance()
}
}
How to use:
DefaultLinkifyText(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
text = "6999999 and https://stackoverflow.com/ works fine"
)