Flutter web - 如何监听打开的抽屉状态并关闭它

Rai*_*ado 5 flutter flutter-layout flutter-web flutter-widget

我正在研究 Flutter 响应式 Web UI。我想在移动和桌面屏幕宽度的特定屏幕宽度上关闭打开的抽屉,所以如果我拉伸浏览器,抽屉应该关闭。

比如我打开抽屉(屏幕宽度小于500)

在此处输入图片说明

而当屏幕宽度大于 500 时,我希望打开的抽屉自动关闭。

注意:当抽屉打开时。我已经有一个代码来检查是否显示按钮菜单抽屉的屏幕宽度。但基本上,当用户打开抽屉然后突然拉伸浏览器时,抽屉应该关闭。

在此处输入图片说明

代码如下。谢谢您的帮助

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size.width;

    return Scaffold(
      drawer: Drawer(),
      body: CustomNavBar(screenSize: size),
    );
  }
}

class CustomNavBar extends StatefulWidget {
  final double screenSize;
  const CustomNavBar({Key key, this.screenSize}) : super(key: key);

  @override
  _CustomNavBarState createState() => _CustomNavBarState();
}

class _CustomNavBarState extends State<CustomNavBar> {
  @override
  Widget build(BuildContext context) {
    if (Scaffold.of(context).isDrawerOpen && widget.screenSize > 500) {
      print("Drawer is Opened");
      Scaffold.of(context).openEndDrawer(); //animation error
      setState(() {});
    }

    return widget.screenSize > 500
        ? Container(color: Colors.red) //desktop screen
        : Center(
            //mobile screen
            child: IconButton(
              icon: Icon(Icons.menu),
              onPressed: () => Scaffold.of(context).openDrawer(),
            ),
          );
  }
}

Run Code Online (Sandbox Code Playgroud)

uan*_*dhx 5

您不必手动关闭抽屉。当屏幕宽度小于 500 时,为什么不去掉抽屉?

class SampleDrawer extends StatelessWidget {
  final GlobalKey<ScaffoldState> k = GlobalKey();

  @override
  Widget build(BuildContext context) {
    // new
    final size = MediaQuery.of(context).size.width;
    if (k.currentState.isDrawerOpen && size < 500) {
      Navigator.pop(context); // close drawer
    }

    return Scaffold(
      key: k,
      drawer: size > 500 ? Drawer() : null,
      body: CustomNavBar(),
    );
  }
}

class CustomNavBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size.width;

    return size > 500
        ? Container(color: Colors.red) //desktop screen
        : Center( //mobile screen
            child: IconButton(
              icon: Icon(Icons.menu),
              onPressed: () => Scaffold.of(context).openDrawer(),
            ),
          );
  }
}
Run Code Online (Sandbox Code Playgroud)

每当设备宽度发生变化时都会重新构建 Scaffold,如果宽度小于 500,则会自动省略抽屉。


DIV*_*AHU 3

这是解决方案。

你的代码就足够了。只需对您的代码进行一些更改

  1. 把这个包Scaffold.of(context).openEndDrawer();起来

    WidgetsBinding.instance.addPostFrameCallback((_) {
      Scaffold.of(context).openEndDrawer(); //No Error
        ///The Error was coming, As you're trying to build a widget when it is 
        ///rebuilding widget Tree due to the change in the width of the browser.
        ///Wrapping it inside ensures that the code will run after the build.
    });
    
    Run Code Online (Sandbox Code Playgroud)
  2. 不要使用setState(() {});

  3. 使用 520 而不是 500


原因

错误出现了,当您尝试构建一个小部件时,由于浏览器宽度的变化而正在重建小部件树。将其包装Scaffold.of(context).openEndDrawer();在内部WidgetsBinding.instance.addPostFrameCallback((_) {});可确保代码在构建小部件后运行。

这是更新的代码

  class HomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      final size = MediaQuery.of(context).size.width;
      print(size);
      return Scaffold(
        drawer: Drawer(),
        body: CustomNavBar(
          screenSize: size,
        ),
      );
    }
  }
  
  class CustomNavBar extends StatefulWidget {
    final double screenSize;
  
    const CustomNavBar({
      Key key,
      this.screenSize,
    }) : super(key: key);
  
    @override
    _CustomNavBarState createState() => _CustomNavBarState();
  }
  
  class _CustomNavBarState extends State<CustomNavBar> {
    @override
    Widget build(BuildContext context) {
      if (Scaffold.of(context).isDrawerOpen && widget.screenSize > 520) {
        print("Drawer is Opened");
        WidgetsBinding.instance.addPostFrameCallback((_) {
          Scaffold.of(context).openEndDrawer(); //No Error
          ///The error was coming, As you're trying to build a widget when it is 
          ///rebuilding widget Tree due to the change in the width of the browser.
          ///Wrapping it inside ensure that the code will run after the build.
        });
        // Don't call setState((){}); Not Required;
        // as every time you change the width it rebuilds all the widget again
        // setState(() {});
      }
  
      return widget.screenSize > 520
          ? Container(color: Colors.red) //desktop screen
          : Center(
              //mobile screen
              child: IconButton(
                icon: Icon(Icons.menu),
                onPressed: () => Scaffold.of(context).openDrawer(),
              ),
            );
    }
  }
Run Code Online (Sandbox Code Playgroud)

错误是

  The following assertion was thrown while notifying status listeners for 
  AnimationController:
  setState() or markNeedsBuild() called during build.
  This Scaffold widget cannot be marked as needing to build because the framework is 
  already in the
  process of building widgets.  A widget can be marked as needing to be built during 
  the build phase
  only if one of its ancestors is currently building. This exception is allowed 
  because the framework
  builds parent widgets before children, which means a dirty descendant will always be 
  built.
  Otherwise, the framework might not visit this widget during this build phase.
  The widget on which setState() or markNeedsBuild() was called was:
  Scaffold
  The widget which was currently being built when the offending call was made was:
  CustomNavBar
Run Code Online (Sandbox Code Playgroud)

提示

使用响应式 UI 的布局生成器。

@override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 800) {
          return XScreen();
        } else if (constraints.maxWidth < 1200 && constraints.maxWidth > 800) {
          return Yscreen()?? ZScreen();
        } else {
          return XScreen()?? ZScreeen();
        }
      },
    );
  }
Run Code Online (Sandbox Code Playgroud)