颤动:可以检测抽屉何时打开?

时间:2018-04-22 11:03:38

标签: flutter

是否可以检测抽屉何时打开,以便我们可以运行一些例程来更新其内容?

我的一个典型用例是显示关注者的数量,喜欢...为此,我需要轮询服务器以获取此信息,然后显示它。

我尝试实现NavigatorObserver来捕捉Drawer可见/隐藏的时刻,但NavigatorObserver没有检测到任何关于Drawer的内容。

以下是链接到NavigatorObserver的代码:

import 'package:flutter/material.dart';

typedef void OnObservation(Route<dynamic> route, Route<dynamic> previousRoute);
typedef void OnStartGesture();

class NavigationObserver extends NavigatorObserver {
  OnObservation onPushed;
  OnObservation onPopped;
  OnObservation onRemoved;
  OnObservation onReplaced;
  OnStartGesture onStartGesture;

  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (onPushed != null) {
      onPushed(route, previousRoute);
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (onPopped != null) {
      onPopped(route, previousRoute);
    }
  }

  @override
  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (onRemoved != null)
      onRemoved(route, previousRoute);
  }

  @override
  void didReplace({ Route<dynamic> oldRoute, Route<dynamic> newRoute }) {
    if (onReplaced != null)
      onReplaced(newRoute, oldRoute);
  }

  @override
  void didStartUserGesture() { 
    if (onStartGesture != null){
      onStartGesture();
    }
  }
}

和这个观察者的初始化

void main(){
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final NavigationObserver _observer = new NavigationObserver()
                                              ..onPushed = (Route<dynamic> route, Route<dynamic> previousRoute) {
                                                print('** pushed route: $route');
                                              }
                                              ..onPopped = (Route<dynamic> route, Route<dynamic> previousRoute) {
                                                print('** poped route: $route');
                                              }
                                              ..onReplaced = (Route<dynamic> route, Route<dynamic> previousRoute) {
                                                print('** replaced route: $route');
                                              }
                                              ..onStartGesture = () {
                                                print('** on start gesture');
                                              };

  @override
  void initState(){
    super.initState();
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Title',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SplashScreen(),
        routes: <String, WidgetBuilder> {
          '/splashscreen': (BuildContext context) => new SplashScreen(),
        },
        navigatorObservers: <NavigationObserver>[_observer],
    );
  }
}

感谢您的帮助。

8 个答案:

答案 0 :(得分:4)

我认为一个简单的解决方案是覆盖leading的{​​{1}}属性,这样您就可以在按下菜单图标时根据该操作调用API。

然而,我可能误解了您的问题,因为根据您提供的用例,您通常需要以一种可以监听的方式管理它,以便自动更新值,这样我就可以了不知道抽屉打开时你想要触发什么。

无论如何,这是一个例子。

enter image description here

AppBar

答案 1 :(得分:3)

这对我有用。

通过任何操作打开抽屉时运行initState。

通过任何动作关闭抽屉时运行。

希望它能对您有所帮助。

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"),
        ],
      ),
    );
  }
}

答案 2 :(得分:3)

不幸的是,目前没有readymade solution

您可以为此使用肮脏的技巧:观察抽屉的可见位置。

例如,我使用这种方法来同步按钮上图标的动画和“抽屉”框的位置。

enter image description here

解决此问题的代码,您可以在下面看到:

    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,
        );
      }
    }

如果您只对打开或关闭框的最终事件感兴趣,那么只需在initStatedispose函数中调用回调即可。

答案 3 :(得分:3)

由于 https://github.com/flutter/flutter/pull/67249 已经与 Flutter 2.0 合并并发布,这里是检测抽屉打开/关闭的正确方法:

Scaffold(
      onDrawerChanged: (isOpened) {
        //todo what you need for left drawer
      },
      onEndDrawerChanged: (isOpened) {
        //todo what you need for right drawer
      },
)

答案 4 :(得分:0)

最佳解决方案

ScaffoldState有一个有用的方法isDrawerOpen,它提供了打开/关闭状态。

示例

GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(context) {
   return WillPopScope(
  child: Scaffold(
    key: _scaffoldKey,
    drawer: SideNavigation(),
  onWillPop: () async {
    // drawer is open do things accordingly
    if (_scaffoldKey.currentState.isDrawerOpen) {
      Navigator.of(context).pop();
      return false;
    }
    return true;
  });}

答案 5 :(得分:0)

当这个问题被发布时,完成这个问题有点技巧。但是从 Flutter 2.0 开始,这很容易。在您的 Scaffold 中,您可以按如下方式检测右侧抽屉和左侧抽屉。

@override
  Widget build(BuildContext context) {
    return Scaffold(
      onDrawerChanged: (isOpened) {
        *//Left drawer, Your code here,*
      },
      onEndDrawerChanged: (isOpened) {
        *//Right drawer, Your code here,*
      },
    );
  }

答案 6 :(得分:0)

您可以简单地使用 onDrawerChanged 来检测抽屉是在 Scaffold 小部件中打开还是关闭。

财产:

{void 函数(布尔)? onDrawerChanged}
类型:void Function(bool)?
Scaffold.drawer 打开或关闭时调用的可选回调。

示例:

@override 小部件构建(BuildContext 上下文){

return Scaffold(
  onDrawerChanged:(val){
    if(val){
      setState(() {
        //foo bar;
      });
    }else{
      setState(() {
        //foo bar;
      });
    }
},     
  drawer: Drawer(        
      child: Container(
      )
  ));

}

答案 7 :(得分:-1)

ScaffoldState 中有 isDrawerOpen 属性,所以你可以随时查看。

创建全局密钥;

GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

将其分配给脚手架

Scaffold(
      key: scaffoldKey,
      appBar: ..)

检查应用程序中的任何位置

bool opened =scaffoldKey.currentState.isDrawerOpen;