Y&S SoftWareのホームページ
使いかってのいいソフトウェアを開発します

Composeでの、【key】 の意味・使いどころ・なぜ効くのか

key とは

key は「Compose が ある子コンポーネント(=スロット) を どのデータ項目に結びつけるか を決めるための識別子」です。

表示されている各行(カード)に IDタグ を貼るようなもの。
タグで紐づければ項目の並び替えや追加削除があっても「このタグのUI」は正しいデータに紐づいたまま扱えます。


なぜ key を設定するのか

Compose は 「スロット(位置)」ベースで子コンポーネントを管理します。
デフォルトでは、インデックス順にスロットを割り当て、再コンポーズ時はそのスロットに前回の状態を再利用します。

問題になるのは次のパターンです。

リストの途中に要素が挿入/削除、あるいは並び替えが起きる場合にCompose は「位置ベース」でスロットを再利用するため、行の見た目は移動しても内部で保持している状態(remember 等)が別のデータ項目に割り当てられてしまう

そして、ある行(A)に対して保持していた状態が、別の行に移ってしまい、見た目と内部状態がズレる不具合になります。


key

key を与えると Compose はスロットと要素を キー(通常はユニークな id)でマッチ させます。
そのため項目の追加/削除/順序変更があっても、Compose は「この id のためのスロット」を探して正しい状態を紐づけ直すか、必要なら破棄して新しいスロットを作ります。結果、状態のズレが起こらなくなります。




具体例使い方
LazyColumn の場合
LazyColumn {
  items(
   items = folders,
    key = { it.id } // ← ここが重要(ユニークで安定したキーを与える)
  ) { folder ->
    FolderTreeItem(folder = folder, ...)
  }
}

Column / forEach で使う場合

Column { folders.forEach { ... } } のように書いているときは key コンポーザブルを使います:

Column {
  folders.forEach { folder ->
    key(folder.id) { // folder.id をキーにする
      FolderTreeItem(folder = folder, ...)
    }
  }
}


再帰的に子を表示する部分も、子の forEach 内で key(child.id) { FolderTreeItem(...) } を使うと安全です。



key は 「データ項目 ↔ UI スロット」 を安定的に結びつけるラベルです。

順序変更や追加/削除が起きるリスト、あるいは子コンポーネントに remember を使っているときは key がほぼ必須ということです。



注意点

UI 状態(選択中の folderId や expandedIds など)は できるだけ ViewModel 側で管理する。
Composeの remember にUI依存状態を置きすぎると再利用時にズレやすい。

key を与えると「該当要素の内部状態が破棄される」こともあるので、例えば編集中のテキストを失いたくないなら ViewModel へ保存するなどを検討する。

key の過剰使用は不要な破棄・再生成を招く場合がある。
一貫して「項目の固有ID」をキーにするのが自然。