OpenAIExpoNov 25, 2025, 4:30 PM

How to create Apple Maps style liquid glass sheets in Expo (the real way)

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

openaienmodel: gpt-5-mini-2025-08-07

How to create Apple Maps style liquid glass sheets in Expo (the real way)

Key Points

  • iOS 26 detents: floating → medium → full sheet
  • TrueSheet: native liquid glass + fine-grained control
  • Expo Router: quick setup but limited customization

Summary

This note shows three practical ways to recreate iOS 26’s Apple Maps “liquid glass” bottom sheet in Expo: expo-swift-ui BottomSheet, Expo Router’s formSheet presentation, and the native TrueSheet. iOS 26 detents behave like this: lowest detent = floating card with gap and full rounding, middle detent = closer but still floating, top detent = full sheet with no gap. The goal is to get the liquid glass appearance plus smooth detent transitions while choosing the right tradeoff between convenience and control.

Key Points

  • expo-swift-ui BottomSheet

    • Native and supports the new liquid glass effect out of the box.
    • Configure presentationDetents (e.g. [0.1, 0.5, 1]) for the same detent transitions.
    • Beta status: good for experimentation, not recommended for production builds yet.
  • Expo Router formSheet

    • Quick to set up via screen options: presentation: "formSheet", sheetAllowedDetents, sheetInitialDetentIndex.
    • Must set contentStyle: { backgroundColor: "transparent" } so the OS liquid glass shows through.
    • Good for simple flows, but limited if you need custom footers, fine-grained control, or synced animations.
  • TrueSheet (recommended when you need control)

    • Native, supports liquid glass (use blurTint="default"), detents, footers, and exposes animation values to sync UI with sheet motion.
    • Control with a ref: present/dismiss via sheet.current?.present() / dismiss() and set sizes like [75, "medium", "large"] or percentages.
    • If using auto, prefer explicitly setting the lowest height (e.g. 75) to avoid sizing issues.
  • Practical tips

    • Use detents intentionally to mirror the iOS behavior: small (floating) → medium (closer) → full (no gap).
    • Disable gestureEnabled if you want to prevent pull-to-dismiss on lower detents.
    • Prefer TrueSheet for animation syncing and custom content; use Expo Router for simple quick wins; use expo-swift-ui for experimenting with the native API.

Recommended approach

For production apps that require the Apple Maps interaction fidelity and animation sync, use TrueSheet (native) and control detents + animation values. Use Expo Router for fast implementations where fine control is unnecessary, and try expo-swift-ui only for experiments until it leaves beta.

Full Translation

Translations

A translation section that keeps the flow of the original article.

openaijamodel: gpt-5-mini-2025-08-07

ExpoでApple Maps風のLiquid Glass(リキッドグラス)シートを作る方法(本当のやり方)

ExpoでApple Maps風のLiquid Glass(リキッドグラス)シートを作る方法(本当のやり方)

Expo UI、Expo Router、TrueSheet を使って、iOS 26 の新しい Liquid Glass ボトムシートを再現する方法を解説します。スムーズな detent(デタント)遷移と iOS 26 の挙動をできるだけ忠実に再現する手順と注意点をまとめています。

iOS 26 におけるシートの挙動

  • 最低の detent(最小位置)ではシートは“浮いた”状態で、画面上にギャップがあり、角は完全に丸くなっています。
  • 中間の detent にドラッグすると、まだ浮いた状態のままですが画面端に近づき、ギャップが狭まり角丸が変化します。
  • 最高の detent にドラッグするとギャップが消え、浮いたカードのような挙動から通常のフルシートの挙動へ遷移します。

Apple Maps の最新挙動と同じインタラクションが目標です。

試したアプローチ

以下の 3 つのアプローチを試しました。Expo 環境で実現できる順に紹介します。

1) expo-swift-ui の BottomSheet

