androidkotlinandroid-jetpack-composeandroid-jetpack

How to align view at end in Row jetpack compose


I want to make view at end of screen. Something like this

Expected Output

enter image description here

I tried some piece of code but it not working correctly

@Preview(showBackground = true)
@Composable
fun Xyz(
    title: String = "Hello world",
    subTitle: String = "How are you?",
    showImage: Boolean = true
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon(Icons.Rounded.ShoppingCart, contentDescription = null)
        Column(Modifier.padding(start = dimensionResource(R.dimen.home_progress_test_in_progress_padding))) {
            AnimatedVisibility(visible = title.isNotEmpty()) {
                Text(text = title, maxLines = 1, overflow = TextOverflow.Ellipsis)
            }
            AnimatedVisibility(visible = subTitle.isNotEmpty()) {
                Text(text = subTitle, maxLines = 1, overflow = TextOverflow.Ellipsis)
            }
        }
        AnimatedVisibility(visible = showImage) {
            Image(
                Icons.Rounded.AccountBox,
                contentDescription = null
            )
        }
    }
}

Actual Output

enter image description here

I am trying to add Modifier.align(End) with the help of this answer but it gives me error

    AnimatedVisibility(
        modifier = Modifier.align(End),
        visible = showImage
    ) {
        Image(
            Icons.Rounded.AccountBox,
            contentDescription = null
        )
    }

enter image description here


Solution

  • You can use a Spacer with Modifier.weight(1f) inside Row. It's normal that you can't find Modifier.align that takes Alignment.Horizontal inside RowScope. That answer in link calls Modifier.align of ColumnScope

    @Preview(showBackground = true)
    @Composable
    fun Xyz(
        title: String = "Hello world",
        subTitle: String = "How are you?",
        showImage: Boolean = true
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .border(1.dp, Color.Red),
            verticalAlignment = Alignment.CenterVertically
        ) {
    
            Icon(Icons.Rounded.ShoppingCart, contentDescription = null)
            Column(Modifier.padding(start = 16.dp)) {
                AnimatedVisibility(visible = title.isNotEmpty()) {
                    Text(text = title, maxLines = 1, overflow = TextOverflow.Ellipsis)
                }
                AnimatedVisibility(visible = subTitle.isNotEmpty()) {
                    Text(text = subTitle, maxLines = 1, overflow = TextOverflow.Ellipsis)
                }
            }
    
            
            Spacer(modifier = Modifier.weight(1f))
            
            AnimatedVisibility(
                visible = showImage
            ) {
                Image(
                    Icons.Rounded.AccountBox,
                    contentDescription = null
                )
            }
        }
    }
    

    RowScope which is available inside a Row is defined as

    @LayoutScopeMarker
    @Immutable
    @JvmDefaultWithCompatibility
    interface RowScope {
        /**
         * Size the element's width proportional to its [weight] relative to other weighted sibling
         * elements in the [Row]. The parent will divide the horizontal space remaining after measuring
         * unweighted child elements and distribute it according to this weight.
         * When [fill] is true, the element will be forced to occupy the whole width allocated to it.
         * Otherwise, the element is allowed to be smaller - this will result in [Row] being smaller,
         * as the unused allocated width will not be redistributed to other siblings.
         *
         * @param weight The proportional width to give to this element, as related to the total of
         * all weighted siblings. Must be positive.
         * @param fill When `true`, the element will occupy the whole width allocated.
         */
        @Stable
        fun Modifier.weight(
            /*@FloatRange(from = 0.0, fromInclusive = false)*/
            weight: Float,
            fill: Boolean = true
        ): Modifier
    
        /**
         * Align the element vertically within the [Row]. This alignment will have priority over the
         * [Row]'s `verticalAlignment` parameter.
         *
         * Example usage:
         * @sample androidx.compose.foundation.layout.samples.SimpleAlignInRow
         */
        @Stable
        fun Modifier.align(alignment: Alignment.Vertical): Modifier
    
        /**
         * Position the element vertically such that its [alignmentLine] aligns with sibling elements
         * also configured to [alignBy]. [alignBy] is a form of [align],
         * so both modifiers will not work together if specified for the same layout.
         * [alignBy] can be used to align two layouts by baseline inside a [Row],
         * using `alignBy(FirstBaseline)`.
         * Within a [Row], all components with [alignBy] will align vertically using
         * the specified [HorizontalAlignmentLine]s or values provided using the other
         * [alignBy] overload, forming a sibling group.
         * At least one element of the sibling group will be placed as it had [Alignment.Top] align
         * in [Row], and the alignment of the other siblings will be then determined such that
         * the alignment lines coincide. Note that if only one element in a [Row] has the
         * [alignBy] modifier specified the element will be positioned
         * as if it had [Alignment.Top] align.
         *
         * @see alignByBaseline
         *
         * Example usage:
         * @sample androidx.compose.foundation.layout.samples.SimpleAlignByInRow
         */
        @Stable
        fun Modifier.alignBy(alignmentLine: HorizontalAlignmentLine): Modifier
    
        /**
         * Position the element vertically such that its first baseline aligns with sibling elements
         * also configured to [alignByBaseline] or [alignBy]. This modifier is a form
         * of [align], so both modifiers will not work together if specified for the same layout.
         * [alignByBaseline] is a particular case of [alignBy]. See [alignBy] for
         * more details.
         *
         * @see alignBy
         *
         * Example usage:
         * @sample androidx.compose.foundation.layout.samples.SimpleAlignByInRow
         */
        @Stable
        fun Modifier.alignByBaseline(): Modifier
    
        /**
         * Position the element vertically such that the alignment line for the content as
         * determined by [alignmentLineBlock] aligns with sibling elements also configured to
         * [alignBy]. [alignBy] is a form of [align], so both modifiers
         * will not work together if specified for the same layout.
         * Within a [Row], all components with [alignBy] will align vertically using
         * the specified [HorizontalAlignmentLine]s or values obtained from [alignmentLineBlock],
         * forming a sibling group.
         * At least one element of the sibling group will be placed as it had [Alignment.Top] align
         * in [Row], and the alignment of the other siblings will be then determined such that
         * the alignment lines coincide. Note that if only one element in a [Row] has the
         * [alignBy] modifier specified the element will be positioned
         * as if it had [Alignment.Top] align.
         *
         * Example usage:
         * @sample androidx.compose.foundation.layout.samples.SimpleAlignByInRow
         */
        @Stable
        fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int): Modifier
    }