12

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() ,
    );
  }
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
mohammad nasr
  • 145
  • 1
  • 2
  • 9

2 Answers2

29

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')),
      ),
    ),
  );
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
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.

Saurabh Kumar
  • 2,088
  • 14
  • 17
  • 1
    `NotificationListener` 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 @CopsOnRoad is not inefficient, but it's good to think about efficiency with state updates in general. Also, you can use `ValueNotifier` with [`ValueListenableBuilder`](https://api.flutter.dev/flutter/widgets/ValueListenableBuilder-class.html) – Aleksandar Mar 03 '22 at 14:27