SODA Engineering Blog
🌀

より良いユーザー体験を求めて "角丸" を深掘りする

2024/07/18に公開2

先日、他社のFlutter製のアプリを触っていて、よくできているなーと感心していました。
いい意味でFlutter感がないなと。

しかし、そのことをデザイナーの友だちに伝えたところ、「まだFlutter感ある!」と言っていたのです。
さすがデザイナーだなと感心していたのですが、その視点はどこに向けられているのかを深掘りしてみました。

角丸に現れるFlutterっぽさ

この2つのオブジェクト、よく見ると微妙に角丸が違うのがわかりますでしょうか。
右の方が優しい印象を受けます。
この2つの違いはCorner smoothingを取り入れているかどうかになります。

もうちょいわかりやすいようにオブジェクトを重ねてみました。
どちらもRadiusは20pxですが、青色の方ははCorner smoothingを取り入れています。
青色の方が丸くなっていく角度が滑らかになっています。

角を滑らかにするCorner smoothing

Corner smoothingとは普通の角丸よりも滑らかにする技術で、iOS7のアプリアイコンから導入されました。
人間にとって尖っているものは触ると痛いため、嫌悪感を抱きます。
デジタルでも同じ印象を受けます。そのため角丸をつけて柔らかい印象を作ります。
Corner smoothingを使うことで、より柔らかく、優しい印象の見た目にすることが可能です。

この方法を知り、いろんな"角丸"のスクショを撮るようになったのですが、Corner smoothingを使っているもの、使っていないもの様々で面白いです。

Corner smoothingを使っている"角丸"


ChatGPTのメッセージ入力TextField


iOS純正のリマインダーアプリ


Arcのタブ

Corner smoothingではないもの


Slackのチャンネル名


ElevatedButton

RoundedRectangleBorderはCorner smoothingではない

先ほどのデザイナーが感じるFlutterぽさとはCorner smoothingにありました。
Flutterで角丸を作る際によく使うRoundedRectangleBorderはCorner smoothingではありません。
そのため、Corner smoothingを実装するには他の方法を取る必要があります。
逆にSwiftUIを使えば、デフォルトでCorner smoothingが採用されているため、意識せずに使うことができます。
(SwiftUIに詳しいわけではないので間違っていたら指摘ください)

ElevatedButton(
  child: const Text('Button'),
  style: ElevatedButton.styleFrom(
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10),
    ),
  ),
  onPressed: () {},
),

Flutter標準の方法では微妙...

Flutter標準のWidgetでCorner smoothingを作る方法は存在しています。
それがContinuousRectangleBorderです。
しかしContinuousRectangleBorderをそのまま使うと同じRadiusのpxを指定しても、全く異なった見た目になってしまいます。

以下はRoundedRectangleBorderContinuousRectangleBorderを同じ10指定で実装したボタンです。
下がContinuousRectangleBorderです。
そもそも全然見た目が違うことがわかります。


同じ10pxを指定した場合でも見た目が大きく違う。(下がContinuousRectangleBorder)

ElevatedButton(
  child: const Text('Normal'),
  style: ElevatedButton.styleFrom(
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10),
    ),
  ),
  onPressed: () {},
),
const SizedBox(height: 8),
ElevatedButton(
  child: const Text('Corner smoothing'),
  style: ElevatedButton.styleFrom(
    shape: ContinuousRectangleBorder(
      borderRadius: BorderRadius.circular(10),
    ),
  ),
  onPressed: () {},
),

打開策はある。

ContinuousRectangleBorderをそのまま使うことはできませんが、他のの方法を検討してみます。

方法1: ContinuousRectangleBorder x 2.3529

ContinuousRectangleBorderをそのまま使うことはできませんが、指定するRadiusにx 2.3529してあげると綺麗な形に近づくそうです。

ElevatedButton(
  child: const Text('Corner smoothing'),
  style: ElevatedButton.styleFrom(
    shape: ContinuousRectangleBorder(
      // ここ
      borderRadius: BorderRadius.circular(10 * 2.3529),
    ),
  ),
  onPressed: () {},
),

