Extending Material Theme with Kotlin extension property doesn't work - kotlin

The official documentations gave this code example:
// Use with MaterialTheme.typography.textFieldInput
val Typography.textFieldInput: TextStyle
get() = TextStyle(/* ... */)
So I defined my own h1 style as follows
val Typography.h1: TextStyle
get() = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
and use it
#Preview
#Composable
fun GenericEmptyState(
#PreviewParameter(EmptyStatePreviewParameters::class) data: EmptyStateData
) {
MaterialTheme {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize(1f)
.background(Color.White)
.padding(24.dp)
) {
Image(
painterResource(data.image),
contentDescription = "illustration",
modifier = Modifier
.size(150.dp)
.padding(6.dp)
)
Text(data.title, Modifier.padding(6.dp), style = MaterialTheme.typography.h1)
//...
But the result text style I got is definitely not size 20 or bold font weight
It looks like the default h1 style in Material Theme
Can't see what's wrong myself
Can anyone?

Well, the Typography class itself has a member named h1 which will be used instead of your extension. You should actually get this warning in Android studio which explains it pretty well:
Extension is shadowed by a member: public final val h1: TextStyle
The documentation you mention is called "Custom design systems in Compose" - this is meant for someone who doesn't want to use the default Typography properties - either needs more text styles than provided or simply wants to name them differently. You should probably look at this section first - it shows how to create your own theme and override the default Typography styles. It should look something like this:
#Composable
fun MyTheme(
content: #Composable () -> Unit,
) {
MaterialTheme(
typography = Typography(
h1 = TextStyle(...),
),
content = content,
)
}
#Composable
fun MyContent() {
MyTheme {
// MaterialTheme.typography.h1 will be your defined TextStyle here
}
}

Related

change background color surface light/dark jetpack compose

I'm trying to start with jetpack compose and it's getting complicated to be able to change the background automatically according to the theme chosen by the user (light dark).
I am editing the colors from theme.kt
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80,
surface = Color(0xFF0BB000),
background = Color(0xFF0BB000),
onBackground = Color(0xFF0BB000))
The problem is that when I run the app, the background color is still grey.
I think the problem is that my app doesn't take the colors from the theme, since I tried to set it directly, but it doesn't change the background color either.
Surface ( color = MaterialTheme.colorScheme.background)
If anyone has any ideas why it doesn't change color automatically and point me to it, I'd appreciate it.
I can set the palette again from my activity and change it, it depends on the mode chosen by the user, but it is not an optimal solution and it looks ugly.
I leave my activity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TruequeGualeguaychuTheme {
Surface ( color = MaterialTheme.colorScheme.background){
Text(
modifier = Modifier
.fillMaxSize()
.wrapContentHeight(),
textAlign = TextAlign.Center,
text = "Hello Android!",
)
}
}
}
}
}
It should have a green background but it does not take the color declared in ( background = Color(0xFF0BB000) )
val Colors.myBackgroundColor get() = if (isLight) Color.White else Color.Black
you can create color like this and use this color in Surface background color.
Surface(modifier = Modifier
.fillMaxSize()
.background(color = MaterialTheme.colors.myBackgroundColor)) {
// your code
}
The part it looks like you're missing is the hinted at in #vaspike's answer. All views that should utilize your custom theme should derive from an instance of the theme. For example:
#Composable
fun MyTheme(content: #Composable () -> Unit) {
MaterialTheme(
colors = LightColors, // A list of key/values I define.
content = content
)
}
#Composable
fun HomePage(){
MyTheme(){
// composables that use the theme.
}
}
This comes from the example found here, https://developer.android.com/codelabs/jetpack-compose-theming#3
The problem is that newer versions of android have dynamic colors that use a color palette based on the system.
By default the theme has this option activated which limits the setting that one can make of the color palette.
dynamicColor: Boolean = true
I have implemented similar functions before,but it's at Jb-compose desktop.Provide a reference:
MainApp.kt:
#OptIn(ExperimentalMaterialApi::class)
#Composable
#Preview
fun launchApp() {
MaterialTheme(
colors = darkColors(
primary = MyColor.primaryColor,
primaryVariant = MyColor.primaryColor,
secondary = MyColor.primaryColor,
secondaryVariant = MyColor.primaryColor,
background = MyColor.darkBackgroundColor,
surface = MyColor.darkBackgroundColor,
onPrimary = Color.White,
onSecondary = Color.White,
onBackground = Color.White,
onSurface = Color.White,
error = Color.Red,
onError = Color.White
),
){
//...
}
}
Main.kt:
fun main(args: Array<String>) = application {
Window(
onCloseRequest = ::exitApplication,
icon = painterResource("icon/xx.ico"),
title = "xx ${RuntimeContext.APP_VERSION}",
) {
MainApp.launchApp()
}
}

