voyager icon indicating copy to clipboard operation
voyager copied to clipboard

Pass data between screens

Open Syer10 opened this issue 3 years ago • 20 comments

My application has a case where it needs to pass a variable to a previous screen from the current screen. With fragments I have been using setFragmentResultListener api to do this, but with Voyager there is not a way to properly handle cases like this.

My suggestions:

  • A sample of a safe way to do this. since Voyager is more open than fragments about its API I imagine there could be a safe way to do this already that I haven't found
  • An API to handle use cases like this

Syer10 avatar Jan 11 '22 17:01 Syer10

I am also interested in such functionality

aurimas-zarskis avatar Jan 17 '22 13:01 aurimas-zarskis

I'm trying to figure out best way to do this too.

JohnBuhanan avatar Feb 08 '22 15:02 JohnBuhanan

I think about this every week or so.

Any updates?

JohnBuhanan avatar Apr 27 '22 14:04 JohnBuhanan

I'm focusing on fixing the known bugs to release a stable 1.0.0. After that will start working on new features like this one.

adrielcafe avatar Apr 27 '22 20:04 adrielcafe

I also have this problem. I have a solution that seems working, even though I am not 100% that what I am doing is correct. The point is that kotlin lambdas are serializable by default, so I thought that we could simply when, in the first screen, we create the second screen we simply pass a callback as an argument of the second screen class, to be called to pass back the result.

so basically something like this

class FirstScreenModel(var theResultFromScreen2:Long=0):ScreenModel

class FirstScreen(): Screen {
    @Composable
    override fun Content() {
        val screenModel = rememberScreenModel { FirstScreenModel() }
        //... the code for defining UI of FirstScreen
        val navigator = LocalNavigator.currentOrThrow
        Column {
            Text("First Screen")
            TextButton(onClick = { navigator.push(SecondScreen({ screenModel.theResultFromScreen2 = it })) }) {
                Text("click here to open second screen")
            }
            Text("result from second screen ${screenModel.theResultFromScreen2}")
        }
    }
}

class SecondScreen(val onResult:(Long)->Unit): Screen {
    @Composable
    override fun Content() {

        val navigator = LocalNavigator.currentOrThrow
        //update result for caller screen
        onResult(System.currentTimeMillis())
        Column {
            Text("Second Screen")
            TextButton(onClick = { navigator.pop() }) {
                Text("click here to return to previous screen")
            }
        }

    }
}

I am still a beginner with Android Compose so I am not 100% if what I am doing could potential behave not as expected in some cases. But at least apparently it is working

beyondeye avatar May 31 '22 08:05 beyondeye

@beyondeye I think it will crash the app. Try to open your screen, minimize (press home screen) then open your app. You should see crashes caused by ``java.lang.RuntimeException: Parcelable encountered IOException writing serializable object`

DjakaTechnology avatar Aug 08 '22 15:08 DjakaTechnology

@DjakaTechnology you are right! @Syer10
Hi , I have just published a fork of voyager with bug fixes and new features here. I have integrated voyager with a port of flutter_bloc library that make passing data between screen very easy. (Screen can share data through shared blocs). I will be happy to have your feedback.

beyondeye avatar Aug 19 '22 09:08 beyondeye

I made PoC how you can pass a data between screens and don't care about the process death:

@Composable
internal fun App(
    systemAppearance: (isLight: Boolean) -> Unit = {}
) = AppTheme(systemAppearance) {
    Navigator(ScreenA())
}



interface AppScreen : Screen {
    fun <T> onResult(obj: T) {}
}

fun <T> Navigator.popWithResult(obj: T) {
    val prev = if (items.size < 2) null else items[items.size - 2] as? AppScreen
    prev?.onResult(obj)
    pop()
}

class ScreenA : AppScreen {
    var txt = "Init text"

    override fun <T> onResult(obj: T) {
        txt = obj as String
    }

    @Composable
    override fun Content() {
        val navigator = LocalNavigator.currentOrThrow

        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                text = txt,
                style = MaterialTheme.typography.headlineMedium,
                modifier = Modifier.padding(16.dp)
            )
            Button(
                onClick = {
                    navigator.push(ScreenB())
                },
                modifier = Modifier.fillMaxWidth().padding(16.dp)
            ) {
                Text("Enter text")
            }
        }
    }
}

class ScreenB : Screen {
    @Composable
    override fun Content() {
        val navigator = LocalNavigator.currentOrThrow
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            var txt by rememberSaveable { mutableStateOf("") }
            OutlinedTextField(
                value = txt,
                onValueChange = { txt = it },
                modifier = Modifier.padding(16.dp)
            )
            Button(
                onClick = {
                    navigator.popWithResult(txt)
                },
                modifier = Modifier.fillMaxWidth().padding(16.dp)
            ) {
                Text("Return result")
            }
        }
    }
}

terrakok avatar Sep 19 '23 09:09 terrakok

@terrakok how can I get previous screen within a bottomsheet screen? items.size is 1 in that bottomsheet obviously.

osrl avatar Sep 25 '23 09:09 osrl

Take a parent

terrakok avatar Sep 25 '23 11:09 terrakok

I'm sorry I couldn't understand what you mean by that.

osrl avatar Sep 25 '23 11:09 osrl

navigator?.parent is null if you meant that.

osrl avatar Sep 25 '23 13:09 osrl

Hello, any updates on result api ?