【问题标题】:Flutter Navigation stack with Drawer带抽屉的颤振导航堆栈
【发布时间】:2019-11-03 16:34:16
【问题描述】:

我正在使用 Flutter 构建一个多页面应用程序,但我似乎在错误地处理导航。我注意到,当我在屏幕之间导航时,它似乎只是不断地将页面推送到我的堆栈上,并且每次都“评估”整个堆栈。我有一个应用抽屉小部件,它包含在我的所有页面上,如下所示:

@override
  Widget build(BuildContext context) {
    AuthenticationContext authenticationContext =
      AuthenticationContext.of(context);
    auth = authenticationContext.auth;
    var drawerOptions = <Widget>[];
    for (var i = 0; i < drawerItems.length; i++) {
      var d = drawerItems[i];
      drawerOptions.add(new ListTile(
        leading: new Icon(d.icon),
        title: new Text(d.title),
        selected: i == _selectedDrawerIndex,
        onTap: () => _onSelectItem(i),
      ));
    }

    return new Drawer(
      child: new Column(
        children: <Widget>[
          UserAccountsDrawerHeader(
            accountName: Text(widget.name != null ? widget.name : ""),
            accountEmail: Text(widget.email),
            currentAccountPicture: CircleAvatar(
              child: new Text(
                  widget.photoUrl == null ? widget.email[0].toUpperCase() : ""),
            ),
          ),
          new Column(children: drawerOptions)
        ],
      ),
    );
  }

  _getDrawerItemWidget(int pos) {
    switch (pos) {
      case 0:
        return new HomePage();
      case 1:
        return new UserPantryPage();
      case 2:
        return new ShoppingListPage();
      case 3:
        return new FavoritesPage();
      case 4:
        auth.signOut();
        return new RootPage();
      default:
        return new Text("Error");
    }
  }

  _onSelectItem(int index) {
    Navigator.of(context).pop();
    Navigator.of(context)
        .push(MaterialPageRoute<Null>(builder: (BuildContext context) {
      return _getDrawerItemWidget(index);
    }));
  }

我的每个页面小部件中都有调试打印语句,如果我开始在抽屉上单击以在页面之间导航,我注意到它将开始从主页打印调试语句,即使我'正在导航到一个完全独立的页面。我最初注意到它是因为我的一个页面调用了一些 API 来获取其构建方法中的数据,而且我使用该应用程序的次数越多,调用的 API 就越多。我将其追溯到这样一个事实,即所有页面小部件的构建方法似乎都被调用,即使当我要去另一个页面时,在多页面导航方面,我在这里做错了什么吗?页面应用?

