Hello. Android noob here. I’m developing an app us...
# android
i
Hello. Android noob here. I’m developing an app using compose MP, that also has a widget in android using glance (didn’t get to the iOS widget part yet). I understand that glance provides it’s own composable UI functions like Row, Column etc, but is not using the equivalent compose functions of Row, Column etc. It also has it’s own Modifier and the function params are a bit different. My question is, is there a good way/pattern to share ui logic between my main app code and the widget? like if i want to reuse some components/ui logic between the two. So far i can’t find or think of a way to do it without copy/paste/tweak.
c
Hey, welcome to Slack! Things starting with
#
are not hashtags, they're different channels in which different people read messages. Since this question is about the #glance library, it's more likely you will get good answers by asking them directly, instead of asking in the more general #android in which many people have never heard of #glance.
i
Yeah i was thinking that as i was writing it, not new to slack, just stupid 😄
😅 1
Ok i’ll try there. Thanks!
m
You can share the content itself of those composables. This article should help you understanding how to do it. https://joebirch.co/android/sharing-composables-between-mobile-and-tv-apps/
i
Yeah but my issue is that my content also calls composable component functions
I was thinking of creating like a uiProvider interface that has functions like row, column etc and have implementations calling either compose functions or glance functions, but that doesn’t seem elegant, also the different modifier types make it hard, since they don’t share an interface
m
Yeah, I see. I assumed your composables are simple enough to be shared using the above technique. but apparently it isn't the case, then you might get a better answer in #glance as Ivan said.
i
Thanks for your help anyway, i’ll share what i have when i have it
👌 1
I did create this already as i was attempting a similar approach
Copy code
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.glance.text.FontFamily
import androidx.glance.text.FontStyle
import androidx.glance.text.FontWeight
import androidx.glance.text.TextAlign
import androidx.glance.text.TextDecoration
import androidx.glance.unit.ColorProvider

fun TextStyle.glancify() = androidx.glance.text.TextStyle(
    color = ColorProvider(color),
    fontSize = fontSize,
    fontWeight = fontWeight?.glancify(),
    fontStyle = fontStyle?.glancify(),
    textAlign = textAlign?.glancify(),
    textDecoration = textDecoration?.glancify(),
    fontFamily = fontFamily?.glancify(),
)

internal fun Color.glancify() = ColorProvider(this)

internal fun androidx.compose.ui.text.font.FontWeight.glancify() = when (this.weight) {
    FontWeight.Normal.value -> FontWeight.Normal
    FontWeight.Medium.value -> FontWeight.Medium
    FontWeight.Bold.value -> FontWeight.Bold
    else -> error("Invalid fontWeight: $this")
}

internal fun androidx.compose.ui.text.font.FontStyle.glancify() = when (this.value) {
    0 -> FontStyle.Normal
    1 -> FontStyle.Italic
    else -> error("Invalid fontStyle: $this")
}

internal fun androidx.compose.ui.text.style.TextAlign.glancify() = TextAlign.values().firstOrNull { it.toString() == this.toString() }
    ?: error("Invalid textAlign: $this")

internal fun androidx.compose.ui.text.style.TextDecoration.glancify() = when (this.mask) {
    0x0 -> TextDecoration.None
    0x1 -> TextDecoration.Underline
    0x2 -> TextDecoration.LineThrough
    else -> error("Invalid textDecoration: $this")
}


