【Flutter】グラスモーフィズムを実現する

グラスモーフィズム(Glassmorphism)とはなにか

・2021年のデザイントレンドのひとつ
・曇りガラスのような質感を表現

詳しくは、「【グラスモーフィズム】デザインをつくってみた。」をご参照ください。こちらのデザインをFlutterで再現します。こちらのデザインを再現しているつもりです。大事なことなので、二回言いました。

べた書きで実装

import 'package:flutter/material.dart';

class TestPage extends StatefulWidget {

  const TestPage({Key? key}) : super(key: key);

  @override
  State createState() => _TestPageState();
}

class _TestPageState extends State {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          colors: [
            Color.fromRGBO(250, 86, 146, 0.8),
            Color.fromRGBO(64, 208, 243, 0.8),
          ],
          begin: FractionalOffset.bottomLeft,
          end: FractionalOffset.topRight,
        ),
      ),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: Center(
            child: Container(
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.15),
            border: Border.all(color: Colors.white),
            borderRadius: BorderRadius.circular(32),
          ),
          child: const Padding(
            padding: EdgeInsets.all(80.0),
            child: Text(
              'グラスモーフィズム',
              style: TextStyle(color: Colors.white),
            ),
          ),
        )),
      ),
    );
  }
}

雑な解説

Scaffoldに背景を出す

ScaffoldのbackgourndColorに色を指定すると、その色が背景となる。指定しないと、白背景になる。
transparentを指定すると、透過になり、背景が表示される。

Scaffold(
        backgroundColor: Colors.transparent,

グラデーションの背景を作る

以下のようにすると、グラデーションの背景があるContanerが作れる。左下から右上にグラデーションが変化するようにしてある。
単一色でなく、グラデーションをしている方が、グラスモーフィズムが映えるそうです。
Opacity(透過しない率)を0.8にしたけど、意味ないかなぁ。

Container(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          colors: [
            Color.fromRGBO(250, 86, 146, 0.8),
            Color.fromRGBO(64, 208, 243, 0.8),
          ],
          begin: FractionalOffset.bottomLeft,
          end: FractionalOffset.topRight,
        ),
      ),

グラスモーフィズムのContainerを作る

本記事の肝。Containerを白にして、Opacityを0.15にすることで、背景の色が出るようにして、グラスモーフィズムを表現している。0.1では背景と代わり映えがなく、0.2では白すぎる印象がありました。そのため、0.15にしました。
角丸にしている方がそれっぽいので、BorderRadius.circularを使用している。

Container(
      decoration: BoxDecoration(
        color: Colors.white.withOpacity(0.15),
        border: Border.all(color: Colors.white),
        borderRadius: BorderRadius.circular(32),
      ),

改善

このままでは、グラスモーフィズムのWidgetとして使い回せないため、グラデーションの壁紙と、グラスモーフィズムのContainerを、それぞれ別のWidgetにしました。

import 'package:flutter/material.dart';

class TestPage extends StatefulWidget {

  const TestPage({Key? key}) : super(key: key);

  @override
  State createState() => _TestPageState();
}

class _TestPageState extends State {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return const GradientWallpaper(
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: Center(
            child: GlassmorphismContainer(
          child: Padding(
            padding: EdgeInsets.all(80.0),
            child: Text(
              'グラスモーフィズム',
              style: TextStyle(color: Colors.white),
            ),
          ),
        )),
      ),
    );
  }
}

class GlassmorphismContainer extends StatelessWidget {
  const GlassmorphismContainer({
    Key? key,
    required this.child,
  }) : super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white.withOpacity(0.1),
        border: Border.all(color: Colors.white),
        borderRadius: BorderRadius.circular(32),
      ),
      child: child,
    );
  }
}

class GradientWallpaper extends StatelessWidget {
  const GradientWallpaper({
    Key? key,
    required this.child,
  }) : super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          colors: [
            Color.fromRGBO(250, 86, 146, 0.8),
            Color.fromRGBO(64, 208, 243, 0.8),
          ],
          begin: FractionalOffset.bottomLeft,
          end: FractionalOffset.topRight,
        ),
      ),
      child: child,
    );
  }
}

参考