raamcosta / compose-destinations

Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate.
https://composedestinations.rafaelcosta.xyz
Apache License 2.0
3.2k stars 132 forks source link

Support for sharing ResultBackNavigator between nested destinations #535

Closed Ezard closed 9 months ago

Ezard commented 11 months ago

Imagine the following scenario:

There is normal screen destination, called Screen

There is a bottom sheet destination, called BottomSheet

Screen and BottomSheet are part of the same nav graph

BottomSheet has multiple "screens" within it, which can be navigated around by the user, called InnerScreen1 and InnerScreen2. These exist in a separate nav graph inside BottomSheet

I would like to pass the same type of data back to Screen from either InnerScreen1 or InnerScreen2

Currently, the only way I could manage to get working for this was to manually specify composable blocks for each of the 2 inner screens inside BottomSheet's nav graph, e.g.

@Composable
fun BottomSheet(
  resultBackNavigator: ResultBackNavigator<Foo>,
) {
  DestinationsNavHost(
    ...
  ) {
    composable(InnerScreen1Destination) {
      InnerScreen1(
        resultBackNavigator = resultBackNavigator,
      )
    }
    composable(InnerScreen2Destination) {
      InnerScreen1(
        resultBackNavigator = resultBackNavigator,
      )
    }
  }
}

The above way of handling it isn't too bad for 1 or 2 destinations, but in our case, we have a bunch of inner screens (in our case it's for multiple ways for the user to select an address - selecting an existing one from their account, using some postcode lookup functionality, entering the address manually, etc)

Is there a better way of doing this? I tried setting up a manual dependency block that provides the ResultBackNavigator, but this didn't work (I assume because this class is part of the library, so the library's default process for providing it takes precedence over manual dependencies)

raamcosta commented 9 months ago

HI!

Thanks for opening the issue! This seems similar to sharing outer NavControllers: I would create a new class that holds the ResultBackNavigator and this way you can send it via dependency:

class ParentBackNavigator(navigator: ResultBackNavigator<Boolean>): ResultBackNavigator<Boolean> by navigator

@Composable
fun BottomSheet(
  resultBackNavigator: ResultBackNavigator<Foo>,
) {
  DestinationsNavHost(
    dependenciesContainerBuilder = {
        dependency(ParentBackNavigator(resultBackNavigator))
    }
  )
}

@Composable
@Destination
fun InnerScreen1(
    backNavigator: ParentBackNavigator
) {
    /* some callback */ {
        backNavigator.navigateBack(result = true)
    }
}
Ezard commented 9 months ago

Ahh, I didn't realise that this was possible - this is super neat, thanks!