PR

[Flutter]Flutter Widget of the Week「#3 Wrap」、「#4 AnimatedContainer」、「#5 Opacity」

Flutter

はじめに

前回はFlutter Widget of the Weekの「#1 SafeArea」、「#2 Expanded」を紹介しました。

今回はその続きで「#3 Wrap」、#4 AnimatedContainer」、#5 Opacity」の3つです。

前回の記事はこちら

またコードはGitHubに公開していますので見たい方は自由に使ってください。

GitHub - nobushiueshi/flutter_widget_of_the_week
Contribute to nobushiueshi/flutter_widget_of_the_week development by creating an account on GitHub.

Flutter Widget of the Week

環境

  • Flutter 2.5.3

記事にした時点でのバージョンです。GitHubに公開しているのは常に最新の安定版(stable)を使う予定です。

#3 Wrap

Wrapとは

ウィジェットをレイアウトする時にはRowColumnをよく使いますが、スペース不足になる場合があります。

この問題を解決してくれるのがWrapウィジェットです。

WrapはRowやColumnのように子ウィジェットを順番に配置しますが、縦や横のスペースがなくなった場合は改行(wrap)してくれます。

またdirectionプロパティを使うことで縦横どちらの方向に改行(wrap)するのか設定できます。
alignmentプロパティやspacingプロパティもあります。

WrapはダイアログのボタンChip系ウィジェットと組み合わせると良いそうです。

サンプルコード

import 'package:flutter/material.dart';

class SamplePage003 extends StatelessWidget {
  const SamplePage003({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Wrap'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('Row'),
              Container(
                color: Colors.lightBlue,
                child: Row(
                  children: const [
                    _SamplePage003Child(),
                    _SamplePage003Child(),
                    _SamplePage003Child(),
                    _SamplePage003Child(),
                  ],
                ),
              ),
              const SizedBox(height: 30),
              const Text('Wrap'),
              Container(
                color: Colors.lightBlue,
                child: Wrap(
                  direction: Axis.horizontal,
                  alignment: WrapAlignment.center,
                  spacing: 10,
                  runSpacing: 10,
                  children: const [
                    _SamplePage003Child(),
                    _SamplePage003Child(),
                    _SamplePage003Child(),
                    _SamplePage003Child(),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _SamplePage003Child extends StatelessWidget {
  const _SamplePage003Child();

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: Colors.red,
    );
  }
}

結果

Rowを使った場合は「A RenderFlex overflowed by 10.0 pixels on the right.」というエラーが出ますが、Wrapを使った場合は自動で改行してくれています。

動画

公式リファレンス

Wrap class - widgets library - Dart API
API docs for the Wrap class from the widgets library, for the Dart programming language.

#4 AnimatedContainer

AnimatedContainerとは

Flutterには開発者が自分でコーディングする明示的アニメーション用のウィジェットが沢山ありますが
自動で変化する暗示的アニメーションウィジェットもあります。

暗示的アニメーションに便利なのがAnimatedContainerウィジェットです。
色などの属性を加えて一度作成するだけで簡単に使えます。

例えばsetStateの呼び出しに対応させる形で値を書き換えクラスをビルドさせると
AnimatedContainerがその二つの値の線形補間を行います。

colorだけでなく、border,border radius,background,images,shadows,gradients,shapes,padding,width,height,alignment,transformsなど色々なプロパチhあります。
アニメーションの長さはdurationプロパティで変更し、Curveを設定するとエフェクトをカスタマイズすることも出来ます。

サンプルコード

import 'package:flutter/material.dart';

class SamplePage004 extends StatefulWidget {
  const SamplePage004({
    Key? key,
  }) : super(key: key);

  @override
  _SamplePage004State createState() => _SamplePage004State();
}

class _SamplePage004State extends State<SamplePage004> {
  bool toggle = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('AnimatedContainer'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('Container'),
              Container(
                color: toggle ? Colors.red : Colors.blue,
                width: toggle ? 100 : 150,
                height: 150,
                child: const Icon(
                  Icons.face,
                  color: Colors.white,
                ),
              ),
              const SizedBox(height: 50),
              const Text('AnimatedContainer'),
              AnimatedContainer(
                duration: const Duration(milliseconds: 500),
                curve: Curves.easeIn,
                color: toggle ? Colors.red : Colors.blue,
                width: toggle ? 100 : 150,
                height: 150,
                child: const Icon(
                  Icons.face,
                  color: Colors.white,
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            toggle = !toggle;
          });
        },
        child: const Icon(Icons.change_circle),
      ),
    );
  }
}

結果

Containerの場合、パッと切り替わるのに対して、AnimatedContainerだとアニメーションしながらキレイに切り替わりました。

動画

公式リファレンス

AnimatedContainer class - widgets library - Dart API
API docs for the AnimatedContainer class from the widgets library, for the Dart programming language.

#5 Opacity

Opacityとは

ウィジェット削除する場合はウィジェットなしでビルドし直すだけです。
しかしウィジェットを非表示にしても領域を確保し、他のレイアウトを保ちたい場合にはOpacityウィジェットを使います。

ウィジェットをOpacityで囲んでopacityプロパティの値を0に設定するだけです。

またOpacityを使ってStackの子を重ねて一体化することができます。
表示変化をアニメーションさせたければAnimatedOpacityを使います

durationとopacityプロパティを設定して、ビルドし直せば自動的にアニメーションします。

サンプルコード

import 'package:flutter/material.dart';

class SamplePage005 extends StatefulWidget {
  const SamplePage005({Key? key}) : super(key: key);

  @override
  _SamplePage005State createState() => _SamplePage005State();
}

class _SamplePage005State extends State<SamplePage005> {
  bool toggle = false;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Opacity'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('Opacityなし'),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const _SamplePage005Child(),
                  if (!toggle) const _SamplePage005Child(),
                  const _SamplePage005Child(),
                ],
              ),
              const Text('Opacityあり'),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const _SamplePage005Child(),
                  Opacity(
                    opacity: toggle ? 0 : 1,
                    child: const _SamplePage005Child(
                      color: Colors.red,
                    ),
                  ),
                  const _SamplePage005Child(),
                ],
              ),
              const Text('Stackを利用したサンプル(AnimatedOpacity)'),
              Stack(
                children: [
                  const FlutterLogo(
                    size: 100,
                  ),
                  AnimatedOpacity(
                    opacity: toggle ? 1 : 0,
                    duration: const Duration(milliseconds: 500),
                    child: const _SamplePage005Child(),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.change_circle),
        onPressed: () {
          setState(() {
            toggle = !toggle;
          });
        },
      ),
    );
  }
}

class _SamplePage005Child extends StatelessWidget {
  const _SamplePage005Child({
    this.color = Colors.blue,
  });

  final Color color;
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: color,
    );
  }
}

結果

Opacityなしの場合は青色ウィジェットのレイアウトが変わってしまいますが、Opacityありだと変わらずにすることができます。

またAnimatedOpacity使うことで画像のフェードを再現することもできそうです。

動画

公式リファレンス

Opacity class - widgets library - Dart API
API docs for the Opacity class from the widgets library, for the Dart programming language.

さいごに

使ったことのないWidgetがあると新しい発見があって楽しい。

おすすめ参考書

リンク

GitHub - nobushiueshi/flutter_widget_of_the_week
Contribute to nobushiueshi/flutter_widget_of_the_week development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました