BETA

[Flutter] スワイプで項目を削除できるリストの作り方

投稿日:2018-11-12
最終更新:2018-11-12

リスト項目をスワイプできるようにするには、Dismissibleで項目のWidgetをラップする

ListViewに対して「スワイプできるリスト」のような設定は不要で、項目WidgetをDismissibleでラップするだけでスワイプできるようになります。

Dismissibleの使い方は公式ドキュメントを参考にしています。

注意点は次の2つです。

  • Dismissibleのkeyにユニークなキーを設定すること
  • onDismissedですぐに不要になったデータを削除すること

ただし、keyについては項目Widgetを自作してカスタマイズしている場合は、項目Widget自体にユニークなキーを設定している必要があります。

公式ドキュメントのサンプルコードではDismissibleにユニークなキーを設定するとしか書いてないので、ここでハマりました・・・。

設定しないとFrameworkが対象のWidgetをdisposeできないので、保持しているデータと描画内容が不一致を起こします。

項目Widgetを自作している場合のサンプル

ほとんどのアプリでサンプルコードのようなシンプルな構成でリストを作らないと思うので、項目Widgetを自作している場合のサンプルはこちらになります。

item.dart

項目Widgetでは渡されたデータオブジェクトからキーを生成して、キーを自身に設定します。 Dismissibleには、そのキーをそのまま渡します。

typedef OnDismissed(Data data);

class Item extends StatefulWidget {
  final Data data;
  final OnDismissed onDismissed;

  Item(this.data, {
    this.onDismissed,
  }): super(key: ObjectKey(data));  // 指定データオブジェクトから生成したユニークキーを設定

  @override
  State<StatefulWidget> createState() => _Item();
}

class _Item extends State<Item> {
  @override
  Widget build(BuildContext context) {
    return Dismissible(
      key: widget.key,  // Widgetに設定されているキーをそのまま渡す
      child: Container(
        height: 70.0,
        // TODO 項目コンテンツ
      ),
      onDismissed: (direction) {
        if (null != widget.onDismissed) {
          widget.onDismissed(_data);
        }
      },
    );
  }
}

list.dart

リストWidgetでは渡されたデータを元にリストを表示しています。

スワイプイベントが発生した場合、保持しているデータリストから対象のデータを削除して表示内容と整合性を取る必要があります。 削除しないと次の描画更新時に不一致が起きて赤い画面になります。

データベースなどのデータも削除する必要がある場合は、onDismissedコールバックで実施しましょう。

typedef OnDismissed(Data data);

class List extends StatefulWidget {
  final OnDismissed onDismissed;

  List({
    @required Key key,
    this.onDismissed,
  }) : super (key: key);

  @override
  State<StatefulWidget> createState() => ListState();
}

class ListState extends State<List> {
  List<Data> _list;

  update(List<Data> list) {
    setState(() {
      _list = list;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (null == _list) {
      return Center(
        child: CircularProgressIndicator()
      );
    } else if (_list.isEmpty) {
      return Center(
        child: Text('No data.'),
      );
    } else {
      return _buildListView();
    }
  }

  Widget _buildListView() {
    var length = _list?.length ?? 0;
    return ListView.builder(
      itemCount: length,
      itemBuilder: (BuildContext context, int index) {
        if (length <= index) {
          return null;
        } else {
          return Item(_list[index],
            onDismissed: (data) {
              setState(() {
                _list.remove(data);  // スワイプされたらデータリストから削除する
              });
              if (null != widget.onDismissed) {
                widget.onDismissed(data);
              }
            },
          );
        }
      },
    );
  }
}
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@chihara'の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!