How can I get the font size of a text when I use Text() in Compose?

I create a new project by Empty Compose Activity wizard in Android Studio.
The Code A is generated code by Android Studio.
Text(text = "Hello $name!") displays a text with default font style, I hope to get the size and unit of the text, such as 16sp, where can I find these informations?
Code A
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Greeting("Android")
}
}
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
#Composable
fun MyApplicationTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
You can check it here.
https://developer.android.com/jetpack/compose/text
Change text size and font style
Text("Hello World", fontSize = 30.sp,fontStyle = FontStyle.Italic)
Text has a fontFamily parameter to allow setting the font used in the composable. By default, serif, sans-serif, monospace and cursive font families are included.
You can use the TextLayoutResult to get the front style
The font style is in TextLayoutInput -> TextStyle
Text("Hello World",
onTextLayout = { result: TextLayoutResult ->
Log.d(TAG, "FrontSize: "+ result.layoutInput.style.fontSize)
Log.d(TAG, "FrontStyle: "+ result.layoutInput.style.toString())
})
If you are using default theme it's in
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
// HERE
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
However, if you apply any other theme or get Text from another library can get size of your text using onTextLayout callback which returns a lot of data about your text.
Without removing default padding on top or bottom of your font size won't be able to get correct height in sp. You get the height of Text in sp with the method below. I tried using LineHeightStyle Api also need to check lineHeight either for correct results.
Column {
var heightInPx by remember { mutableStateOf(0f) }
var info by remember { mutableStateOf("") }
val heightInSp = LocalDensity.current.run { heightInPx.toSp() }
Text("Hello World",
modifier = Modifier
.border(1.dp, Color.Green)
.fillMaxWidth(),
style = TextStyle(
fontSize = 40.sp,
platformStyle = PlatformTextStyle(
includeFontPadding = false
),
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Top,
trim = LineHeightStyle.Trim.Both
)
),
onTextLayout = { result: TextLayoutResult ->
val cursorRect = result.getCursorRect(0)
info = "firstBaseline: ${result.firstBaseline}, " +
"lastBaseline: ${result.lastBaseline}\n" +
"cursorRect: $cursorRect\n" +
"getLineBottom: ${result.getLineBottom(0)}, " +
"getBoundingBox: ${result.getBoundingBox(0)}"
heightInPx =
result.size.height.toFloat()
}
)
Text("Height in px: $heightInPx, height in sp: $heightInSp")
Text(info)
}
As you can see in the Text composable source code, when you explicitly don't specify fontSize or style (which you don't), it takes font size from the default style, which is LocalTextStyle.current.
Now LocalTextStyle can contain different styles throughout your code, you can override it yourself by doing something like this:
CompositionLocalProvider(LocalTextStyle provides TextStyle(...)) {
Text() // will use specified TextStyle
}
A lot of material components do this, for example TopAppBar and Button, so you don't have to care about anything and correct style will always be used.
Finally, your style comes from the MaterialTheme composable, where is this piece of code:
ProvideTextStyle(value = typography.body1) {
content()
}
So your font size comes from the Typography.body1 text style.

JETPACK COMPOSE "Cannot find parameter with this name: contentAlignment"

Am i missing an import or something? Why is this basic function giving me errors all of a sudden
No, you didn't miss anything.
You need only to add your content parameter, and your alignment parameter would be normal.
Example:
Box(modifier = Modifier,
contentAlignment = Alignment.TopStart,
content = {}
)
It happens because exists a Box constructor with no content as in your example code:
#Composable
fun Box(modifier: Modifier): Unit
The contentAlignment doesn't exist in this constructor.
You can use the constructor with the contentAlignment parameter and in this case you have to pass also the content parameter:
#Composable
inline fun Box(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
propagateMinConstraints: Boolean = false,
content: #Composable #ExtensionFunctionType BoxScope.() -> Unit
): Unit
For example:
Box(
modifier = Modifier,
contentAlignment = Alignment.Center
){
//content
}
I have also something to add . After what you have typed just open the semicolons and the error goes away , like i mentioned below
Box(modifier = Modifier,
contentAlignment = Alignment.TopStart
){
// Semicolon opening
}

