voyager icon indicating copy to clipboard operation
voyager copied to clipboard

[Proposal] Pass default parameters into Screen Content() function

Open egorikftp opened this issue 4 years ago • 6 comments

Hello, I'm trying to adopt your library into my pet project. I was faced with an issue, that the inner screen should consider padding from the parent screen.

Please take a look sample. I need to pass paddingValues into CurrentTab(). The Scaffold provides this value to prevent content overlapping.

TabNavigator(FlagsTab) { tabNavigator ->
            Scaffold(
                modifier = Modifier.systemBarsPadding(),
                topBar = {},
                content = { paddingValues ->
                    CurrentTab()
                },
                bottomBar = {
                    BottomNavigation {
                        TabNavigationItem(Tab1)
                        TabNavigationItem(Tab2)
                    }
                }
            )
        }

My suggestion is to make Screen interface like this with default parameters:

interface Screen : Serializable {

    val key: ScreenKey
        get() = this::class.qualifiedName
            ?: error("Default ScreenKey not found, please provide your own key")

    @Composable
    fun Content(
        modifier: Modifier = Modifier,
        paddingValues: PaddingValues = PaddingValues()
    )
}

Thanks a lot)

egorikftp avatar Oct 27 '21 19:10 egorikftp

Hey guys, the voyager owner is very busy and his wife is pregnant. Updates will come soon. Stay up to date.

programadorthi avatar Nov 05 '21 11:11 programadorthi

Nice! Just tried that but got the error Abstract Composable functions cannot have parameters with default values.

I'll try to find another way, suggestions are welcome.

adrielcafe avatar Dec 16 '21 01:12 adrielcafe

Maybe you find a solution in the future - for now I just wanted to share my solution if someone else is looking here.

I simply solve that by using local compositions to provide custom data to sub composables and this looks like following for this use case:

// define a custom local composition
val LocalContentPadding = compositionLocalOf { mutableStateOf(PaddingValues()) }

fun MainScreen() {

  // rememeber the paddings here inside the root level
  val paddingValues = remember { mutableStateOf(PaddingValues()) }

  // provide the padding state  inside the local provider
  CompositionLocalProvider(
    LocalContentPadding provides paddingValuesTab
  ) {

    MyAppTheme {
      BottomSheetNavigator(
        sheetContent = {
          Navigator(NavScreenBottomSheet)
        },
        content = {
          TabNavigator(NavScreenTabHome) {
            Scaffold(
              topBar = {
                
              },
              content = { pv ->
                // update the paddings
                paddingValues.value = pv
                CurrentTab()
              },
              bottomBar = {
                
              }
            )
          }
        }
      )
    }
  }
}

And inside your tabs and/or sub screens you can now do following:

@Parcelize
object NavScreenTabHome : Screen, Parcelable {

  @Composable
  override fun Content() {
    val paddingValues = LocalContentPadding.current
    // use the padding in your composables...
  }
}

MFlisar avatar Feb 23 '23 11:02 MFlisar

Create a new class ScreenX:

abstract class ScreenX: Screen {

    @Composable
    abstract fun Content(modifier: Modifier)

    @Composable
    final override fun Content() = Content(Modifier)
}

Inheriting from it the class of a specific screen:

object MainScreen : ScreenX() {
    @Composable
    override fun Content(modifier: Modifier) { 
       Column(modifier) {/* code */}
    }
}

create own CurrentScreen function with param:

@Composable
public fun CurrentScreen(modifier: Modifier) {
    val navigator = LocalNavigator.currentOrThrow
    val currentScreen = navigator.lastItem as ScreenX

    navigator.saveableState("currentScreen") {
        currentScreen.Content(modifier)
    }
}

and next in Scaffold:

Navigator(
        MainScreen
    ) {         
            Scaffold(content = {
                CurrentScreen(Modifier.padding(it))
            })
        }
)

ITmind avatar Oct 20 '23 00:10 ITmind

We currently are using simpler solution

 Box(
    modifier = Modifier
        .padding(paddingValues)
        //.consumeWindowInsets(paddingValues), in case if you have internal scaffolds
) {
    CurrentTab()
}

nvkleban avatar Feb 21 '24 16:02 nvkleban