【Flutter】人気なパッケージの使い方 〜flutter_slidable編〜

## まえがき
2020年入ってからFlutterの勉強を初めてきました。
今後いろんなパッケージをもっと勉強していきたいと思ってるので、人気なものをアウトプットとして記事にしていきたいと思います。

今回は**url_launcher**と言うパッケージを紹介します。

これまで載せた記事
- [shared_preferencesの紹介](https://qiita.com/ryota47/items/bc381f85080dbf4b2454)
- [url_launcherの紹介](https://qiita.com/ryota47/items/0cd30257f063c06df1b8)
- [flutter_slidable](https://qiita.com/ryota47/items/6ed90ce011ea6aaa8698)←今これ

## flutter_slidable
flutter_slidableは`ListTile`に使うWidgetです。
`ListTile`を横にスライドさせることができるようになります。

スライドさせると、アイコン、タイトル文字、色、押した時の挙動を設定できます。

## サンプル
簡単なTODOリストを作りました。
今回はFirestoreは使用していませんが、使った場合にも応用できるようにモデルクラスも作成しました。

![flutter_slidable.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311390/a554b511-2d58-6d6f-2c06-301a95bcda43.gif)


```dart
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';

final toDoList = [
  {"title": "買い物にいく"},
  {"title": "犬の散歩"},
  {"title": "明日の準備"},
  {"title": "家族に電話する"},
];

class SampleSlidable extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => SampleSlidableState();
}

class SampleSlidableState extends State<SampleSlidable> {
  final itemController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('flutter_slidable'),
      ),
      body: ListView(
        children: toDoList.map((data) => _buildItem(context, data)).toList(),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          formForNewItem(context);
        },
      ),
    );
  }

  Widget _buildItem(BuildContext context, Map data) {
    Item toDoItem = Item.fromMap(data);
    //Slidableはここから
    return Slidable(
      actionExtentRatio: 0.2,
      actionPane: SlidableScrollActionPane(),
      // 左側に表示するWidget
      actions: <Widget>[
        IconSlideAction(
          caption: '重要',
          color: Colors.yellow,
          icon: Icons.star,
          onTap: () {},
        ),
        IconSlideAction(
          caption: '共有',
          color: Colors.blue,
          icon: Icons.share,
          onTap: () {},
        ),
      ],
      // 右側に表示するWidget
      secondaryActions: [
        IconSlideAction(
          caption: '削除',
          color: Colors.red,
          icon: Icons.remove,
          onTap: () {
            toDoList.remove(data);
            setState(() {});
          },
        ),
      ],
      // 一つ一つのアイテム
      child: Container(
        padding: EdgeInsets.only(left: 20),
        decoration: BoxDecoration(
            border: Border(bottom: BorderSide(width: 1.0, color: Colors.grey))),
        child: ListTile(
          leading: Icon(Icons.chevron_right),
          title: Text(toDoItem.title),
        ),
      ),
    );
  }

// アイテムを追加するダイアログ
  formForNewItem(BuildContext context) {
    showDialog(
      context: context,
      builder: (_) {
        return SimpleDialog(
          title: Text('新しく追加する'),
          children: [
            Container(
              padding: EdgeInsets.all(20),
              child: TextFormField(
                controller: itemController,
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                FlatButton(
                  child: Text('キャンセル'),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
                FlatButton(
                  child: Text('OK'),
                  onPressed: () {
                    toDoList.add({"title": itemController.text});
                    Navigator.of(context).pop();
                    itemController.clear();
                    setState(() {});
                  },
                )
              ],
            )
          ],
        );
      },
    );
  }
}

// アイテムクラス
class Item {
  String title;
  bool star;

  Item({this.title, this.star});

  Item.fromMap(Map<String, dynamic> mapData) {
    this.title = mapData["title"];
    this.star = mapData["star"];
  }
}
```

## 説明
説明は、`Slidable`にします。他のコードは簡単なので読んで理解していただきたいです。。

- `actionExtentRatio`：スライドさせたときに表示するWidgetの大きさの指定
- `actionPane`：スライドさせた時のアニメーション(4種類あるのであとで詳細説明)
- `actions`：左側に表示するWidget。`IconSlideAction`を使う
- `secondaryActions`：右側に表示するWidget。`IconSlideAction`を使う

最低限これが使えれば大丈夫そう。

`actions`や`secondaryActions`に使う`IconSlideAction`は
- `caption`：タイトル文字
- `color`：Widgetの色
- `icon`：アイコン
- `onTap`：タップした時の挙動

を設定できます。
今回は削除だけタップした時の挙動をちゃんと作成しました。

```
IconSlideAction(
  caption: '削除',
  color: Colors.red,
  icon: Icons.remove,
  onTap: () {
    toDoList.remove(data);
    setState(() {});
  },
 ),
```

これで削除できます。


次に`actionPane`の説明します。
**公式ドキュメントの方がわかりやすいかも**しれませんが、Gifも載せておきます。

- `SlidableBehindActionPane()`

`ListTile`の後ろのWidgetが隠れているような表示されます。
しかしこれは`ListTile`の文字が重なって見えてしまうので、僕はあまり好きではありません。
(僕の知識、技術不足でこうなってるかもしれないけど)

![SlidableBehindActionPane.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311390/16e49ef0-fde6-7fed-383b-d04cd03f0978.gif)


- `SlidableScrollActionPane`

`ListTile`の横にWidgetがついてるような表示されます。したがって、ただスライドさせてるようなアニメーションですね。

![SlidableScrollActionPane.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311390/bc2130ed-8a95-2c51-7e7a-198c4f0b3f45.gif)

- `SlidableDrawerActionPane`

`ListTile`の横から引き出しのように表示されます。

![SlidableDrawerActionPane.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311390/2d4eb7dc-3e83-3622-de11-a45df6d16da0.gif)

- `SlidableStrechActionPane`

`ListTile`の横からWidgetが延びるように表示されます。

![SlidableStrechActionPane.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311390/709723c0-e990-b5b5-ee8a-72379504dd0e.gif)


僕は`SlidableScrollActionPane`が一番好きですね。

あとこのパッケージはスライドさせて`Slidable`を表示してる状態で縦にスクロールすると、自動で`Slidable`を閉じてくれたりするのも便利でいいですね〜〜

## 思いつく使い道
ListTileを使うところでつかいますよね

## パッケージのソースコード読んでみた感想
Slidableクラスはアニメーションとかたくさん使ってて、まだすんなりとは読めない
IconSlideActionクラスは結構簡単でFlexible使ってるんだーって思いました。
あと、プロパティが他に何があるのか知る機会になりました。

`iconWidget`って言うのもあって、
```
icon
iconWidget
caption
```
の順番い表示されてた。テキストはcaptionで十分だし特に使う機会はあまりなさそう。

パッケージのソースコードちゃんと見たのは初めてでしたが、すごい複雑でまだまだ理解しきれない。