🏹

ScrollControllerを使ってみた

2023/10/08に公開

Overview

https://api.flutter.dev/flutter/widgets/ScrollController-class.html
公式より引用
スクロール可能なウィジェットを制御します。

スクロール コントローラーは通常、State オブジェクトのメンバー変数として保存され、各 State.build で再利用されます。 単一のスクロール コントローラーを使用して複数のスクロール可能なウィジェットを制御できますが、スクロール オフセットの読み取りなどの一部の操作では、コントローラーを単一のスクロール可能なウィジェットで使用する必要があります。

スクロール コントローラーは、ScrollPosition を作成して、個々の Scrollable ウィジェットに固有の状態を管理します。 カスタム ScrollPosition を使用するには、ScrollController をサブクラス化し、createScrollPosition をオーバーライドします。

スクロール情報へのアクセス
スクロールおよびスクロール可能なウィジェットに関する情報を取得するにはいくつかの方法がありますが、それぞれの方法で、スクロール アクティビティ、位置、ビューポートの寸法に関するさまざまな種類の情報が提供されます。

ScrollController は Listenable です。 スクロールが発生したときなど、アタッチされた ScrollPosition のいずれかがリスナーに通知するたびに、リスナーに通知します。 これは、ScrollNotification タイプの NoticeListener を使用してスクロール位置の変更をリッスンするのとよく似ていますが、通知リスナーがスクロール アクティビティに関する情報を提供する点が異なります。 通知リスナーは、UserScrollNotification など、ScrollNotification の特定のサブクラスをさらにリッスンできます。

summary

今回使用するScrollControllerはどんな目的で使うかと言いますと、スクロールするときの位置を制御してくれます。
たまにホームページで見るボタンを押すと、上に戻るボタンを作ってみました。これを押すと、画面に表示されているリストが逆向きにスクロールして、画面上の方へ戻っていく仕組みになっております。

CustomScrollViewを使用して、SNSぽいUIに仕上げてみました。

import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    List<int> numListRow = List.generate(10, (index) => index);
    List<int> numListColumn = List.generate(100, (index) => index);
    ScrollController controller = ScrollController();

    return Scaffold(
      backgroundColor: Colors.black87,
      body: CustomScrollView(
        slivers: [
          const SliverAppBar(
            backgroundColor: Colors.transparent,
            title: Text('SliverAppBar', style: TextStyle(color: Colors.white)),
            floating: true,
            snap: true,
          ),
          // 横スクロール
          SliverToBoxAdapter(
            child: SizedBox(
              height: 100,
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemCount: numListRow.length,
                itemBuilder: (BuildContext context, int index) {
                  return const SizedBox(
                    width: 100,
                    child: ListTile(
                      leading: CircleAvatar(
                        backgroundColor: Colors.grey,
                        child:
                            Text('太郎', style: TextStyle(color: Colors.white)),
                      ),
                    ),
                  );
                },
              ),
            ),
          ),
          // 縦スクロール
          SliverToBoxAdapter(
            child: SizedBox(
              height: 500,
              child: ListView.builder(
                controller: controller,
                itemCount: numListColumn.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    width: 100,
                    child: ListTile(
                        leading: const Icon(Icons.circle,
                            size: 50, color: Colors.grey),
                        title: Text('ユーザー ${numListColumn[index]}',
                            style: const TextStyle(color: Colors.white))),
                  );
                },
              ),
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          double nowOffset = controller.offset.floorToDouble();
          // ボタンを押すと、スクロール位置が一番下になる
          controller.animateTo(
            controller.position.minScrollExtent,
            duration: const Duration(milliseconds: 1000),
            curve: Curves.easeOut,
          );
        },
        tooltip: 'Increment',
        child: const Icon(Icons.arrow_upward, color: Colors.white),
      ),
    );
  }
}

main.dartでimportして実行する

import 'package:flutter/material.dart';
import 'package:widget_cook/time_line.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(),
      home: const TimeLine(),
    );
  }
}

gif画像なので、小さいですがこんな動きをします。

thoughts

ScrollControllerを使ってみた感想ですが、SNSアプリで上に戻るボタンはないと思いますが、何かの申し込みができるアプリでは上に戻るボタンがあった気がします。
他には、Flutter Webだとメニューボタンで、TOPやINFOをタップすると画面下の特定の位置にスクロールするロジックを作るのに使えるようです。

Discussion