【问题讨论】:

    标签: flutter


    【解决方案1】:

    当您四处导航时,Flutter 会播放屏幕转换的动画。因此,被动画化的小部件会不断重建,因为它们可能会在动画过程中发生视觉变化。使用默认的淡入淡出和滑动动画,您往往会同时构建 3 个屏幕 - 旧的屏幕逐渐消失,新的屏幕淡入,而旧屏幕下方的屏幕由于其他两个屏幕的一部分而略微可见褪色时透明。

    您的问题的解决方案(以及一般使用 Flutter 的方法)是将与 UI 无关的所有内容移出 build 函数。如果您需要在初始化小部件时获取一些数据,请将该代码移至您的 StatefulWidgetinitState 函数。

    【讨论】:

    • 试图把所有的部分放在一起。如果我的 Widget 使用 FutureBuilder ,其中 Future 依赖于 Context 中的某些信息(我正在使用一些 InheritedWidgets 来传递某些信息),那么我将如何在 initState 中执行此操作,因为我无法访问 Context ?我想我只是在这里有点绕轴,可能过于复杂了。
    • 听起来您确实使事情变得过于复杂,但是如果不了解您的用例,我无法确定-这很可能是您要实现的目标的最佳方法。至于 FutureBuilder,我个人会因为类似的问题而尽量避免,并使用一些老式的代码来确保我得到我想要的状态。您有各种复杂性不同的选项 - 在initState 中使用WidgetsBinding.instance.addPostFrameCallback(),因为您将在下一帧中拥有上下文,或者将未来存储在状态中并在build 中初始化它,如果它为空。
    • 我对 Flutter 和 Dart 都很陌生(我的背景主要是 Java),我遇到的大多数资源都表明使用 FutureBuilder,但我绝对没有大量的成功。它做了很多我不希望它做的事情。我有很多 firebase 交互,而且所有这些都是异步的,所以我需要一些方法来使用这些结果来填充我的小部件而不阻塞 UI 线程。
    • 我想也许切换到这样的东西:medium.com/coding-with-flutter/… 不过会更好。这似乎更符合我过去所做的其他事情,并且看起来更直观。
    【解决方案2】:

    试试下面的伪代码。问题是 build 方法可以在应用程序生命周期中被多次调用,所以为了避免你的代码被多次执行,你将它移动到只调用一次的初始状态,要么使用覆盖方法,要么如下面的示例所示.更多信息在这里 - https://flutterbyexample.com/stateful-widget-lifecycle/

    class NavigationDraw extends StatefulWidget {
      NavigationDraw({Key key, this.email, this.name}) : super(key:key);
      final String email;
      final String name;
      @override
      _NavigationDrawState createState() =>
          new _NavigationDrawState();
    }
    
    class _NavigationDrawState extends State<NavigationDraw> {
      BuildContext _ctx;
      var navi;
    
    _NavigationDrawState () {
        navi = sideNavi();
      }
    
    
    @override
      Widget build(BuildContext context) {
    
        return navi;
    
      }
    
    sideNavi(){
         _getDrawerItemWidget(int pos) {
            switch (pos) {
              case 0:
                return new HomePage();
              case 1:
                return new UserPantryPage();
              case 2:
                return new ShoppingListPage();
              case 3:
                return new FavoritesPage();
              case 4:
                auth.signOut();
                return new RootPage();
              default:
                return new Text("Error");
            }
          }
    
          _onSelectItem(int index) {
            Navigator.of(context).pop();
            Navigator.of(context)
                .push(MaterialPageRoute<Null>(builder: (BuildContext context) {
              return _getDrawerItemWidget(index);
            }));
          }
      AuthenticationContext authenticationContext = AuthenticationContext.of(context);
        auth = authenticationContext.auth;
        var drawerOptions = <Widget>[];
    
        for (var i = 0; i < drawerItems.length; i++) {
          var d = drawerItems[i];
          drawerOptions.add(
             new ListTile(
             leading: new Icon(d.icon),
             title: new Text(d.title),
             selected: i == _selectedDrawerIndex,
             onTap: () => _onSelectItem(i),
            ));
        }
       return new Drawer(
          child: new Column(
            children: <Widget>[
              UserAccountsDrawerHeader(
                accountName: Text(widget.name != null ? widget.name : ""),
                accountEmail: Text(widget.email),
                currentAccountPicture: CircleAvatar(
                  child: new Text(
                      widget.photoUrl == null ? widget.email[0].toUpperCase() : ""),
                ),
              ),
              new Column(children: drawerOptions)
            ],
          ),
        );
    }
    

    【讨论】:

    • 这可以防止为特定的小部件多次调用构建方法,但它仍然不能解决我遇到的问题,例如,如果我在其中单击一下,顺序:ShoppingListPage、FavoritesPage、HomePage。当我点击 HomePage 时,我可以看到 ShoppingListPage 和 FavoritesPage 构建方法都被调用了,尽管我什至不在这些页面上。
    猜你喜欢
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 2018-12-09
    • 2020-12-11
    • 2018-03-26
    • 2020-04-15
    • 2022-06-30
    • 1970-01-01
    相关资源
    最近更新 更多