ネイティブ実装なので Liquid Glass をサポートしています。presentationDetents(または同等の設定)で detents を設定すれば、期待通りの挙動が得られます。ただし現時点では beta のため、本番に出すには注意が必要です。実験用途には最適です。

import { BottomSheet, Host } from "@expo/ui/swift-ui";
<Host style={{ position: "absolute", width }}>
  <BottomSheet
    isOpened={isOpened}
    presentationDragIndicator="visible"
    presentationDetents={[0.1, 0.5, 1]}
    onIsOpenedChange={(e) => setIsOpened(e)}
  >
    <Text>Hello, world!</Text>
  </BottomSheet>
</Host>

ここでは isOpened state でシートを制御しています。detents は 0 から 1(フル高さ)で指定します。presentationDragIndicator は上部のハンドル(ドラッグインジケーター)です。

2) Expo Router の formSheet プレゼンテーション

Evan Bacon が 𝕏(Twitter)で共有していた方法を試すと期待通りに動きました。基本的なシート挙動を手早く実現できます。

<Stack.Screen
  name="liquidGlassSheet"
  options={{
    headerShown: false,
    presentation: "formSheet",
    gestureEnabled: false,
    sheetGrabberVisible: true,
    contentStyle: { backgroundColor: "transparent" },
    sheetAllowedDetents: [0.1, 0.5, 1],
    sheetInitialDetentIndex: 0,
    sheetLargestUndimmedDetentIndex: 0,
  }}
/>

ポイント:

  • sheetGrabberVisible は上部のドラッグインジケーターです。
  • Liquid Glass 表現を通すために contentStyle の backgroundColor を "transparent" にする必要があります。
  • detents の範囲は 0 〜 1(前述)です。
  • gestureEnabled: false にしておくと、ユーザーが下方向へドラッグして閉じるのを無効化できます。
  • sheetInitialDetentIndex: 0 を設定すると初期位置が 0.1(シートが浮いた状態・角丸)になります。

この方法は「普通のシート」を手早く出すには完璧ですが、カスタムフッターやシートの移動に同期した細かなアニメーション制御が必要なケースでは柔軟性に欠けます。

3) TrueSheet(Jovanni Lo / @lodev09)

細かい制御やアニメーション同期が必要なら TrueSheet が非常に良い選択です。ネイティブ実装でシート背景のブラー(iOS 26 では Liquid Glass)をサポートし、detents、フッター、アニメーション値の取得などができます。

const sheet = useRef<TrueSheet>(null);
<TrueSheet ref={sheet} sizes={[75, "medium", "large"]} blurTint="default">
  <View>
    <Button title="Close Sheet" onPress={() => sheet.current?.dismiss()} />
  </View>
</TrueSheet>
<Button title="Open Sheet" onPress={() => sheet.current?.present()} />

ポイント:

  • シートは ref 経由で制御します(Gorhom の Sheet と同様)。
  • sizes が detents を制御します。数値、パーセンテージ、または "auto", "medium", "large" のようなプリセット文字列を混在して使えます。
  • Auto sizing が完全に安定しない場合は最低高さを明示的に指定(例: 75)してください。
  • Liquid Glass 表現には blurTint="default" を使います。
  • README がまだ最新でない可能性がありますが、ネイティブ API を呼んでいるため効果は問題なく動作します。

結論と推奨

  • 迅速に試したい場合: Expo Router の formSheet は最も手軽で多くのケースに十分です。
  • 細かい制御やアニメーション同期が必要な場合: TrueSheet が最も柔軟で本物に近い表現を提供します。
  • expo-swift-ui はネイティブで直接 Liquid Glass を扱えますが、beta のため本番運用は慎重に。

detents の値は意図的に決めて、OS のレンダリングに任せる(vibe を活かす)ことをおすすめします。

React Native のアニメーションやトリックに興味がある方は、筆者が 𝕏(@iamarunabh)で投稿しているのでそちらもチェックしてください。