6

I have the following ViewModel

@HiltViewModel
class ShareViewModel @Inject constructor(
    private val taskRepository: TaskRepository
): ViewModel() {

    private val searchAppBarStateMutableState: MutableState<SearchAppBarState> = mutableStateOf(SearchAppBarState.CLOSED)
    val searchAppBarState: State<SearchAppBarState> = searchAppBarStateMutableState

    private val listOfTaskMutableStateFlow = MutableStateFlow<List<TodoTaskEntity>>(emptyList())
    val listOfTaskStateFlow = listOfTaskMutableStateFlow.asStateFlow()
}

I never expose mutableStateFlow as in the example above. And SonarLint will show a warning when doing this.

MutableStateFlow" and "MutableSharedFlow" should not be exposed

So I apply the same technique to the mutableState

However, If I do like this below, I don't get any warning.

val searchAppBarStateMutableState: MutableState<SearchAppBarState> = mutableStateOf(SearchAppBarState.CLOSED)

Just wondering what is the best practice for using MutableState with jetpack compose.

ant2009
  • 27,094
  • 154
  • 411
  • 609
  • A common practice is to expose read-only states from viewmodels because you want to avoid the view modifying the state directly. I'd follow the same guideline with Compose. – gpunto May 22 '22 at 15:52
  • https://stackoverflow.com/a/71817269/15880865 – Richard Onslow Roper May 22 '22 at 16:29
  • The shortest version for mutable state is using delegation with private setter, like showed [here](https://stackoverflow.com/a/69742362/3585796). `: State` is also a possible variant if you need to pass a state for some reason. Not sure why your linter is not happy, the flow variant seems fine too. – Phil Dukhov May 22 '22 at 17:55
  • Could it be that settings private variables need to have an underscore in front, " private val _listOfTaskMutableStateFlow.". Other than that seems all fine in regards to the lint issue. https://rules.sonarsource.com/kotlin/RSPEC-6305 – mmmatey May 27 '22 at 13:42

2 Answers2

5

To use mutableState with viewmodel, define mutableState with private setter inside viewmodel, ex -

var isVisible by mutableState(false)
    private set

By doing above we can read the mutable state from outside of viewmodel but not update it. To update create a public function inside viewmodel, ex -

fun setVisibility(value: Boolean) {
    isVisible = value
}

By making a setter function we are following separation of concerns and having a single source of truth for editing the mutableState.

sarthak gupta
  • 826
  • 4
  • 12
1

I think the error is in that you are setting

    val searchAppBarState: State<SearchAppBarState> = searchAppBarStateMutableState

if you want to share private value as not mutable you shouldn't set it equal rather you can use get modifier

    val searchAppBarState: State<SearchAppBarState> get() = searchAppBarStateMutableState

Also it would be better to name it with underscore as many developers are used to like:

    private val _searchAppBarState: MutableState<SearchAppBarState> = mutableStateOf(SearchAppBarState.CLOSED)
    val searchAppBarState: State<SearchAppBarState> get() = _searchAppBarState
Islam Assem
  • 1,376
  • 13
  • 21