Flutter の Liquid Glass 対応について SNS ではいろいろ言われたり言ったりしていますが、それはそれとして「Flutter でも Liquid Glass してみたい!」ということで、liquid_glass_renderer
パッケージで少し遊んでみました。
いったん出来上がったものがこちらです。
自作パッケージの crop_your_image と組み合わせて、ガラス風の画像切り抜き画面を作ってみました。どうでしょう?
ということで、この記事では liquid_glass_renderer パッケージの紹介をしながら Flutter の Liquid Glass 対応の状況(2025.06.20 現在)を整理してみたいと思います。
なお、Flutter の Liquid Glass に対する議論はこちらの issue を追っていただくのが手っ取り早いと思います。
liquid_glass_renderer パッケージ
Flutter フレームワークは元々プラットフォーム固有のデザインを再現することをあまり重視していません。そのため、Liquid Glass についても「フレームワーク本体としては」対応には消極的です。
たしかに、仮に本体で対応されたとしても実際にアプリでそれを使おうと思った時、「じゃあ Android はどうするの?」という問題が出ることは簡単に予想できますので、Flutter チームが頑張って対応してもあまり使われないことが予想されます。そう考えるとこの方針は妥当だと個人的に思えます。
いっぽうだからと言って Flutter ではずっと Liquid Glass 風の UI が実現できないかというとそうではありません。(詳しい仕組みは理解できていないですが)シェーダーを駆使して Liquid Glass を再現しようという取り組みがコミュニティ内で盛り上がっており、今回紹介する liquid_glass_renderer パッケージなどはいち早くそれをパッケージとして触れるように作り込んでくれました。ありがたや。
liquid_glass_renderer
を使うことで、任意の円形、四角形のガラス風 Widget を配置することができたり、さらには複数のガラスを近づけるときれいに溶け込んでひとつになるような動きをさせたり、テキスト自体をガラス風にしたりとさまざまなことが可能です。
今回はこれを使って、画像切り抜きの UI をガラス風にしてみました。
liquid_glass_renderer の使い方
では使い方を見ていきましょう。一点注意ですが、このパッケージはまだ開発中ですので、今後使い方が大きく変わる可能性もあります。この記事ではバージョン 0.1.1-dev.9
時点での使い方を前提に説明していきます。
LiquidGlass を配置する
もっともシンプルな使い方は LiquidGlass
という Widget を配置することです。
return LiquidGlass(
shape: LiquidRoundedSuperellipse(
borderRadius: Radius.circular(20),
),
child: SizedBox(width: 160, height: 160),
),
ただし、基本的に LiquidGlass
は透明ですので、無地の背景にこれを配置してもほぼ何も見えません。ドキュメントでも Stack
を使ってアプリの画面に重ねて使うように説明しています。
今回は、適当な画像を背景に LiquidGlass
を配置してみます。
return Scaffold(
body: Stack(
children: [
Positioned.fill(
child: Image.asset('assets/photo.jpg', fit: BoxFit.cover),
),
Center(
child: LiquidGlass(
shape: LiquidRoundedSuperellipse(
borderRadius: Radius.circular(20),
),
child: SizedBox(width: 160, height: 160),
),
),
],
),
),
実行すると、以下のような画面が表示されます。
中心にそれらしい枠が出ているような気がしますが、まだ若干分かりづらいです。
Liquid Glass
には settings
という引数が用意されていて、これを使ってガラスの見た目をいろいろと調整できるようになっています。
今回は見た目に分かりやすいよう少し大げさに設定してみて、その変化を確認してみたいと思います。
child: LiquidGlass(
settings: LiquidGlassSettings(
blur: 2, // 背景をぼかす
glassColor: Colors.white.withAlpha(40), // ガラス自体に色をつける
lightAngle: 0.25 * pi, // 光を斜め45度からあてる
lightIntensity: 20, // 光を少し強めにする
thickness: 40, // ガラスの縁を太くする
),
shape: LiquidRoundedSuperellipse(
borderRadius: Radius.circular(20),
),
child: SizedBox(width: 200, height: 200),
),
だいぶ目に見えるようになってきました。
LiquidGlass を動かす
LiquidGlass
のエフェクトはコンテンツが動いているときにとても目立ちます。
今回は画像を InteractiveViewer
に乗せることで、ズームしたり動かしたりできるようにしてみます。
Positioned.fill(
child: InteractiveViewer(
child: Image.asset('assets/crop.jpg', fit: BoxFit.cover),
),
),
背景が動いているとだいぶ分かりやすいですね!
画像切り抜き UI に組み込む
さらに、Liquid Glass
と crop_your_image を組み合わせてガラス風の画像切り抜き UI を作ってみたいと思います。
まずは Stack
の中に crop_your_image の Crop
を配置します。1
Positioned.fill(
child: Crop(
controller: _controller, // 切り抜き処理を呼び出すコントローラー
image: image, // 画像
interactive: true, // ズームで画像を動かせるように
initialRectBuilder: InitialRectBuilder.withSizeAndRatio(
size: 0.5, // 画面サイズの半分の幅、高さで
aspectRatio: 1, // 正方形で
),
fixCropRect: true, // 切り抜きエリア自体は動かせないようにする
radius: 20, // 丸角にする
onMoved: (viewportRect, _) {
setState(() => _cropRect = viewportRect), // 切り抜き範囲を覚えておく
},
),
),
Crop
は onMoved
で現在の切り抜き範囲を表す Rect
を通知してくれるので、これを State に保持しておきます。
次に、先ほどの LiquidGlass
を onMoved
で受け取った切り抜き範囲にちょうど重なる位置に配置します。
if (_cropRect != null) Positioned(
left: _cropRect!.left, // x座標を合わせる
top: _cropRect!.top, // y座標を合わせる
child: LiquidGlass(
... 細かい引数は省略
child: SizedBox(
width: _cropRect!.width, // 幅を合わせる
height: _cropRect!.height, // 高さを合わせる
),
),
),
こうすることで、切り抜き範囲にぴったりとガラスが乗り、ガラス風の UI で切り抜き範囲を選択できます。
この際切り抜きボタンも LiquidGlass
で作ってしまいましょう。Stack
にさらにボタンを重ねます。
Center(
child: LiquidGlass(
glassContainsChild: false, // child がガラスの上に乗る(ガラスのエフェクトを受けない)ようにする
shape: LiquidOval(), // 円形のガラス
child: GestureDetector(
onTap: _controller.crop, // タップしたら切り抜き実行
child: SizedBox(
height: 120,
width: 120,
child: Center(
child: Icon( // 切り抜きアイコン
Icons.cut,
size: 40,
color: Colors.black87,
),
),
),
),
),
),
あとはもろもろの切り抜き前後の処理(画像を読み込む、切り抜いたら UI を変える、など)を整えると、冒頭のような一連のガラス風画像切り抜き画面が完成です。
できあがったコードはこちらで公開しました。main.dart
の 200 行ほどでzですので、もし興味があれば見てみてください。
まとめや諸注意など
「Flutter は Liquid Glass に対応できない」とは言われていますが(誤解も含まれますが)、同じような表現を再現しようとする取り組みはすでにかなり進んでいます。
ただし、これがあればネイティブと同等の UI が作れるかどうかはまた別の話です。
端末の傾きを利用した反射表現など原理的に再現できない表現もあるかもしれませんし、エフェクトやコンポーネントが再現できたとしても Apple のデザイン思想を理解しないまま使うと「それっぽい別物」感がどうしても出てしまいます。(同じ問題は Liquid Glass 以前の Material や Cupertino でも発生しますが)
今回紹介した liquid_glass_renderer
はあくまで「Liquid Glass と同じようなエフェクトを Widget に与える」パッケージであり、これを利用して Apple のコンポーネントを再現したければまたそれを研究した別パッケージが必要になりそうです。
ということで、これで Flutter もネイティブの Liquid Glass コンポーネント(と同等の Widget)が使える、と言えるまでにはまだいくつかハードルがありそうですが、とはいえ同じような表現が全くできないわけではないこともこの記事で伝わったのではないかと思います。
なお、この表現は Impeller に用意された機能を利用しているため、Impeller に対応していない Web やデスクトップではパッケージ自体が使えないことに注意してください。Android は Impeller に対応していますが、別途動作確認してみます。
ということで、Flutter で Liquid Glass 風の UI を作って遊んでみた、という記事でした。実際にプロダクトで取り入れるかどうかは置いておいて、こういう取り組みはシンプルに楽しいですね!
-
crop_your_image の細かい使い方はこの記事では割愛します。詳しくは Readme 等をご参照ください。 ↩