ChipTextField icon indicating copy to clipboard operation
ChipTextField copied to clipboard

Support MVVM approch

Open hlayan opened this issue 2 years ago • 1 comments

Currently, chips are handled by ChipTextFieldState internally. Chips should come from outside like ViewModel. Also there should be Events or Listeners while removing chips that is currently handled internally.

For example, LIKE TextField's value and onValueChanged design (unidirectional)

hlayan avatar Jan 07 '24 15:01 hlayan

Thanks for the feedback. That's right, currently, there is no onValueChange callback.

The main reason is that the Chip class also holds a TextFieldValue and a FocusInteraction.Focus, so it may not be a good idea to maintain these ui states in ViewModel in my opinion.

But if you need the UDF pattern or TextField like API, you can create a wrapper to sync chips between the view model and the UI:

@Composable
fun ChipTextField(
    state: ChipTextFieldState<Chip>,
    chips: List<Chip>,
    onValueChange: (chips: List<Chip>) -> Unit,
    modifier: Modifier = Modifier,
) {
    LaunchedEffect(chips) {
        state.chips = chips
    }

    LaunchedEffect(state, onValueChange) {
        snapshotFlow { state.chips.map { it.text } }
            .collect { onValueChange(state.chips) }
    }

    ChipTextField(
        state = state,
        modifier = modifier,
        onSubmit = ::Chip,
    )
}

This will make your view model dependent on the library class. For List<String> or your types, you can do a 'half-sync':

data class UiState(
    val tags: List<String> = emptyList(),
)

@Composable
fun SomeScreen(
    modifier: Modifier = Modifier,
    viewModel: ViewModel = remember { ViewModel() },
) {
    val uiState by viewModel.uiState.collectAsState()

    val state = rememberChipTextFieldState<Chip>()

    LaunchedEffect(state, uiState.tags) {
        if (state.chips.isEmpty() && uiState.tags.isNotEmpty()) {
            // Set the initial chips from the view model
            state.chips = uiState.tags.map(::Chip)
        }
    }

    LaunchedEffect(state) {
        snapshotFlow { state.chips.map { it.text } }
            .collect { viewModel.updateTags(it) } // Update the view model chips
    }

    ChipTextField(
        state = state,
        onSubmit = ::Chip,
    )
}

dokar3 avatar Jan 08 '24 02:01 dokar3