29

There are two options to retrieve the scroll position for a CustomScrollView. The documentation states the following:

ScrollNotification and NotificationListener, which can be used to watch the scroll position without using a ScrollController.

So we have the following options:

  1. NotificationListener with ScrollNotification
  2. ScrollController

In which situation do you useNotificationListener with ScrollNotification vs ScrollController?

Thank you :)

Renato Stauffer
  • 738
  • 2
  • 14
  • 30

3 Answers3

41

If you're using NestedScrollView with nested scrollers, using a scrollController on the inner scrollers will break the link with NestedScrollView meaning NestedScrollView will no longer control the complete scrolling experience. To get information about the scroll positions of the inner scrollers in this case you would use a NotificationListener with ScrollNotification.

NotificationListener<ScrollNotification>(
  child: ListView.builder(
    itemCount: 10
    itemBuilder: (BuildContext context, int index) {
      return Text('Item $index');
    },
  ),
  onNotification: (ScrollNotification scrollInfo) {
    if (scrollInfo.metrics.pixels ==
        scrollInfo.metrics.maxScrollExtent) {
      onLoadMore();
    }
  },
);

Related Answer here.

Michael Tedla
  • 1,079
  • 1
  • 12
  • 19
20

Although you can use both ScrollNotification and ScrollController to listen to scroll position changes, and many times it is not going to be different, you should keep in mind some details in order to better choose the best tool for the job:

ScrollController

  • To use a ScrollController you should use a Stateful widget, so that it can be properly disposed.
  • Even if you can use the same ScrollController to control several scrollable widgets, some operations, such as reading the scroll offset, require the controller to be used with a single scrollable widget.
  • If you want to change the scroll position, you are going to need a ScrollController (jumpTo / animateTo).
  • When you listen to a ScrollController, you are listening to ScrollPosition changes, which is not exactly the same as ScrollNotification. Using ScrollNotifications you can differentiate among several types of events related to the scrolling action, as scroll start, scroll end, dirección change, etc.

NotificationListener

  • NotificationListener is just another widget, so yo don't need to create a Stateful widget.
  • It listens ScrollNotifications from all scrollable widgets below in the widget tree, as notifications propagate upwards. So it is not a constraint to listen to only one widget.
  • You cannot change the scroll position from a NotificationListener. It is read-only.
  • You listen to ScrollNotification, and not to changes in a ScrollPosition, so you can recognize easily which kind of event has just happened.
zex_rectooor
  • 692
  • 7
  • 26
pposca
  • 523
  • 3
  • 10
5

it my demo use NotificationListener with ScrollController. After drag a litter left, the blue part will automatic move

import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    List<Widget> list = [];
    for (int i = 0; i < 100; i++) list.add(buildContainer());
    return Scaffold(
        body: ListView(children: list));
  }

  Widget buildContainer() {
    ScrollController _scrollController = ScrollController();

    return NotificationListener<ScrollNotification>(
      onNotification: (scrollState) {
        if (scrollState is ScrollEndNotification && scrollState.metrics.pixels != 160) {
          Future.delayed(const Duration(milliseconds: 100), () {}).then((s) {
            _scrollController.animateTo(160,
                duration: Duration(milliseconds: 500), curve: Curves.ease);
          });
        }
        return false;
      },
      child: Container(
        height: 160,
        margin: EdgeInsets.only(bottom: 1),
        child: ListView(
          shrinkWrap: true,
          scrollDirection: Axis.horizontal,
          controller: _scrollController,
          children: <Widget>[
            Container(
              width: 360,
              height: 20,
              color: Colors.red,
            ),
            Container(
              width: 160,
              height: 20,
              color: Colors.blue,
            ),
          ],
        ),
      ),
    );
  }
}
王良海
  • 201
  • 3
  • 5