Skip to content

Commit 1b6a550

Browse files
committed
sample: pass event between screens
1 parent dd69640 commit 1b6a550

File tree

10 files changed

+278
-28
lines changed

10 files changed

+278
-28
lines changed

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/MainActivity.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ import androidx.navigation.compose.rememberNavController
3333
import androidx.navigation.navOptions
3434
import androidx.navigation.navigation
3535
import com.hoc081098.channeleventbus.sample.android.common.MyApplicationTheme
36-
import com.hoc081098.channeleventbus.sample.android.ui.home.HomeScreen
36+
import com.hoc081098.channeleventbus.sample.android.ui.home.detail.DetailScreen
37+
import com.hoc081098.channeleventbus.sample.android.ui.home.home.HomeScreen
3738
import com.hoc081098.channeleventbus.sample.android.ui.register.stepone.RegisterStepOneScreen
3839
import com.hoc081098.channeleventbus.sample.android.ui.register.stepthree.RegisterStepThreeScreen
3940
import com.hoc081098.channeleventbus.sample.android.ui.register.steptwo.RegisterStepTwoScreen
@@ -72,6 +73,7 @@ class MainActivity : ComponentActivity() {
7273
Route.RegisterStepTwo -> "Register step 2"
7374
Route.RegisterStepThree -> "Register step 3"
7475
Route.Home -> "Home"
76+
Route.Detail -> "Detail"
7577
null -> ""
7678
},
7779
)
@@ -182,7 +184,23 @@ private fun AppNavHost(
182184

183185
navigation(startDestination = Route.Home.routePattern, route = "home_graph") {
184186
composable(route = Route.Home.routePattern) {
185-
HomeScreen()
187+
HomeScreen(
188+
navigateToDetail = remember(navController) {
189+
{
190+
navController.navigate(route = Route.Detail.route)
191+
}
192+
},
193+
)
194+
}
195+
196+
composable(route = Route.Detail.routePattern) {
197+
DetailScreen(
198+
navigateBack = remember(navController) {
199+
{
200+
navController.popBackStack()
201+
}
202+
},
203+
)
186204
}
187205
}
188206
}

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/MyApp.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.hoc081098.channeleventbus.sample.android
33
import android.app.Application
44
import com.hoc081098.channeleventbus.ChannelEventBus
55
import com.hoc081098.channeleventbus.ChannelEventBusLogger
6+
import com.hoc081098.channeleventbus.sample.android.ui.home.HomeModule
67
import com.hoc081098.channeleventbus.sample.android.ui.register.RegisterModule
78
import org.koin.android.ext.koin.androidContext
89
import org.koin.android.ext.koin.androidLogger
@@ -44,6 +45,7 @@ class MyApp : Application() {
4445
modules(
4546
ChannelEventBusModule,
4647
RegisterModule,
48+
HomeModule,
4749
)
4850
}
4951
}

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/Route.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ internal sealed class Route {
4444
val route = routePattern
4545
}
4646

