Search code examples
flutterdartflutter-layout

Flutter - Hiding FloatingActionButton


Is there any built in way in Flutter to hide a FloatingActionButton on ListView scrolling down and then showing it on scrolling up?


Solution

  • import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    
    void main() {
      runApp(new MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
    
            primarySwatch: Colors.blue,
          ),
          home: new MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
     }
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => new _MyHomePageState();
     }
    
     class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
      ScrollController _hideButtonController;
      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }
      var _isVisible;
      @override
      initState(){
        super.initState();
        _isVisible = true;
        _hideButtonController = new ScrollController();
        _hideButtonController.addListener((){
          if(_hideButtonController.position.userScrollDirection == ScrollDirection.reverse){
            if(_isVisible == true) {
                /* only set when the previous state is false
                 * Less widget rebuilds 
                 */
                print("**** ${_isVisible} up"); //Move IO away from setState
                setState((){
                  _isVisible = false;
                });
            }
          } else {
            if(_hideButtonController.position.userScrollDirection == ScrollDirection.forward){
              if(_isVisible == false) {
                  /* only set when the previous state is false
                   * Less widget rebuilds 
                   */
                   print("**** ${_isVisible} down"); //Move IO away from setState
                   setState((){
                     _isVisible = true;
                   });
               }
            }
        }});
      }
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text(widget.title),
          ),
          body: new Center(
            child: new CustomScrollView(
              controller: _hideButtonController,
              shrinkWrap: true,
              slivers: <Widget>[
                new SliverPadding(
                  padding: const EdgeInsets.all(20.0),
                  sliver: new SliverList(
                    delegate: new SliverChildListDelegate(
                      <Widget>[
                        const Text('I\'m dedicating every day to you'),
                        const Text('Domestic life was never quite my style'),
                        const Text('When you smile, you knock me out, I fall apart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('And I thought I was so smart'),
                        const Text('I realize I am crazy'),   
                      ],
                    ),
                  ),
                ),
              ],
            )
          ),
          floatingActionButton: new Visibility( 
            visible: _isVisible,
            child: new FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: new Icon(Icons.add),
            ),     
          ),
        );
      }
    }
    

    I apologize if I did not use listview since I do not know how to scroll with listview. I will answer the other parts of your question.

    First you need to create a scrollcontroller that will listen scrollPostion events

    If scrollcontroller manages to find either scrolldirection forward or reverse. You add a state that set a state to visible.

    When you draw the button, you wrap the button in a visibility class. You set the visible flag and the widget should ignore input commands.

    Edit: I cant seem to add links to ScrollController, ScrollerPosition, ScrollDirection, and Opacity. I guess you can search it yourself or somebody else edit in the links

    Edit2: Use CopsonRoad or use visibility widget, unless you want an unpainted widget in the layout tree

    Edit3: In light of newcomers using code as is, I would update the code to encourage better practices. Use visibility instead of Opacity. Remove io from setState. tested on Flutter 1.5.4-hotfix.2