I am following the Now In Android sample for modularization and best practices with Jetpack Compose and I am a bit stuck with sharing a simple LocalDate state across several modules/features.
I want to be able to change a selected date in several places, and update the UI accordingly in several places (i.e. AppState, BottomSheet (BottomSheetState), Screen A (Viewmodel) / Screen B (Viewmodel), Dialog (if opened), etc.).
Now considering concepts like Ownership and Encapsulation, I am not sure which would be the preferred way to handle this. Or how I should rethink the logic.
So far I tried:
Hoisting the state in AppState and passing it onto Composables below;
- Problem here was that I cannot pass the stateflow on to the VMs due to DI with hiltViewModel() and Compose Navigation. (Example of implementation:)
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun ScreenARoute(viewModel: ScreenAViewModel = hiltViewModel()) {
ScreenA(/***/)
}
@HiltViewModel
class ScreenAViewModel @Inject constructor(
private val userPrefsRepository: UserPrefsRepository,
) : ViewModel() {
/***/
}
Using SavedStateHolder in ViewModels, including the MainActivityViewmodel where the date should be initialized (today);
- Problem here was that the state was only updated in the respective VM/SavedStateHolder and not across all places. For example I was able to update the date and state in Screen A and the UI got updated accordingly, other places/screens remained without updates though. (Example of implementation:)
@HiltViewModel
class ScreenAViewModel @Inject constructor(
private val userPrefsRepository: UserPrefsRepository,
val savedStateHandle: SavedStateHandle,
) : ViewModel() {
/***/
val selectedDate: StateFlow<LocalDate> =
savedStateHandle.getStateFlow<LocalDate>(SELECTED_DATE_SAVED_STATE_KEY, LocalDate.now())
.stateIn(
viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = LocalDate.now()
)
fun updateSelectedDate(date: LocalDate) {
savedStateHandle[SELECTED_DATE_SAVED_STATE_KEY] = newDate
/***/
}
Other things I am considering:
Creating a Singleton object that holds the state, which is initialized in the MainActivityViewModel and implemented in the VMs via hilt DI.
Using Room or Preferences to save the state and share across the app per Singleton Repository and DI. But this might be overkill?
The state should survive configuration changes but can be disposed when the app gets killed. Any help will be much appreciated.