【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

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

関連する記事


コメントする

メールアドレスが公開されることはありません。

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください