How to handle ListView scroll direction
Asked Answered
H

3

14

I have a dummy list of items I want to show a floating action button in swip up direction and hide it in down direction. how can I implement this functionality ?

class _MyHomePageState extends State<MyHomePage> {
  bool upDirection = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Container(
          child: Row(
            children: <Widget>[
              Expanded(
                  child: ListView.builder(
                    itemCount: 100,
                    itemBuilder: (context,index){
                      return ListTile(
                        title: Text(index.toString()),
                      );
                    },
                  ),
              )
            ],
          ),
        ),
      ),
      floatingActionButton:upDirection==true?FloatingActionButton(onPressed: (){},):Container() ,
    );
  }
}
Homer answered 10/8, 2019 at 8:47 Comment(0)
P
33

Screenshot:

enter image description here

All you need is a NotificationListener.onNotification callback:

NotificationListener<UserScrollNotification>(
  onNotification: (notification) {
    final ScrollDirection direction = notification.direction;
    return true;
  },
  child: ListView.builder(
    itemCount: 100,
    itemBuilder: (_, i) => ListTile(title: Text('$i')),
  ),
)

Full code:

bool _visible = true;
  
@override
Widget build(BuildContext context) {
  return Scaffold(
    floatingActionButton: AnimatedOpacity(
      duration: Duration(milliseconds: 400),
      opacity: _visible ? 1 : 0,
      child: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {},
      ),
    ),
    body: NotificationListener<UserScrollNotification>(
      onNotification: (notification) {
        final ScrollDirection direction = notification.direction;
        setState(() {
          if (direction == ScrollDirection.reverse) {
            _visible = false;
          } else if (direction == ScrollDirection.forward) {
            _visible = true;
          }
        });
        return true;
      },
      child: ListView.builder(
        itemCount: 100,
        itemBuilder: (_, i) => ListTile(title: Text('$i')),
      ),
    ),
  );
}
Prem answered 10/8, 2019 at 10:11 Comment(5)
Great! do we really have to dispose the controller?Parishioner
@WesleyBarnes Yes, it's a good practice to do so (I didn't include it though).Prem
It just needed import 'package:flutter/rendering.dart';Kirkkirkcaldy
It's interesting how we get ScrollDirection.forward when we are going back (to the beginning of the list) and ScrollDirection.reverse when we are going down forward (towards the end of the list).Hiatt
@Hiatt Yes, I also feel that it should be the other way around.Prem
T
2

Using ScrollController we can check up and down scroll using this method in listview, singlechildscrollview, girdview when axis.vertical and when axis.horizontal can check scroll left to right

final ScrollController _scrollController = ScrollController();
@override
void initState() {
_scrollController.addListener(() {
  if (_scrollController.position.userScrollDirection ==
      ScrollDirection.forward) {
    update(0.0, true);
  } else if (_scrollController.position.userScrollDirection ==
      ScrollDirection.reverse) {
    update(70, false);
  }
});
  super.initState();
}
Thayne answered 17/12, 2023 at 20:4 Comment(0)
D
1

Though the above solution answers the question, I think its a bit inefficient since you're calling setState() everytime a change in direction happens. This might work in small apps but in big complex apps can lead high time lags as when you call setState() the whole widget tree rebuild itself and its subsequent children.

My solution? Use a Provider and Consumer.

Provider acts like a reference to a widget and a certain variable of which you want to keep a track of and using consumer you can listen to change when you need and where you need.

Benefit over setState(), when the builder method of consumer is called it only rebuilds the widget which listens to it(in your case the bottom app bar) and doesn't affect the rest of the widget tree.

Also Providers are back-bone of flutter state management so I would highly recommend them knowing as soon as possible.

Drus answered 28/2, 2021 at 3:44 Comment(1)
NotificationListener<UserScrollNotification> tracks only events of scroll direction change, so for each scroll there is only two events - one when the user starts scrolling (you get the direction from that) and one when the scroll is finished (in which case the direction is ScrollDirection.idle. So, the code from @Prem is not inefficient, but it's good to think about efficiency with state updates in general. Also, you can use ValueNotifier with ValueListenableBuilderHiatt

© 2022 - 2024 — McMap. All rights reserved.