1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ニューモフィックな入力欄をflutterで作る

Last updated at Posted at 2026-01-09

発端

人類「ニューモーフィズムなUI欲しい」
蝦「はい」

ニューモーフィズム(Neumorphism)とは

ニューモーフィズムは、2020年頃から注目されるようになったUIデザインのスタイル。
AI曰く

「ソフトな影効果を使用して、要素があたかも表面から浮き出ているような、あるいは押し込まれているような立体的な表現を実現します。通常、背景色と似た色調で、微妙な明暗の差を付けることで、この効果を生み出します。」
(Claude3.5)

具体例

Skeuomorph Mobile Banking

やり方

まず実装が簡単なHTMLとCSSで実装し、それをFlutter上で再現するという流れでやってみる。(FlutterはWebアプリとしてビルドもできるのだから、WebでできるならFlutterでもできるのでは?という考え。)

HTMLとCSSでの実装

上で取り上げたSkeuomorph Mobile Bankingでは、右上からページ内の要素に光を当てた状態を表現している。そのため光が当たる部分を明るく、当たっていない部分を暗く見せることができれば凹凸を表現できる。

image.png

背景色

背景が白だと光が当たっている面を表現することが難しくなるため、ニューモフィックなUIは背景色を白ではなく灰色がかった感じの色にすると実装しやすい。

        body {
            background-color: #e0e5ec;
        }

要素が飛び出す場合

この場合は左上が明るく、右下が暗くなれば良い。

        .button-raised {
            box-shadow: 
                6px 6px 12px #b8bec7,
                -6px -6px 12px #ffffff;
            transition: all 0.2s ease;
        }

要素が凹む場合

逆に凹むこの場合は左上が暗く、右下が明るくなれば良い。ただし影が要素の内側に発生する。

        .button-pressed {
            box-shadow: 
                inset 6px 6px 12px #b8bec7,
                inset -6px -6px 12px #ffffff;
        }

Flutterでの実装

上記の方法をFlutterで再現してみる。

既存のパッケージを使う

すでにパッケージが存在する。

clay containerはともかく、flutter_neumorphicは最終更新から3年経過しているため今後使うのは怖い。

パッケージを使わずに実装

飛び出している場合

ContainerでラップしてCSSのbox-shadowをそのまま再現すれば表現可能。

class RaisedButton extends StatelessWidget {
  const RaisedButton({
    required this.onPressed,
    required this.text,
    super.key,
  });
  final VoidCallback onPressed;
  final String text;

  @override
  Widget build(BuildContext context) {
    const double distanceValue = 4;
    const double blur = 8;
    return Container(
      height: 64,
      width: double.infinity,
      decoration: BoxDecoration(
        color: backgroundColor,
        borderRadius: BorderRadius.circular(12),
        boxShadow: const [
          BoxShadow(
            color: highlightColor,
            offset: Offset(distanceValue, distanceValue),
            blurRadius: blur,
          ),
          BoxShadow(
            color: shadowColor,
            offset: Offset(-1 * distanceValue, -1 * distanceValue),
            blurRadius: blur,
          ),
        ],
      ),
      child: ElevatedButton(
        style: ButtonStyle(
          backgroundColor: WidgetStateProperty.all(backgroundColor),
          overlayColor: WidgetStateProperty.all(Colors.transparent),
          shadowColor: WidgetStateProperty.all(Colors.transparent),
          elevation: WidgetStateProperty.all(0),
          shape: WidgetStateProperty.all(
            RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12),
            ),
          ),
        ),
        onPressed: onPressed,
        child: Text(text),
      ),
    );
  }
}
凹んでいる場合

基本は飛び出ている場合と同じで、内側に影を作る必要がある。
標準のBoxShadowでは内側に影をつけることができないため、flutter_inset_shadowを用いる。

class PushedButton extends StatelessWidget {
  const PushedButton({
    required this.onPressed,
    required this.text,
    super.key,
  });
  final VoidCallback onPressed;
  final String text;

  @override
  Widget build(BuildContext context) {
    const double distanceValue = 4;
    const double blur = 8;
    return Container(
      height: 64,
      width: double.infinity,
      decoration: BoxDecoration(
        color: backgroundColor,
        borderRadius: BorderRadius.circular(12),
        boxShadow: const [
          BoxShadow(
              color: highlightColor,
              offset: Offset(distanceValue, distanceValue),
              blurRadius: blur,
              inset: true // 影を内側にする
              ),
          BoxShadow(
              color: shadowColor,
              offset: Offset(-1 * distanceValue, -1 * distanceValue),
              blurRadius: blur,
              inset: true // 影を内側にする
              ),
        ],
      ),
      child: ElevatedButton(
        style: ButtonStyle(
          backgroundColor: WidgetStateProperty.all(Colors.transparent), // 影が見えなくなるため透明にする
          overlayColor: WidgetStateProperty.all(Colors.transparent),
          shadowColor: WidgetStateProperty.all(Colors.transparent),
          elevation: WidgetStateProperty.all(0),
          shape: WidgetStateProperty.all(
            RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12),
            ),
          ),
        ),
        onPressed: onPressed,
        child: Text(text),
      ),
    );
  }
}

複数の影(+内容に応じて丸み)を付ける事が可能なら凹凸を表現できるため、Containerでラップ可能なウィジェットであれば大体がニューモフィックなUIとして実装できるのでは?と考え中
image.png

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?