参考: https://github.com/rydmike/squircle_study?tab=readme-ov-file#continuousrectangleborder-x-23529

方法2: package①(figma_squircle)

その他の方法としてPackageを使うこともできます。
Corner smoothingを実現するpackageでメジャーなものとしてFigmaSquircleがあります。
デザインツールのFigmaでもCorner smoothingを設定できるのですが、Figmaの計算方法に合わせた実装がされているみたいです。

使う際の懸念としてはRadiusに指定するpxが大きすぎるとレイアウトが崩れるみたいです。
なので崩れない範囲で使用する必要があります。


30を指定した場合に崩れている

ElevatedButton(
  child: const Text('Corner smoothing'),
  style: ElevatedButton.styleFrom(
    shape: SmoothRectangleBorder(
      borderRadius: SmoothBorderRadius(
        cornerRadius: 10,
        cornerSmoothing: 0.6,
      ),
    ),
  ),
  onPressed: () {},
),

https://pub.dev/packages/figma_squircle

方法3: package②(smooth_corner)

他のpackageにsmooth_cornerというのもあります。
こちらはfigma_squircleのようは崩れはありません。
懸念点としてはlerpが実装されていないため、アニメーションに対応していない点が挙げられます。

その他の方法

他にも色々な方法を検討しているGitHubのリポジトリがあり、導入する際に参考になりそうだなと思ったので貼っておきます。
https://github.com/rydmike/squircle_study

それぞれの方法でメリット・デメリットがあるので、検討して導入するのが良さそうです。

最後に

Corner smoothingを導入するべきかはプロジェクトによると思いますが、エンジニアから「Flutterでもこんなことができますよ!」とデザイナーに提案できるとより良い議論ができるのではないでしょうか。

Flutterは特にiOS周りに詳しいデザイナーから嫌厭されがちですが、工夫すればできることはたくさんあります。
もちろんそのためにはエンジニアがよりデザインを理解し、デザイナーに歩み寄る必要があります。

他にも深掘り記事を出しているのでよかったらみていってください。
そのついでに記事にいいねをしてくれると嬉しいです。

https://zenn.dev/imajoriri/articles/3d003d05b16b69
https://zenn.dev/imajoriri/articles/60c886585bc4c4

Corner smoothingについてもっと知りたい方へ

https://github.com/flutter/flutter/issues/91523

https://help.figma.com/hc/en-us/articles/360050986854-Adjust-corner-radius-and-smoothing
https://spinners.work/posts/kudakurage-superellipse-desgin/
https://note.com/ajike_saito/n/na33b5ae607d0
https://webflow.com/blog/squircle-vs-rounded-squares
https://www.flaticon.com/blog/the-meaning-of-round-corners/
https://medium.com/minimal-notes/rounded-corners-in-the-apple-ecosystem-1b3f45e18fcc

SODA Engineering Blog
SODA Engineering Blog

Discussion

unoswunosw

普段なかなか意識することがない (or 優先度が下がりがち) ですが、こういう細部のクオリティも大事にしていきたいですね!

SwiftUIを使えば、デフォルトでCorner smoothingが採用されているため、意識せずに使うことができます。
(SwiftUIに詳しいわけではないので間違っていたら指摘ください)

SwiftUI ではおっしゃる通りデフォルトで continuous スタイルが適用されますし、意図的に circular にすることも可能です (こういうところは Apple らしいですね) 。

https://developer.apple.com/documentation/swiftui/roundedcornerstyle

struct ContentView: View {
    var body: some View {
        HStack {
            RoundedRectangle(cornerRadius: 30)
            // RoundedRectangle(cornerRadius: 30, style: .continuous)
                .foregroundStyle(.blue)
                .frame(width: 150, height: 150)
                .overlay {
                    Text("Continuous")
                }
            
            RoundedRectangle(cornerRadius: 30, style: .circular)
                .foregroundStyle(.blue)
                .frame(width: 150, height: 150)
                .overlay {
                    Text("Circular")
                }
        }
    }
}

Imajo / FlutterImajo / Flutter

ありがとうございます!
やはりデフォルトで対応されているのは良いですね...!

SwiftUI周りの知見が全くないので、助かります!!❤️