草庐IT

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

coder 2023-07-24 原文

我想在所有屏幕上保留 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,
      ),
      ........
    ],),);
  }
}

我对上面的类进行了很多编辑,所以这里或那里可能缺少括号或分号

最佳答案

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

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

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

当您执行 Scaffold.of(context) 时,它会向上查找树以找到最近的脚手架,在本例中恰好是没有抽屉的脚手架 2。你的 D 类中的脚手架似乎没有做任何事情,所以删除它应该可以解决你的问题。

然而

您的代码中存在一些更严重的问题。您几乎不应该使用堆栈在页面之间切换。一方面,flutter 无法以这种方式缓存小部件,但它也消除了屏幕转换等内容。

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

一个在flutter中有意义的架构如下:

  • 顶级有状态小部件(有状态更适合热重载,无状态小部件可能会导致问题),类似于 MyApp(但将 My 更改为实际应用的名称)。
  • 在构建函数中,这会创建一个 MaterialApp。您可能会将构建各种页面的逻辑放入 materialApp 的 onGenerateRoute - 我发现这可以比动态构建更清晰、更容易地判断正在发生的事情。
  • 如果您通过名称(字符串)在页面之间转换,您应该将名称设为全局常量。
  • 每个页面的有状态或无状态小部件(除非页面足够相似,可以通过一些简单的参数进行区分),在构建函数中将制作脚手架、应用栏、抽屉导航等
  • 可选地,脚手架也可以是它自己的小部件
  • 您的应用栏的小部件(即 CustomAppBar)
  • 用于您的抽屉导航的小部件

还有一些需要注意的事情是:

  1. 按构建函数而不是类拆分内容似乎更快,但事实并非如此。在一个构建函数中有两个小部件(不要被愚弄,调用一个从构建函数返回一个小部件的函数与将代码直接放在函数中以提高性能是一样的)实际上意味着更多的计算每次需要重建小部件时,都需要构建和检查整个类集,而如果您在小部件中使用适当的封装,那么如果小部件树的那部分没有任何变化,则可以跳过很多工作。
  2. 在每个页面中构建 Scaffold、AppBar 和 Drawer 似乎很浪费。然而,由于 Flutter 缓存事物并智能地扫描小部件树的方式,它实际上非常高效,这就是 Flutter 团队推荐的做事方式。
  3. 始终弄清楚您从哪个构建上下文调用东西,然后您可以跟进构建树以确定在调用 .of(context) 时实际调用了哪个小部件。

希望对您有所帮助!

关于dart - 抽屉导航不适用于屏幕内的屏幕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54540486/

有关dart - 抽屉导航不适用于屏幕内的屏幕的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - inverse_of 是否适用于 has_many? - 2

    当我使用has_one时,它​​工作得很好,但在has_many上却不行。在这里您可以看到object_id不同,因为它运行了另一个SQL来再次获取它。ruby-1.9.2-p290:001>e=Employee.create(name:'rafael',active:false)ruby-1.9.2-p290:002>b=Badge.create(number:1,employee:e)ruby-1.9.2-p290:003>a=Address.create(street:"123MarketSt",city:"SanDiego",employee:e)ruby-1.9.2-p290

  4. 屏幕录制为什么没声音?检查这2项,轻松解决 - 2

    相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声

  5. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  6. ruby - "undefined method"用于 rails 模型 - 2

    我正在使用带有Rails的Devise,我想添加一个方法“getAllComments”,所以我这样写:classUser在我的Controller中:defdashboard@user=current_user@comments=@user.getAllComments();end当我访问我的url时,我得到了undefinedmethod`getAllComments'for#我做错了什么?谢谢 最佳答案 因为getAllComments是一个类方法,而您正试图将其作为实例方法访问。您要么需要访问它:User.getAllCom

  7. Ruby on Rails regexp equals-tilde 与 array include 用于检查选项列表 - 2

    我正在使用Rails3.2.3和Ruby1.9.3p0。我发现我经常需要确定某个字符串是否出现在选项列表中。看来我可以使用Ruby数组.includemethod:或正则表达式equals-tildematchshorthand用竖线分隔选项:就性能而言,一个比另一个好吗?还有更好的方法吗? 最佳答案 总结:Array#include?包含String元素,在接受和拒绝输入时均胜出,对于您的示例只有三个可接受的值。对于要检查的更大的集合,看起来Set#include?和String元素可能会获胜。如何测试我们应该根据经验对此进行测试

  8. ruby - 如何在ruby中提取方括号内的内容 - 2

    我正在尝试提取方括号内的内容。到目前为止,我一直在使用它,它有效,但我想知道我是否可以直接在正则表达式中使用某些东西,而不是使用这个删除功能。a="Thisissuchagreatday[coolawesome]"a[/\[.*?\]/].delete('[]')#=>"coolawesome" 最佳答案 差不多。a="Thisissuchagreatday[coolawesome]"a[/\[(.*?)\]/,1]#=>"coolawesome"a[/(?"coolawesome"第一个依赖于提取组而不是完全匹配;第二个利用前瞻和

  9. ruby-on-rails - Ruby "Undefined Method"用于类方法 - 2

    Ruby初学者努力简单地将这个@@people散列的值打印到控制台classPerson#haveafirst_nameandlast_nameattributewithpublicaccessorsattr_accessor:first_nameattr_accessor:last_name#haveaclassattributecalled`people`thatholdsanarrayofobjects@@people=[]#havean`initialize`methodtoinitializeeachinstancedefinitialize(first_name,last_

  10. python - 用于 Python 或 Ruby 的 Amazon Book API? - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:AmazonAPIlibraryforPython?我正在寻找一个AmazonAPI,它可以让我:按书名或作者查找书籍显示书籍封面获取有关每本书的信息(价格、评级、评论数、格式、页数等)Python或Ruby库都可以(我只想要最容易使用的库)。有什么建议么?我知道在SO上还有其他一些关于此的帖子,但这些API似乎很快就过时了。[几个月前我尝试了几个建议的Ruby库,但无法让它们中的任何一个工作。]

随机推荐