3

Is there a way to use a custom composable as a parameter type in another custom composable? Trying this only throws an unresolved reference compiler error. However, using build-in composables provided by Jetpack Compose as parameters like an AlertDialog don't throw any errors. I want to use the slots API pattern in a custom composable but restrict the slots to only other custom composables.

Example structure

@Composable
fun Permission(
    PermissionNotGrantedContent: CustomDialog, //Unresolved reference error here
    PermissionNotAvailableContent: CustomDialog, //unresolved reference error here
    PermissionGrantedContent: @Composable () -> Unit
) { ... }

Permission(
    PermissionNotGrantedContent = {
        CustomDialog(...)           
    },
    PermissionNotAvailableContent = { 
        CustomDialog(...)
    }
) {
    Text("Thanks for granting the permission!")
}
Raj Narayanan
  • 2,443
  • 4
  • 24
  • 43
  • What is the problem with your sample code? – Rafsanjani Aug 17 '22 at 07:42
  • @Rafsanjani It throws an `Unresolved reference` error for `CustomDialog`. – Raj Narayanan Aug 17 '22 at 09:16
  • And are you sure your `CustomDialog` is available within scope? I don't see any reason why `CustomDialog` will be unresolved unless you've declared it as private within a different file – Rafsanjani Aug 17 '22 at 10:04
  • `CustomDialog` is from another library of mine, but it's not declared private. And I even tested this with a public composable created in the same file, and it still throws `Unresolved reference` error. That error occurs in the `Permission` declaration site shown above. But, `CustomDialog` works outside of the parameter scope. And even if I use a built-in composable, which doesn't throw that error, like `AlertDialog`, I still can't get it to work at the `Permission` call site. I can't use a lamda nor use it straight as it throws errors. I'm at a loss as to how to implement this in my code. – Raj Narayanan Aug 17 '22 at 13:27
  • Did you figure out how to fix this problem? – ShadeToD Feb 11 '23 at 13:25
  • @ShadeToD Not yet. I'm hoping someone will come along with a solution. – Raj Narayanan Feb 11 '23 at 17:22
  • Checkout how `RowScope` is used in `Row` for example (you can look at others as well like `Column`). `rowContent: @Composable RowScope.() -> Unit` This isn't exactly what you're looking for, but it may get you headed in the right direction. However, I don't think this is directly possible as methods don't have types (reflection notwithstanding). – prodaea Apr 04 '23 at 18:16

2 Answers2

0

I think what you want is how compose uses scoping interfaces. For that checkout the source for RowScope and RowScopeInstance and how it is used in Row. I'm not sure how "compose-y" this is, but you can do something more or less concrete like this:

interface CustomDialog {
  @Composable
  fun Content()
}

class PermissionNotGrantedContent(
  modifier: Modifier = Modifier,
  /* other data needed */
) {
  @Composable
  override fun Content() {
    /* draw the dialog */
  }
}

@Composable
fun Permission(
  notgranted: CustomDialog,
  /* etc. */
) {
  notgranted.Content()
  /* etc. */
}

@Preview
@Composable
fun PreviewPermissions() {
  Permission(
     notgranted = PermissionNotGranted(/* params */)
  )
}

Hope that helps!

prodaea
  • 1,720
  • 1
  • 14
  • 20
-1

For exemple TextField use :

trailingIcon: @Composable (() -> Unit)? = null

And I do that :

trailingIcon = {
    IconButton(onClick = { viewModel.changeVisibility() }) {
        if (viewModel.clearPassword.collectAsState().value!!) {
            Icon(painter = painterResource(id = R.drawable.ic_visibility), contentDescription = "visible icon")
        } else {
            Icon(
                painter = painterResource(id = R.drawable.ic_invisible), contentDescription = "invisible icon")
        }
    }
},

You should use command + left click(Probably CTRL + left click on Window) on a composable fonction like Box, TextField, it can show you lot of parameters and you can see how it works.

lerey
  • 39
  • 6