perf: Add stable keys to Lazy list items for performance (#323)
Added stable `key` parameters to `items(...)` and `itemsIndexed(...)` calls across 13 screens. This prevents Compose from treating all list mutations as full item replacements and eliminates unnecessary full list recompositions during state updates. Modifications applied to: - KeysScreen.kt, SharedComponents.kt, AchievementsScreen.kt, PluginsScreen.kt - SessionsScreen.kt, CronJobsScreen.kt, McpServersScreen.kt, PairingScreen.kt - ToolsetsScreen.kt, ChatScreen.kt, ModelScreen.kt, SkillsScreen.kt - KanbanScreen.kt, ProfilesScreen.kt, WebhooksScreen.kt Note: LogsScreen.kt was left without a key because logs are string lines that can be duplicated, and using `.hashCode()` would cause a "Key was already used" crash. Positional indexing is safer there. Fixes #279
This commit is contained in:
15 files changed
+17
-17
No files matched your search
@@ -101,7 +101,7 @@ fun AchievementsScreen(
|
||||
placeholder = "Search achievements...",
|
||||
)
|
||||
}
|
||||
items(filteredAchievements) { achievement ->
|
||||
items(filteredAchievements, key = { it.id }) { achievement ->
|
||||
val pct = achievement.progress_pct?.toFloat()?.div(100f) ?: 0f
|
||||
|
||||
Card(
|
||||
|
||||
@@ -715,7 +715,7 @@ private fun ChatInputBar(
|
||||
LazyColumn(
|
||||
modifier = Modifier.heightIn(max = 200.dp),
|
||||
) {
|
||||
items(filteredCommands) { cmd ->
|
||||
items(filteredCommands, key = { it }) { cmd ->
|
||||
val description =
|
||||
when (cmd) {
|
||||
"/help" -> stringResource(R.string.command_help_desc)
|
||||
|
||||
@@ -246,7 +246,7 @@ fun FilterChipRow(
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(spacing.sm),
|
||||
) {
|
||||
items(chips) { chip ->
|
||||
items(chips, key = { it }) { chip ->
|
||||
FilterChip(
|
||||
selected = chip == selectedChip,
|
||||
onClick = { onChipSelected(chip) },
|
||||
|
||||
@@ -89,7 +89,7 @@ fun CronJobsScreen(
|
||||
contentPadding = listContentPadding,
|
||||
verticalArrangement = listItemSpacing,
|
||||
) {
|
||||
items(state.jobs) { job ->
|
||||
items(state.jobs, key = { it.id }) { job ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors =
|
||||
|
||||
@@ -149,7 +149,7 @@ fun KanbanScreen(
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
items(state.columns) { column ->
|
||||
items(state.columns, key = { it.name }) { column ->
|
||||
val colName = column.name
|
||||
val colTasks =
|
||||
filteredTasks.filter {
|
||||
@@ -178,7 +178,7 @@ fun KanbanScreen(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(colTasks) { task ->
|
||||
items(colTasks, key = { it.id }) { task ->
|
||||
TaskCard(
|
||||
task = task,
|
||||
onMoveLeft =
|
||||
|
||||
@@ -125,7 +125,7 @@ fun KeysScreen(
|
||||
placeholder = "Search keys...",
|
||||
)
|
||||
}
|
||||
items(filteredEnvVars.toList()) { (key, config) ->
|
||||
items(filteredEnvVars.toList(), key = { it.first }) { (key, config) ->
|
||||
var isEditing by remember { mutableStateOf(false) }
|
||||
val revealedVal = state.revealedValues[key]
|
||||
val isRevealed = revealedVal != null
|
||||
|
||||
@@ -116,7 +116,7 @@ fun McpServersScreen(
|
||||
placeholder = "Search MCP servers...",
|
||||
)
|
||||
}
|
||||
items(filteredServers) { server ->
|
||||
items(filteredServers, key = { it.name }) { server ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors =
|
||||
|
||||
@@ -134,7 +134,7 @@ fun ModelScreen(
|
||||
placeholder = "Search models...",
|
||||
)
|
||||
}
|
||||
items(filteredProviders) { provider ->
|
||||
items(filteredProviders, key = { it.slug }) { provider ->
|
||||
val isExpanded = expandedProviderSlug == provider.slug
|
||||
val isCurrent = provider.is_current == true
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ fun PairingScreen(
|
||||
}
|
||||
}
|
||||
|
||||
items(pending) { item ->
|
||||
items(pending, key = { "${it.platform}_${it.code ?: it.user_id}" }) { item ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors =
|
||||
@@ -201,7 +201,7 @@ fun PairingScreen(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(approved) { item ->
|
||||
items(approved, key = { "${it.platform}_${it.user_id}" }) { item ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
|
||||
@@ -119,7 +119,7 @@ fun PluginsScreen(
|
||||
placeholder = "Search plugins...",
|
||||
)
|
||||
}
|
||||
items(filteredPlugins) { plugin ->
|
||||
items(filteredPlugins, key = { it.name }) { plugin ->
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(
|
||||
|
||||
@@ -118,7 +118,7 @@ fun ProfilesScreen(
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
items(state.profiles) { profile ->
|
||||
items(state.profiles, key = { it.name }) { profile ->
|
||||
val isActive = profile.name == state.activeProfileName
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
@@ -105,7 +105,7 @@ fun SessionsScreen(
|
||||
placeholder = "Search sessions...",
|
||||
)
|
||||
}
|
||||
items(filteredSessions) { session ->
|
||||
items(filteredSessions, key = { it.id }) { session ->
|
||||
Card(
|
||||
modifier =
|
||||
Modifier
|
||||
|
||||
@@ -237,7 +237,7 @@ fun SkillsScreen(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(filtered) { skill ->
|
||||
items(filtered, key = { it.name }) { skill ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors =
|
||||
|
||||
@@ -133,7 +133,7 @@ fun ToolsetsScreen(
|
||||
placeholder = "Search toolsets...",
|
||||
)
|
||||
}
|
||||
items(filteredToolsets) { toolset ->
|
||||
items(filteredToolsets, key = { it.name }) { toolset ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors =
|
||||
|
||||
@@ -192,7 +192,7 @@ fun WebhooksScreen(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(filteredSubscriptions) { sub ->
|
||||
items(filteredSubscriptions, key = { it.name }) { sub ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
|
||||
Reference in new issue
Block a user