
Cross-target app template sharing UI and core logic with Compose integration, platform-specific entry points, and ready-made build and test tasks including Web/Wasm options.
KitFlow is a Kotlin Multiplatform SDK for building adaptive Compose UI with small, reusable utilities.
KitFlow is not a Material theme wrapper. It does not replace Text, Button, Column, Row, Card, or Material components. It helps you keep adaptive values and adaptive layout decisions clean.
The responsive design will come from proper code arrangement. KitFlow will help with proper coding.
Add Maven Central:
repositories {
mavenCentral()
}Add the SDK:
dependencies {
implementation("io.github.vedangj72:kit-flow:1.0.3")
}Use the latest version you have published.
Use AdaptiveKitProvider once near the root of your Compose app.
@Composable
fun App() {
AdaptiveKitProvider {
MainScreen()
}
}Everything inside this provider can use KitFlow APIs.
Use Adaptive.value(...) when a value should change by screen class.
val screenPadding = Adaptive.value(
sm = 12.dp,
md = 16.dp,
lg = 20.dp,
tab = 32.dp,
desktop = 48.dp
)
Column(
modifier = Modifier.padding(screenPadding)
) {
Text("Hello KitFlow")
}Adaptive.value(...) is generic, so it works with Dp, TextUnit, Int, Float, Color, shapes, and your own classes.
val titleSize = Adaptive.value(
sm = 18.sp,
md = 20.sp,
lg = 22.sp,
tab = 26.sp,
desktop = 32.sp
)
Text(
text = "Adaptive title",
fontSize = titleSize,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)For text, always think about truncation, wrapping, and large font scale. Adaptive size alone is not enough.
KitFlow supports:
SM
MD
LG
TAB
DESKTOP
Default thresholds:
MD >= 360dp
LG >= 480dp
TAB >= 600dp
DESKTOP >= 840dp
Fallback rules:
SM -> sm
MD -> md
LG -> lg
TAB -> tab ?: lg
DESKTOP -> desktop ?: tab ?: lg
So this is valid:
val spacing = Adaptive.value(
sm = 8.dp,
md = 12.dp,
lg = 16.dp
)If tab or desktop is missing, KitFlow falls back safely to lg.
Each app can decide what SM, MD, LG, TAB, and DESKTOP mean.
AdaptiveKitProvider(
breakpointThresholds = AdaptiveBreakpointThresholds(
md = 400,
lg = 520,
tab = 720,
desktop = 1000
)
) {
App()
}Use custom thresholds when your product design needs different screen classes.
Use Adaptive.onOrientationChange(...) only when portrait and landscape need different UI.
Adaptive.onOrientationChange(
portrait = { info ->
Column {
LeftPanel()
RightPanel()
}
},
landscape = { info ->
Row {
LeftPanel(modifier = Modifier.weight(1f))
RightPanel(modifier = Modifier.weight(1f))
}
}
)Do not use orientation branching for every small value. For padding, text size, color, radius, and spacing, prefer Adaptive.value(...).
Read current window state with rememberWindowInfo().
val info = rememberWindowInfo()
Text("widthDp: ${info.widthDp}")
Text("heightDp: ${info.heightDp}")
Text("screenClass: ${info.screenClass}")
Text("layoutClass: ${info.layoutClass}")
Text("orientation: ${info.orientation}")Important difference:
screenClass = stable screen class based on shortest side
layoutClass = current width-based class
orientation = Portrait / Landscape / Square / Unknown
This prevents a phone from being treated like a tablet only because it rotated.
KitFlow helps keep UI stable when font scale increases.
val accessibility = LocalAdaptiveAccessibilityInfo.currentAvailable values:
accessibility.fontScale
accessibility.fontScaleClass
accessibility.minimumTouchTarget
accessibility.reducedMotion
accessibility.highContrast
accessibility.differentiateWithoutColorUse AdaptiveAccessibleLayout when a Row may break with large text.
AdaptiveAccessibleLayout(
normal = {
Row {
Title()
Actions()
}
},
largeText = {
Column {
Title()
Actions()
}
}
)Use adaptiveTouchTarget() for clickable UI.
Modifier
.adaptiveTouchTarget()
.clickable { onClick() }Use adaptiveSemantics() for clear labels and state.
Modifier.adaptiveSemantics(
label = "Download file",
role = Role.Button
)Use AdaptiveIconButton for icon-only actions.
AdaptiveIconButton(
icon = Icons.Default.Close,
contentDescription = "Close",
onClick = onClose
)Icon-only buttons must have meaningful labels.
There are certain things to keep in mind while making layouts or components in Jetpack Compose:
aspectRatio(...), weight(...), fillMaxWidth(), widthIn(...), heightIn(...), and constraints.maxLines, wrapping strategy, and TextOverflow.Ellipsis where needed.LazyColumn or LazyRow for lists.Example with official window size class style:
when (windowSizeClass) {
WindowWidthSizeClass.Compact -> {
Column {
LeftPanel()
RightPanel()
}
}
WindowWidthSizeClass.Medium,
WindowWidthSizeClass.Expanded -> {
Row {
LeftPanel(
modifier = Modifier.weight(1f)
)
RightPanel(
modifier = Modifier.weight(1f)
)
}
}
}With KitFlow, the same idea can be kept inside your SDK-driven layout flow:
val info = rememberWindowInfo()
when (info.layoutClass) {
AdaptiveBreakpoint.SM,
AdaptiveBreakpoint.MD -> {
Column {
LeftPanel()
RightPanel()
}
}
AdaptiveBreakpoint.LG,
AdaptiveBreakpoint.TAB,
AdaptiveBreakpoint.DESKTOP -> {
Row {
LeftPanel(modifier = Modifier.weight(1f))
RightPanel(modifier = Modifier.weight(1f))
}
}
}KitFlow does not remove the need to understand Compose layout. It helps you arrange adaptive code in one clear flow.
Keep these official docs close while designing adaptive Compose UI:
KitFlow should resolve adaptive context and adaptive values, not own the UI.
Developers keep using normal Compose components. KitFlow helps those components adapt.
KitFlow is a Kotlin Multiplatform SDK for building adaptive Compose UI with small, reusable utilities.
KitFlow is not a Material theme wrapper. It does not replace Text, Button, Column, Row, Card, or Material components. It helps you keep adaptive values and adaptive layout decisions clean.
The responsive design will come from proper code arrangement. KitFlow will help with proper coding.
Add Maven Central:
repositories {
mavenCentral()
}Add the SDK:
dependencies {
implementation("io.github.vedangj72:kit-flow:1.0.3")
}Use the latest version you have published.
Use AdaptiveKitProvider once near the root of your Compose app.
@Composable
fun App() {
AdaptiveKitProvider {
MainScreen()
}
}Everything inside this provider can use KitFlow APIs.
Use Adaptive.value(...) when a value should change by screen class.
val screenPadding = Adaptive.value(
sm = 12.dp,
md = 16.dp,
lg = 20.dp,
tab = 32.dp,
desktop = 48.dp
)
Column(
modifier = Modifier.padding(screenPadding)
) {
Text("Hello KitFlow")
}Adaptive.value(...) is generic, so it works with Dp, TextUnit, Int, Float, Color, shapes, and your own classes.
val titleSize = Adaptive.value(
sm = 18.sp,
md = 20.sp,
lg = 22.sp,
tab = 26.sp,
desktop = 32.sp
)
Text(
text = "Adaptive title",
fontSize = titleSize,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)For text, always think about truncation, wrapping, and large font scale. Adaptive size alone is not enough.
KitFlow supports:
SM
MD
LG
TAB
DESKTOP
Default thresholds:
MD >= 360dp
LG >= 480dp
TAB >= 600dp
DESKTOP >= 840dp
Fallback rules:
SM -> sm
MD -> md
LG -> lg
TAB -> tab ?: lg
DESKTOP -> desktop ?: tab ?: lg
So this is valid:
val spacing = Adaptive.value(
sm = 8.dp,
md = 12.dp,
lg = 16.dp
)If tab or desktop is missing, KitFlow falls back safely to lg.
Each app can decide what SM, MD, LG, TAB, and DESKTOP mean.
AdaptiveKitProvider(
breakpointThresholds = AdaptiveBreakpointThresholds(
md = 400,
lg = 520,
tab = 720,
desktop = 1000
)
) {
App()
}Use custom thresholds when your product design needs different screen classes.
Use Adaptive.onOrientationChange(...) only when portrait and landscape need different UI.
Adaptive.onOrientationChange(
portrait = { info ->
Column {
LeftPanel()
RightPanel()
}
},
landscape = { info ->
Row {
LeftPanel(modifier = Modifier.weight(1f))
RightPanel(modifier = Modifier.weight(1f))
}
}
)Do not use orientation branching for every small value. For padding, text size, color, radius, and spacing, prefer Adaptive.value(...).
Read current window state with rememberWindowInfo().
val info = rememberWindowInfo()
Text("widthDp: ${info.widthDp}")
Text("heightDp: ${info.heightDp}")
Text("screenClass: ${info.screenClass}")
Text("layoutClass: ${info.layoutClass}")
Text("orientation: ${info.orientation}")Important difference:
screenClass = stable screen class based on shortest side
layoutClass = current width-based class
orientation = Portrait / Landscape / Square / Unknown
This prevents a phone from being treated like a tablet only because it rotated.
KitFlow helps keep UI stable when font scale increases.
val accessibility = LocalAdaptiveAccessibilityInfo.currentAvailable values:
accessibility.fontScale
accessibility.fontScaleClass
accessibility.minimumTouchTarget
accessibility.reducedMotion
accessibility.highContrast
accessibility.differentiateWithoutColorUse AdaptiveAccessibleLayout when a Row may break with large text.
AdaptiveAccessibleLayout(
normal = {
Row {
Title()
Actions()
}
},
largeText = {
Column {
Title()
Actions()
}
}
)Use adaptiveTouchTarget() for clickable UI.
Modifier
.adaptiveTouchTarget()
.clickable { onClick() }Use adaptiveSemantics() for clear labels and state.
Modifier.adaptiveSemantics(
label = "Download file",
role = Role.Button
)Use AdaptiveIconButton for icon-only actions.
AdaptiveIconButton(
icon = Icons.Default.Close,
contentDescription = "Close",
onClick = onClose
)Icon-only buttons must have meaningful labels.
There are certain things to keep in mind while making layouts or components in Jetpack Compose:
aspectRatio(...), weight(...), fillMaxWidth(), widthIn(...), heightIn(...), and constraints.maxLines, wrapping strategy, and TextOverflow.Ellipsis where needed.LazyColumn or LazyRow for lists.Example with official window size class style:
when (windowSizeClass) {
WindowWidthSizeClass.Compact -> {
Column {
LeftPanel()
RightPanel()
}
}
WindowWidthSizeClass.Medium,
WindowWidthSizeClass.Expanded -> {
Row {
LeftPanel(
modifier = Modifier.weight(1f)
)
RightPanel(
modifier = Modifier.weight(1f)
)
}
}
}With KitFlow, the same idea can be kept inside your SDK-driven layout flow:
val info = rememberWindowInfo()
when (info.layoutClass) {
AdaptiveBreakpoint.SM,
AdaptiveBreakpoint.MD -> {
Column {
LeftPanel()
RightPanel()
}
}
AdaptiveBreakpoint.LG,
AdaptiveBreakpoint.TAB,
AdaptiveBreakpoint.DESKTOP -> {
Row {
LeftPanel(modifier = Modifier.weight(1f))
RightPanel(modifier = Modifier.weight(1f))
}
}
}KitFlow does not remove the need to understand Compose layout. It helps you arrange adaptive code in one clear flow.
Keep these official docs close while designing adaptive Compose UI:
KitFlow should resolve adaptive context and adaptive values, not own the UI.
Developers keep using normal Compose components. KitFlow helps those components adapt.