Search code examples
androidkotlinandroid-jetpack-composeandroid-vectordrawable

Rotate ImageVector in Compose


I'm storing a material icon in a data class like so:

import androidx.compose.ui.graphics.vector.ImageVector

data class Item(
    val icon: ImageVector
)

val item = Item(Icons.Filled.Send)

The item is later passed to a composable where it's drawn using a VectorPainter.

How do I rotate the ImageVector by 90 degrees? Ideally this would result in an ImageVector that I can still store in the data class.


Solution

  • You can build a rotated ImageVector copying all the groups and paths from the source ImageVector, and applying required rotation parameter (combined with correct values of pivotX/pivotY that determines the center point of rotation).

    fun ImageVector.Companion.copyFrom(
        src: ImageVector,
        rotation: Float = src.root.rotation,
        pivotX: Float = src.root.pivotX,
        pivotY: Float = src.root.pivotY,
    ) = ImageVector.Builder(
        name = src.name,
        defaultWidth = src.defaultWidth,
        defaultHeight = src.defaultHeight,
        viewportWidth = src.viewportWidth,
        viewportHeight = src.viewportHeight,
        tintColor = src.tintColor,
        tintBlendMode = src.tintBlendMode,
        autoMirror = src.autoMirror,
    ).addGroup(
        src = src.root,
        rotation = rotation,
        pivotX = pivotX,
        pivotY = pivotY,
    ).build()
    
    private fun ImageVector.Builder.addNode(node: VectorNode) {
        when (node) {
            is VectorGroup -> addGroup(node)
            is VectorPath -> addPath(node)
        }
    }
    
    private fun ImageVector.Builder.addGroup(
        src: VectorGroup,
        rotation: Float = src.rotation,
        pivotX: Float = src.pivotX,
        pivotY: Float = src.pivotY,
    ) = apply {
        group(
            name = src.name,
            rotate = rotation,
            pivotX = pivotX,
            pivotY = pivotY,
            scaleX = src.scaleX,
            scaleY = src.scaleY,
            translationX = src.translationX,
            translationY = src.translationY,
            clipPathData = src.clipPathData,
        ) {
            src.forEach { addNode(it) }
        }
    }
    
    private fun ImageVector.Builder.addPath(src: VectorPath) = apply {
        addPath(
            pathData = src.pathData,
            pathFillType = src.pathFillType,
            name = src.name,
            fill = src.fill,
            fillAlpha = src.fillAlpha,
            stroke = src.stroke,
            strokeAlpha = src.strokeAlpha,
            strokeLineWidth = src.strokeLineWidth,
            strokeLineCap = src.strokeLineCap,
            strokeLineJoin = src.strokeLineJoin,
            strokeLineMiter = src.strokeLineMiter,
        )
    }
    

    The usage can look like this:

    val icon = ImageVector.vectorResource(R.drawable.ic_smile)
    val rotatedIcon = ImageVector.copyFrom(
        src = icon,
        rotation = 90f,
        pivotX = icon.defaultWidth.value / 2,
        pivotY = icon.defaultHeight.value / 2,
    )
    

    preview