🐀

【Flutter Widget】「Dismissible」を学ぼう

2022/11/25に公開

こんにちは、Saeです。
私は「さえないエンジニア」から「さえたエンジニア」へクラスチェンジするために、日々レベル上げに勤しんでます。

今日は「Dismissible」というWidgetについて解説します。

また今回使用しているサンプルコードはGitHubで公開しております。参考になれば幸いです。
https://github.com/Sae-Eng/dismissible_example

目次

忙しい人のための「Dismissible」

日々忙しさで忙殺されているエンジニアへ向けた「Dismissible」の説明です。
ここだけ見れば、なんとなくわかります。

  • 「Dismissible」とは、指定された方向にスライドして、閉じることができるウィジェットのこと

  • 使用すると、以下のような挙動になる

  • 実装は以下

Dismissible(
  // background: 第1背景
  // ・backgroundのみ指定 → 両スワイプ時の背景
  // ・secondaryBackgroundも指定 → 右スワイプ時の背景
  background: Container(
    color: Colors.greenAccent,
    child: const Align(
        alignment: Alignment.centerLeft, child: Icon(Icons.check)),
  ),

  // secondaryBackground: 第2背景
  // ・左スワイプ時の背景
  secondaryBackground: Container(
    color: Colors.red,
    child: const Align(
        alignment: Alignment.centerRight, child: Icon(Icons.delete)),
  ),

  // onDismissed: ウィジェットが閉じられたときに呼び出される
  onDismissed: (DismissDirection direction) {
    // DismissDirection.startToEnd → 右スワイプ背景
    if (direction == DismissDirection.startToEnd) {
      ScaffoldMessenger.of(context)
          .showSnackBar(const SnackBar(content: Text('COMPLETE!')));
    }

    // DismissDirection.endToStart → 左スワイプ背景
    if (direction == DismissDirection.endToStart) {
      ScaffoldMessenger.of(context)
          .showSnackBar(const SnackBar(content: Text('DELETE!')));
    }

    setState(() {
      items.removeAt(index);
    });
  },

  // key: 表示するリストアイテムにキーを付与し、正常にアイテムが置き換わるようにする
  key: ValueKey<int>(items[index]),

  // child: 表示するリストアイテム
  child: ListTile(
    title: Text('Item ${items[index]}'),
  ),
);

Dismissibleとは

それではここから「Dismissible」について詳しく説明します。

そもそも「Dismissible」とは何なのでしょうか。
公式ドキュメントには以下のように記載されてます。

指定された方向にドラッグして閉じることができるウィジェット。

https://api.flutter.dev/flutter/widgets/Dismissible-class.html

公式docに記載の通り、DismissibleというWidgetで囲むと
「指定された方向にスライドして、閉じることができるウィジェット」になります。

Dismissibleの使い方

ではDismissibleの使い方を、実際のコードと合わせて説明します。

0. 事前準備

まずはDismissibleを囲むために必要なクラス、リストアイテムを作成しましょう。

import 'package:flutter/material.dart';

// リストアイテムを置き換えるためにStatefulWidgetで作成
class DismissibleExample extends StatefulWidget {
  const DismissibleExample({Key? key}) : super(key: key);

  
  State<DismissibleExample> createState() => _DismissibleExampleState();
}

class _DismissibleExampleState extends State<DismissibleExample> {
  // 表示するリストアイテムを作成
  List<int> items = List<int>.generate(100, (index) => index);

  
  Widget build(BuildContext context) {
    return Scaffold(
      // 作成したリストアイテムを表示
      body: ListView.builder(
        itemBuilder: (BuildContext context, int index) {
          return ListTile(
              title: Text('Item ${items[index]}'),
          );
        },
      ),
    );
  }
}

ここまで実装すると以下のようにリスト表示されます

1. DismissibleでWidgetを囲む

それではここから実際に「Dismissible」を使っていきましょう。

