アプリ開発をしていて、リストViewで「現在表示されているリストアイテムを取得したい」という場合がよくありますよね。簡単にできる方法がありましたので記事にしておきます。ついでに、指定インデックスへのスクロールの方法も。
目次
scrollable_positioned_list
↑その名の通りのプラグインです。任意インデックスへのスクロールと位置を取得できるListViewのプラグインです。Google製のプラグインですのでサポートも安心でしょう(たぶん)。
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
ここに置いてますのでご自由にどうぞ。
関連する記事
- Android StudioからAndroid-PullToRefreshを使用する
- 【Android】リストビューの自動スクロール機能を実装する
- 【Android】安全にonActivityResultを実行する
- 「で、結局オブジェクト指向って何が良いわけ?」という手続き型脳の貴男へ
- 【Android】フリックイベントを実装する