voyager
voyager copied to clipboard
Pass data between screens
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
I am also interested in such functionality
I'm trying to figure out best way to do this too.
I think about this every week or so.
Any updates?
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.
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 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 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.
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 how can I get previous screen within a bottomsheet screen? items.size is 1 in that bottomsheet obviously.
Take a parent
I'm sorry I couldn't understand what you mean by that.
navigator?.parent is null if you meant that.
Hello, any updates on result api ?