🪐

Flutter neumorphic designs

2024/05/09に公開

📕Overview

ニューモーフィズム(Neumorphism)とは、立体感と質感を備えた「スキューモーフィズム」と、シンプルでミニマムな「フラットデザイン」や「マテリアルデザイン」を組み合わせたデザイン手法です。

https://note.com/hironobukimura/n/n0431c73714e8

https://fastcoding.jp/blog/all/info/neumorphism/


時々見かける立体的なUI/UXデザインをFlutterでやってみたいと思った。Dartのパッケージがあるようなので、使ってみようと思います。

https://pub.dev/packages/clay_containers

とはいえこれはしばらくメンテナンスされていなかったようだ💦

🧷summary

main.dartを修正してコンポーネントを別にファイル作成して読み込んでみよう。

main.dart
import 'package:clay_containers_example/my_example.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo'),
      ),
      body: Center(
        child: MyExampleScreen(),
      ),
    );
  }
}

🏺シンプルな粘土の容器

最良の結果を得るには、周囲のウィジェットの背景色を、粘土コンテナに設定する色と一致するように設定します。この基本色を複数回再利用する可能性が高いため (特に、何か派手なことをする場合には)、この色を 1 つの値に設定することをお勧めします。次の例では、baseColor に設定されています。

立体的な正方形

import 'package:clay_containers/clay_containers.dart';
import 'package:flutter/material.dart';

class MyExampleScreen extends StatelessWidget {
  const MyExampleScreen({super.key});

  
  Widget build(BuildContext context) {
    Color baseColor = const Color(0xFFF2F2F2);

    return Container(
        color: baseColor,
        child: Center(
          child: ClayContainer(
            color: baseColor,
            height: 200,
            width: 200,
          ),
        ),
      );
  }
}

ClayContainer with a ClayText Child.

前の例では、ClayContainer には子がないため、高さと幅が指定されています。 ClayContainer は通常の Container と同じように動作し、高さと幅を指定するか、子を表示する必要があります。次の例では、ClayContainer が子を受け取ります。

受け取る子は、何らかの Padding でラップされた ClayText です。

import 'package:clay_containers/clay_containers.dart';
import 'package:flutter/material.dart';

class MyExampleScreen extends StatelessWidget {
  const MyExampleScreen({super.key});

  
  Widget build(BuildContext context) {
    Color baseColor = const Color(0xFFF2F2F2);

    return Container(
        color: baseColor,
        child: Center(
          child: ClayContainer(
          color: baseColor,
          child: Padding(
            padding: EdgeInsets.all(20),
            child: ClayText("Seize the Clay!", emboss: true, size: 40),
          ),
        ),
        ),
      );
  }
}

Rounded ClayContainers

四角くならないでください! borderRadius を使用してフレアを追加します。均一な borderRadius が必要な場合は、ClayContainer コンストラクターで直接設定するだけです。

公式のサンプルは、borderRadiusが50だったが、100にしないと丸にならなかった😅

import 'package:clay_containers/clay_containers.dart';
import 'package:flutter/material.dart';

class MyExampleScreen extends StatelessWidget {
  const MyExampleScreen({super.key});

  
  Widget build(BuildContext context) {
    Color baseColor = const Color(0xFFF2F2F2);

    return Container(
        color: baseColor,
        child: Center(
          child: ClayContainer(
          color: baseColor,
          height: 150,
          width: 150,
          borderRadius: 100,
        ),
      ),);
  }
}

emboss: trueのパラメーターを追加すると丸が凹んだデザインになります。

import 'package:clay_containers/clay_containers.dart';
import 'package:flutter/material.dart';

class MyExampleScreen extends StatelessWidget {
  const MyExampleScreen({super.key});

  
  Widget build(BuildContext context) {
    Color baseColor = const Color(0xFFF2F2F2);

    return Container(
        color: baseColor,
        child: Center(
          child: ClayContainer(
          emboss: true,// add
          color: baseColor,
          height: 150,
          width: 150,
          borderRadius: 100,
        ),
      ),);
  }
}

🧑‍🎓thoughts

以前から時々見かける立体的なUI/UXデザインを作ってみたいと思いました。まさかパッケージを使えばできるとは。でも待って。これ長いことメンテナンスされてなかった。もしまたされなくなったらどうすればいいのか?
自作する方法はあるはず。

やり方

clay_containersパッケージを使わずに、BoxDecorationとBoxShadowを使って凹んだ丸を作ることができます。以下にそのコードを示します。

https://api.flutter.dev/flutter/painting/BoxDecoration-class.html

https://api.flutter.dev/flutter/painting/BoxShadow-class.html

import 'package:flutter/material.dart';

class ShowContainer extends StatelessWidget {
  const ShowContainer({super.key});

  
  Widget build(BuildContext context) {
    Color baseColor = const Color(0xFFF2F2F2);

    return Container(
      color: baseColor,
      child: Center(
        child: Container(
          height: 150,
          width: 150,
          decoration: BoxDecoration(
            color: baseColor,
            borderRadius: BorderRadius.circular(100),
            boxShadow: [
              BoxShadow(
                color: Colors.grey.withOpacity(0.5),
                spreadRadius: 5,
                blurRadius: 7,
                offset: const Offset(-3, -3), // changes position of shadow
              ),
              const BoxShadow(
                color: Colors.white,
                spreadRadius: 5,
                blurRadius: 7,
                offset: Offset(3, 3), // changes position of shadow
              ),
            ],
          ),
        ),
      ),
    );
  }
}

このコードでは、BoxDecorationとBoxShadowを使って、ClayContainerのemboss効果を再現しています。BoxShadowは2つ定義されており、一つは灰色の影で、もう一つは白色の影です。これにより、凹んだ効果が生まれます。

立体的なフォームを作る

同じように立体的な入力フォームを作ることもできます。

import 'package:flutter/material.dart';

class BoxShowForm extends StatelessWidget {
  const BoxShowForm({super.key});

  
  Widget build(BuildContext context) {
    Color baseColor = const Color(0xFFF2F2F2);

    return Container(
      color: baseColor,
      child: Center(
        child: Container(
          height: 60,
          width: 300,
          decoration: BoxDecoration(
            color: baseColor,
            borderRadius: BorderRadius.circular(30),
            boxShadow: [
              BoxShadow(
                color: Colors.grey.withOpacity(0.5),
                spreadRadius: 5,
                blurRadius: 7,
                offset: const Offset(-3, -3), // changes position of shadow
              ),
              const BoxShadow(
                color: Colors.white,
                spreadRadius: 5,
                blurRadius: 7,
                offset: Offset(3, 3), // changes position of shadow
              ),
            ],
          ),
          child: const TextField(
            decoration: InputDecoration(
              border: InputBorder.none,
              contentPadding: EdgeInsets.all(10),
            ),
          ),
        ),
      ),
    );
  }
}

このコードでは、TextFieldウィジェットをContainerウィジェットの中に配置し、Containerウィジェットに影をつけることで凹んだ効果を作り出しています。また、TextFieldのdecorationプロパティを使って、入力フォームの内側の余白を調整しています。

こちらに使用したコードがあります

Discussion