本文へスキップ
すべての記事

2026年のAndroidにおけるedge-to-edgeとpredictive back:今や必須になったことと、UIを壊さない方法

edge-to-edgeはAPI 35以降で強制になり、predictive backはデフォルトのジェスチャーになった。Composeのinsets、WindowInsets、PredictiveBackHandlerについての実践ガイド。

MFKAPPS 1 分で読めます

このサイクルで、2つのAndroid UIの変更がオプションではなくなった。API 35以降をターゲットにするアプリはedge-to-edgeが強制される——もはやシステムに不透明なバーを描画させるという選択肢を選ぶことはできない。そしてpredictive back、つまり確定する前にどこへ向かうかをプレビューする戻るスワイプジェスチャーは、実験的なフラグではなく、ジェスチャーナビゲーション端末におけるデフォルトの操作になった。どちらも再設計ではない。どちらも、それ向けに作られていないUIを目に見える形で壊す。そして失敗の仕方はどちらも同じだ——テストしたエミュレーターでは問題なく動くのに、ノッチ付きや三ボタンナビゲーションをオフにした実機では壊れて見える。

これが実際に変わった内容と、両方の変更の下で5つのアプリの画面を正しく保つCompseコードだ。

edge-to-edgeはもはや選択肢ではない

API 35以前は、enableEdgeToEdge()は特定の画面のためにオプトインするものだった——半透明のステータスバーの下にコンテンツを流したいアプリにとっての、ちょっとした追加機能だった。API 35をターゲットにすると、その選択肢はなくなる。Window.setDecorFitsSystemWindows(false)の動作が強制され、システムバーは透明になり、あなたが何も呼び出していなくても、ルートコンテンツはその背後に描画される。

実際の影響はこうだ。ステータスバーとナビゲーションバーが自分専用のスペースを確保していると想定していたレイアウトはすべて、物理ピクセルのゼロからコンテンツが始まることになる。上部のアプリバーのタイトルは時計の下に埋もれる。下部のナビゲーションバーのアイコンはジェスチャーピルの下に埋もれる。これは見た目の仕上げの問題ではない——実機では、ユーザーがUIの一番下の行をタップできなくなることを意味する。

解決策は「どこにでもパディングを追加する」ことではない。WindowInsetsを正しい境界に、一度だけ適用することだ。そうすればシステムバーは、コンテンツが必要とする場所にだけ正確にスペースを確保し、それ以外の場所には確保しない。

setContent {
    AppTheme {
        Scaffold(
            contentWindowInsets = WindowInsets.safeDrawing,
            topBar = { TopAppBar(title = { Text("Stocky") }) },
        ) { padding ->
            LazyColumn(contentPadding = padding) {
                items(pantryItems) { PantryRow(it) }
            }
        }
    }
}

Scaffoldは与えられたトップバーとボトムバーをすでに考慮しているので、ほとんどの画面はこの1つのパラメータを配線するだけでよい。落とし穴は、それを二重にやってしまうことだ。Scaffoldはそのpadding値にsafe-drawing insetsを適用するが、子コンポーネントがその上にさらに.systemBarsPadding()を呼び出すと、ステータスバーの下に2倍の隙間ができる——デザインのバグに見えるが、実際にはinsetsコードのバグだ。

insetsを手動で扱う必要がある場所

すべての画面がScaffoldを通るわけではない。Granyn、Stocky、Mintlyで手動で修正しなければならなかった3つの箇所。

  • ボトムシートとダイアログ。 ModalBottomSheetは親のScaffoldのパディングの外側に自分のサーフェスを描画するので、そのコンテンツは最も内側のカラムに独自の.navigationBarsPadding()が必要だ。そうしないと最後の行がジェスチャーバーの下に埋もれる。
  • キーボード。 WindowInsets.imenavigationBarsとは別のinsetで、キーボードの開閉に合わせてアニメーションする。画面下部に固定されたテキストフィールドには.navigationBarsPadding()ではなく.imePadding()が必要だ——キーボードが閉じているときは両者は重なるが、開いた瞬間に食い違う。
  • フルブリードの画像やヘッダー。 ステータスバーの下にコンテンツを意図的に描画させたい場合——スクリーンショットギャラリーやヒーロー画像など——コンテナ全体にパディングを付けるのではなく、その上に重ねたインタラクティブなコントロール(戻るボタン、共有アイコン)に.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top))を適用する。そうしないと狙った効果が失われる。

