1

I'm building an Android app using purely Jetpack Compose. My entire application is wrapped under one scaffold, and has a ViewModel for each "screen" (which are composables) in my app. Because of that, I have some conditional statements in my scaffold to determine the floating action button (FAB) based on the route. However, one of the FABs needs access to a function in a ViewModel, which is only created when I navigate to the route that holds that composable, and I'm at a loss on the best way to give the FAB access to that viewmodel function.

Take the following example (based off my code), and note the FAB for the route "route3".

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            MyApp()
        }
    }

    @Composable
    fun MyApp() {
        val navController = rememberNavController()
        val backstackEntry = navController.currentBackStackEntryAsState()
        val scaffoldState = rememberScaffoldState()

        Surface(color = MaterialTheme.colors.background) {
            Scaffold(
                topBar = { ... },
                bottomBar = { ... },
                floatingActionButton = {
                    when (backstackEntry.value?.destination?.route) {
                        "route2" -> FAB2(navController)
                        "route3" -> FAB3(navController) // Needs to access function from viewModel3
                    }
                },
                scaffoldState = scaffoldState,
            ) {
                MyNavHost(navController, scaffoldState)
            }
        }
    }

    @Composable
    fun MyNavHost(navController: NavHostController, scaffoldState: ScaffoldState) {
        NavHost(
            navController = navController,
            startDestination = "route1"
        ) {
            composable("route1") { Destination1() }
            composable("route2") { Destination2() }
            composable("route3") { Destination3() }
        }
    }

    @Composable
    fun Destination1() { ...}

    @Composable
    fun Destination2() { ... }

    @Composable
    fun Destination3() {
        val viewModel3: CustomViewModel = hiltViewModel()
        Screen3(viewModel3)
    }

}

So my main question is, if the FAB3 variable needs to access a function from viewModel3, how would I go about doing it?

sping
  • 35
  • 9

1 Answers1

1

I decided to just switch over to using a scaffold for every screen.

Honestly, this makes managing routes a lot easier, because in the single scaffold scenario, it was getting difficult managing all of the possible routes for things like the TopBar and FAB in large when() blocks.

But if anyone has a solution to the original question that would be appreciated!

sping
  • 35
  • 9
  • 1
    Consider the solution provided here: https://stackoverflow.com/questions/71186641/how-to-control-a-scaffolds-floatingactionbutton-onclick-from-child-composable That's basically the same way it's done on e.g. `ReactJS` applications which also use a declarative UI. – stefan.at.kotlin Jun 05 '22 at 12:28
  • @stefan.at.wpf Thanks for linking that! That solution does answer my original question and I urge people to take a look if they landed here first. However, that solution doesn't work for me because I want to keep track of extra things: 1. Different FAB icons for different screens. 2. I also have a FAB that de-extends itself when a LazyGrid has scrolled past a certain point, so that's something else to keep track of. So unless storing a Composable in state (if possible), keeping track of those other states would be difficult. Thus, using a scaffold per screen is still the best solution for me. – sping Jan 13 '23 at 06:24
  • how did it go with your solution, especially, do you use a NavigationBar (BottomNavigationBar)? Wouldn't having a new one on each screen interrupt the touch animations when the new screen is rendered? – stefan.at.kotlin Apr 25 '23 at 20:43