<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Studio Knowledge]]></title><description><![CDATA[Knowledge. Insight into our process, ethos and how we build world class digital experiences at Studio.]]></description><link>https://dev.buildwithstudio.com/knowledge/</link><image><url>https://dev.buildwithstudio.com/knowledge/favicon.png</url><title>Studio Knowledge</title><link>https://dev.buildwithstudio.com/knowledge/</link></image><generator>Ghost 5.47</generator><lastBuildDate>Fri, 22 May 2026 07:10:27 GMT</lastBuildDate><atom:link href="https://dev.buildwithstudio.com/knowledge/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How to Build Something Your Users Will Share]]></title><description><![CDATA[<p>There&#x2019;s a myth out there about social sharing from apps.</p><p>The common sentiment seems to be: &#x201C;just slap on a share button&#x201D; and like magic, your content will start making its way around the wilds of the internet. This is especially true if the target is</p>]]></description><link>https://dev.buildwithstudio.com/knowledge/how-to-build-something-your-users-will-share/</link><guid isPermaLink="false">67630538b65afc0001d7bd78</guid><category><![CDATA[member]]></category><dc:creator><![CDATA[Vikalp Jain]]></dc:creator><pubDate>Wed, 18 Dec 2024 17:25:47 GMT</pubDate><media:content url="https://dev.buildwithstudio.com/knowledge/content/images/2024/12/product.png" medium="image"/><content:encoded><![CDATA[<img src="https://dev.buildwithstudio.com/knowledge/content/images/2024/12/product.png" alt="How to Build Something Your Users Will Share"><p>There&#x2019;s a myth out there about social sharing from apps.</p><p>The common sentiment seems to be: &#x201C;just slap on a share button&#x201D; and like magic, your content will start making its way around the wilds of the internet. This is especially true if the target is a younger audience. Unfortunately, there is no &#x201C;go viral&#x201D; button.</p><p>If you want users to share content around your product, you&#x2019;re going to need to consider:</p><ol><li>What motivates people to share? And;</li><li>How to align your feature or content so it speaks directly to #1?</li></ol><p>Let&#x2019;s get into it.</p><h3 id="why-people-share"><strong>Why People Share</strong></h3><p>There are innumerable reasons people feel compelled to share (too many to list). We&#x2019;re not psychologists, but we have developed enough apps to have seen the share-cycle in action, and we&#x2019;ve distilled the primary motivations for sharing into four categories:</p><h3 id="1-the-cocktail-party-effect"><strong>1. The Cocktail Party Effect</strong></h3><p><strong>Motivation:</strong> to be seen as &#x201C;in the know.&#x201D; People who share for this reason are often positioning themselves as knowledgeable or cutting edge. They want to be the very first one in their network to share the cool new thing or be able to wax philosophically on a brand-new product over a glass of red.</p><p><strong>Reward:</strong> praise &amp; validation from their peers.</p><p><strong>Notable Cocktail Shares:</strong> the latest and greatest article on a cultural topic, newly released features on an existing product or a brand new cutting-edge one (think Google Glass when it first arrived on the scene. RIP.)</p><h3 id="2-the-bandwagon-effect"><strong>2. The Bandwagon Effect</strong></h3><p><strong>Motivation:</strong> to participate in a viral moment. Users who share for this reason are often motivated by FOMO: they don&#x2019;t want to be the only ones in their circle (either physical or virtual) who haven&#x2019;t yet joined the movement.</p><p><strong>Reward:</strong> feeling a part of a larger, like-minded, community</p><p><strong>Notable Bandwagon Shares:</strong> the ALS ice-bucket challenge was a great example of the bandwagon effect. Being tagged to participate (and share) was a badge of honor. Political movements also capitalize on the bandwagon mentality, getting supporters to forward and share content as a means of participation in a campaign. Often there is some branded element that ties this together - a hashtag, a sticker, etc. </p><h3 id="3-the-omg-effect"><strong>3. The OMG Effect</strong></h3><p><strong>Motivation:</strong> the content has elicited such a strong, organic emotion &#x2013; joy, sadness, fear, surprise &#x2013; the user feels compelled to share it. Their motivation is simply that: to spread the feeling, good or bad, to others.</p><p><strong>Reward:</strong> validation and expansion of their experience</p><p><strong>Notable OMG Shares:</strong> the early adopters of the &#x201C;Faceapp&#x201D; AI aging filter were compelled to spread the word far and wide. This wasn&#x2019;t just because of the novelty of the tech, but because for young people, seeing themselves in the latter half of their golden years was too delightful, terrifying, and compelling not to share.</p><h3 id="4-the-need-to-know-effect"><strong>4. The Need-to-Know Effect</strong></h3><p><strong>Motivation:</strong> possibly the most difficult to inspire, this kind of sharing is motivated by a genuine desire to inform their network. Either to clue them in to an important cause, tap into collective wisdom, or uplift and elevate their community&#x2019;s knowledge.</p><p><strong>Reward:</strong> having an impact on friends, family, and like-minded others.</p><p><strong>Notable Need-to-Know Shares:</strong> those looking to raise awareness on important issues, seek or offer travel recommendations, or share new results/innovations on a topic close to their heart are all examples of this kind of spontaneous (and hard to capture) sharing.</p><h3 id="how-to-light-the-sharing-fire"><strong>How to light the sharing fire</strong></h3><p>Instead of making assumptions about shareability, consider the primary motivations for your target audience and focus there. Do they want to be moved? Shocked? Beckoned onto a fast-moving bandwagon? Design your features and content accordingly.</p><p>Think about sparking the impulse to share, rather than just declaring something shareable and hoping for the best. After all, sharing isn&#x2019;t about the button - it&#x2019;s about giving users something worth pressing for.</p>]]></content:encoded></item><item><title><![CDATA[Using Screen Navigation with Compose]]></title><description><![CDATA[In this guide, we deep dive into Compose navigation to help establish efficient navigation flows for your Android apps.]]></description><link>https://dev.buildwithstudio.com/knowledge/using-screen-navigation-with-compose/</link><guid isPermaLink="false">6759fadeb65afc0001d7bd57</guid><category><![CDATA[Enginnering]]></category><category><![CDATA[Guides]]></category><category><![CDATA[member]]></category><dc:creator><![CDATA[Vikalp Jain]]></dc:creator><pubDate>Wed, 11 Dec 2024 20:52:56 GMT</pubDate><media:content url="https://dev.buildwithstudio.com/knowledge/content/images/2024/12/CleanShot-2024-12-11-at-12.52.02@2x.png" medium="image"/><content:encoded><![CDATA[<img src="https://dev.buildwithstudio.com/knowledge/content/images/2024/12/CleanShot-2024-12-11-at-12.52.02@2x.png" alt="Using Screen Navigation with Compose"><p>Moving between screens and views is a crucial aspect of creating robust and user-friendly Android applications. The approach to navigation has been totally transformed with the advent of Jetpack Compose, Google&apos;s modern declarative UI toolkit.</p><p>Managing navigation effectively can be a daunting task, especially when it involves passing data between screens. So in this guide, we will dive into Compose navigation to help establish efficient navigation flows for your Android apps.</p><p>To illustrate this, we&#x2019;ll construct a basic app with 3 distinct screens. The app will feature a Home Screen with a button to navigate to the Profile Screen. Additionally, the Home Screen will include a list of clickable items that lead to a detail screen. We&#x2019;ll develop the app using Compose and <a href="https://insert-koin.io/docs/quickstart/android/?ref=dev.buildwithstudio.com">Koin</a> - straightforward library for injecting dependencies and viewModels.</p><h2 id="getting-started">Getting Started</h2><p>Now that we&#x2019;ve set up the foundation, it&apos;s time to explore how to manage navigation using Jetpack Compose. To achieve this, we&apos;ll utilize the <code>NavHost</code> Component to encapsulate the various destinations within our app:</p><h2 id="building-the-ui">Building the UI</h2><h3 id="home-screen">Home Screen</h3><p>The home screen is a very simple one that inputs a list of items and functions as the user taps on the button to navigate to the profile and item screens.</p><figure class="kg-card kg-image-card"><img src="https://buildwithstudio.com/knowledge/content/images/2024/07/image-1-1.png" class="kg-image" alt="Using Screen Navigation with Compose" loading="lazy" width="1583" height="1600"></figure><pre><code class="language-kotlin">@Composable
fun HomeScreen(items: List&lt;ItemData&gt;, onTapProfile: () -&gt; Unit, onTapItem: (ItemData) -&gt; Unit) {

    Box(
        Modifier.fillMaxSize().background(Color(0xFF3F51B5)).safeDrawingPadding(),
        Alignment.Center
    ) {
        LazyColumn {
            item {
                Box(Modifier.fillMaxWidth(), Alignment.Center) {
                    Button(
                        modifier = Modifier.fillMaxWidth().padding(20.dp),
                        onClick = { onTapProfile() }
                    ) {
                        Text(&quot;Go to Profile&quot;)
                    }
                }
            }

            item {
                Text(
                    &quot;Items : &quot;,
                    fontSize = 30.sp,
                    color = Color.White,
                    modifier = Modifier.padding(10.dp)
                )
            }

            items(items) { item -&gt;
                Column(
                    Modifier.fillMaxWidth()
                        .height(50.dp)
                        .background(Color(item.color))
                        .clickable { onTapItem(item) }
                        .padding(horizontal = 20.dp)
                ) {
                    Spacer(Modifier.size(8.dp))

                    Text(&quot;Item&#xA0; #${item.id}&quot;, fontSize = 20.sp, color = Color.White)

                    Spacer(Modifier.size(8.dp))

                    Box(Modifier.fillMaxWidth().height(1.dp).background(Color.White))
                }
            }
        }
    }
}
</code></pre><h3 id="profile-screen">Profile Screen</h3><p>The profile screen is the simplest screen of all. It just has a title and text positioned at the center. We will come back to the ProfileViewModel later.</p><figure class="kg-card kg-image-card"><img src="https://buildwithstudio.com/knowledge/content/images/2024/07/image-2-profile.png" class="kg-image" alt="Using Screen Navigation with Compose" loading="lazy" width="1583" height="1600"></figure><pre><code class="language-kotlin">@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProfileScreen() {

    val viewModel: ProfileViewModel = koinViewModel()

    Scaffold(
        topBar = {
            CenterAlignedTopAppBar(
                title = { Text(&quot;Profile&quot;, color = Color.Black, fontSize = 30.sp) },
                navigationIcon = {
                    IconButton({ viewModel.navigateBack() }) {
                        Icon(
                            painterResource(R.drawable.back_arrow),
                            &quot;back&quot;,
                            tint = Color.Black,
                            modifier = Modifier.size(30.dp)
                        )
                    }
                },
                modifier = Modifier,
                colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
            )
        }
    ) { paddingValues -&gt;
        Box(Modifier.fillMaxSize().background(Color.White), Alignment.Center) {
            Text(&quot;Profile Screen&quot;, fontSize = 30.sp, color = Color.Black)
        }
    }
}
</code></pre><h3 id="item-details-screen">Item Details Screen</h3><p>The ItemDetailsScreen will take the data from the ItemDetailsViewModel to display the number of the item and use the color for the background.</p><figure class="kg-card kg-image-card"><img src="https://buildwithstudio.com/knowledge/content/images/2024/07/image-3-details.png" class="kg-image" alt="Using Screen Navigation with Compose" loading="lazy" width="1583" height="1600"></figure><pre><code class="language-kotlin">@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ItemDetailsScreen() {

    val viewModel: ItemDetailsViewModel = koinViewModel()

    Scaffold(
        topBar = {
            CenterAlignedTopAppBar(
                title = { Text(&quot;Item Details&quot;, color = Color.White, fontSize = 30.sp) },
                navigationIcon = {
                    IconButton({
                        // TODO: Add back navigation
                    }) {
                        Icon(
                            painterResource(R.drawable.back_arrow),
                            &quot;back&quot;,
                            tint = Color.White,
                            modifier = Modifier.size(30.dp)
                        )
                    }
                },
                modifier = Modifier,
                colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
            )
        }
    ) { paddingValues -&gt;
        val item by remember { mutableStateOf(viewModel.item) }

        Box(Modifier.fillMaxSize().background(Color(item.color)), Alignment.Center) {
            Text(&quot;Item #${item.id}&quot;, fontSize = 30.sp, color = Color.White)
        }
    }
}
</code></pre><h2 id="the-domain-layer">The Domain layer</h2><p>To replicate a real-life scenario, let&#x2019;s utilize a service that will supply us with the necessary data to showcase on our various screens. The ItemService will be injected into both the AppViewModel and ItemDetailsViewModel. These viewModels will then retrieve the required data and send it to the view.</p><pre><code class="language-kotlin">interface ItemService {
    val items: List&lt;ItemData&gt;
}

class RealItemService : ItemService {

    override val items: List&lt;ItemData&gt; = generateItems()

    // Generate a list of 100 items with the index as ID and a random color from the list below
    private fun generateItems(): List&lt;ItemData&gt; =
        (0 until 100).map { ItemData(id = it, color = getRandomColor()) }

    private fun getRandomColor(): Long {

        val colorList =
            listOf(
                0xFFF44336,
                0xFF009688,
                0xFF9C27B0,
                0xFF673AB7,
                0xFF3F51B5,
                0xFF4CAF50,
                0xFFFF5722,
                0xFF5F1700,
                0xFF690024,
                0xFF000D53,
                0xFF003602
            )

        return colorList[(0..colorList.lastIndex).random()]
    }
}

data class ItemData(val id: Int, val color: Long)
</code></pre><h2 id="dependency-injection">Dependency Injection</h2><p>In order to be able to inject the service into the ViewModels and inject the ViewModels into the views, we are going to use <a href="https://insert-koin.io/docs/quickstart/android/?ref=dev.buildwithstudio.com">Koin</a>. The setup is very simple.</p><p>First, we need to create a custom App class:</p><pre><code class="language-kotlin">class MainApp : Application() {

    override fun onCreate() {

        super.onCreate()

        startKoin {
            androidContext(this@MainApp)

            modules(appModule)
        }
    }
}
</code></pre><p>Set the MainApp as the class to run at launch in the manifest:</p><pre><code class="language-xml">&lt;manifest xmlns:android=&quot;&lt;http://schemas.android.com/apk/res/android&gt;&quot; xmlns:tools=&quot;&lt;http://schemas.android.com/tools&gt;&quot;&gt;

&lt;application android:name=&quot;.MainApp&quot;&gt;</code></pre><p>Almost there! Just create a kotlin file called <code>appModule.kt</code>. This file will contain all the different components to inject:</p><pre><code class="language-kotlin">val appModule = module {

    // create a viewModel with all the required dependencies
    viewModelOf(::ItemDetailsViewModel)

    viewModelOf(::ProfileViewModel)

    viewModelOf(::AppViewModel)

    // Inject a Singleton
    single&lt;ItemService&gt; { RealItemService() }
}
</code></pre><p>And now we can create our viewModels to inject in the views:</p><pre><code class="language-kotlin">
val viewModel: ViewModel = koinViewModel()

</code></pre><pre><code class="language-kotlin">class AppViewModel(itemService: ItemService) : ViewModel() {
    val itemList = itemService.items
}

class ItemDetailsViewModel(itemService: ItemService) : ViewModel()

class ProfileViewModel(private val navigator: Navigator) : ViewModel()
</code></pre><h2 id="navigation-handling">Navigation Handling</h2><p>Now that we&#x2019;ve set up the foundation, it&apos;s time to explore how to manage navigation using Jetpack Compose. To achieve this, we&apos;ll utilize the <code>NavHost</code> Component to encapsulate the various destinations within our app:</p><pre><code class="language-kotlin">// Call this view from the MainActivity
// setContent {
// &#xA0; &#xA0; TutorialNavigationTheme {
// &#xA0; &#xA0; &#xA0; &#xA0; AppScreen()
// &#xA0; &#xA0; }
// }

@Composable
fun AppScreen() {

    val viewModel: AppViewModel = koinViewModel()

    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = HomeDestination.route) {
        composable(HomeDestination.route) {
            HomeContent(
                viewModel.itemList,
                {
                    // TODO: Add Profile Navigation
                }
            ) {
                // TODO: Add Item Navigation
            }
        }

        composableSlideInOut(ProfileDestination.route) { ProfileScreen() }

        composableSlideInOut(
            ItemDetailsDestination.route,
            arguments = ItemDetailsDestination.navArgs
        ) {
            ItemDetailsScreen()
        }
    }
}
</code></pre><p><code>composableSlideInOut</code> is just an extension to customize the transition between two screens:</p><pre><code class="language-kotlin">private const val transitionDuration = 700

fun NavGraphBuilder.composableSlideInOut(
    route: String,
    arguments: List&lt;NamedNavArgument&gt; = emptyList(),
    deepLinks: List&lt;NavDeepLink&gt; = emptyList(),
    enterTransition:
        (@JvmSuppressWildcards
        AnimatedContentTransitionScope&lt;NavBackStackEntry&gt;.() -&gt; EnterTransition?)? =
        {
            slideIntoContainer(
                AnimatedContentTransitionScope.SlideDirection.Left,
                tween(transitionDuration)
            )
        },
    exitTransition:
        (@JvmSuppressWildcards
        AnimatedContentTransitionScope&lt;NavBackStackEntry&gt;.() -&gt; ExitTransition?)? =
        {
            slideOutOfContainer(
                AnimatedContentTransitionScope.SlideDirection.Left,
                tween(transitionDuration)
            )
        },
    popEnterTransition:
        (@JvmSuppressWildcards
        AnimatedContentTransitionScope&lt;NavBackStackEntry&gt;.() -&gt; EnterTransition?)? =
        {
            slideIntoContainer(
                AnimatedContentTransitionScope.SlideDirection.Right,
                tween(transitionDuration)
            )
        },
    popExitTransition:
        (@JvmSuppressWildcards
        AnimatedContentTransitionScope&lt;NavBackStackEntry&gt;.() -&gt; ExitTransition?)? =
        {
            slideOutOfContainer(
                AnimatedContentTransitionScope.SlideDirection.Right,
                tween(transitionDuration)
            )
        },
    content: @Composable AnimatedContentScope.(NavBackStackEntry) -&gt; Unit
) {

    composable(
        route,
        arguments,
        deepLinks,
        enterTransition,
        exitTransition,
        popEnterTransition,
        popExitTransition,
        content
    )
}
</code></pre><p>Within the <code>NavHost</code>, you&apos;ll find composables that specify the various views and how to navigate between them. Remember to include a <code>NavController</code> using <code>rememberNavController</code> to handle the navigation.</p><p>The <code>NavHost</code> must designate a start destination to determine the initial screen to show. This start destination is simply a <code>String</code> that corresponds to one provided to a <code>composable</code>.</p><p>Here, we want the app to start on the HomeScreen.</p><h3 id="destinations">Destinations</h3><p>There are numerous effective approaches to handle destinations - scattered <code>Strings</code> however, is not one of the recommended approaches. It&apos;s quite easy to misspell them and encounter a crash. Instead, a popular approach is to utilize either an <code>enum class</code> or a <code>sealed class</code>.</p><p>For now, we&apos;ll handle destinations differently. Every destination will be a class that contains a static string for the route.</p><pre><code class="language-kotlin">object HomeDestination {

    private const val root = &quot;home&quot;

    const val route = root
}
</code></pre><p>Or:</p><pre><code class="language-kotlin">object ProfileDestination {

    private const val root = &quot;profile&quot;

    const val route = root
}
</code></pre><p>This will work nicely for a simple navigation without passing any arguments between screens. It might even be overkill.</p><p>Issues may arise when trying to send data - like the item id - to <code>ItemDetailsScreen</code> in this case.</p><p>To send parameters to a different screen, you must include the parameter name after the destination name in the route definition, for example: <code>&quot;$root/$index&quot;</code>. As you construct the route, the argument name will be substituted with its actual value.</p><p>To make it easy, each destination will extend <code>NavDestination</code>.</p><pre><code class="language-kotlin">interface NavDestination {

    fun buildRoute(): String
}

object HomeDestination : NavDestination {

    override fun buildRoute(): String = route

    private const val root = &quot;home&quot;

    const val route = root
}

object ProfileDestination : NavDestination {

    override fun buildRoute(): String = route

    private const val root = &quot;profile&quot;

    const val route = root
}

class ItemDetailsDestination(val index: Int) : NavDestination {

    constructor(
        savedStateHandle: SavedStateHandle
    ) : this(index = requireNotNull(savedStateHandle.get&lt;Int&gt;(inputArg)))

    override fun buildRoute(): String = &quot;$root/$index&quot;

    companion object {

        private const val root = &quot;item_details&quot;

        private const val inputArg = &quot;index&quot;

        const val route = &quot;$root/{$inputArg}&quot;

        val navArgs = listOf(navArgument(inputArg) { type = NavType.IntType })
    }
}
</code></pre><p>As you can see, <code>ItemDetailsDestination</code> will pass the index of the selected item to the detailed screen. For this, we pass the static <code>route</code> and navigation arguments <code>navArgs</code> which consist of a list of name (<code>inputArg</code> which is <code>index</code>) and a type (Here <code>NavType.IntType</code>).</p><p>The custom constructor will be used to recreate ItemDetailsDestination with the params that were passed from <code>HomeScreen</code> thanks to <code>SavedStateHandle</code>. More to come on that.</p><h3 id="navigator">Navigator</h3><p>Let&apos;s look at the main component of this architecture - the <code>Navigator</code> :</p><pre><code class="language-kotlin">interface Navigator {

    fun setController(navController: NavController)

    fun navigate(route: NavDestination, builder: NavOptionsBuilder.() -&gt; Unit = {})

    fun popBackStack()

    fun popBackStack(route: NavDestination, inclusive: Boolean, saveState: Boolean = false)
}

