π€ AI AGENTS: Read AGENTS.md and PROGRESS.md before starting any task. Follow the 5-step process in
docs/AI_PLANNING_PROCESS.md.
A library that dynamically converts JSON into Jetpack Compose components.
json-to-compose is an Android library that allows you to convert JSON structures into Jetpack Compose components at runtime. This makes it easy to create dynamic interfaces and update UI based on remote data.
This library is perfect for implementing Server-Driven UI patterns, where your backend controls the UI layout and content. Some benefits include:
- Update your app's UI without deploying new versions
- A/B testing different layouts and components
- Dynamic content presentation based on user segments
- Consistent UI across platforms by sharing the same JSON structure
Example:
@Composable
fun App() {
var uiDefinition by remember { mutableStateOf(api.fetchUIDefinition()) }
LaunchedEffect(Unit) {
uiDefinition = api.fetchUIDefinition()
}
uiDefinition.ToCompose()
}Integrate with AI services to create dynamic UIs based on user input or context:
- AI-powered layout suggestions
- Dynamic form generation based on user preferences
- Context-aware UI adaptations
- Prototype testing with AI-generated interfaces
Example:
@Composable
fun App(userContext: String) {
val aiGeneratedUI = aiService.generateUIForUser(userContext)
aiGeneratedUI.ToCompose()
}Add the following dependency to your build.gradle.kts file:
dependencies {
implementation("com.jesusdmedinac:json-to-compose:1.1.0")
}@Composable
fun App() {
MaterialTheme {
JSON_AS_STRING.ToCompose()
}
}
val JSON_AS_STRING = """
{
"type": "Column",
"properties": {
"type": "ColumnProps",
"children": [
{
"type": "Text",
"properties": {
"type": "TextProps",
"text": "Hello World"
}
}
]
}
}
""".trimIndent()@Composable
fun App() {
val behaviors = mapOf(
"button_clicked" to object : Behavior {
override fun onClick() {
println("Button was clicked!")
}
}
)
CompositionLocalProvider(LocalBehavior provides behaviors) {
MaterialTheme {
JSON_AS_STRING.ToCompose()
}
}
}@Composable
fun App() {
var textValue by remember { mutableStateOf("") }
val stateHosts = mapOf(
"my_text_field" to object : StateHost<String> {
override val state: String get() = textValue
override fun onStateChange(state: String) {
textValue = state
}
}
)
CompositionLocalProvider(LocalStateHost provides stateHosts) {
JSON_AS_STRING.ToCompose()
}
}@Composable
fun App() {
val drawableResources = mapOf(
"my_icon" to Res.drawable.my_icon
)
CompositionLocalProvider(LocalDrawableResources provides drawableResources) {
JSON_AS_STRING.ToCompose()
}
}json-to-compose supports advanced declarative action chains natively without writing custom behavior lambdas. Provide platform and navigation handlers via LocalNavigationHandler and LocalPlatformHandler CompositionLocals:
@Composable
fun App() {
val navHandler = remember {
object : NavigationHandler {
override fun navigate(route: String, args: Map<String, Any?>) {
println("Navigate to $route with args: $args")
}
override fun navigateBack() {
println("Navigate Back")
}
}
}
val platformHandler = remember {
object : PlatformHandler {
override fun launchUrl(url: String) {
// Open external browser URL
}
override fun copyToClipboard(text: String) {
// Copy text to system clipboard
}
}
}
CompositionLocalProvider(
LocalNavigationHandler provides navHandler,
LocalPlatformHandler provides platformHandler
) {
JSON_AS_STRING.ToCompose()
}
}navigate: Navigates to a specific screen route.navigateBack: Pops the current navigation stack.delay: Asynchronously waits fordurationMillisbefore executing the next action in a sequence.conditional: Compares a state value usingoperator(Equals,NotEquals,GreaterThan,LessThan,Contains) and executesthenActionor optionalelseAction.incrementState/decrementState: Safely increments or decrements a numeric state.launchUrl: Opens an external URL.copyToClipboard: Copies text to the device clipboard.updateList: Adds or removes items from a list state.
| Component | Description | Properties |
|---|---|---|
| Text | Display text | text, fontSize, fontWeight, fontStyle, color, textAlign, maxLines, overflow, letterSpacing, lineHeight, textDecoration, minLines |
| Button | Standard filled button | child, enabled, onClickEventName, shape, containerColor, contentColor |
| ElevatedButton | Button with elevation | child, enabled, onClickEventName, shape, containerColor, contentColor |
| FilledTonalButton | Button with secondary container color | child, enabled, onClickEventName, shape, containerColor, contentColor |
| OutlinedButton | Button with border | child, enabled, onClickEventName, shape, containerColor, contentColor |
| TextButton | Button without border or background | child, enabled, onClickEventName, shape, containerColor, contentColor |
| IconButton | Button containing an icon | child, enabled, onClickEventName, shape, containerColor, contentColor |
| FloatingActionButton | Circular FAB | child, containerColor, onClickEventName |
| ExtendedFAB | FAB with icon and text | icon, text, containerColor, onClickEventName |
| Icon | Material Design icon | iconName, tint |
| Column | Vertical layout container | verticalArrangement, horizontalAlignment, children |
| Row | Horizontal layout container | horizontalArrangement, verticalAlignment, children |
| Box | Stacking layout container | contentAlignment, propagateMinConstraints, children |
| Spacer | Fixed size empty space | Used with Width or Height modifiers |
| HorizontalDivider | Material 3 horizontal line | thickness, color |
| VerticalDivider | Material 3 vertical line | thickness, color |
| FlowRow | Wrapping horizontal layout | horizontalArrangement, verticalArrangement, children |
| FlowColumn | Wrapping vertical layout | horizontalArrangement, verticalArrangement, children |
| Surface | Material 3 surface container | tonalElevation, shadowElevation, color, shape, child |
| Image | Display images | url, resourceName, contentDescription, contentScale, alignment, colorFilter |
| TextField | Text input field | value, placeholder, label, leadingIcon, trailingIcon, isError, supportingText, singleLine, maxLines, keyboardType, visualTransformation, readOnly, prefix, suffix |
| OutlinedTextField | Outlined text input field | value, placeholder, label, leadingIcon, trailingIcon, isError, supportingText, singleLine, maxLines, keyboardType, visualTransformation, readOnly, prefix, suffix |
| LazyColumn | Lazy vertical scrolling list | children |
| LazyRow | Lazy horizontal scrolling list | children |
| HorizontalPager | Horizontally swipeable pager | pages, currentPage, beyondViewportPageCount, userScrollEnabled |
| VerticalPager | Vertically swipeable pager | pages, currentPage, beyondViewportPageCount, userScrollEnabled |
| Scaffold | Material Design scaffold | topBar, bottomBar, floatingActionButton, floatingActionButtonPosition, containerColor, child |
| Card | Material Design card | elevation, cornerRadius, containerColor, borderColor, borderWidth, child |
| ElevatedCard | Card with elevated shadow | elevation, cornerRadius, containerColor, borderColor, borderWidth, child |
| OutlinedCard | Card with border outline | borderColor, cornerRadius, containerColor, borderWidth, child |
| AlertDialog | Material Design alert dialog | title, text, confirmButton, dismissButton, icon, tonalElevation, visibilityState |
| ModalBottomSheet | Bottom sheet modal container | child, visible, visibleStateHostName, onDismissRequestEventName, dragHandle, showDragHandle, shape, scrimColor |
| TopAppBar | Material 3 TopAppBar | title, navigationIcon, actions, backgroundColor, contentColor |
| CenterAlignedTopAppBar | Material 3 CenterAlignedTopAppBar | title, navigationIcon, actions, backgroundColor, contentColor |
| MediumTopAppBar | Material 3 MediumTopAppBar | title, navigationIcon, actions, backgroundColor, contentColor |
| LargeTopAppBar | Material 3 LargeTopAppBar | title, navigationIcon, actions, backgroundColor, contentColor |
| NavigationBar | Material Design bottom navigation | children, containerColor, contentColor |
| NavigationBarItem | Item for NavigationBar | selected, label, icon, enabled, alwaysShowLabel, onClickEventName |
| NavigationRail | Material Design side navigation | children, header, containerColor, contentColor |
| NavigationRailItem | Item for NavigationRail | selected, label, icon, enabled, alwaysShowLabel, onClickEventName |
| ModalNavigationDrawer | Side drawer navigation | isOpen, drawerContent, child |
| NavigationDrawerItem | Item for NavigationDrawer | selected, label, badge, icon, onClickEventName |
| TabRow | Row of tabs | selectedTabIndex, children, containerColor, contentColor |
| ScrollableTabRow | Scrollable row of tabs | selectedTabIndex, children, containerColor, contentColor |
| Tab | Item for TabRow | selected, text, icon, enabled, onClickEventName |
| Badge | Display notification badge | text, textStateHostName, containerColor, contentColor |
| BadgedBox | Layout container for a badge & child | badge, child |
| AssistChip | Action chip | label, leadingIcon, onClickEventName, enabled, enabledStateHostName |
| SuggestionChip | Suggestion chip | label, leadingIcon, onClickEventName, enabled, enabledStateHostName |
| FilterChip | Selection filter chip | selected, selectedStateHostName, label, leadingIcon, onClickEventName, enabled, enabledStateHostName |
| InputChip | Entry / tag chip | selected, selectedStateHostName, label, leadingIcon, trailingIcon, onClickEventName, enabled, enabledStateHostName |
| CircularProgressIndicator | Circular progress indicator | progress, progressStateHostName, color, trackColor |
| LinearProgressIndicator | Linear progress indicator | progress, progressStateHostName, color, trackColor |
| PlainTooltip | Plain tooltip | text, anchor |
| RichTooltip | Rich tooltip | title, text, action, anchor |
| Custom | User-defined components | customType, customData |
| Switch | Material toggle switch | checked, onCheckedChange, enabled |
| Checkbox | Material checkbox | checked, onCheckedChange, enabled |
| Slider | Range selection slider | value, valueRange, steps, onValueChange, enabled |
| RadioButton | Single choice button | selected, onClick, enabled |
| SegmentedButtonRow | Row of segmented buttons | children (supports single and multi-choice) |
| DatePicker | Material 3 date picker | selectedDateMillis |
| TimePicker | Material 3 time picker | hour, minute, is24Hour |
| SearchBar | Material 3 search bar | query, placeholder, leadingIcon, trailingIcon, active, children |
| ListItem | Material 3 list item component | headlineContent, supportingContent, overlineContent, leadingContent, trailingContent, onClickEventName |
Extend the library with your own components without forking:
// 1. Define your custom renderer
val customRenderers = mapOf(
"ProductCard" to { node: ComposeNode ->
val props = node.properties as? NodeProperties.CustomProps
val data = props?.customData
val title = data?.get("title")?.jsonPrimitive?.content ?: ""
val price = data?.get("price")?.jsonPrimitive?.content ?: ""
Card {
Column {
Text(text = title)
Text(text = "$$price")
}
}
}
)
// 2. Provide it to the composition
CompositionLocalProvider(LocalCustomRenderers provides customRenderers) {
JSON_AS_STRING.ToCompose()
}JSON for custom component:
{
"type": "Custom",
"properties": {
"type": "CustomProps",
"customType": "ProductCard",
"customData": {
"title": "My Product",
"price": "99.99"
}
}
}Apply modifiers to any component using composeModifier:
{
"type": "Box",
"properties": { "type": "BoxProps" },
"composeModifier": {
"operations": [
{ "type": "FillMaxWidth" },
{ "type": "Height", "value": 200 },
{ "type": "Padding", "value": 16 },
{ "type": "BackgroundColor", "color": "#FF5722" }
]
}
}Available operations:
Padding- Add padding (value in dp)FillMaxSize- Fill maximum available sizeFillMaxWidth- Fill maximum widthFillMaxHeight- Fill maximum heightWidth- Set specific width (value in dp)Height- Set specific height (value in dp)BackgroundColor- Set background color (hex color)Border- Add border (width, color, shape)Shadow- Add shadow (elevation, shape, clip)Clip- Clip to shapeBackground- Set background (color, shape)Alpha- Set opacity (value)Rotate- Set rotation (degrees)Clickable- Handle click events (onClickEventName)Weight- Assign weight in Row/Column (weight)VerticalScroll- Enable vertical scrollingHorizontalScroll- Enable horizontal scrollingOffset- Offset position (x,yin dp)Size- Set exact size (width,heightin dp)WrapContentWidth- Wrap width to contentWrapContentHeight- Wrap height to contentAspectRatio- Constrain to ratio (ratio)ZIndex- Adjust drawing order (zIndex)MinWidth/MinHeight/MaxWidth/MaxHeight- Set constraints (valuein dp)AnimateContentSize- Animate size changesTestTag- Apply tag for UI testing (tag)
{
"type": "Box",
"properties": {
"type": "BoxProps",
"contentAlignment": "Center"
}
}Values: TopStart, TopCenter, TopEnd, CenterStart, Center, CenterEnd, BottomStart, BottomCenter, BottomEnd
{
"type": "Column",
"properties": {
"type": "ColumnProps",
"verticalArrangement": "SpaceBetween",
"horizontalAlignment": "CenterHorizontally"
}
}Vertical Arrangement: Top, Bottom, Center, SpaceEvenly, SpaceBetween, SpaceAround, SpacedBy:X (e.g. SpacedBy:16)
Horizontal Alignment: Start, CenterHorizontally, End
{
"type": "Row",
"properties": {
"type": "RowProps",
"horizontalArrangement": "SpaceEvenly",
"verticalAlignment": "CenterVertically"
}
}Horizontal Arrangement: Start, End, Center, SpaceEvenly, SpaceBetween, SpaceAround
Vertical Alignment: Top, CenterVertically, Bottom
Composy is a web and desktop app that facilitates the creation of JSON structures for json-to-compose. It provides a compose-tree editor, real-time preview, and export functionality. Composy is perfect for designing dynamic UIs and generating JSON definitions for your app.
- Compose-tree editor
- Real-time preview
- Export functionality
This library prioritizes rigorous UI testing. Beyond basic existence checks, it exposes visual properties (like fontSize, color, padding, and layout arrangements) through Jetpack Compose Semantics.
For detailed guidance, see:
- Compose Multiplatform UI Testing - General overview and tutorial.
- Note: Do NOT use
./gradlew :library:testfor UI tests as it will cause aNullPointerExceptionon Android Unit Tests. Use./gradlew :library:desktopTestinstead.
- Note: Do NOT use
- Testing Guide (Phase 1) - Project-specific testing patterns.
- Semantics Phase - Details on semantic-based testing.
WIP
WIP
This project is licensed under the MIT License - see the LICENSE file for details.
If you have questions or suggestions, feel free to: