Skip to content

Commit 4b4f189

Browse files
committed
fix(compose): migrate collectAsState to lifecycle-aware and fix state issues
- Replace 18 collectAsState() calls with collectAsStateWithLifecycle() across 14 files to stop flow collection when backgrounded - Fix var LocalSheetGesturesState to val to prevent accidental reassignment of the CompositionLocal reference - Replace Timer() in TopBarContainer composition body with LaunchedEffect, preventing uncancelled timer creation on every recompose - Replace SideEffect with LaunchedEffect(cameraPermission.status) in BillContainerView to prevent permission alert firing on every recompose
1 parent 31b4f2d commit 4b4f189

15 files changed

Lines changed: 43 additions & 45 deletions

File tree

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.Box
1313
import androidx.compose.runtime.Composable
1414
import androidx.compose.runtime.CompositionLocalProvider
1515
import androidx.compose.runtime.LaunchedEffect
16-
import androidx.compose.runtime.collectAsState
16+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1717
import androidx.compose.runtime.getValue
1818
import androidx.compose.runtime.mutableLongStateOf
1919
import androidx.compose.runtime.mutableStateOf
@@ -109,7 +109,7 @@ internal fun App(
109109
}
110110

111111
val session = LocalSessionController.current!!
112-
val userState by userManager.state.collectAsState()
112+
val userState by userManager.state.collectAsStateWithLifecycle()
113113

