Как избежать дублирования чекбоксов в моем LazyVerticalGrid в Jetpack Compose?

Вопрос или проблема

Я пытаюсь создать экран отчета о неисправностях. Пользователь должен ввести описание неисправности и может выбрать различные чекбоксы. Затем я извлекаю эту информацию, чтобы написать с ней электронное письмо.

Моя проблема в том, что чекбоксы восстанавливаются, когда текст вводится в поле ввода или когда экран поворачивается.

Почему это происходит и как я могу этого избежать?

Элементы BugItems являются фиксированными элементами, которые я беру из массива ресурсов. Я создал два класса данных, чтобы представить отчет о неисправностях.

data class BugReport(
    val message: String,
    val bugItems: List<BugItem>,
)

data class BugItem(
    val title: String,
    val completed: Boolean = false,
)

Вот композируемая функция экрана:

@ExperimentalFoundationApi
@Composable
fun BugReportScreen(
    modifier: Modifier = Modifier
) {
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult(),
        onResult = { }
    )

    val message = remember { mutableStateOf("") }

    val bugItems = remember {
        mutableStateListOf<BugItem>()
    }

    stringArrayResource(id = R.array.bug_report).forEach {
        bugItems += BugItem(title = it, completed = false)
    }
    val bugReport = remember {
        mutableStateOf(BugReport(message.value, bugItems))
    }

    Box(
        modifier = modifier,
    ) {
        Column(
            modifier = modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
        ) {

            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(50.dp)
            )

            GenericToolbar(title = "Отчет о неисправностях")
            Spacer(modifier = Modifier.weight(1f)) 
            Text(text = stringResource(id = R.string.bug_intro))
            Text(text = stringResource(id = R.string.bug_description))

            TextFieldBugDescription(message)
            Text(text = stringResource(id = R.string.bug_check))    
            
            LazyVerticalGrid(
                modifier = Modifier.heightIn(max = 1000.dp),
                columns = GridCells.Fixed(2),
                content = {
                    items(bugReport.value.bugItems.size) { index ->
                        Row(
                            modifier = Modifier.padding(1.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {

                            Checkbox(
                                checked = bugItems[index].completed,
                                onCheckedChange = { checked ->
                                    bugItems[index] = bugItems[index].copy(completed = checked)
                                }
                            )
                            Text(
                                text = bugItems[index].title,
                                style = MaterialTheme.typography.bodyLarge,
                            )
                        }
                    }

                }

            )
            Text(text = stringResource(id = R.string.bug_send))
            Row(
                horizontalArrangement = Arrangement.Center,
                modifier = Modifier.fillMaxWidth(),
            ) {
                LPlusButton(
                    onClick = {
                        sendEmail(launcher, bugReport)
                    },
                    text = { Text(text = stringResource(R.string.send_email)) },
                    leadingIcon = {
                        Icon(
                            imageVector = LPlusIcons.Email,
                            contentDescription = stringResource(R.string.send_email)
                        )
                    },
                    modifier = Modifier
                        .padding(horizontal = 24.dp)
                        .widthIn(364.dp)
                        .fillMaxWidth(),
                )
            }
        }
    }
}

@Composable
fun TextFieldBugDescription(message: MutableState<String>) {
    TextField(
        value = message.value,
        onValueChange = { message.value = it },
        label = { Text(stringResource(id = R.string.bug_label)) },
        maxLines = 2,
        modifier = Modifier
            .padding(20.dp)
            .fillMaxWidth()
            .height(200.dp)
    )
}

Как вы видите на этом первом снимке экрана, чекбоксы правильные:

введите описание изображения здесь

Но когда я активирую поле ввода и вводжу текст, элементы восстанавливаются. И то же самое происходит, если я поворачиваю экран:

введите описание изображения здесь

Ответ или решение

Ваша проблема с повторной инициализацией чекбоксов в вашем LazyVerticalGrid в Jetpack Compose, вероятнее всего, связана с тем, как вы сохраняете состояние ваших объектов BugItem. При изменении состояния текста в TextField или при повороте экрана, ваша композиция пересоздается, и вы теряете информацию о выборе чекбоксов.

Чтобы избежать повторных инициализаций и сохранить состояние чекбоксов, вам следует использовать более устойчивые структуры данных и обеспечить, чтобы состояние ваших чекбоксов сохранялось даже при переработке композиции. Вот несколько шагов, которые помогут вам с этим.

Шаги для решения проблемы

  1. Используйте remember для bugItems:

    Вместо того чтобы использовать mutableStateListOf для хранения bugItems, можно сохранить их в remember, чтобы данные не терялись, когда пользователь взаимодействует с TextField или поворачивает экран.

    val bugItems = remember {
       mutableStateListOf<BugItem>().apply {
           stringArrayResource(id = R.array.bug_report).forEach {
               add(BugItem(title = it, completed = false))
           }
       }
    }
  2. Используйте LazyColumn или LazyVerticalGrid с items и ключами:

    Когда вы создаете элементы в LazyVerticalGrid или LazyColumn, вы можете использовать items с указанием ключа, чтобы Compose мог отслеживать элементы, даже когда происходит переработка композиции.

    LazyVerticalGrid(
       modifier = Modifier.heightIn(max = 1000.dp),
       columns = GridCells.Fixed(2),
       content = {
           items(bugItems, key = { it.title }) { bugItem ->
               Row(
                   modifier = Modifier.padding(1.dp),
                   verticalAlignment = Alignment.CenterVertically
               ) {
                   Checkbox(
                       checked = bugItem.completed,
                       onCheckedChange = { checked ->
                           bugItems[bugItems.indexOf(bugItem)] = bugItem.copy(completed = checked)
                       }
                   )
                   Text(
                       text = bugItem.title,
                       style = MaterialTheme.typography.bodyLarge,
                   )
               }
           }
       }
    )
  3. Структура состояния:

    Если у вас есть возможность, подумайте о том, чтобы использовать более сложные структуры данных или дополнительные библиотеки для управления состоянием, такие как ViewModel, и хранить данные о состоянии BugReport в нем. Это обеспечит, что данные остаются в общественном состоянии даже при изменениях конфигурации.

    Пример использования ViewModel:

    class BugReportViewModel : ViewModel() {
       var message by mutableStateOf("")
       var bugItems = mutableStateListOf<BugItem>().apply {
           stringArrayResource(id = R.array.bug_report).forEach {
               add(BugItem(title = it, completed = false))
           }
       }
    }
    
    @Composable
    fun BugReportScreen(viewModel: BugReportViewModel = viewModel()) {
       // Используйте viewModel.message и viewModel.bugItems вместо локальных переменных
       // Ваш код здесь...
    }

Заключение

Следуя этим шагам, вы сможете избежать проблемы с повторной инициализацией чекбоксов и сохранить их состояние во время взаимодействия пользователя с интерфейсом. Использование remember и устойчивых структур данных, таких как ViewModel, поможет вам создать более надежный и предсказуемый интерфейс для вашего экрана отчета о ошибках.

Оцените материал
Добавить комментарий

Капча загружается...