Flutter: possible to detect when a drawer is open?

后端 未结 4 1683
Happy的楠姐
Happy的楠姐 2021-01-04 13:53

Is it possible to detect when a Drawer is open so that we can run some routine to update its content?

A typical use case I have would be to display the number of fol

相关标签:
4条回答
  • 2021-01-04 14:33

    I think one simple solution is to override the leading property of your AppBar so you can have access when the menu icon is pressed an run your API calls based on that.

    Yet I may have misunderstood your question because with the use case you provided, you usually need to manage it in a way that you can listen to any change which will update the value automatically so I am not sure what are you trying to trigger when the drawer is open.

    Anyway here is the example.

    class DrawerExample extends StatefulWidget {
      @override
      _DrawerExampleState createState() => new _DrawerExampleState();
    }
    
    class _DrawerExampleState extends State<DrawerExample> {
      GlobalKey<ScaffoldState> _key = new GlobalKey<ScaffoldState>();
      int _counter =0;
      _handleDrawer(){
          _key.currentState.openDrawer();
    
               setState(() {
              ///DO MY API CALLS
              _counter++;
            });
    
      }
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          key: _key,
          appBar: new AppBar(
            title: new Text("Drawer Example"),
            centerTitle: true,
            leading: new IconButton(icon: new Icon(
              Icons.menu
            ),onPressed:_handleDrawer,),
          ),
          drawer: new Drawer(
            child: new Center(
              child: new Text(_counter.toString(),style: Theme.of(context).textTheme.display1,),
            ),
          ),
        );
      }
    }
    
    0 讨论(0)
  • 2021-01-04 14:46

    Unfortunately, at the moment there is no readymade solution.

    You can use the dirty hack for this: to observe the visible position of the Drawer.

    For example, I used this approach to synchronise the animation of the icon on the button and the location of the Drawer box.

    The code that solves this problem you can see below:

        import 'package:flutter/material.dart';
        import 'package:flutter/scheduler.dart';
    
        class DrawerListener extends StatefulWidget {
          final Widget child;
          final ValueChanged<FractionalOffset> onPositionChange;
    
          DrawerListener({
            @required this.child,
            this.onPositionChange,
          });
    
          @override
          _DrawerListenerState createState() => _DrawerListenerState();
        }
    
        class _DrawerListenerState extends State<DrawerListener> {
          GlobalKey _drawerKey = GlobalKey();
          int taskID;
          Offset currentOffset;
    
          @override
          void initState() {
            super.initState();
            _postTask();
          }
    
          _postTask() {
            taskID = SchedulerBinding.instance.scheduleFrameCallback((_) {
              if (widget.onPositionChange != null) {
                final RenderBox box = _drawerKey.currentContext?.findRenderObject();
                if (box != null) {
                  Offset newOffset = box.globalToLocal(Offset.zero);
                  if (newOffset != currentOffset) {
                    currentOffset = newOffset;
                    widget.onPositionChange(
                      FractionalOffset.fromOffsetAndRect(
                        currentOffset,
                        Rect.fromLTRB(0, 0, box.size.width, box.size.height),
                      ),
                    );
                  }
                }
              }
    
              _postTask();
            });
          }
    
          @override
          void dispose() {
            SchedulerBinding.instance.cancelFrameCallbackWithId(taskID);
            if (widget.onPositionChange != null) {
              widget.onPositionChange(FractionalOffset(1.0, 0));
            }
            super.dispose();
          }
    
          @override
          Widget build(BuildContext context) {
            return Container(
              key: _drawerKey,
              child: widget.child,
            );
          }
        }
    

    If you are only interested in the final events of opening or closing the box, it is enough to call the callbacks in initState and dispose functions.

    0 讨论(0)
  • 2021-01-04 14:47

    Detecting & Running Functions When Drawer Is Opened / Closed

    • Run initState() when open drawer by any action.
    • Run dispose() when close drawer by any action.
    class MyDrawer extends StatefulWidget {
        @override
        _MyDrawerState createState() => _MyDrawerState();
    }
    
    class _MyDrawerState extends State<MyDrawer> {
    
        @override
        void initState() {
            super.initState();
            print("open");
        }
    
        @override
        void dispose() {
            print("close");
            super.dispose();
        }
    
        @override
        Widget build(BuildContext context) {
            return Drawer(
                child: Column(
                    children: <Widget>[
                        Text("test1"),
                        Text("test2"),
                        Text("test3"),
                    ],
                ),
            );
        }
    }
    

    State Management Considerations

    If you are altering state with these functions to rebuild drawer items, you may encounter the error: Unhandled Exception: setState() or markNeedsBuild() called during build.

    This can be handled by using the following two functions in initState() source

    Option 1

    WidgetsBinding.instance.addPostFrameCallback((_){
      // Add Your Code here.
    });
    

    Option 2

    SchedulerBinding.instance.addPostFrameCallback((_) {
      // add your code here.
    });
    

    Full Example of Option 1

    @override
    void initState() {
        super.initState();
        WidgetsBinding.instance.addPostFrameCallback((_) {
            // Your Code Here
        });
    }
    
    0 讨论(0)
  • 2021-01-04 14:56

    Best solution

    ScaffoldState has a useful method isDrawerOpen which provides the status of open/close.

    Example: Here on the back press, it first checks if the drawer is open, if yes then first it will close before exit.

    /// create a key for the scaffold in order to access it later.
    GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
    
    @override
    Widget build(context) {
       return WillPopScope(
      child: Scaffold(
        // assign key (important)
        key: _scaffoldKey,
        drawer: SideNavigation(),
      onWillPop: () async {
        // drawer is open then first close it
        if (_scaffoldKey.currentState.isDrawerOpen) {
          Navigator.of(context).pop();
          return false;
        }
        // we can now close the app.
        return true;
      });}
    
    0 讨论(0)
提交回复
热议问题