Table of contents
- Too long didn't read. Give me the code!!!
- Resources
- The most basic of organizations
- Does this work with Recomposition?
- Scoping to enforce Compose types
- Defining the Scopes
My app on the Google play store
Resources
Too long didn't read (TLDR)
- Essentially, we are trying to create a
design system
that we can implement across our compose code base - step 1: organize your composables by wrapping them like this:
object SharedComponents {
@Composable
fun TooLongDidNotRead(){
Text("No time to read! I need the code now!")
}
}
- step 2: Implement Scoping to enforce what type of composables go where
GitHub code
The most basic of organizations | Grouping
- If you find your Jetpack Compose code confusing to understand and copy and pasting is faster than reusing, then you might need to do some reorganization.
- The most basic of Jetpack compose organization is what I call
grouping
. Essentially, grouping takes place when you use Kotlin's object declaration to group similar jetpack compose components together. Like so,
/**
* SharedComponents represents the most used and most stable versions of components used throughout this application
* **/
object SharedComponents {
/**
* very fancy text*/
@Composable
fun FancyText(){
}
/**Less fancy text*/
@Composable
fun NormalText(){
}
}
// how to use
SharedComponents.FancyText()
- While this reorganization seems trivial at first glance is really does give you a better understanding of what composables are from where.
Does this work with Recomposition?
- So you are wondering if this will still allow compose to do recomposition even without the
@Stable
annotation. The answer is....I think so
. I tested it with this code:
var timesClicked by remember { mutableStateOf(0) }
Testing.Combined(
timesClicked.toString(),
onClick = {timesClicked++}
)
object Testing{
@Composable
fun Combined(
timesClicked:String,
onClick:()->Unit,
){
Column() {
RecomposeEveryTime(
timesClicked,
onClick = {onClick()}
)
DontReCompose()
}
}
@Composable
fun RecomposeEveryTime(
timesClicked:String,
onClick:()->Unit,
){
Log.d("TESTINGRECOMPOSE","RecomposeEveryTime")
Button(onClick ={onClick()}) {
Text(
timesClicked,
fontSize = 30.sp,
color = Color.Red
)
}
}
@Composable
fun DontReCompose(){
Log.d("TESTINGRECOMPOSE","DontReCompose ")
Text(
"NO RECOMPOSITION",
fontSize = 30.sp,
color = Color.Red
)
}
}
- Judging from the log statements, only the
RecomposeEveryTime()
is recomposing (which is the desired effect) andDontReCompose()
is not. So from these tests I can proudly say, I think recomposition works as intended
Scoping to enforce Compose types
- Read the original Bumbletech article if you want a better and deeper understanding of the Scoping principle
- My GitHub code for scoping
Essentially, we can use Scoping when we want to use strong typing to enforce what type of Composables goes where. Here is a basic example:
@Composable
fun NoDrawerScaffold(
topBar:@Composable ScaffoldTopBarScope.() -> Unit,
bottomBar:@Composable ScaffoldBottomBarScope.() -> Unit,
content:@Composable (contentPadding: PaddingValues,) -> Unit,
) {
val topBarScaffoldScope = remember(){ScaffoldTopBarScope(35.dp)}
val bottomBarScaffoldScope = remember(){ScaffoldBottomBarScope(25.dp)}
Scaffold(
containerColor = MaterialTheme.colorScheme.primary,
topBar = {
Row(modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.primary)){
with(topBarScaffoldScope){
topBar()
}
}
},
bottomBar = {
with(bottomBarScaffoldScope){
bottomBar()
}
},
) { contentPadding ->
content(contentPadding)
}
}
- The composable function above uses
ScaffoldTopBarScope
andScaffoldBottomBarScope
to only allow composables of those instances to be allowed. - If you are wondering why we have to define an instance of the scope to first use it, like so:
with(topBarScaffoldScope){
topBar()
}
- It is because of the way that extention functions work in kotlin.
ScaffoldTopBarScope.() -> Unit
defines a extension function on an instance ofScaffoldTopBarScope
. So to be able to properly use topBar() we need an instance of ScaffoldTopBarScope
Defining the Scopes
- When defining scopes we need to do so in a very particular way:
@Stable
class ScaffoldBottomBarScope(
private val iconSize: Dp,
){
@Composable
fun DualButtonNavigationBottomBarRow(){
// composable function
Icon(
imageVector = imageVector,
contentDescription = contentDescription,
tint = color,
modifier = Modifier.size(iconSize)
)
}
}
- Notice the
@Stable
annotation. This is done because of how Jetpack compose defines Stability. Long story short,@Stable
allows recomposition to work as it is intended to
Please read the original Bumble tech post
- If you are really looking to find a better organization stragety for your jetpack compose code, then please read the original Bumble tech article on this topic
Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.
Top comments (0)