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

问题描述

我正在使用响应时间短的Web UI。而且我想在移动和台式机屏幕宽度特定的屏幕宽度上关闭打开的抽屉,因此,如果我拉伸浏览器,则抽屉应该关闭。

例如,我打开了抽屉(屏幕宽度小于500)

enter image description here

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

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

enter image description here

下面的代码。感谢您的帮助

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

解决方法

您不必手动关闭抽屉。当屏幕宽度小于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(),),);
  }
}

只要更改设备的宽度,便会重新构建脚手架;如果宽度小于500,则会自动省略抽屉。

,

这是解决方案。

您就是代码就足够了。只需对您的代码进行几处更改

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

错误是

  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

提示

将布局生成器用于响应式用户界面。

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...