👓

ScrollNotificationを使ってスクロール位置を検知した。

2023/09/27に公開

はじめに

以下の記事でつまずいた時の解決策としてScrollNotificationを使用しました。
その時の学習した内容をまとめました。

NotificationListenerとは

NotificationListenerはWidgetツリー内で発生する通知(notifications)を監視し、それに対するカスタムの処理を実行するためのWidgetです。通知は、Widgetツリー内でさまざまなイベントや操作が発生したときに生成され、他のWidgetに通知するために使用されます。

NotificationListenerは、通知を受信し、特定の種類の通知に対するリスナーを提供する役割を果たします。通知の種類には、スクロール通知(ScrollNotification)、フォーカス通知(FocusNotification)、テキスト編集通知(TextEditingNotification)などがあります。通知リスナーを使用することで、Widgetはこれらの通知を検知し、通知に含まれる情報を利用して特定のアクションを実行することができます。

NotificationListenerは通常、次のように使用されます

NotificationListener<NotificationType>(
  onNotification: (notification) {
    // 通知が発生したときの処理をここで行う
    return true; // trueを返すと、通知は伝搬し続け、それ以外の場合は伝搬が停止します
  },
  child: // 通知をリッスンする子Widgetを配置する
)

NotificationTypeはリッスンする通知の型を示し、onNotificationコールバックは通知が発生したときに実行されます。

ScrollNotificationとは

ScrollNotificationは、Widgetツリー内のスクロールイベントに関する通知を提供する、Widget通知の一種です。ScrollNotificationは、ScrollableWidget(例:ListView、GridView、SingleChildScrollViewなど)内でスクロールが発生したときに生成され、Widgetツリーを通じて伝播されます。

先ほどのNotificationListenerScrollNotificationをリッスンし、スクロールに応じた処理を行うことができます。

ScrollNotificationを利用すると、スクロールに関する情報をリッスンし、それに応じてアプリケーションの状態やUIを更新できます。たとえば、特定の位置までスクロールしたときに特定のアクションを実行するなどの操作が可能です。

ScrollNotificationには、以下のような情報を含むプロパティがあります。

  • metrics:
    ScrollMetricsオブジェクト。現在のスクロール位置やスクロール速度などのスクロールメトリクス情報を提供します。
  • context:
    通知が発生したWidgetのビルドコンテキストへの参照です。
  • dragDetails:
    スクロールがユーザーによってトリガーされた場合、ドラッグジェスチャーに関する情報を含みます。

NotificationListenerWidgetを使用してScrollNotificationをリッスンする例です。

NotificationListener<ScrollNotification>(
  onNotification: (scrollNotification) {
    if (scrollNotification is ScrollStartNotification) {
      // スクロールが始まったときの処理
    } else if (scrollNotification is ScrollUpdateNotification) {
      // スクロール中の処理
    } else if (scrollNotification is ScrollEndNotification) {
      // スクロールが終了したときの処理
    }
    return true;
  },
  child: SingleChildScrollView(
    // ここにスクロール可能なコンテンツを配置
  ),
)

サンプル

import 'package:flutter/material.dart';

class ZennScreen extends StatelessWidget {
  const ZennScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final screenSize = MediaQuery.of(context).size;
    return Scaffold(
      body: NotificationListener<ScrollNotification>(
        onNotification: (scrollInfo) {
          print(
              "axis: ${scrollInfo.metrics.axis} pixels: ${scrollInfo.metrics.pixels}");
          return false;
        },
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
              width: screenSize.width,
              height: screenSize.height / 2,
              child: ListView.builder(
                itemCount: 10,
                itemBuilder: (context, index) {
                  return Container(
                    width: screenSize.width,
                    height: screenSize.height / 8,
                    color: Colors.blue.withOpacity(index / 10),
                    child: Text(index.toString()),
                  );
                },
              ),
            ),
            SizedBox(
              width: screenSize.width,
              height: screenSize.height / 2,
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemCount: 10,
                itemBuilder: (context, index) {
                  return Container(
                    width: screenSize.width / 4,
                    color: Colors.red.withOpacity(index / 10),
                    child: Text(index.toString()),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Discussion