internal fun androidx.compose.ui.text.font.FontFamily.glancify() = when (toString()) {
    FontFamily.SansSerif.toString() -> FontFamily.SansSerif
    FontFamily.Serif.toString() -> FontFamily.Serif
    FontFamily.Monospace.toString() -> FontFamily.Monospace
    else -> error("Invalid fontFamily: $this")
}
So you can see how convoluted that gets
But it could be a potential library idea: Glancify
blob sweat smile 1
m
Yeah, but I don't think writing mappings manually like this is a good idea or a fun project to maintain.
i
I feel it should be provided by glance, meaning some ability to share ui logic. but maybe it’s not conventional to share ui between main apps and widgets, i don’t know cause i’m a noob 🙂
m
This thread might be interesting to you to understand why sharing the same compose ui components with widgets is not easily achievable: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1688186198741169
i
Thanks, i think i understood the reason already, it’s explained on the glance docs
c
But it could be a potential library idea: Glancify
It's interesting that this would be suggested in a thread in which I participated 😅 Well, I'm working on exactly this: polymorphism for Compose. The goal is to allow creating "high-level components" that are implemented once per design system, per platform. This allows: • Switching between multiple design systems without changing application-level code (sell your application to multiple clients) • Adapting the application to each platform's design system (the layout automatically adapts to what is natural on iOS or Android) • Allow using the same composable functions transparently for technologies that use different appliers (e.g. Jetpack Compose and Glance) It's still very early stages, but you can follow the development in our repository or in the #decouple channel 🙂 If you're interested in how it works, the technical explanation is here.
🙌 1
m
So, something like Redwood ? or how does it compare to it?
👀 1
c
Yes, more or less the same goal. Before reading the rest, my entire knowledge on Redwood is based on Jake Wharton's talk at last year's KotlinConf. If they have changed anything since then, I wouldn't know. The main differences are: • You can actually use Redwood right now 😅 Decouple is really early days… • Redwood is very heavily based on code generation. You declare a schema (not in Kotlin) and it generates Kotlin composables for you on all platforms. • Decouple is instead based on regular polymorphism (interfaces), heavily using context receivers and extension functions to make it enjoyable. There is no compiler plugin, no code generation, no magic—it's pure Kotlin all the way down. • Redwood cannot support multiple implementations for a single platform (so no having an in-app way to switch between design systems) • Redwood cannot support multiple appliers using the same signature (so it wouldn't solve what this issue was originally about) • Redwood can bind to non-compose UI toolkits. Decouple is only about creating higher-level composable functions, so if you want to integrate with something else you'll have to write the appliers yourself (or use Decouple on top of Redwood). • Redwood aims to provide you with a basis on which you declare all your components yourself. Decouple aims to provide a baseline with most common components. I'm not sure about this one, but I also don't think you can have multiple schemas with Redwood. With Decouple, you can have components that are only available on some platforms, but not all, checked at compile-time.
👌 1
But you're right that both have the same underlying goal: declaring higher-level components that describe what components are used for rather than what they look like, which gives a lot of flexibility when changing your design system, your branding, or just to adapt to different platforms. The tech used to get there is nothing alike, though.
m
very comprehensive comparison! Great, your approach sounds promising as well (no code generation magic!!), and the fact that you can use Decouple on top of Redwood seems interesting for advanced use-cases.
🙏 1
i
Thanks guys, that was very helpful! I wonder how i didn’t find redwood when searching for a solution. I’m gonna try it out now, but a quick glance (pun not intended) at the readme seems promising!
@CLOVIS I couldn’t see a usage example anywhere here:https://gitlab.com/opensavvy/decouple Am i missing something? i would expect the main README to contain it, as it makes it easier to get the gist of
c
It will be in the README, in the future. It's really too soon to try to use it 🙂 If you want to learn more about it, don't hesitate to ask questions in #decouple
1
i
@MR3Y I’m not sure if i understand where to place redwood in the context of compose multiplatform, it seems like it’s trying to do the same thing?
m
No, they serve two different purposes. Redwood lets you build basically the design system or schema of your UI components/screens, and share them across all your targeted platforms for consistency & uniformity. Then on top of that you need to render those components (draw the pixels) on screen to make them visible, here where compose multiplatform UI comes into play to let you write shared UI across all platforms. Redwood is agnostic from UI/rendering layer because you can use compose multiplatform UI or use the native UI toolkit for each respective platform (So, Jetpack compose for Android, SwiftUI for Ios...etc). Hope that answers your question.
🙏 1
You can watch this:

https://www.youtube.com/watch?v=G4LK_euTadU

and take a look at samples: https://github.com/cashapp/redwood/tree/trunk/samples to have better understanding of redwood.