Using State in Jetpack Compose
Tags: computerscience, kotlin, programming, android
Published: 2021-04-21 07:14:03

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

<!-- wp:paragraph -->
<p>We will cover briefly about</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Jetpack Compose</li><li>Create a Custom Card field</li><li>Using state in the custom card</li></ol>
<!-- /wp:list -->

{% youtube s2jXxh9RLSQ %}

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: Jetpack Compose or Compose are the same.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:heading {"level":3} -->
<h3>Jetpack Compose</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><a href="https://developer.android.com/jetpack/compose" rel="noreferrer noopener" target="_blank">What is Jetpack Compose?</a></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>As per the documentation,</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Jetpack Compose is Android’s modern toolkit for building native UI. It simplifies and accelerates UI development on Android.&nbsp;</p></blockquote>
<!-- /wp:quote -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*pu6wDFDwQ8B0GxyYiIvegg.png" alt="Jetpack Compose"/><figcaption>Jetpack Compose</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Compose makes building Android UIs faster and easier</li><li>There is no need to edit the XML layouts</li><li>Instead, use the Compose functions for the elements you want and see them change in the preview.</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":4} -->
<h4>Setup&nbsp;</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>As of today, when I write this article, Compose is still in beta</li><li>You need to download <a href="https://developer.android.com/studio/preview" rel="noreferrer noopener" target="_blank">Android Studio 4.2 Canary</a></li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*m5owvhaOgF2L50gg20n13Q.png" alt="Android Studio Version having Jetpack Compose"/><figcaption>Android Studio Version having Jetpack&nbsp;Compose</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Once downloaded, create a <strong>new project. </strong>Now you would see a new option of <strong>Empty Compose Activity -> </strong>then<strong> Finish (create as per the default settings)</strong></li></ul>
<!-- /wp:list -->

<!-- wp:image -->
<figure class="wp-block-image"><img src="https://cdn-images-1.medium.com/max/1600/1*hB0SEXqL328e4OhksLqv5g.png" alt="Empty Compose Activity"/><figcaption>Empty Compose&nbsp;Activity</figcaption></figure>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Notice, there is no <code><em>activity_main.xml</em></code>layout.</li><li>Your <strong>main activity extends a Component Activity</strong>, instead of the AppCompatActivity.</li><li>Open your module gradle file and notice</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">buildFeatures {<br>    compose true<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>Check the dependencies inside the dependencies section</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"

// Can have some extra also</pre>
<!-- /wp:preformatted -->

<!-- wp:heading {"level":3} -->
<h3>Custom Card Field Using&nbsp;Compose</h3>
<!-- /wp:heading -->

<!-- wp:image -->
<figure class="wp-block-image"><img src="https://cdn-images-1.medium.com/max/1600/1*-hJFrCOs3jA-Fqp2SkaDwA.png" alt="Checkbox with Text — CustomCard"/><figcaption>Checkbox with Text — CustomCard</figcaption></figure>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>In the MainActivity, you would see something like this</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">setContent {<br>    HandsOnKotlinTheme {<br>         Surface(color = MaterialTheme.colors.background) {<br>              // YOUR COMPONENTS<br>         }<br>    }<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: <strong>HandsOnKotlinTheme </strong>is a variable name, that depends upon the name of your project, (in my case <strong>HandsOnKotlin</strong>)</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>anything inside <strong>setContent</strong>, becomes the root view of the activity</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><a href="https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#surface" rel="noreferrer noopener" target="_blank">surface</a>: A container influenced by material design, over which you design your UI.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>For making custom card, we create a <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable" rel="noreferrer noopener" target="_blank">composable</a> function. Composable functions (marked with <code>@ Composable</code> ), are the building blocks of an application built with Compose</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@Composable<br>fun CreateCustomCard(<br>    title: String,<br>    checkboxState: Boolean,<br>    onCheckboxPressed: ((Boolean) -&gt; Unit)?<br>) {<br>    Row(<br>        modifier = Modifier.padding(4.dp),<br>        verticalAlignment = Alignment.CenterVertically<br>    ) {<br>    Checkbox(<br>        checked = checkboxState,<br>        onCheckedChange = onCheckboxPressed,<br>    )<br>    Spacer(modifier = Modifier.padding(end = 4.dp))<br>    Text(text = title)<br>  }<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p><strong>modifier</strong>: Allow you to decorate a composable</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Row, Checkbox, Spacer, Text:</strong> Pre-defined composable functions</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>You can preview a composable by writing another function and marking with <code>@ Preview</code> and <code>@ Composable</code></li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@Preview(showBackground = true)<br>@Composable<br>fun PreviewCustomCard() {<br>    CreateCustomCard(title = "Dummy Item 1", false, null)<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:image -->
<figure class="wp-block-image"><img src="https://cdn-images-1.medium.com/max/1600/1*9K7nupJkuHSqw5xg7itmcA.png" alt="Composable Preview"/><figcaption>Composable Preview</figcaption></figure>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Now put your CreateCustomCard inside the surface and run your app</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">Surface(color = MaterialTheme.colors.background) {<br>    CreateCustomCard(title = "Dummy Item 1", false, {})      <br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:image -->
<figure class="wp-block-image"><img src="https://cdn-images-1.medium.com/max/1600/1*JIJZHweOCqWa7fgQm2aWMw.png" alt="Output on Running the App"/><figcaption>Output on Running the&nbsp;App</figcaption></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Using state in the custom&nbsp;card</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Our checkbox would have a state in which it can either be <strong>checked (true)</strong> or<strong> unchecked (false).</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We will look into 4 approaches, for saving state.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Approach 1 (Remember)</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Inside our createCustomCard, lets define a variable like this</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">val checkboxState = remember { mutableStateOf(false) }</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>We will use the value from the checkboxState and assign to checkbox composable</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">// The lines with Row remain same, and won't be shown in next snippets
Row(
     modifier = Modifier.padding(4.dp),
     verticalAlignment = Alignment.CenterVertically
) {
     // Approach 1
     val checkboxState = remember { mutableStateOf(false) }

     Checkbox(
         checked = checkboxState.value,
         onCheckedChange = {checkboxState.value = it},
     )
     Spacer(modifier = Modifier.padding(end = 4.dp))
     Text(text = title)
}</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>Any change in the value of checkboxState will trigger recomposition (rebuild of component)</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>Drawbacks:&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>The state won’t be retained across configuration changes (landscape/portrait)</li><li>Our composable is coupled with the business logic</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":4} -->
<h4>Approach 2 (RememberSaveable)</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Inside our createCustomCard, initialise our checkboxState like</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">val checkboxState = rememberSaveable() { mutableStateOf(false) }</pre>
<!-- /wp:preformatted -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Rest remains the same as Approach 1</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p><strong>rememberSaveable:</strong> saves the state across configuration changes</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Drawbacks:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Still our composable is coupled with business logic</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":4} -->
<h4>Approach 3 (State Hoisting)</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>State hoisting is a programming pattern where you move the state of a composable to the caller of that composable.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Extract the params, <code>checkboxState</code> and the <code>onCheckboxChange</code> from the CreateCustomCard and create a new composable (<strong>CustomCardState</strong>)</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">//Approach 3: State Hoisting
@Composable
fun CustomCardState() {
   val checkboxState = rememberSaveable { mutableStateOf(false) }

   CreateCustomCard(
        title = "Dummy Item 1",
        checkboxState.value
   ) { checkboxState.value = it }
}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>and our CreateCustomCard looks like&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">fun CreateCustomCard(<br>    title: String,<br>    checkboxState: Boolean,<br>    onCheckboxPressed: ((Boolean) -&gt; Unit)?<br>) {<br>    Row(<br>        modifier = Modifier.padding(4.dp),<br>        verticalAlignment = Alignment.CenterVertically<br>    ) {<br>    Checkbox(<br>        checked = checkboxState,<br>        onCheckedChange = onCheckboxPressed,<br>    )<br>    Spacer(modifier = Modifier.padding(end = 4.dp))<br>    Text(text = title)<br>  }<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>This way our composable is decoupled from logic, hence the createCustomCard can be tested and reused.</li></ul>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: In case of any errors try importing (below) in app.gradle</p></blockquote>
<!-- /wp:quote -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha02"</pre>
<!-- /wp:preformatted -->