114114
FlipcashTheme {
115115
rememberQrBitmapPainter(

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/decorators/NavBlockingOverlayEntryDecorator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.flipcash.app.internal.ui.navigation.decorators
33
import androidx.compose.foundation.layout.Box
44
import androidx.compose.foundation.layout.fillMaxSize
55
import androidx.compose.runtime.Composable
6-
import androidx.compose.runtime.collectAsState
6+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
77
import androidx.compose.runtime.getValue
88
import androidx.compose.runtime.remember
99
import androidx.compose.ui.Modifier
@@ -26,7 +26,7 @@ fun NavBlockingOverlayEntryDecorator(): NavEntryDecorator<NavKey> {
2626
entry.Content()
2727

2828
val userManager = LocalUserManager.current
29-
val userState by userManager!!.state.collectAsState()
29+
val userState by userManager!!.state.collectAsStateWithLifecycle()
3030
val authState = userState.authState
3131

3232
if (authState is AuthState.LoggedIn) {

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/ui/AmountWithKeypad.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.fillMaxWidth
66
import androidx.compose.foundation.layout.padding
77
import androidx.compose.runtime.Composable
8-
import androidx.compose.runtime.collectAsState
8+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
99
import androidx.compose.runtime.getValue
1010
import androidx.compose.ui.Alignment
1111
import androidx.compose.ui.Modifier
@@ -32,7 +32,7 @@ fun AmountWithKeypad(
3232
onDecimal: () -> Unit = { },
3333
) {
3434
val networkObserver = LocalNetworkObserver.current
35-
val networkState by networkObserver.state.collectAsState()
35+
val networkState by networkObserver.state.collectAsStateWithLifecycle()
3636

3737
Column(
3838
modifier = modifier,

apps/flipcash/features/backupkey/src/main/kotlin/com/flipcash/app/backupkey/internal/BackupKeyScreenContent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.windowInsetsPadding
2121
import androidx.compose.material.Text
2222
import androidx.compose.runtime.Composable
2323
import androidx.compose.runtime.LaunchedEffect
24-
import androidx.compose.runtime.collectAsState
24+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2525
import androidx.compose.runtime.getValue
2626
import androidx.compose.runtime.mutableStateOf
2727
import androidx.compose.runtime.remember
@@ -57,7 +57,7 @@ import com.getcode.util.permissions.rememberStoragePermission
5757
internal fun BackupKeyScreenContent(viewModel: BackupKeyScreenViewModel) {
5858
val context = LocalContext.current
5959
val resources = LocalResources.current
60-
val dataState by viewModel.uiFlow.collectAsState()
60+
val dataState by viewModel.uiFlow.collectAsStateWithLifecycle()
6161

6262
var isExportSeedRequested by remember { mutableStateOf(false) }
6363
var isStoragePermissionGranted by remember { mutableStateOf(false) }

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/OnboardingFlowScreen.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.fillMaxSize
1010
import androidx.compose.foundation.layout.fillMaxWidth
1111
import androidx.compose.runtime.Composable
1212
import androidx.compose.runtime.LaunchedEffect
13-
import androidx.compose.runtime.collectAsState
1413
import androidx.compose.runtime.getValue
1514
import androidx.compose.runtime.mutableStateOf
1615
import androidx.compose.runtime.remember
@@ -281,7 +280,7 @@ private fun onboardingEntryProvider(
281280
@Composable
282281
private fun LoginStepContent(seed: String?) {
283282
val vm = hiltViewModel<LoginViewModel>()
284-
val state by vm.stateFlow.collectAsState()
283+
val state by vm.stateFlow.collectAsStateWithLifecycle()
285284
val flowNavigator = rememberFlowNavigator<OnboardingStep, OnboardingResult>()
286285
val outerNavigator = LocalOuterCodeNavigator.current
287286
var visible by remember { mutableStateOf(false) }
@@ -427,7 +426,7 @@ private fun AccessKeyStepContent() {
427426
private fun PurchaseStepContent() {
428427
val viewModel = hiltViewModel<PurchaseAccountViewModel>()
429428
val flowNavigator = rememberFlowNavigator<OnboardingStep, OnboardingResult>()
430-
val state by viewModel.stateFlow.collectAsState()
429+
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
431430

432431
LaunchedEffect(viewModel) {
433432
viewModel.eventFlow

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/screens/AccessKeyScreenContent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import androidx.compose.foundation.layout.windowInsetsPadding
2323
import androidx.compose.material.Text
2424
import androidx.compose.runtime.Composable
2525
import androidx.compose.runtime.LaunchedEffect
26-
import androidx.compose.runtime.collectAsState
26+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2727
import androidx.compose.runtime.getValue
2828
import androidx.compose.runtime.mutableStateOf
2929
import androidx.compose.runtime.remember
@@ -74,7 +74,7 @@ internal fun AccessKeyScreen(
7474
val navigator = LocalCodeNavigator.current
7575
val context = LocalContext.current
7676
val resources = LocalResources.current
77-
val dataState by viewModel.uiFlow.collectAsState()
77+
val dataState by viewModel.uiFlow.collectAsStateWithLifecycle()
7878

7979
val composeScope = rememberCoroutineScope()
8080

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/screens/SeedInputContent.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import androidx.compose.material.icons.Icons
2222
import androidx.compose.material.icons.filled.SettingsBackupRestore
2323
import androidx.compose.runtime.Composable
2424
import androidx.compose.runtime.LaunchedEffect
25-
import androidx.compose.runtime.collectAsState
25+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2626
import androidx.compose.runtime.getValue
2727
import androidx.compose.runtime.remember
2828
import androidx.compose.runtime.rememberCoroutineScope
@@ -65,7 +65,7 @@ internal fun SeedInputContent(
6565
viewModel: SeedInputViewModel,
6666
onCantFind: () -> Unit,
6767
) {
68-
val dataState by viewModel.uiFlow.collectAsState()
68+
val dataState by viewModel.uiFlow.collectAsStateWithLifecycle()
6969
SeedInputContent(
7070
state = dataState,
7171
onTextChange = { viewModel.onTextChange(it) },
@@ -86,7 +86,7 @@ private fun SeedInputContent(
8686
val focusManager = LocalFocusManager.current
8787
val focusRequester = remember { FocusRequester() }
8888
val featureFlags = LocalFeatureFlags.current
89-
val restoreEnabled by featureFlags.observe(FeatureFlag.CredentialManager).collectAsState()
89+
val restoreEnabled by featureFlags.observe(FeatureFlag.CredentialManager).collectAsStateWithLifecycle()
9090

9191
val keyboardVisible by keyboardAsState()
9292
val ime = LocalSoftwareKeyboardController.current

apps/flipcash/features/purchase/src/main/kotlin/com/flipcash/app/purchase/internal/PurchaseAccountScreenContent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import androidx.compose.material.icons.Icons
1212
import androidx.compose.material.icons.filled.CheckCircleOutline
1313
import androidx.compose.runtime.Composable
1414
import androidx.compose.runtime.LaunchedEffect
15-
import androidx.compose.runtime.collectAsState
15+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1616
import androidx.compose.runtime.getValue
1717
import androidx.compose.ui.Alignment
1818
import androidx.compose.ui.Modifier
@@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.onEach
4545
fun PurchaseAccountScreen(viewModel: PurchaseAccountViewModel) {
4646
val navigator = LocalCodeNavigator.current
4747

48-
val state by viewModel.stateFlow.collectAsState()
48+
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
4949

5050
LaunchedEffect(viewModel) {
5151
viewModel.eventFlow

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/Scanner.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import android.annotation.SuppressLint
44
import androidx.compose.runtime.Composable
55
import androidx.compose.runtime.DisposableEffect
66
import androidx.compose.runtime.LaunchedEffect
7-
import androidx.compose.runtime.collectAsState
7+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
88
import androidx.compose.runtime.getValue
99
import androidx.compose.runtime.mutableFloatStateOf
1010
import androidx.compose.runtime.mutableStateOf
@@ -43,8 +43,8 @@ internal fun Scanner() {
4343
val router = LocalRouter.current!!
4444
val navigator = LocalCodeNavigator.current
4545
val session = LocalSessionController.current!!
46-
val state by session.state.collectAsState()
47-
val billState by session.billState.collectAsState()
46+
val state by session.state.collectAsStateWithLifecycle()
47+
val billState by session.billState.collectAsStateWithLifecycle()
4848
val analytics = rememberAnalytics()
4949

5050
var isPaused by remember { mutableStateOf(false) }

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/BillContainerView.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import androidx.compose.material.DismissValue
2020
import androidx.compose.material.ExperimentalMaterialApi
2121
import androidx.compose.runtime.Composable
2222
import androidx.compose.runtime.LaunchedEffect
23-
import androidx.compose.runtime.SideEffect
24-
import androidx.compose.runtime.collectAsState
23+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2524
import androidx.compose.runtime.derivedStateOf
2625
import androidx.compose.runtime.getValue
2726
import androidx.compose.runtime.mutableStateOf
@@ -95,12 +94,12 @@ internal fun BillContainer(
9594

9695
val cameraPermission = rememberCameraPermission { onPermissionResult(it) }
9796

98-
SideEffect {
97+
LaunchedEffect(cameraPermission.status) {
9998
onPermissionResult(cameraPermission.status)
10099
}
101100

102-
val state by session.state.collectAsState()
103-
val billState by session.billState.collectAsState()
101+
val state by session.state.collectAsStateWithLifecycle()
102+
val billState by session.billState.collectAsStateWithLifecycle()
104103

105104
val autoStart = state.autoStartCamera == true
106105
var cameraStarted by remember { mutableStateOf(autoStart) }

0 commit comments

Comments
 (0)