You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No drawing primitives are wrapped today. Without these, all rendering has to compose into existing widgets — there's no way to draw custom shapes, charts, signature pads, sparklines, progress arcs, custom dividers, gradients, or anything else off the Material-component grid.
State stack: clipRect, clipPath, translate, rotate, scale, inset, withTransform
Properties: size, center, layoutDirection, drawContext
On the C# side this needs a managed wrapper class (DrawScope) that proxies into the underlying androidx.compose.ui.graphics.drawscope.DrawScope JNI handle. Each method becomes a [ComposeBridge] taking the scope handle + draw parameters.
Brush
What you fill / stroke with — used by DrawScope methods, Modifier.Background(brush), Modifier.Border(width, brush).
Used today by Modifier.background(brush, shape), Border(width, brush, shape), Surface(shape), Card(shape), Modifier.clip(shape), etc. — already referenced in several existing bridges (e.g. ModifierBackgroundDefault has a shape slot) but never actually exposed in the C# facade.
newCanvas(Modifier.Companion.Size(200)){Draw= scope =>{scope.DrawCircle(Color.Red,radius:50f,center:scope.Center);scope.DrawLine(color:Color.Blue,start:scope.TopLeft,end:newOffset(scope.Size.Width,scope.Size.Height),strokeWidth:4f);scope.DrawPath(myPath,brush:Brush.LinearGradient(new[]{Color.Cyan,Color.Magenta},start:scope.TopLeft,end:scope.BottomRight));}}// Or just a background:newBox{Modifier.Companion.Size(64).Background(Brush.RadialGradient(new[]{Color.White,Color.Black})).Clip(newRoundedCornerShape(corner:12)),}
Effort estimate
Medium-high. Breakdown:
Shape factories — small, can land first. Each is a static call on a *Kt class returning a Shape JNI handle. RoundedCornerShape/CircleShape likely hash-mangled because of Dp inline-class corner params. Once these exist, all the existing bridges with shape slots become user-callable (background-with-shape, border-with-shape, clip).
Brush factories — small, same shape as Shape. Returns a Brush JNI handle that flows into draw calls.
Path builder — medium. Mutable state holder; needs [ComposeBridge] partials for each builder method, plus a managed Path class wrapping the underlying IntPtr.
Canvas composable + Modifier.drawBehind / drawWithContent — medium. The tricky part is the lambda shape: the body takes a DrawScope receiver, so we need a DrawScope managed wrapper that holds the underlying IntPtr and forwards every draw call through a [ComposeBridge].
DrawScope wrapper — medium-high. ~30 methods, each a [ComposeBridge] on DrawScope.${methodName} taking the scope handle as the first param. Most of these are hash-mangled because of Color/Offset/Size/Dp inline-class params.
Offset / Size value types (small Kotlin @JvmInline value classes over packed longs) — would need similar small C# readonly struct wrappers. Could land alongside Color.
Sample
A "Drawing" tab in the sample with a sparkline + a custom gradient-backed circle that pulses (paired with the animation primitives issue, if/when that lands).
No drawing primitives are wrapped today. Without these, all rendering has to compose into existing widgets — there's no way to draw custom shapes, charts, signature pads, sparklines, progress arcs, custom dividers, gradients, or anything else off the Material-component grid.
Scope
Canvascomposable + drawing modifiersCanvas(modifier) { DrawScope -> ... }CanvasKtModifier.drawBehind { DrawScope -> ... }DrawModifierKtModifier.drawWithContent { DrawScope -> drawContent(); ... }DrawModifierKtModifier.drawWithCache { CacheDrawScope -> onDrawBehind { ... } }DrawModifierKtDrawScopeThe receiver passed to every draw lambda. Has ~30 methods:
Drawing:
drawRect,drawCircle,drawArc,drawLine,drawOval,drawRoundRect,drawPath,drawPoints,drawImage,drawTextState stack:
clipRect,clipPath,translate,rotate,scale,inset,withTransformProperties:
size,center,layoutDirection,drawContextOn the C# side this needs a managed wrapper class (
DrawScope) that proxies into the underlyingandroidx.compose.ui.graphics.drawscope.DrawScopeJNI handle. Each method becomes a[ComposeBridge]taking the scope handle + draw parameters.BrushWhat you fill / stroke with — used by
DrawScopemethods,Modifier.Background(brush),Modifier.Border(width, brush).Brush.linearGradient(colors, start, end, tileMode)BrushKtBrush.horizontalGradient(colors, startX, endX, tileMode)BrushKtBrush.verticalGradient(colors, startY, endY, tileMode)BrushKtBrush.radialGradient(colors, center, radius, tileMode)BrushKtBrush.sweepGradient(colors, center)BrushKtSolidColor(color)SolidColordata classPathMutable geometry builder for
DrawScope.drawPathandModifier.clip(GenericShape { ... }).Path()(constructor)Path(Android shim aroundandroidx.compose.ui.graphics.Path)moveTo,lineTo,relativeLineTo,quadraticBezierTo,cubicTo,relativeCubicTo,arcTo,close,reset,rewindPathaddRect,addOval,addRoundRect,addArc,addPathPathPathMeasurePathOperation(Union, Intersect, Difference, ReverseDifference, Xor) +Path.op(p1, p2, op)ShapeUsed today by
Modifier.background(brush, shape),Border(width, brush, shape),Surface(shape),Card(shape),Modifier.clip(shape), etc. — already referenced in several existing bridges (e.g.ModifierBackgroundDefaulthas ashapeslot) but never actually exposed in the C# facade.RoundedCornerShape(corner: Dp)/RoundedCornerShape(topStart, topEnd, bottomEnd, bottomStart)RoundedCornerShapeKtRoundedCornerShape(percent: Int)/ per-corner percentRoundedCornerShapeKtCircleShape(singleton)RoundedCornerShapeinstance inShapesKtCutCornerShape(corner)/ per-corner variantCutCornerShapeKtRectangleShape(singleton)RectangleShapeKtGenericShape { size, layoutDirection -> path }GenericShapeKt— arbitrary path-defined shapeAbsoluteRoundedCornerShape/AbsoluteCutCornerShapeShape on the C# side
Effort estimate
Medium-high. Breakdown:
Shapefactories — small, can land first. Each is a static call on a*Ktclass returning aShapeJNI handle.RoundedCornerShape/CircleShapelikely hash-mangled because ofDpinline-class corner params. Once these exist, all the existing bridges withshapeslots become user-callable (background-with-shape, border-with-shape, clip).Brushfactories — small, same shape asShape. Returns aBrushJNI handle that flows into draw calls.Modifier.Clip(shape)— fold into the modifier expansion issue (Add CompositionLocal + CompositionLocalProvider (LocalContext, LocalDensity, LocalContentColor, LocalTextStyle, ...) #59); needsShapefactories to be useful.Pathbuilder — medium. Mutable state holder; needs[ComposeBridge]partials for each builder method, plus a managedPathclass wrapping the underlyingIntPtr.Canvascomposable +Modifier.drawBehind/drawWithContent— medium. The tricky part is the lambda shape: the body takes aDrawScopereceiver, so we need aDrawScopemanaged wrapper that holds the underlyingIntPtrand forwards every draw call through a[ComposeBridge].DrawScopewrapper — medium-high. ~30 methods, each a[ComposeBridge]onDrawScope.${methodName}taking the scope handle as the first param. Most of these are hash-mangled because ofColor/Offset/Size/Dpinline-class params.Dependencies
Colorstruct from Theming: parameterize MaterialTheme (ColorScheme/Typography/Shapes), Color value type, Material Icons, MaterialTheme.colorScheme reads #61 (theming) — without it,DrawScope.drawCircle(Color.Red, ...)has to pass rawlongARGB and is ugly.Offset/Sizevalue types (small Kotlin@JvmInline value classes over packedlongs) — would need similar small C#readonly structwrappers. Could land alongsideColor.Sample
A "Drawing" tab in the sample with a sparkline + a custom gradient-backed circle that pulses (paired with the animation primitives issue, if/when that lands).
Tracking
Long-term dotnet/java-interop#1440.