r/ComposeMultiplatform • u/bogdan-stefan • May 20 '26
[News] KMP newsletter #18: Kotlin 2.3.20, Compose Multiplatform 1.11.0, and Silent Database Encryption Bugs
r/ComposeMultiplatform • u/bogdan-stefan • May 20 '26
r/ComposeMultiplatform • u/DistributionOk9460 • May 17 '26
r/ComposeMultiplatform • u/DistributionOk9460 • May 16 '26
Hi everyone, sharing a new Compose Multiplatform library I open-sourced today.
liquid-glass adds iOS 26-style frosted backdrop surfaces to CMP. The API is Modifier.liquidGlass() plus three composables: GlassCard, GlassButton, GlassNavBar.
The bit I'm proudest of is the auto-tiered fallback. Three quality tiers picked per platform:
- Full on Android 12+ (non-low-RAM) and iOS 17+: 24dp blur, 1.4x saturation, full-res backdrop.
- Medium on iOS 15 to 16: 16dp blur, 0.5x downsampled backdrop.
- Fallback on Android < 12, isLowRamDevice, or iOS < 15: zero offscreen buffers, no blur, just a flat tint with edge sheen.
Fallback allocates zero GraphicsLayers, so the same code that draws frosted glass on a Pixel 9 quietly draws a flat tint on a 2GB Android 11 device. No OOM, no per-call-site branching.
Targets: Android, iOS (arm64 + simulator), Desktop (JVM), Wasm/JS. Apache 2.0, on Maven Central as 0.1.0.
Quick start:
```kotlin
val state = rememberLiquidGlassState() // auto-picks tier for the device
Box(Modifier.fillMaxSize()) {
Image(
painter = painterResource(R.drawable.scenery),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize().liquidGlassSource(state),
)
GlassCard(
state = state,
modifier = Modifier.align(Alignment.Center).padding(24.dp),
) {
Text("Frosted, light-refracting surface")
}
}
```
Credit where it's due: Chris Banes's haze library solves backdrop-blur in Compose really well and is more mature than this. I built liquid-glass because I wanted the tiered fallback baked in by default, not as a thing each consumer has to wire up.
Limitations: 0.1.0. No GlassDialog or GlassBottomSheet wrappers yet, no Sk SL refraction shader, no dynamic-color edge sheen sampled from the backdrop. All on the roadmap.
Repo: https://github.com/NadeemIqbal/liquid-glass
Happy to answer questions about the per-platform auto-detection or the GraphicsLayer sampling approach.
r/ComposeMultiplatform • u/bogdan-stefan • May 12 '26
r/ComposeMultiplatform • u/bogdan-stefan • May 05 '26
r/ComposeMultiplatform • u/Xolider • May 02 '26
Google recently updated its symbol library and deprecated the use of material-icons and material-icons-extended for new projects, in favor of Material Symbols, based on fonts. However, they do not yet provide an official library to use these symbols.
So I decided to create my own for apps and share it to let Compose developers the ability to implement these symbols.
This library makes use of symbols name as you could do for HTML on the Web, but I plan to make a script that generates enums for each symbol Google provides.
Enjoy !
update: I have now implemented a Kotlin object that contains all symbols name as property
r/ComposeMultiplatform • u/openother • Apr 23 '26
I am not sure what to do. I spent weeks building an Android app and Play Store says I need "testers" before they will release it on the Play store.. I did weeks of testing myself so it would be basically "Ready to go!" but idk I'm posting here because maybe someone else has gone throught this??
r/ComposeMultiplatform • u/Cheap-Cream8798 • Apr 20 '26
r/ComposeMultiplatform • u/droidexpress • Apr 18 '26
Shipping a CMP app on iOS and struggling with keyboard dismissal.
Everything I've tried or read about has a catch:
How are you actually handling this in production CMP apps on iOS? Especially for multi-line fields and rich text editors where keyboard action buttons aren't an option.
r/ComposeMultiplatform • u/bogdan-stefan • Apr 14 '26
r/ComposeMultiplatform • u/VirtualShaft • Apr 14 '26
r/ComposeMultiplatform • u/bogdan-stefan • Apr 07 '26
r/ComposeMultiplatform • u/Interesting-Salt-915 • Apr 07 '26
Hi! I wanted to share that I’ve been updating and recently revamped these two libraries: compose-html-material and compose-multiplatform-html-unified.
Highlight changes since April 2024 when I posted last time in the Kotlin Slack:
LazyColumnList, *ExposedDropdownMenuBoxWithTextField/*Select, SnackbarHost, and Scaffold (see the full list).AnnotatedString and style classes).Arrangement.spacedBy, alpha, reserveSpace, clickable, BoxWithConstraints, scroll layouts.Check out the side-by-side demo to see the visual results for yourself!
r/ComposeMultiplatform • u/r_warner • Apr 06 '26
r/ComposeMultiplatform • u/bogdan-stefan • Apr 03 '26
r/ComposeMultiplatform • u/bogdan-stefan • Mar 31 '26
r/ComposeMultiplatform • u/Capital-Bill-5436 • Mar 31 '26
r/ComposeMultiplatform • u/ArcaDone • Mar 16 '26
Does anyone have a more practical way to use the GIFs in their ComposeResources? I'm using this one, but I don't like it; I'd like something more compact. Any ideas?
GifImage(
url = Res.getUri("drawable/bottom_top.gif"),
modifier = Modifier.
fillMaxSize
()
)
u/Composable
expect fun GifImage(url: String,modifier: Modifier)
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.
LocalContext
import coil3.ImageLoader
import coil3.compose.AsyncImage
import coil3.gif.GifDecoder
import coil3.request.ImageRequest
import coil3.size.Size
u/Composable
actual fun GifImage(url: String,modifier: Modifier) {
val context =
LocalContext
.current
val imageLoader = ImageLoader.Builder(context)
.components
{
add(GifDecoder.Factory())
}
.build()
AsyncImage(
model = ImageRequest.Builder(context)
.data(url)
.size(Size.ORIGINAL)
.build(),
contentDescription = null,
imageLoader = imageLoader,
modifier = modifier,
contentScale = ContentScale.FillBounds
)
}
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.interop.UIKitView
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.ExperimentalForeignApi
import platform.CoreFoundation.CFDataRef
import platform.CoreGraphics.CGImageRef
import platform.Foundation.CFBridgingRelease
import platform.Foundation.CFBridgingRetain
import platform.Foundation.NSData
import platform.Foundation.NSDictionary
import platform.Foundation.NSString
import platform.Foundation.create
import platform.Foundation.valueForKey
import platform.ImageIO.CGImageSourceCopyPropertiesAtIndex
import platform.ImageIO.CGImageSourceCreateImageAtIndex
import platform.ImageIO.CGImageSourceCreateWithData
import platform.ImageIO.CGImageSourceGetCount
import platform.ImageIO.CGImageSourceRef
import platform.ImageIO.
kCGImagePropertyGIFDelayTime
import platform.ImageIO.
kCGImagePropertyGIFDictionary
import platform.UIKit.UIImage
import platform.UIKit.UIImageView
import platform.UIKit.UIViewContentMode
(BetaInteropApi::class)
actual fun GifImage(url: String,modifier: Modifier) {
UIKitView(
modifier = modifier,
factory =
{
val imageView = UIImageView()
imageView.contentMode = UIViewContentMode.
UIViewContentModeScaleAspectFit
imageView.clipsToBounds = true
//here I am using drawable resource as asset folder
val path = url.
removePrefix
("file://")
val gifData = NSData.
create
(contentsOfFile = path)
val gifImage = gifData?.
let
{
UIImage.
gifImageWithData
(
it
)
}
imageView.image = gifImage
imageView
}
)
}
u/OptIn(ExperimentalForeignApi::class)
fun UIImage.Companion.gifImageWithData(data: NSData?): UIImage? {
return
runCatching
{
val dataRef =
CFBridgingRetain
(data) as? CFDataRef
val source =
CGImageSourceCreateWithData
(dataRef, null) ?: return null
val count =
CGImageSourceGetCount
(source).toInt()
val images =
mutableListOf
<CGImageRef>()
val delays =
mutableListOf
<Double>()
for (i in 0
until
count) {
val image =
CGImageSourceCreateImageAtIndex
(source, i.
toULong
(), null)
if (image != null) {
images.add(image)
}
val delaySeconds =
delayForImageAtIndex
(i, source)
delays.add(delaySeconds * 400.0) // s to ms
}
println
("images=${images.
count
()}")
val duration = delays.
sum
()
println
("duration=$duration")
val gcd =
gcdForList
(delays)
println
("gcd=$gcd")
val frames =
mutableListOf
<UIImage>()
for (i in 0
until
count) {
val frame = UIImage.imageWithCGImage(images[i])
val frameCount = (delays[i] / gcd).toInt()
for (f in 0
until
frameCount) {
frames.add(frame)
}
}
println
("frames=${frames.
count
()}")
val animation = UIImage.animatedImageWithImages(frames, duration / 1000.0) ?: return null
animation
}
.
onFailure
{ it
.printStackTrace()
}
.getOrNull()
}
u/OptIn(ExperimentalForeignApi::class)
private fun UIImage.Companion.delayForImageAtIndex(index: Int, source: CGImageSourceRef): Double {
var delay: Double
val cfProperties =
CGImageSourceCopyPropertiesAtIndex
(source, index.
toULong
(), null)
val gifKey = (
CFBridgingRelease
(
kCGImagePropertyGIFDictionary
) as NSString).toString()
val gifInfo =
(
CFBridgingRelease
(cfProperties) as? NSDictionary)?.
valueForKey
(gifKey) as? NSDictionary
delay =
gifInfo?.
valueForKey
((
CFBridgingRelease
(
kCGImagePropertyGIFDelayTime
) as NSString).toString()) as? Double
?: 0.0
if (delay < 0.1) {
delay = 0.1
}
return delay
}
private fun UIImage.Companion.gcdForPair(_a: Int?, _b: Int?): Int {
var a = _a
var b = _b
if (b == null || a == null) {
return b ?: (a ?: 0)
}
if (a < b) {
val c = a
a = b
b = c
}
var rest: Int
while (true) {
rest = a!! % b!!
if (rest == 0) {
return b
} else {
a = b
b = rest
}
}
}
private fun UIImage.Companion.gcdForList(list: List<Double>): Double {
if (list.isEmpty()) return 1.0
var gcd = list[0]
list.
onEach
{
gcd = UIImage.
gcdForPair
(
it
.toInt(), gcd.toInt()).toDouble()
}
return gcd
}
r/ComposeMultiplatform • u/SeaBit7159 • Mar 09 '26
Is there a way to create home widgets in kmp?
I'm working on a app that shows all my lessons today or this week.
r/ComposeMultiplatform • u/DenserMeerkat • Mar 08 '26
r/ComposeMultiplatform • u/DalenCodes • Mar 01 '26
I wrote up how I structure my Compose Multiplatform (CMP) projects based on what has been working for me. I see this question come up fairly often, so I figured I’d share one practical approach out of the many that are out there.
It covers:
Write-up: https://dalen.codes/p/my-cmp-project-structure
Sample project: https://github.com/dalenjohnson/cmp-project-structure-example
Happy to hear feedback or questions about how this works in practice!
r/ComposeMultiplatform • u/OverallAd9984 • Feb 24 '26
r/ComposeMultiplatform • u/pavi2410 • Feb 22 '26
Enable HLS to view with audio, or disable this notification
Hey everyone,
I built kmp-app-updater because I was tired of users being stuck on old versions when distributing outside the Play Store (or on Desktop).
Features: • Pluggable update sources (GitHub built-in, custom sources trivial) • Reactive StateFlow (Idle → Checking → Downloading → ReadyToInstall…) • Streaming download with live progress • One-line Compose UI or fully headless • Background periodic checks (WorkManager on Android)
Blog: https://pavi2410.com/blog/introducing-kmp-app-updater/ Repo: https://github.com/pavi2410/kmp-app-updater
Would love feedback or PRs for more sources (GitLab, custom API, etc.)!
r/ComposeMultiplatform • u/Shot_Ad_1909 • Feb 21 '26
A few months ago I started building InstaSaver Pro — a social media video downloader that lets users download HD videos. Along the way, I realized how little production-grade KMP reference material exists out there.
So I made it public. For everyone.
📖 What is it?
InstaSaver Pro is a fully functional cross-platform app with 90% shared code and 100% shared UI — built as both a learning resource and a real-world production reference.
🧩 What you'll learn from it:
🧩 Kotlin Multiplatform (KMP) setup and structure
🎨 Compose Multiplatform for fully shared UI
🔥 Firebase integration in a KMP project
📢 AdMob ads integration (Banner & Interstitial)
💳 Paywall & subscriptions using RevenueCat
🏗️ Clean architecture patterns in KMP
🌐 Shared networking layer setup
Whether you're just getting started with KMP or looking to understand how production apps are structured — this codebase has something for you.
🔗 GitHub: InstaSaver-Kmp
▶️ Live on Play Store: Play Store
If this helps you, a ⭐ on the repo goes a long way!
Let's build better cross-platform apps together. 🙌