@ComposableアノテーションはJetpack Composeの基本的な要素であり、通常のKotlin関数を宣言的なユーザーインターフェースの構成要素に変換します。このアノテーションなしでは、現代のAndroid開発において画面を作成することは不可能です。Google Android Developers, 2026によると、80%以上の新しいKotlinプロジェクトがUI構築にComposeを使用しており、@Composableはエコシステムで最も頻繁に使用されるアノテーションです。
重要なポイント
@ComposableはKotlin言語のアノテーションであり、関数をJetpack Composeフレームワークでユーザーインターフェースを記述するためのものとしてマークします。Kotlinコンパイラがこのアノテーションを検出すると、関数がコンポジションコンテキスト(UIツリー管理システム)で動作できるようにする追加コードを生成します。
@Composableアノテーションは、2021年にJetpack Compose 1.0の最初の安定版とともにGoogleによって導入されました。登場以前は、Androidのインターフェース開発はXMLマークアップとViewシステムのみを介して行われていました。@Composableはアプローチを根本的に変えました。別のマークアップファイルでUIを記述する代わりに、開発者はKotlinで直接インターフェースを記述します。
@Composableと通常のKotlin関数の主な違いは、状態を読み取り、その変化に反応する能力です。Composable関数が読み取る変数の値が変更されると、システムは自動的に関数の再起動(再コンポジション)をスケジュールします。これにより、開発者はfindViewByIdやsetTextによる手動のUI更新から解放されます。
@Composableの内部メカニズムはスロットの概念に基づいています — コンポジション内の各関数に割り当てられる特別なメモリ領域です。このスロットには関数に渡された値と、後続の呼び出しでの比較に必要なサービス情報が格納されます。
Composable関数を宣言するには、funキーワードの前に@Composableアノテーションを追加するだけです。関数はandroidx.compose.runtimeからアノテーションをインポートするパッケージに存在する必要があります。関数名は大文字で始めることを推奨します — これはComposeコミュニティで広く受け入れられている慣習であり、UIコンポーネントを通常の関数から視覚的に区別します。
import androidx.compose.runtime.Composable
@Composable
fun Greeting(name: String) {
var count by remember { mutableStateOf(0) }
Column {
Text("こんにちは、$nameさん")
Button(onClick = { count++ }) {
Text("$count回クリックされました")
}
}
}
Composable関数のパラメータは任意のものにできます — プリミティブ型、文字列、ラムダ、さらにはSlot APIを介して渡される他のComposable関数も含みます。推奨されるのは、再コンポジション中の副作用を避けるためにパラメータを不変(val)にすることです。すべての可変データはComposeの状態メカニズムを介して管理する必要があります。
Composable関数は通常の関数のように任意の値を返すことはできません — その唯一のタスクはUIツリーの一部を構築または更新することです。ただし、State Hoistingのような特別なパターンが存在し、Compose関数がパラメータを介して状態とコールバックを受け入れ、純粋で再利用可能な状態を保ちます。
Composeシステムは、Composable関数の外観と動作についていくつかの厳格な制約を課します。最初のルール:Composable関数は他のComposable関数または副作用のない通常の関数のみを呼び出すことができます。これにより、コンポジションの予測可能性とCompose最適化の正確な動作が保証されます。
2番目のルールは実行順序に関するものです。ComposeはComposable関数を任意の順序で呼び出す権利があるため、そのような関数の本体のコードは隣接する関数の呼び出しシーケンスに依存してはいけません。各Composable関数はUIツリー内の位置のレベルで自己完結している必要があります。
3番目のルール — Composable関数の本体内での副作用の禁止。データベースへの書き込み、ネットワークリクエストの送信、外部変数の変更などの操作は、LaunchedEffect、DisposableEffect、SideEffectなどの特別なエフェクト内でのみ実行する必要があります。このルールに違反すると、再コンポジション中に予期しない動作が発生します。
4番目のルール:Composable関数は冪等性を持つ必要があります。同じ引数で再度呼び出すと、同じUIが生成される必要があります。この要件は、入力データが変更されていない関数の再描画をComposeがスキップするスキップ最適化の正しい動作に必要です。
// 正しい: 副作用のない純粋なComposable関数
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
Card(modifier = Modifier.clickable { onClick() }) {
Text(text = user.name)
}
}
// 間違い: ボディ内の副作用
@Composable
fun WrongCard(userId: String) {
// val result = viewModel.loadUser(userId) // 不可
Text("読み込み中...")
}
@Composableアノテーションを使用したプロフィール画面作成の実践的な例を見てみましょう。ここでは、複数のComposable関数の組み合わせ、状態と修飾子の操作をデモンストレーションします — これはComposeレイアウトの重要な要素です。
@Composable
fun ProfileScreen(userId: String) {
var isFollowed by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
ProfileHeader(userId = userId)
Spacer(modifier = Modifier.height(16.dp))
StatsRow(posts = 42, followers = 1280)
Spacer(modifier = Modifier.height(24.dp))
FollowButton(
isFollowed = isFollowed,
onToggle = { isFollowed = !isFollowed }
)
}
}
@Composable
fun ProfileHeader(userId: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = "https://example.com/avatars/$userId",
contentDescription = "User avatar")
Spacer(modifier = Modifier.width(12.dp))
Text(text = "ユーザー #$userId", style = MaterialTheme.typography.headlineMedium)
}
}
@Composable
fun StatsRow(posts: Int, followers: Int) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
StatItem("Posts", posts)
StatItem("Followers", followers)
}
}
@Composable
fun StatItem(label: String, value: Int) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "$value", style = MaterialTheme.typography.headlineSmall)
Text(text = label, style = MaterialTheme.typography.bodySmall)
}
}
この例では、各Composable関数が画面の各部分を担当しています:ProfileScreenは全体的な状態と子関数のコンポジションを管理し、ProfileHeaderはアバターと名前を表示し、StatsRowは統計ブロックを表示します。このアプローチは単一責任の原則に従い、コンポーネントの再利用を簡素化します。
Jetpack ComposeにはComposable関数の3つの主要なタイプがあります。最初のタイプ — コンテナ(Row、Column、Box、LazyColumn) — 子要素のレイアウトを決定します。2番目のタイプ — 表示要素(Text、Image、Icon、Button) — 特定のUIコンポーネントをレンダリングします。3番目のタイプ — カスタムComposable関数 — 組み込みコンポーネントを再利用可能なブロックに結合します。
コンテナは、contentラムダ(@Composable () -> Unit型の最後のパラメータ)を受け入れる点で通常の要素と異なります。このメカニズムにより、ネストされたUIツリーの構築が可能になります。各コンテナは独自のコンテキストとメモリ領域を持つ子コンポジションを生成します。
カスタムComposable関数は2つのサブタイプに分類されます:スマート(smart)とダム(dumb)。スマート関数は状態とロジックを管理し、remember、LaunchedEffect、その他のCompose APIの呼び出しを含みます。ダム関数はパラメータを介してすべてのデータを受け取り、それを表示するだけです。スマートコンポーネントとダムコンポーネントの分離は、テスタビリティとコードの再利用性を向上させます。
| タイプ | 例 | 目的 |
|---|---|---|
| コンテナ | Column, Row, Box | 子要素のレイアウト管理 |
| 要素 | Text, Image, Button | コンテンツの表示と入力処理 |
| カスタム | ProfileCard, UserList | 標準コンポーネントの組み合わせ |
@Composableアノテーションの主な利点は、継承や複雑なクラス階層なしで再利用可能なUIコンポーネントを作成できることです。各カスタム要素にコンストラクタ付きのJavaクラスの作成が必要だったViewシステムとは異なり、Composableコンポーネントは単にパラメータを持つKotlin関数です。
再利用性を確保するために、Slot APIパターンが使用され、Composable関数はレイアウトの異なる領域に対してcontentラムダを受け入れます。たとえば、Cardコンポーネントはヘッダー、ボディ、フッターに別々のコンテンツを受け入れることができ、アプリケーションの任意の画面で汎用的に使用できます。
修飾子(Modifier)は再利用性において重要な役割を果たします:コンポーネント自体を変更せずにパディング、サイズ、クリック、アニメーションを設定できます。デフォルト値(Modifier = Modifier)を指定してComposable関数のパラメータとしてModifierを常に渡すことを推奨します — これはGoogleの公式ライブラリで採用されている標準的なプラクティスです。
@Composable
fun SectionCard(
modifier: Modifier = Modifier,
title: String,
content: @Composable () -> Unit
) {
Card(modifier = modifier) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = title, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
content()
}
}
}
Slot APIのおかげで、SectionCardコンポーネントは異なるコンテンツ(フォーム、リスト、テキストブロック)を持つ異なる画面で使用できます。修飾子とSlot APIの組み合わせにより、Kotlinが提供する型安全性を失うことなく、Composeコンポーネントを非常に柔軟にできます。
よくある質問
@Composable関数はコンポジションコンテキストで実行され、状態を読み取り、変更時に自動的に再起動します。通常のKotlin関数は状態追跡メカニズムにアクセスできず、UIツリーの構築に参加しません。
いいえ、Composable関数は特別なコンポジションコンテキストが必要なため、他のComposable関数からのみ呼び出せます。Composeコードを通常のKotlinと統合するには、ActivityのsetContent { }メソッドまたはViewシステムのComposeViewを使用します。
これはComposeコミュニティで採用された命名規則です。大文字はUIコンポーネントを通常の関数から視覚的に区別し、クラス命名規則に従います。コンパイラの要件ではなく、Googleのドキュメントで推奨されるプラクティスです。
数に制限はありません。実際には、大きな画面には組み込みコンポーネント(Text、Button)とカスタムコンポーネントを含む50~100個のComposable関数を含めることができます。Composeは関数ツリーを最適化し、入力データが変更されたものだけを実行します。
通常、Composable関数はUnitを返します。そのタスクはUIを構築することだからです。ただし、@Composableでマークされ値を返すrememberやderivedStateOfのような特化した関数が存在します。これは例外であり、規則ではありません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。