<!-- wp:heading {"level":4} -->
<h4>Approach 4 (ViewModel)</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>You can use the <code>ViewModel</code> to expose the state in an observable holder (like <code>LiveData</code>) and also to handle events that affect that state.</li><li>We implement a ViewModel</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">// Approach 4: ViewModel
class CheckedViewModel : ViewModel() {
    
private val _isDone : MutableLiveData&lt;Boolean> =  MutableLiveData(false)
    
    val isDone : LiveData&lt;Boolean> = _isDone

    fun onCheckboxChange(state:Boolean) {
        _isDone.value = state
    }
}</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li><strong>isDone</strong>: LiveData that holds the current state.&nbsp;</li><li><strong>onCheckboxChange</strong>: event we define that the UI can invoke.</li><li>Our <strong>CustomCardState</strong> (defined in approach 3) looks like</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@Composable
fun CustomCardState(itemViewModel: CheckedViewModel) {
   val state : Boolean by itemViewModel.isDone.observeAsState(false)

   CreateCustomCard(
        title = "Dummy Item 1",
        state
   ) { itemViewModel.onCheckboxChange(it) }
}</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li><strong>observeAsState</strong>: Observes a <strong>LiveData (isDone)</strong> and returns a state that is updated whenever the <strong>LiveData</strong> changes (using the <strong>onCheckboxChange</strong>)</li></ul>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note&nbsp;: In case of any errors, try importing (below) in MainActivity</p></blockquote>
<!-- /wp:quote -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">import androidx.compose.runtime.getValue</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p><strong>Calling from Activity</strong></p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>We create a variable itemViewModel and assign its value inside <strong>onCreate</strong></li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">private lateinit var itemViewModel: CheckedViewModel</pre>
<!-- /wp:preformatted -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">override fun onCreate(savedInstanceState: Bundle?) {<br>     super.onCreate(savedInstanceState)<br>     itemViewModel = CheckedViewModel()<br>     <br>     setContent {<br>         // LINES OMITTED<br>         CustomCardState(itemViewModel)<br>     }<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:heading {"level":4} -->
<h4>Bonus: Showing a&nbsp;toast</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Since, we are using a viewmodel, we can observe the value <code>isDone</code>&nbsp;</li><li>If the value is true, then we show the toast</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">itemViewModel.isDone.observe(this, { status -&gt;<br>     if(status) {<br>         // Show toast<br>     }<br>})</pre>
<!-- /wp:preformatted -->

{% youtube s2jXxh9RLSQ %}

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" href="https://github.com/AseemWangoo/hands_on_kotlin" target="_blank"><em>Source code.</em></a>

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>