【Flutter】表示中のアイテムを取得できたり任意のindexにスクロールできたりするListView

アプリ開発をしていて、リストViewで「現在表示されているリストアイテムを取得したい」という場合がよくありますよね。簡単にできる方法がありましたので記事にしておきます。ついでに、指定インデックスへのスクロールの方法も。

scrollable_positioned_list

↑その名の通りのプラグインです。任意インデックスへのスクロールと位置を取得できるListViewのプラグインです。Google製のプラグインですのでサポートも安心でしょう(たぶん)。

scrollable_positioned_list

flutter.widgetsというレポジトリ内にあります。FlutterコアチームではないGoogle社員製なのかな?まあ鋭意開発中みたいなので、今後の期待も込めてスター押しておきましょう。

いつものようにpubspec.yamlにプラグインを記述してPug getしてください。

dependencies:
  flutter:
    sdk: flutter
  ...
  scrollable_positioned_list: ^0.1.5

サンプル

以下がサンプルコードです。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ome_scrollable_positioned_list',
      home: _MyHomePage(),
    );
  }
}

class _MyHomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<_MyHomePage> {
  final items = List<String>.generate(100, (i) => 'Item$i');
  // スクロールを司るコントローラ
  final ItemScrollController _itemScrollController = ItemScrollController();
  // リストアイテムのインデックスを司るリスナー
  final ItemPositionsListener _itemPositionsListener =
      ItemPositionsListener.create();

  @override
  void initState() {
    super.initState();
    // 表示中のアイテムを知るためにリスナー登録
    _itemPositionsListener.itemPositions.addListener(_itemPositionsCallback);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('scrollable_positioned_list example'),
      ),
      body: ScrollablePositionedList.separated(
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(items[index]),
          );
        },
        separatorBuilder: (context, index) => const Divider(height: 1),
        itemCount: items.length,
        itemScrollController: _itemScrollController,    // コントローラ指定
        itemPositionsListener: _itemPositionsListener,  // リスナー指定
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _scroll, // ボタンを押したらスクロールするよ
        child: Icon(Icons.airplanemode_active),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  @override
  void dispose() {
    // 使い終わったら破棄
    _itemPositionsListener.itemPositions.removeListener(_itemPositionsCallback);
    super.dispose();
  }

  void _scroll() {
    // スムーズスクロールを実行する
    _itemScrollController.scrollTo(
      index: 50,
      duration: Duration(seconds: 1),
      curve: Curves.easeInOutCubic,
    );
  }

  void _itemPositionsCallback() {
    // 表示中のリストアイテムのインデックス情報を取得
    final visibleIndexes = _itemPositionsListener.itemPositions.value
        .toList()
        .map((itemPosition) => itemPosition.index);
    print('現在表示中アイテムのindexは $visibleIndexes');
  }
}

ポイントとなるところにはコメントとハイライトを付けておきましたが少し補足しておきます。

まずListViewの代わりに ScrollablePositionedList を使用します。基本的には標準のListViewと同じですが、 itemScrollController と itemPositionsListener が指定できるようになっています。それぞれスクロールするためのコントローラと、インデックスを取得するためのリスナーです。

あとはコードを見てもらえればだいたい理解できるかと思います。

スクロールする箇所はこんな感じ。ウィ〜〜〜ンと1秒かけてindex=50のアイテムにスムーズスクロールします。

void _scroll() {
  // スムーズスクロールを実行する
  _itemScrollController.scrollTo(
    index: 50,
    duration: Duration(seconds: 1),
    curve: Curves.easeInOutCubic,
  );
}

ウィ〜〜〜ンが嫌な人はjumpToというメソッドを使用すれば一気にスクロールします。

 

次に表示中アイテムの取得ですが、実際にリストをスクロールしてみると次のような感じでログが表示されるようになってます。

いま見えているアイテムのインデックスが出力されているのが分かるでしょうか? なお表示中というのは『一部でも見えていたら』表示中という扱いのようです。

プチ注意

リストアイテムのインデックス情報を以下のように取得していますが、