まずDismissibleを実装する際に、最低限必要になってくるプロパティを使用して実装します。

必要になってくるプロパティは以下です。

プロパティ 内容
background スワイプ時の背景
onDismissed ウィジェットが閉じられたときに呼び出される
key 表示するリストアイテムにキーを付与し、正常にアイテムが置き換わるようにする
child 表示するリストアイテム

以下実装コードになります。


Widget build(BuildContext context) {
return Scaffold(
    // 作成したリストアイテムを表示
    body: ListView.builder(
    itemBuilder: (BuildContext context, int index) {
          return Dismissible(
            // background: スワイプ時の背景
            background: Container(
              color: Colors.greenAccent,
              child: const Align(
                  alignment: Alignment.center, child: Icon(Icons.check)),
            ),

            // onDismissed: ウィジェットが閉じられたときに呼び出される
            onDismissed: (DismissDirection direction) {
              setState(() {
                // スワイプしたリストのアイテムを削除
                items.removeAt(index);
              });
            },

            // key: 表示するリストアイテムにキーを付与し、正常にアイテムが置き換わるようにする
            key: ValueKey<int>(items[index]),

            // child: 表示するリストアイテム
            child: ListTile(
              title: Text('Item ${items[index]}'),
            ),
          );
      },
    ),
  );
}

上記を実装すると以下のような挙動になります。

2. Dismissibleにプロパティを追加する

ただ上記の実装コードだと、挙動的に少し違和感を感じる部分もあるかと思います。

そこで以下の内容を追加で実装していきます。

  • 左スワイプ時には、完了アイコンを表示
  • 右スワイプ時には、削除アイコンを表示
  • 完了、削除時にはスナックバーを表示

追加するには以下のプロパティを追加・編集していきます。

プロパティ 内容
(追加)secondaryBackground 左スワイプ時の背景
(編集)onDismissed ウィジェットが閉じられたときの方向を引数から取得

以下実装コードになります。

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemBuilder: (BuildContext context, int index) {
          return Dismissible(
            // background: 第1背景
            // ・backgroundのみ指定 → 両スワイプ時の背景
            // ・secondaryBackgroundも指定 → 右スワイプ時の背景
            background: Container(
              color: Colors.greenAccent,
              child: const Align(
                  alignment: Alignment.centerLeft, child: Icon(Icons.check)),
            ),

            // secondaryBackground: 第2背景
            // ・左スワイプ時の背景
            secondaryBackground: Container(
              color: Colors.red,
              child: const Align(
                  alignment: Alignment.centerRight, child: Icon(Icons.delete)),
            ),

            // onDismissed: ウィジェットが閉じられたときに呼び出される
            onDismissed: (DismissDirection direction) {
              // DismissDirection.startToEnd → 右スワイプ背景
              if (direction == DismissDirection.startToEnd) {
                ScaffoldMessenger.of(context)
                    .showSnackBar(const SnackBar(content: Text('COMPLETE!')));
              }

              // DismissDirection.endToStart → 左スワイプ背景
              if (direction == DismissDirection.endToStart) {
                ScaffoldMessenger.of(context)
                    .showSnackBar(const SnackBar(content: Text('DELETE!')));
              }

              setState(() {
                items.removeAt(index);
              });
            },

            // key: 表示するリストアイテムにキーを付与し、正常にアイテムが置き換わるようにする
            key: ValueKey<int>(items[index]),

            // child: 表示するリストアイテム
            child: ListTile(
              title: Text('Item ${items[index]}'),
            ),
          );
        },
      ),
    );
  }

上記を実装すると以下のような挙動になります。

まとめ

いかがでしたでしょうか。
Dismissibleを使えば、シンプルなコードで指定された方向へ閉じるウィジェットを作成できることがわかったと思います。

みなさんもDismissibleを活用し、素敵なFlutterライフをお過ごしくださいませ。では〜。

Discussion