47+
data object Detail : Route() {
48+
override val routePattern = "detail"
49+
val route = routePattern
50+
}
51+
4752
companion object {
4853
private val VALUES: ImmutableList<Route> by lazy {
4954
persistentListOf(

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/ui/home/HomeScreen.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.hoc081098.channeleventbus.sample.android.ui.home.detail
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.text.KeyboardActions
12+
import androidx.compose.foundation.text.KeyboardOptions
13+
import androidx.compose.material3.ElevatedButton
14+
import androidx.compose.material3.MaterialTheme
15+
import androidx.compose.material3.OutlinedTextField
16+
import androidx.compose.material3.Text
17+
import androidx.compose.runtime.Composable
18+
import androidx.compose.runtime.getValue
19+
import androidx.compose.runtime.remember
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.text.input.ImeAction
23+
import androidx.compose.ui.text.style.TextAlign
24+
import androidx.compose.ui.unit.dp
25+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
26+
import kotlinx.coroutines.Dispatchers
27+
import org.koin.androidx.compose.koinViewModel
28+
29+
@Composable
30+
fun DetailScreen(
31+
navigateBack: () -> Unit,
32+
modifier: Modifier = Modifier,
33+
vm: DetailVM = koinViewModel(),
34+
) {
35+
val text by vm
36+
.textStateFlow
37+
.collectAsStateWithLifecycle(context = Dispatchers.Main.immediate)
38+
39+
Box(
40+
modifier = modifier.fillMaxSize(),
41+
contentAlignment = Alignment.Center,
42+
) {
43+
Column(
44+
modifier = Modifier.matchParentSize(),
45+
horizontalAlignment = Alignment.CenterHorizontally,
46+
verticalArrangement = Arrangement.Center,
47+
) {
48+
Text(
49+
text = "Detail",
50+
textAlign = TextAlign.Center,
51+
style = MaterialTheme.typography.titleLarge,
52+
)
53+
54+
Spacer(modifier = Modifier.height(16.dp))
55+
56+
OutlinedTextField(
57+
modifier = Modifier
58+
.fillMaxWidth()
59+
.padding(horizontal = 16.dp),
60+
value = text,
61+
onValueChange = remember { vm::onTextChanged },
62+
singleLine = true,
63+
maxLines = 1,
64+
label = { Text("Enter the text") },
65+
keyboardActions = KeyboardActions.Default,
66+
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
67+
)
68+
69+
Spacer(modifier = Modifier.height(16.dp))
70+
71+
ElevatedButton(
72+
onClick = {
73+
vm.sendResultToHome()
74+
navigateBack()
75+
},
76+
) {
77+
Text(text = "Send to home screen")
78+
}
79+
}
80+
}
81+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.hoc081098.channeleventbus.sample.android.ui.home.detail
2+
3+
import androidx.lifecycle.SavedStateHandle
4+
import androidx.lifecycle.ViewModel
5+
import com.hoc081098.channeleventbus.ChannelEventBus
6+
import com.hoc081098.channeleventbus.sample.android.common.SafeSavedStateHandle
7+
import com.hoc081098.channeleventbus.sample.android.common.SavedStateHandleKey
8+
import com.hoc081098.channeleventbus.sample.android.ui.home.DetailResultToHomeEvent
9+
import kotlinx.coroutines.flow.StateFlow
10+
11+
class DetailVM(
12+
private val channelEventBus: ChannelEventBus,
13+
savedStateHandle: SavedStateHandle,
14+
) : ViewModel() {
15+
private val safeSavedStateHandle = SafeSavedStateHandle(savedStateHandle)
16+
17+
internal val textStateFlow: StateFlow<String> = safeSavedStateHandle.getStateFlow(TextKey)
18+
19+
internal fun onTextChanged(text: String) {
20+
safeSavedStateHandle[TextKey] = text
21+
}
22+
23+
internal fun sendResultToHome() {
24+
channelEventBus.send(DetailResultToHomeEvent(textStateFlow.value))
25+
}
26+
27+
private companion object {
28+
private val TextKey = SavedStateHandleKey("text", "")
29+
}
30+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.hoc081098.channeleventbus.sample.android.ui.home
2+
3+
import com.hoc081098.channeleventbus.sample.android.ui.home.detail.DetailVM
4+
import com.hoc081098.channeleventbus.sample.android.ui.home.home.HomeVM
5+
import org.koin.androidx.viewmodel.dsl.viewModelOf
6+
import org.koin.dsl.module
7+
8+
val HomeModule = module {
9+
viewModelOf(::DetailVM)
10+
viewModelOf(::HomeVM)
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.hoc081098.channeleventbus.sample.android.ui.home
2+
3+
import com.hoc081098.channeleventbus.ChannelEvent
4+
import com.hoc081098.channeleventbus.ChannelEventKey
5+
6+
internal data class DetailResultToHomeEvent(val value: String) : ChannelEvent<DetailResultToHomeEvent> {
7+
override val key get() = Key
8+
9+
companion object Key : ChannelEventKey<DetailResultToHomeEvent>(DetailResultToHomeEvent::class)
10+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.hoc081098.channeleventbus.sample.android.ui.home.home
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.PaddingValues
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.lazy.LazyColumn
11+
import androidx.compose.foundation.lazy.itemsIndexed
12+
import androidx.compose.material3.ElevatedButton
13+
import androidx.compose.material3.MaterialTheme
14+
import androidx.compose.material3.Text
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.ui.Alignment
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.text.style.TextAlign
20+
import androidx.compose.ui.text.style.TextOverflow
21+
import androidx.compose.ui.unit.dp
22+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
23+
import org.koin.androidx.compose.koinViewModel
24+
25+
@Composable
26+
fun HomeScreen(
27+
navigateToDetail: () -> Unit,
28+
modifier: Modifier = Modifier,
29+
vm: HomeVM = koinViewModel(),
30+
) {
31+
val detailResults by vm.detailResultsStateFlow.collectAsStateWithLifecycle()
32+
33+
Box(
34+
modifier = modifier.fillMaxSize(),
35+
contentAlignment = Alignment.Center,
36+
) {
37+
Column(
38+
modifier = Modifier.matchParentSize(),
39+
horizontalAlignment = Alignment.CenterHorizontally,
40+
verticalArrangement = Arrangement.Center,
41+
) {
42+
Text(
43+
text = "Home",
44+
textAlign = TextAlign.Center,
45+
style = MaterialTheme.typography.titleLarge,
46+
)
47+
48+
Spacer(modifier = Modifier.height(8.dp))
49+
50+
ElevatedButton(
51+
onClick = navigateToDetail,
52+
) {
53+
Text(
54+
text = "Click to go to detail",
55+
)
56+
}
57+
58+
LazyColumn(
59+
modifier = Modifier.weight(1f),
60+
contentPadding = PaddingValues(all = 16.dp),
61+
) {
62+
itemsIndexed(
63+
items = detailResults,
64+
key = { _, item -> item },
65+
contentType = { _, _ -> "DetailResult" },
66+
) { index, result ->
67+
Text(
68+
modifier = Modifier.fillParentMaxWidth(),
69+
text = result,
70+
style = MaterialTheme.typography.bodyMedium,
71+
textAlign = TextAlign.Start,
72+
maxLines = 2,
73+
overflow = TextOverflow.Ellipsis,
74+
)
75+
76+
if (index != detailResults.lastIndex) {
77+
Spacer(modifier = Modifier.height(8.dp))
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.hoc081098.channeleventbus.sample.android.ui.home.home
2+
3+
import androidx.lifecycle.ViewModel
4+
import androidx.lifecycle.viewModelScope
5+
import com.hoc081098.channeleventbus.ChannelEventBus
6+
import com.hoc081098.channeleventbus.sample.android.ui.home.DetailResultToHomeEvent
7+
import kotlinx.collections.immutable.PersistentList
8+
import kotlinx.collections.immutable.persistentListOf
9+
import kotlinx.collections.immutable.plus
10+
import kotlinx.coroutines.CancellationException
11+
import kotlinx.coroutines.flow.SharingStarted
12+
import kotlinx.coroutines.flow.StateFlow
13+
import kotlinx.coroutines.flow.map
14+
import kotlinx.coroutines.flow.onCompletion
15+
import kotlinx.coroutines.flow.scan
16+
import kotlinx.coroutines.flow.stateIn
17+
18+
class HomeVM(
19+
channelEventBus: ChannelEventBus,
20+
) : ViewModel() {
21+
internal val detailResultsStateFlow: StateFlow<PersistentList<String>> = channelEventBus
22+
.receiveAsFlow(DetailResultToHomeEvent)
23+
.onCompletion {
24+
check(it is CancellationException) { "Expected CancellationException but was $it" }
25+
// Close the bus when this ViewModel is cleared.
26+
channelEventBus.closeKey(DetailResultToHomeEvent)
27+
}
28+
.map { it.value }
29+
.scan(persistentListOf<String>()) { acc, e -> acc + e }
30+
.stateIn(
31+
scope = viewModelScope,
32+
// Using SharingStarted.Lazily is enough, because the event bus is backed by a channel.
33+
started = SharingStarted.Lazily,
34+
initialValue = persistentListOf(),
35+
)
36+
}

0 commit comments

Comments
 (0)