- Tech
- アニメーション
- アプリ開発
Flutterで「浮いたUI」を実装する設計方法


Flutterで、下記画像のようなタップ可能なカードを浮かせるようなUIを実装しようとすると、
- カード全体がタップできなくなる
- 端末サイズごとにレイアウトが崩れる
- position の数値調整が泥沼化する
UIは「見た目」ではなく「構造」で分解する
今回のUIは、直感的には次の3つのパーツに見えます。
- 上半分の背景画像コンテンツ
- 下半分の白背景コンテンツ
- 浮いているカードWidget
よくある実装例
- Columnで上下を分ける
- Stackでカードを浮かせる
- bottom: -80 などでめり込ませる
図解:NGパターン(3分割設計)

問題点
Positioned(bottom: -80)で無理やり移動させたWidgetは、見た目とhitTest領域が一致しなくなる場合があります。結果として、カード全体がタップできなくなります。例えば、次のような実装を行った場合
Positioned(bottom: 600)
- 小さい端末:カードが見切れる
- 大きい端末:カードの位置が不自然になる
UIの分解方法を見直す
ここでいう「擬似領域」とは、
本来は存在しないが、レイアウト上あえて用意する仮想的な背景領域のことです。
カードを直接移動させて重ねるのではなく、
カードが自然に存在できる空間を先に作るということです。
新しい構造
- 上半分の背景画像コンテンツ
- カード下部までの白背景の擬似領域(タップ領域)
- カードWidget
- 下半分の白背景コンテンツ
図解:OKパターン(4分割設計)

カードを無理に移動させるのではなく、
カードが存在できる「空間」を先に作るという考え方です。
hitTest領域の意識
Flutterでは、Widgetがタップ可能かどうかは、
見た目ではなく「レイアウト上の領域」によって決まります。
NGパターン
見た目:カードが存在する
実際のhitTest領域:存在しない
OKパターン
見た目:カードが存在する
実際のhitTest領域:擬似領域によって確保されている
擬似領域を作成することで、
カード全体が正しくhitTest領域内に収まり、
意図した通りタップ可能になります。
position hack はアンチパターンである
- 端末サイズ依存のバグ
- 意図不明なマジックナンバーの増加
- hitTest領域の不整合
まとめ
本記事では、Flutterで背景画像の上にカードを浮かせるようなUIを、どのように設計・実装するかについて紹介しました。
一見すると、StackとPositionedを使って位置を調整すれば簡単に実装できそうですが、実際にはタップ領域が正しく機能しなかったり、端末サイズによってレイアウトが崩れてしまったりと、思わぬ問題が発生しやすいポイントでもあります。
そこで今回は、UIを「上半分の背景画像」「下半分の白背景」「カード」「擬似領域」の4つに分解し、カードを無理に動かすのではなく、カードが自然に配置される空間を先に作るというアプローチを採用しました。
FlutterのUI実装は、見た目を再現するだけでなく、Widgetツリーやレイアウトの構造を意識して設計することが重要です。
positionの数値調整に頼るのではなく、構造的に考えることで、可読性や保守性の高いコードにつながります。
今回紹介した考え方が、複雑なUIを実装する際の一つのヒントになれば幸いです。
ドコドア エンジニア部
このブログでは、アプリ開発の現場で培ったフロントエンド、バックエンド、インフラ構築の知識から生成AI活用のノウハウまで、実践的な情報をアプリ開発に悩む皆様へ向けて発信しています!
【主な技術スタック】 Flutter / Firebase / Svelte / AWS / GCP / OpenAI API