void _itemPositionsCallback() {
  // 表示中のリストアイテムのインデックス情報を取得
  final visibleIndexes = _itemPositionsListener.itemPositions.value
      .toList()
      .map((itemPosition) => itemPosition.index);
  print('現在表示中アイテムのindexは $visibleIndexes');
}

どうもこのitemPositions.value(Iterable)の中身ですが、インデックスの小さいものから並んでいるとは限らないみたい・・・。というわけですから、『表示中で一番上/下に表示されているアイテムのインデックス』を期待してvalue.first/lastを使用するとバグることがあるんじゃないかな・・。

サンプルコードGithub

ここに置いてますのでご自由にどうぞ。

画面一杯の高さでヘッダー・フッター固定のテーブルを作る【CSS Grid】

Webアプリを開発していると、画面いっぱいまで高さを広げたヘッダー/フッター固定のテーブルを作りたいことが結構あります。いままではJavascriptで高さを設定してたりしましたが、tableタグを捨てればCSSグリッドで簡単に実現することができます。

どんなテーブル?

こんなテーブルです。

See the Pen
Header-Scroll-Footer
by itmammoth (@itmammoth)
on CodePen. ...続きを読む

iPhoneのホーム画面に特定のスプレッドシートを追加する

Googleのスプレッドシートの中でよく使うシートをiPhoneのホーム画面に追加する方法を紹介します。

普通にできないの?

長年Androidユーザーだった私は、当然iPhoneでも簡単にできるんだろうと思っていたのですが、一筋縄ではいきませんでした。色々と試行錯誤した結果、iOSのショートカットという機能を使って実現することができました。ググってもあまり情報が無いようなので、手順をまとめておきます。

手順

たとえば次の画像のように、『よく使うシート』がお目当てのスプレッドシートシートだとします。

中身はこんな感じ。

このシートをホーム画面に追加することにします。

まずは次の2つのアプリが必須です。『Google ドライブ』と『Google スプレッドシート』です。まあこのページを見ている時点ですでにインストールしてるよって方が多いとは思いますが。

 

では実際に設定していきましょう!

まずはiPhoneに標準でインストールされている『ショートカット』というアプリを起動しましょう。

下のような画面が表示されると思います。まだ何も登録していないから空っぽですね。

ここで『ショートカットを作成』をタップします。

続いて、『アクションを追加』をタップします。

アクションを選択する画面に遷移します。ここで『App』を選択します。

どのアプリを使用するか選択します。一覧にスプレッドシートがなかったので、ここでは『ドライブ』を選択します。

ドライブでできることが表示されます。ここで『よく使うシート というファイルを開く』を選択します。

先程のアクションを追加する画面に戻ってきました。今回はこれ以上のアクションは必要ないので、『次へ』をタップします。

ショートカットに名前をつける画面になります。ここで名前を変更しましょう。今回は『よく使う』というショートカット名にしてみました。また、アイコンをタップするとアイコンを変更することができますよ。ぜひ変更しておきましょう。

カラーを黄色にして、電球アイコンを設定してみました。

名前とアイコンを変更したら『完了』をタップします。

はい、これでショートカットは完成です。続いて最終目標であるホーム画面に追加をやってみましょう。先程作成したショートカットをタップしてください。

ショートカットの画面では『…』をタップします。

ショートカットの詳細画面には『ホーム画面に追加』というボタンがありますね。これを押しましょう。

プレビューの確認とホーム画面用の名前・アイコンの編集ができるようですが、今回はこのままでいいので『追加』をタップします。

追加が成功したらホーム画面に戻ってみましょう。無事アイコンが表示されていますか?

アイコンをタップするとショートカットが起動してドライブ経由でスプレッドシートがウィンウィン表示されると思います。カッコいい!!

 

というわけでなんとか目的を達することができましたが、iPhone歴3日なので他にもっといいやり方があるのかもしれません・・・。(あったら教えて)

今回使用したショートカットは応用次第で色んなことができそうですね。ショートカットアプリのホームにギャラリーがあるので、そこを漁ってみるのも良さそうですな。