[Flutter] List からデータをアニメーション付き削除する方法(Dismissible, AnimatedList)

[Flutter] List からデータをアニメーション付き削除する方法(Dismissible, AnimatedList)

Flutter の List でデータをアニメーション付きで削除したい

Flutter には一覧表示のための List というウィジェットがあります。これのデータをいい感じにアニメーション付きで削除する方法をまとめます。

3つの方法を簡単にまとめます。リストのアイテムをスワイプ(スライド)して消すUIと、スワイプしたら削除ボタンが出てきてくるUI、それから任意のイベントでリストからアニメーション付きで削除する方法の3つです。

スワイプして削除する(Dismissible)

Implement Swipe to Dismiss – Flutter

Dismissible は指定の方向にスワイプすることで子要素を破棄するウィジェットです。スワイプして消えた後、高さ分だけ全体がアニメーションにより調整されます。

上のページに Flutter 公式のサンプルがありますのでそちらも参考にどうぞ。

上のようなイメージです。この機能を実装するサンプルコードは以下の通りです。

サンプル

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Delete List Item',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SamplePage1(),
    );
  }
}

class SamplePage1 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => SamplePage1State();

}

class SamplePage1State extends State<SamplePage1> {
  final _data = ['data1', 'data2', 'data3', 'data4', 'data5'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sample Page 1')),
      body: ListView(
        children: _data.map((text) {
          return Dismissible(
            key: Key(text),
            child: ListTile(
              title: Text(text),
            ),
            background: Container(color: Colors.red),
            confirmDismiss: (direction) async {
              // ここで確認を行う
              // Future<bool> で確認結果を返す
              // False の場合削除されない
              return text != 'data3';
            },
            onDismissed: (direction) {
              // 削除アニメーションが完了し、リサイズが終了したときに呼ばれる
              setState(() {
                _data.removeWhere((t) => t == text);
              });
            },
          );
        }).toList(),
      ),
    );
  }
}

上の例は “data3″ だけ削除できないようにした単純なリストです。”data3” だけ削除できずにアニメーションが戻っているのがわかります。

サンプルのため処理を簡略化していますが、確認のダイアログを出したり、様々なカスタマイズが可能です。

通常のリスト表示から変わっている点は1つ、ListView の子要素の ListItem を Dismissible でラップしている点です。

Dismissible はスライドすることでデータを削除することができるウィジェットです。これだけで削除がいい感じにアニメーションされます。

ただしこの削除のためには全データで一意になるようなキーを振ってやる必要があります。このキーが重複するとアニメーションがおかしくなるので注意が必要です。

background でスライドした時の背景をウィジェットで指定できます。ここでは単純な赤の塗りつぶしで表現しています。

confirmDismiss はスライドのアニメーションが完了したタイミグで実行されるコールバックです。ここで本当に破棄してよいデータかどうかを確認することができます。Future<bool> でその可否を返してやります。

onDismissed コールバックは画面からウィジェットが破棄され、そのアニメーションが完了したタイミングで呼ばれるコールバックです。内部的なデータの削除はここで行うことになると思います。画面のデータと内部のデータで不整合があるとエラーになることがあるので、破棄したデータは必ず削除しておきましょう。

スライドしてボタンを出すリスト

AnimatedList – Flutter

List の Item をスライドさせるとボタンが出てきてそこからアクションを行わせる UI はパッケージがすでに作られていていい感じなのでこれを利用するのが良いです。

flutter_slidable | Flutter Package

こんな感じです。

AnimatedList でアニメーション付きでリストから削除する方法

AnimatedList Sample Apps – Flutter

スライドとか関係なく、リストからアニメーションしながらデータを消す方法もあります。AnimatedList というウィジェットを用います。

AnimatedList はその名の通り、データの削除や挿入時に適切なアニメーションを描画することができるリストです。

利用イメージはこんな感じです。タップしたデータを消すだけのサンプルです。

以下、タップしたデータを削除するサンプルコードです。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Delete List Item',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SamplePage2(),
    );
  }
}

class SamplePage2 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => SamplePage2State();
}

class SamplePage2State extends State<SamplePage2> {
  final GlobalKey<AnimatedListState> _listKey = GlobalKey();
  final _data = ['data1', 'data2', 'data3', 'data4', 'data5'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sample Page 2')),
      body: AnimatedList(
        key: _listKey,
        initialItemCount: _data.length,
        itemBuilder: (BuildContext context, int index, Animation animation) {
          return _buildItem(_data[index], animation);
        },
      ),
    );
  }

  Widget _buildItem(String item, Animation animation) {
    return SizeTransition(
      sizeFactor: animation,
      child: ListTile(
        title: Text(item),
        onTap: () {
          // 内部データを消す
          var removeIndex = _data.indexOf(item);
          String removedItem = _data.removeAt(removeIndex);

          // 削除アニメーションで利用されるウィジェットのビルダ関数
          // 削除前のものと同じ描画内容にするといい感じに消える
          AnimatedListRemovedItemBuilder builder = (context, animation) {
            return _buildItem(removedItem, animation);
          };

          // ウィジェット上から削除を実行する
          _listKey.currentState.removeItem(removeIndex, builder);
        },
      ),
    );
  }
}

ListView の代わりに AnimatedList を利用してやることでアニメーションを利用できます。

_buildItem() は List の子要素を構築するための関数です。この各要素は削除時にアニメーションしてほしいので SizedTransition でラップしています。

あとは onTap でデータを削除しています。内部データを削除し、その後 AnimatedListRemovedItemBuilder という型のビルダ関数を作成しています。これは、削除アニメーションが行われるウィジェットを構築するためのものです。ここでは削除前のものと同じものを描画しています。例えば赤色で塗りつぶしたウィジェットを返すと、削除のアニメーションは赤い塗りつぶしに対して行われることになります。

最後に List の Item を削除する処理を呼び出して完了です。

以上。

Flutterカテゴリの最新記事