class RealNavigator : Navigator {

    private var navController: NavController? = null

    override fun setController(navController: NavController) {

        this.navController = navController
    }

    override fun navigate(route: NavDestination, builder: NavOptionsBuilder.() -&gt; Unit) {

        navController?.navigate(route.buildRoute(), builder)
            ?: Log.w(&quot;Navigator&quot;, &quot;No NavController set in the Navigator&quot;)
    }

    override fun popBackStack() {

        navController?.popBackStack() ?: Log.w(&quot;Navigator&quot;, &quot;No NavController set in the Navigator&quot;)
    }

    override fun popBackStack(route: NavDestination, inclusive: Boolean, saveState: Boolean) {

        navController?.popBackStack(route.buildRoute(), inclusive, saveState)
    }
}
</code></pre><p>The <code>Navigator</code> will handle the navigation for us. From now on, every time we want to navigate, we are not going to call the usual <code>navController.navigate(&quot;route&quot;)</code> but <code>navigator.navigate(NavDestination)</code> instead. The <code>Navigator</code> will also take care of the back navigation calling <code>popBackStack()</code> to navigate back to the previous screen or to a desired screen present in the navigation back stack.</p><p>The <code>Navigator</code> will be injected into the viewModel of every screen and the view will call a viewModel function that will handle the desired navigation.</p><p>Now, let&apos;s see how to set that up.</p><p>In the <code>appModule</code> that we created previously, define how to instantiate the <code>Navigator</code></p><pre><code class="language-kotlin">val appModule = module {
    viewModelOf(::ItemDetailsViewModel)

    viewModelOf(::ProfileViewModel)

    viewModelOf(::AppViewModel)

    single&lt;ItemService&gt; { RealItemService() }

    // Add this line
    single&lt;Navigator&gt; { RealNavigator() }
}
</code></pre><p>In order for the <code>Navigator</code> to work, it needs to have the Compose <code>NavController</code> used by the <code>NavHost</code>. As the <code>Navigator</code> is a singleton, only one instance will be created, ensuring that we have a common <code>Navigator</code> across the app.</p><p>So we can inject it in the <code>AppScreen</code> where we will pass the <code>NavController</code>:</p><pre><code class="language-kotlin">@Composable
fun AppScreen(
    // Inject the navigator
    navigator: Navigator = koinInject()
) {

    val viewModel: AppViewModel = koinViewModel()

    val navController = rememberNavController()

    // Pass the navController to the navigator
    LaunchedEffect(navController) { 	
    	navigator.setController(navController) 
    }
    
    ...
}
</code></pre><p>Now we can modify each viewModel by adding the navigator to their constructor. As the navigator is defined in the <code>appModule</code>, Koin knows how to instantiate and pass it to the ViewModel</p><pre><code class="language-kotlin">class AppViewModel(itemService: ItemService, private val navigator: Navigator) : ViewModel() {

    val itemList = itemService.items

    fun navigateToProfile() {
        navigator.navigate(ProfileDestination)
    }

    fun navigateToItem(index: Int) {
        navigator.navigate(ItemDetailsDestination(index))
    }
}
</code></pre><p>The ViewModel will now handle the navigation. We can fill the <code>HomeScreen</code> lambda with the navigation logic:</p><pre><code class="language-kotlin">fun AppScreen(navigator: Navigator = koinInject()) {
    ...
    NavHost(navController = navController, startDestination = HomeDestination.route) {
        composable(HomeDestination.route) {
            HomeScreen(viewModel.itemList, { viewModel.navigateToProfile() }) { item -&gt;
                viewModel.navigateToItem(item.id)
            }
        }

        ...
    }
}</code></pre><h2 id="receiving-arguments">Receiving arguments</h2><p>In order to receive the arguments, we will inject the <code>Navigator</code> and the <code>SavedStateHandle</code> which holds the arguments in a key-value format.</p><p>Koin takes care of <code>SavedStateHandle</code> as stated <a href="https://insert-koin.io/docs/reference/koin-android/viewmodel/?ref=dev.buildwithstudio.com#savedstatehandle-injection-330">here</a>.</p><pre><code class="language-kotlin">class ItemDetailsViewModel(
    itemService: ItemService,
    private val navigator: Navigator,
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val route = ItemDetailsDestination(savedStateHandle)

    val item = itemService.items[route.index]

    fun navigateBack() {
        navigator.popBackStack()
    }
}
</code></pre><p>First, <code>ItemDetailsViewModel</code> retrieves the index of the item from the <code>SavedStateHandle</code> and then gets the item object from the itemService.</p><p>We can now call <code>navigateBack()</code> from the view to navigate back to our <code>HomeScreen</code></p><pre><code class="language-kotlin">Scaffold(
    topBar = {
        CenterAlignedTopAppBar(
            title = { Text(&quot;Item Details&quot;, color = Color.White, fontSize = 30.sp) },
            navigationIcon = {
                IconButton({
                    // Call back navigation here
                    viewModel.navigateBack()
                }) {
                    Icon(
                        painterResource(R.drawable.back_arrow),
                        &quot;back&quot;,
                        tint = Color.White,
                        modifier = Modifier.size(30.dp)
                    )
                }
            }
        )
    }
)
</code></pre><p>So there you have it! We&apos;ve just explored the best way to handle navigation in your Jetpack Compose Android apps.</p><p>By using this architecture you can create a navigation structure that&apos;s not only robust but also super easy to maintain and scale, without using too many callbacks to call the <code>NavController</code> from the parent view with the <code>NavHost</code>.</p><p>This approach makes for cleaner code and reduces the chance of errors. If you&apos;re looking for help building your Android product, <a href="buildwithstudio.com/contact">contact Studio today</a>. </p>]]></content:encoded></item><item><title><![CDATA[Coming soon]]></title><description><![CDATA[<p>This is Studio Knowledge, a brand new site by Vikalp Jain that&apos;s just getting started. Things will be up and running here shortly, but you can <a href="#/portal/">subscribe</a> in the meantime if you&apos;d like to stay up to date and receive emails when new content is published!</p>]]></description><link>https://dev.buildwithstudio.com/knowledge/coming-soon/</link><guid isPermaLink="false">64a566936497e300019586b6</guid><category><![CDATA[News]]></category><category><![CDATA[member]]></category><dc:creator><![CDATA[Vikalp Jain]]></dc:creator><pubDate>Wed, 05 Jul 2023 12:48:19 GMT</pubDate><media:content url="https://static.ghost.org/v4.0.0/images/feature-image.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://static.ghost.org/v4.0.0/images/feature-image.jpg" alt="Coming soon"><p>This is Studio Knowledge, a brand new site by Vikalp Jain that&apos;s just getting started. Things will be up and running here shortly, but you can <a href="#/portal/">subscribe</a> in the meantime if you&apos;d like to stay up to date and receive emails when new content is published!</p><p></p><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, distinctio autem. Ipsa esse repellendus hic at possimus. Aliquam consequatur deleniti inventore aut tenetur sint perferendis neque natus unde eveniet qui odio hic sunt, labore laborum, ratione ut enim modi facilis consequuntur iure quam dolor quidem amet! Corporis dignissimos recusandae placeat repellendus nemo? Architecto maiores tenetur alias non voluptatem! Repellendus, sequi non consectetur dolore animi odit doloremque modi? Impedit praesentium eaque ipsum recusandae iusto delectus perspiciatis sed maxime. </p><p>Similique, provident id beatae quisquam dolorem porro earum illo eligendi numquam blanditiis necessitatibus aliquid consequatur ullam voluptatibus nam tempore corporis libero aliquam fuga veritatis est totam natus. Necessitatibus dolorum error rem et dicta quibusdam culpa voluptatibus illum, dolor tempore, dolore itaque quisquam tenetur. Iure quibusdam sed delectus aspernatur maxime? Ullam, aliquam quisquam. Optio dolorem ipsum fugit corporis, modi soluta rem id totam nam tenetur ex cum cupiditate voluptate eius quod vel sequi labore. Quibusdam, velit! Quaerat maiores fugiat rerum distinctio, quis quae autem non ea ipsa unde repellat ullam minus veritatis expedita labore neque mollitia eos inventore possimus corporis facere. </p><p>Aperiam minima a soluta excepturi cupiditate reiciendis quas amet aliquam eos, autem eum doloribus sit ut nulla. Laboriosam sit ex fuga magnam aperiam soluta corrupti tempore earum debitis omnis officia deleniti aut, aspernatur magni, vero nemo. A optio vel nesciunt quasi. Ea vel consequatur eveniet sint itaque! Officia praesentium eos voluptas ea et temporibus aperiam porro eius corrupti ratione vitae vero, quia necessitatibus rem reiciendis reprehenderit blanditiis, provident ullam optio velit tenetur exercitationem maiores? Dolore mollitia aliquam molestias harum consectetur! </p><p>Unde numquam culpa, molestiae laboriosam obcaecati veniam voluptate neque delectus consequuntur, iusto aut consequatur adipisci at ipsam nulla sapiente beatae est assumenda quis dicta! Error commodi dolorem harum! Ex molestias culpa voluptatibus minus tempore quod maiores nobis commodi vitae ratione. Tenetur, qui amet?</p>]]></content:encoded></item></channel></rss>