导航抽屉不适用于屏幕内的屏幕

时间:2019-02-05 18:04:02

标签: dart flutter navigation-drawer

我想在所有屏幕上保留导航抽屉。对此有很多疑问,但我的问题与其他问题几乎没有什么不同。 Persisting AppBar Drawer across all Pages Flutter

我有一个导航抽屉,其中包含名为A,B和C的项目列表。在导航抽屉中单击A时,屏幕A打开,并且B和C分别相同。现在C屏幕上有一个按钮,然后单击按钮我将转到屏幕D,尽管屏幕D显示了“导航抽屉”图标,但抽屉从未打开过。我尝试在调用抽屉的方法中打印一条语句,并且打印语句可以打印,但是抽屉从不打开。以下是我的代码

我有一个基类,其抽屉如下

 class BaseScreen extends StatefulWidget {
  final List<Menu> menuList;
  final String userType;
  final String userId;

  const BaseScreen({Key key, this.menuList, this.userType, this.userId})
      : super(key: key);

  @override
  BaseScreenState createState() {
    return new BaseScreenState();
  }
}

class BaseScreenState extends State<BaseScreen> {
  String screenNameSelected = "A";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          _getDrawerItemWidget(screenNameSelected),

        ],
      ),
      drawer: SizedBox(
        width: 100,
        child: Drawer(
          child: ListView.separated(
            separatorBuilder: (context, index) => Material(
                  elevation: 2,
                  shadowColor: shadow,
                  child: Divider(
                    color: white,
                  ),
                ),
            itemBuilder: (BuildContext context, int index) {
              return ListTile(
                onTap: () {
                  openScreen(
                      widget.menuList[index].title,
                      widget.userId,
                      MenuList.returnLoginType(widget.userType).toString(),
                      context);
                  Navigator.pop(context);
                },
                title: Padding(
                  padding: const EdgeInsets.only(top: 8),
                  child: Image.asset(
                    widget.menuList[index].image,
                    width: 35,
                    height: 35,
                  ),
                ),

              );
            },
            itemCount: widget.menuList?.length ?? 0,
            shrinkWrap: true,
            physics: BouncingScrollPhysics(),
          ),
        ),
      ),
    );
  }

  _getDrawerItemWidget(String selectedScreenName) {
    switch (selectedScreenName) {
      case A:
        return A();
      case B:
        return B();
      case C:
        return C();

      default:
        return Container();
    }
  }

  void openScreen(String screenName, String userId, String loginType,
      BuildContext context) {
    if (screenName.toLowerCase() == "A".toLowerCase()) {
      setState(() {
        screenNameSelected = "A";
      });
    } else if (screenName.toLowerCase() == "B".toLowerCase()) {
      setState(() {
        screenNameSelected = "B";
      });
    } else if (screenName.toLowerCase() == "C".toLowerCase()) {
      setState(() {
        screenNameSelected = "C";
      });
    }

  }

}

由于我的应用栏高度定制,我为应用栏创建了自定义类

class CustomAppBar extends StatelessWidget {
  final String subTitleText;
  final String subTitleImage;

  const CustomAppBar(
      {Key key, @required this.subTitleText, @required this.subTitleImage})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        SafeArea(
          child: Column(
            children: <Widget>[
              Builder(builder: (context) => customAppBar(context)),
              SizedBox(
                height: 5.0,
              ),
              subTitleRow(
                subTitleText,
                subTitleImage,
              )
            ],
          ),
        ),

      ],
    );
  }
}

Widget subTitleRow(String subtitleText, String subtitleImage) {
.........
}


Widget customAppBar(BuildContext context) {
  return Material(
    elevation: 5.0,
    shadowColor: shadow,
    child: SafeArea(
      child: Stack(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              Image.asset(
                "images/toolbar_logo.webp",
                width: 80,
                height: 50,
              ),
            ],
          ),
          IconButton(
            icon: Image.asset("images/menu.webp"),
            onPressed: () {
              Scaffold.of(context).openDrawer();
            },
            iconSize: 20,
          ),
        ],
      ),
    ),
  );
}