Should I use remember with animateDpAsState?

The Code A is from the official sample code here.
I know that in order to preserve state across recompositions, remember the mutable state using remember.
I think that the code val extraPadding by animateDpAsState(...) should be val extraPadding by remember { animateDpAsState(...) }, is it right?
BTW, val extraPadding by remember { animateDpAsState(...) } will cause the error
Composable calls are not allowed inside the calculation parameter of inline fun remember(calculation: () -> TypeVariable(T)): TypeVariable(T)
Code A
#Composable
private fun Greeting(name: String) {
var expanded by remember { mutableStateOf(false) }
val extraPadding by animateDpAsState( //Should I add remember
if (expanded) 48.dp else 0.dp
)
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier
.weight(1f)
.padding(bottom = extraPadding)
) {
Text(text = "Hello, ")
Text(text = name)
}
OutlinedButton(
onClick = { expanded = !expanded }
) {
Text(if (expanded) "Show less" else "Show more")
}
}
}
}
No, you shouldn't. This function is marked with #Composable so it should be used directly in the view builder.
animateDpAsState will calculate its value depending on targetValue on each recomposition.
If you check it source code you'll see, that it uses remember inside, that's why it's marked with #Composable, and that's why you shouldn't bother about remembering some values manually.
For anyone that comes across this in the future, instead of attempting to remember the animateDpAsState it would be better to remember the expansion state. Because in this case we're trying to remember the state in an item of a list we use rememberSaveable:
var expanded by rememberSaveable { mutableStateOf(false) }
A very similar case of storing indexes across list items is shown in the official documentation here

Adjust Texview on the basis of Imageview in Jetpack compose

Align Imageview in left and 3 textview in right in such a way that height of the image is depending on the aspect ratio and the first text view should be align with the top of imageview and bottom of 3rd textview should be align with bottom of image. The space between these 2 textview should be given to 2nd textview.
Expected:
#Preview
#Composable
fun ShowUi() {
Row
modifier = Modifier
.padding(10.dp)
.wrapContentHeight()
.fillMaxWidth()
) {
Box(
modifier = Modifier
.weight(7f)
.aspectRatio(1.77f)
.background(Color.Yellow)
) {
}
Column(
modifier = Modifier
.weight(3f)
.background(Color.Green)
) {
Text(
text = "Title 1",
fontSize = 20.sp,
maxLines = 1,
modifier = Modifier.background(Color.Green)
)
Text(
text = "You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.",
overflow = TextOverflow.Ellipsis,
modifier = Modifier.background(Color.Gray),
)
Text(
text = "PLAY NOW",
modifier = Modifier.background(Color.Green),
maxLines = 1
)
}
}
}
Output of the above snippet:
Note: Can't use maxLines in 2nd TextView as the number of lines which can be shown is dynamic i.e depends on the space available between 1st and 3rd textview.
You can consider using Compose ConstraintLayout to achive this.
https://developer.android.com/jetpack/compose/layouts/constraintlayout
You can prevent Row from growing with .height(IntrinsicSize.Min): it will not allow the children to grow beyond maximum inherent height.
Next step is to override min intrinsic height for your Column: but default it'll be calculated on sum of text sizes. To do so you can create the following modifier:
fun Modifier.zeroMinIntrinsicHeight() = then(
object : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
) = measurable
.measure(constraints)
.run {
layout(width, height) {
placeRelative(0, 0)
}
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
): Int {
return 0
}
}
)
In order to compress your middle text, you need to apply .weight(1f) modifier: this way, all other view sizes inside Column will be calculated before the long text.
There is also a bug that prevents TextOverflow.Ellipsis from working correctly with the limited size, be sure to star it to bring more attention to the problem. Therefore, I removed this argument.
Result code:
Row(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
.background(Color.Red)
.height(IntrinsicSize.Min)
) {
Box(
modifier = Modifier
.weight(7f)
.aspectRatio(1.77f)
.background(Color.Yellow)
) {
}
Column(
modifier = Modifier
.weight(3f)
.background(Color.Green)
.zeroMinIntrinsicHeight()
) {
Text(
text = "Title 1",
fontSize = 20.sp,
maxLines = 1,
modifier = Modifier.background(Color.Green)
)
Text(
text = "You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.",
modifier = Modifier
.background(Color.Gray)
.weight(1f)
)
Text(
text = "PLAY NOW",
modifier = Modifier.background(Color.Green),
maxLines = 1
)
}
}
Output: