2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutter Webでスワイプ削除を実装する

Posted at

概要

よくあるコレ(スワイプ削除)をFlutterで実装する方法です。

swipe-delete

まずは原型

コードは以下のような状態とします。
flutter createを行ったあと、少しだけ手を入れて静的なリストを一覧表示しています。

main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<String> itemList = ["item1", "item2", "item3", "item4", "item5"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
        itemCount: itemList.length,
        itemBuilder: (context, index) {
          return Card(
            child: ListTile(
              title: Text(itemList[index]),
            ),
          );
        },
      ),
    );
  }
}

見た目はこんな感じです。

image.png

Dismissibleでラッピングする

まずは、静的リストが生成されているCard部分をDismissibleでラッピングします。

main.dart(before)
// ~一部抜粋~ 以下部分をDismissibleで囲む
return Card(
  child: ListTile(
    title: Text(itemList[index]),
  ),
);

このようになります。
なお、Cardがスワイプされた時にどのCardがスワイプされたかが識別できるように一意な値を持たせる必要があります。一意な値はkeyで設定します。何を与えても良いですが、重複しない保証があるUniqueKeyを使用すると良いでしょう。

main.dart(after)
// ~一部抜粋~ Dismissibleで囲んでみた
return Dismissible(
  key: UniqueKey(), 
  child: Card(
    child: ListTile(
      title: Text(itemList[index]),
    ),
  ),
); 

この時点で、以下のようにスワイプ削除機能は完成しています。
GIF 2021-01-29 1-14-26.gif

ただ、実はスワイプ方向が指定されていないので左右どちらにでもスワイプできてしまいます。

GIF 2021-01-29 1-24-10.gif

スワイプ可能な方向を指定する

さすがに気持ちが悪いので、directionパラメータで方向を指定します。
DismissDirection.endToStartが画面上では左から右を表します。
startToEndにすれば右から左になります。

main.dart
return Dismissible(
  key: UniqueKey(),
  direction: DismissDirection.endToStart, //追加
  child: Card(
    child: ListTile(
      title: Text(itemList[index]),
    ),
  ),
);

これで、右から左へはスワイプができなくなりました。

せっかくなので背景色を...

せっかくなので、スワイプ時に赤い背景が出るようにしてみます。
背景はbackgroundで追加することができます。

main.dart
return Dismissible(
  key: UniqueKey(),
  direction: DismissDirection.endToStart,
  //backgroundパラメータを追加
  background: Container(
    alignment: AlignmentDirectional.centerEnd,
    color: Colors.red,
    child: Padding(
      padding: EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0),
    ),
  ), //ここまで追加
  child: Card(
    child: ListTile(
      title: Text(itemList[index]),
    ),
  ),
);

GIF 2021-01-29 1-24-10.gif

ついでにアイコンも!

なんか物足りないので、最後にゴミ箱のアイコンを足してみたいと思います。

main.dart
return Dismissible(
  key: UniqueKey(),
  direction: DismissDirection.endToStart,
  background: Container(
    alignment: AlignmentDirectional.centerEnd,
    color: Colors.red,
    child: Padding(
      padding: EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0),
      //Padding配下にchildを足し、Iconを配置
      child: Icon(
        Icons.delete,
        color: Colors.white,
      ),//ここまで追加
    ),
  ),
  child: Card(
    child: ListTile(
      title: Text(itemList[index]),
    ),
  ),
);

Paddingの配下に新しく子要素(child)を作成し、Iconを置きます。配置できるアイコンは多数が既に用意されているものなので、新しく導入する必要は殆どないでしょう。
利用可能なIconの一覧はここで確認できます。

というわけで、無事に実装完了です!せっかくですので動きを確認してみましょう。
GIF 2021-01-29 1-24-101.gif

コード全体

全体のコードは以下のようになりました。

main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<String> itemList = ["item1", "item2", "item3", "item4", "item5"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
        itemCount: itemList.length,
        itemBuilder: (context, index) {
          return Dismissible(
            key: UniqueKey(),
            direction: DismissDirection.endToStart,
            //backgroundパラメータを追加
            background: Container(
              alignment: AlignmentDirectional.centerEnd,
              color: Colors.red,
              child: Padding(
                padding: EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0),
                child: Icon(
                  Icons.delete,
                  color: Colors.white,
                ),
              ),
            ),
            child: Card(
              child: ListTile(
                title: Text(itemList[index]),
              ),
            ),
          );
        },
      ),
    );
  }
}

まとめ

  • CardをDismissibleでラッピングすればとりあえずスワイプ削除は実現できる
  • 方向指定はDismissible配下のdirectionで
  • 背景色、アイコンも設定できるよ!
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?