现在我的A,B和C类没有脚手架,但我为D类提供了脚手架,我认为这是造成问题的原因。不为D类提供脚手架不会提供正确的布局

A,B和C类的代码如下

class A extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ShortBioProvider(
      child:  Column(
            children: <Widget>[
              CustomAppBar(
                subTitleImage: "images/settings.webp",
                subTitleText: SETTINGS.toUpperCase(),
              ),
              SizedBox(
                height: 20,
              ),
              SettingsList(),
            ],
          ),

    );
  }
}

B和C与A非常相似。

现在可以使用D类代码

class D extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Column(children: <Widget>[
      CustomAppBar(
        subTitleImage: "images/settings.webp",
        subTitleText: D.toUpperCase(),
      ),
      SizedBox(
        height: 20,
      ),
      ........
    ],),);
  }
}

我对上述类进行了很多编辑,因此可能在此处或此处缺少括号或分号

1 个答案:

答案 0 :(得分:1)

嗯,现在您已经添加了代码,很有意义。

发生的事情是,在D的情况下,您有两个不同的支架:

BaseScreenState
--> Scaffold 1
    -->  drawer
         body
           --> stack
             --> D
               --> Scaffold 2
                 -->CustomAppBar ....

当您执行Scaffold.of(context)时,它将在树中向上查找以找到最近的支架,在这种情况下,该支架恰好是没有抽屉的Scaffold 2。 D类中的脚手架似乎没有任何作用,因此删除它应该可以解决您的问题。

如何

您的代码中存在一些更严重的问题。您几乎永远不应使用堆栈在页面之间进行切换。一方面,flutter无法像这样那样缓存小部件,但是它也消除了诸如屏幕过渡之类的事情。

您应该做的是为应用程序中的每个页面制作一个包含脚手架,抽屉,应用程序栏等的页面,然后使用导航器在这些页面之间切换。 (请注意,仅因为您为每个页面都创建了一个小部件,并不意味着不能共享这些组件。)

扑朔迷离的架构如下:

  • 顶级Stateful小部件(有状态的状态对于热重装效果更好,无状态的状态会引起问题),类似于MyApp(但将My更改为实际应用的名称)。
  • 在构建函数中,这将创建一个MaterialApp。您可以潜在地将用于构建各种页面的逻辑放入materialApp的onGenerateRoute中-我发现,与动态构建事物相比,它可以更清楚轻松地讲述正在发生的事情。
  • 如果要按名称(字符串)在页面之间进行转换,则应将名称设置为全局常量。
  • 每个页面的有状态或无状态窗口小部件(除非页面足够相似以使用一些简单的参数进行区分),这在构建功能中将构成支架,应用栏,导航抽屉等
  • 可选地,支架也可以是其自己的小部件
  • 您的应用栏的小部件(即CustomAppBar)
  • 导航抽屉的小部件

其他一些需要注意的事情是:

  1. 通过构建函数而不是类来分解事物似乎更快,但事实并非如此。一个构建函数中包含两个小部件(不要被愚弄,调用一个从构建函数返回一个小部件的函数与出于性能目的将代码直接放入该函数相同)实际上意味着需要进行更多的计算每次需要重新构建窗口小部件时都会产生动摇,因此需要构建和检查整个类集,而如果在窗口小部件中使用适当的封装,则如果窗口小部件树的那部分没有任何变化,则可以跳过很多工作。
  2. 在每个页面中构建一个Scaffold,AppBar和Drawer似乎很浪费。但是,由于flutter如何缓存事物并智能地在小部件树中进行扫描,因此它的性能确实很高,因此flutter团队建议这样做。
  3. 始终确定要从哪个构建上下文中调用事物,然后可以跟进构建树以确定调用.of(context)时实际上正在调用哪个窗口小部件。

希望对您有帮助!