ユーザーに見つけられる前にこの問題の大部分を捕まえる唯一の習慣は、エミュレーターでジェスチャーナビゲーションとステータスバーの「Demo mode」カスタマイズを有効にし、ノッチのシミュレーションを有効にした状態ですべての画面をチェックすることだ。三ボタンナビゲーションとシンプルなステータスバーは、リリースしてしまうinsetsのバグのほぼすべてを隠してしまう。

Predictive back:ハードカットからプレビューへ

もう一つの変更は動作に関するもので、レイアウトの問題ではない。predictive backは、ユーザーが指を離す前に、戻るジェスチャーがどこに着地するかをシステムが示せるようにする——下にある画面が縮小していくプレビューだ——これによりユーザーは行き先を見てジェスチャーの途中で中止できる。まだオプトインしていないアプリは、依然として単純で瞬間的な画面切り替えになる。ジェスチャー自体は動くが、端末上の他のpredictive back対応アプリと並ぶとジャンプカットのように見える。

オプトインするにはマニフェストのフラグとComposeのコールバックが必要だ。

<!-- AndroidManifest.xml -->
<application android:enableOnBackInvokedCallback="true">
PredictiveBackHandler(enabled = showDetail) { progress ->
    try {
        progress.collect { backEvent ->
            // backEvent.progress: 0f (開始) → 1f (確定)
            scale = 1f - (backEvent.progress * 0.1f)
        }
        showDetail = false // ジェスチャー確定
    } catch (e: CancellationException) {
        scale = 1f // ジェスチャー中止 — 元に戻す
    }
}

PredictiveBackHandler(androidx.activity.composeから)は、単一のコールバックではなく進捗イベントのFlowを渡してくれる。これがBackHandlerから来た人がつまずくポイントだ。ジェスチャーの間ずっとそれをcollectし、backEvent.progressに応じてアニメーションを駆動する。中止されたスワイプでのCancellationExceptionはエラーではなく通常の終了経路だ——これはユーザーが確定する前に指を離したことをシステムが伝える方法だ。私はこれをまさにMintlyのセッション詳細画面で使った。タイマー表示は今、戻るスワイプに合わせてわずかに縮小し、背後のリストを見せる。早めに指を離した場合は、ビューが単純に消えるのではなく、きれいに元のサイズに戻る。

リリース前に本当にチェックすべきこと

  • ジェスチャーナビゲーションが有効で、三ボタンナビゲーションが無効な状態で、すべての画面が正しく描画される——ほとんどの新しい端末が出荷される状態だ。
  • ステータスバーの下に二重にパディングされた隙間がない(上述のScaffold+手動の.systemBarsPadding()の罠)。
  • ハードウェアのナビゲーションボタンがない端末で、ボトムシートとスナックバーがジェスチャーバーを避けている。
  • 画面下部に近いテキストフィールドが.imePadding()を使っており、キーボードが開いたときにジャンプしない。
  • 実際のナビゲーションを持つ少なくとも1つの画面(最初にテストする画面だけでなく)にpredictive backが配線されており、画面ごとにジェスチャーの一貫性がないと感じられないようになっている。

一度これにぶつかってしまえば、どれも難しいことではない。ただ、三ボタンナビゲーションの標準的なエミュレーターでは見えない——そしてそれこそが、私たちの多くがデフォルトのまま放置している設定であり、それゆえに本来より頻繁に壊れた状態でリリースされてしまう理由でもある。