Stylianos Gakis
09/30/2025, 1:34 PMShape?Stylianos Gakis
09/30/2025, 1:35 PMval degrees ... animation from 0f to 360f
...
clip(shape)
.padding(borderWidth)
.drawWithContent {
rotate(degrees = degrees) {
drawCircle(
brush = gradient,
radius = size.width,
blendMode = BlendMode.SrcIn,
)
}
drawContent()
},
Which works, however this adds a padding to the content equivalent to the borderWidth which is what I am trying to avoid here.
The second idea was to use a
val stroke = Stroke(4.dp.toPx())
drawWithContent {
drawContent()
drawPath(
Path().apply { addOutline(shape.createOutline(size, layoutDirection, this@drawWithContent)) },
Brush.sweepGradient(colors),
style = stroke,
)
}
But here I do not see a way for me to "rotate" the colors.
I've tried to look into possibilities like:
• Adding a PathEffect to the stroke but I don't think that's the use of this parameter.
• Changing the order of the colors in the sweepGradient brush itself, but then it happens too drastically, I'd optimally like this to rotate smoothly.
The third idea was to do something like:
drawWithContent {
drawContent()
rotate(degrees) {
drawCircle()
}
drawSmallerOutline() // An outline which by hopefully using some sort of BlendMode would cancel out the circle right above, but only the inner part of it, and leave the outer `borderWidth` size as-is. Effectively making it look like a normal border.
}
But I have yet again not found a way here to achieve such a way of one path cancelling out the other, without also affect the content which is drawn behind. best I could do is the inner outline cancelling out the content inside and leaving the border in-tact. But that also cleared out all of the normal content which was not rendered at all.Stylianos Gakis
09/30/2025, 1:35 PMSergey Y.
09/30/2025, 1:40 PMStylianos Gakis
09/30/2025, 1:45 PMSergey Y.
09/30/2025, 1:52 PMDid you mean that there would be X amount of paths, where X is the number of colors, all of which would be on top of each other, and all of them would span the entire border length. But each of those lines would have a start/stop along this path which would be animated throughout time. And it could be made to look seamless by making them just stop at the position which the next one starts?yes
Sergey Y.
09/30/2025, 1:54 PMStylianos Gakis
09/30/2025, 1:56 PMPathMeasure() and try to get the split paths from it from in there using .getSegment(...)?
Perhaps using Path.divide() somehow?
This is completely uncharted territory for me so I am just looking at the names and seeing what might sound fitting 😄Sergey Y.
09/30/2025, 1:58 PMSergey Y.
09/30/2025, 1:59 PMStylianos Gakis
09/30/2025, 2:04 PMfun drawPath(
path: Path,
brush: Brush,
@FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
drawPath itself doesn't seem to give any options to provide custom start and end positions for trimming.
Which API did you have in mind here when you meant to animate the trim values?
Edit: Ah, perhaps in Brush.linearGradient(), giving that a trySergey Y.
09/30/2025, 2:29 PMSergey Y.
09/30/2025, 2:29 PMSeri
09/30/2025, 2:48 PMSeri
09/30/2025, 2:48 PMSeri
09/30/2025, 2:49 PMPathEffect and chainPathEffect as explained in the above two articles may be an easy approachSeri
09/30/2025, 2:49 PMStylianos Gakis
09/30/2025, 2:56 PMSeri
09/30/2025, 3:05 PMintervals to achieve your effectSeri
09/30/2025, 3:08 PMdashPathEffect
onDrawWithContent {
drawPath(
path = ribbonPath,
color = color,
style = Stroke(
width = stroke.toPx(),
pathEffect = PathEffect.dashPathEffect(
intervals = distanceArray.toFloatArray(),
),
)
)
drawContent()
drawPath(
path = ribbonPath,
color = color,
style = Stroke(
width = stroke.toPx(),
pathEffect = PathEffect.dashPathEffect(
intervals = distanceArray.toFloatArray(),
phase = distanceArray.first()
),
)
)
}Seri
09/30/2025, 3:51 PMSeri
09/30/2025, 3:51 PMSeri
09/30/2025, 3:52 PMromainguy
09/30/2025, 3:58 PMromainguy
09/30/2025, 3:59 PMRect inside a Path and then use getSegmentSeri
09/30/2025, 3:59 PMromainguy
09/30/2025, 4:00 PMromainguy
09/30/2025, 4:00 PMStylianos Gakis
09/30/2025, 4:05 PMromainguy
09/30/2025, 4:05 PMPathMeasure + getSegment are your friendsromainguy
09/30/2025, 4:06 PMdivide() is something different, it extracts multiple contours inside a Path as separate Path objects)romainguy
09/30/2025, 4:06 PMPath().apply { addRect(…); addRect(…) }.divide() you get a list of 2 Path, each containing a rectangle)Seri
09/30/2025, 4:09 PMgetSegment and couldn't make sense of it. Does it modify the destination parameter?romainguy
09/30/2025, 4:11 PMromainguy
09/30/2025, 4:12 PMgetSegment does is take a chunk of your source path (from start to end distance) and saves that in the destinationromainguy
09/30/2025, 4:12 PMromainguy
09/30/2025, 4:12 PMromainguy
09/30/2025, 4:14 PMSeri
09/30/2025, 4:21 PMstartD > stopDSeri
09/30/2025, 4:29 PMLouis Pullen-Freilich [G]
09/30/2025, 5:10 PMborder(), in userspace
2. Applying a gradient / shader / etc to that border
1. is very easy for a rectangle, it can be complicated for advanced shapes though 🙂 This is a known 'issue' / something we would like to expose an API for.
2. There are a few different ways you could achieve this once you have the proper inner path, if you need each segment to be exactly the same length that's more complicated, but if you just want to apply a radial gradient for example that's not too badStylianos Gakis
09/30/2025, 5:41 PMLouis Pullen-Freilich [G]
09/30/2025, 5:47 PMLouis Pullen-Freilich [G]
09/30/2025, 5:48 PMromainguy
09/30/2025, 5:50 PMLouis Pullen-Freilich [G]
09/30/2025, 6:03 PMromainguy
09/30/2025, 6:05 PMSkaldebane
10/01/2025, 8:36 AMstartOffset parameter).
Submitted it as a feature request a while ago: https://issuetracker.google.com/issues/303944825
Both skiko and Android implementations are in this gist: https://gist.github.com/Skaldebane/8e042b76023fbe20a7d70b59a9938f90Stylianos Gakis
10/01/2025, 9:42 AMefemoney
10/03/2025, 9:48 AMborderDrawLambda) to get it to look like and be a drop in replacement for the platform border (I imagine thats what you meant @Louis Pullen-Freilich [G]in point number 1) so I wouldnt necessarily recommend this.
Just sharing for information purposes, the entire gist of the approach is: