Home Articles

调用Navigator.pushNamed时,bottomNavigationBar会多次重建

Asked
Viewed 706 times
0

我对Flutter项目的结构有疑问 .

目前结构如下:

带有bottomNavigationBar的主页包含多个选项卡,每个选项卡都是StatefulWidget,包含一些繁重的处理(远程API调用和数据显示) .

如果我从任何选项卡内部调用Navigator.pushNamed,则会发生以下情况:

  • 正在后台重建所有选项卡(进行API调用等) .

  • 新页面正常打开 .

  • 当我按下后退按钮时,页面关闭,所有标签再次重建 .

因此,总共所有(每个选项卡)重建2次只是为了打开外部导航器页面 .

这是某种错误吗?完全不可理解为什么它在推出新路线之前完全重建bottomNavigationBar .

它应该如何工作:

  • 当我从选项卡内部调用Navigator.pushNamed时,应该打开新页面,并且不应重建所有bottomNavigationBar选项卡并保持不变状态 .

  • 当我按下时,页面应该关闭,用户返回到相同的bottomNavigationBar状态和它的选项卡,根本没有重建 .

这有可能实现吗?

这是代码:

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => new _HomePageState();
}

class _HomePageState extends State<HomePage> {


  int index = 0;

  final _tab1 = new tab1();   //StatefulWidget, api calls, heavy data processing
  final _tab2 = new tab2();   //StatefulWidget, api calls, heavy data processing


  @override
  Widget build(BuildContext context) {

    debugPrint('homepage loaded:'+index.toString());


    return new Scaffold(
      body: new Stack(
        children: <Widget>[

          new Offstage(
            offstage: index != 0,
            child: new TickerMode(
              enabled: index == 0,
              child: _tab1,
            ),
          ),
          new Offstage(
            offstage: index != 1,
            child: new TickerMode(
              enabled: index == 1,
              child: _tab2,   
            ),
          ),


        ],
      ),
      bottomNavigationBar: new BottomNavigationBar(
        currentIndex: index,
        type: BottomNavigationBarType.fixed,
        onTap: (int index) { setState((){ this.index = index; }); },
        items: <BottomNavigationBarItem>[
          new BottomNavigationBarItem(
            icon: new Icon(Icons.live_help),
            title: new Text("Tab1"),
          ),
          new BottomNavigationBarItem(
            icon: new Icon(Icons.favorite_border),
            title: new Text("Tab 2"),
          ),


        ],
      ),
    );
  }
}

这是单个标签代码:

class tab1 extends StatefulWidget {



  @override
  tab1State createState() => new tab1State();
}


class tab1State extends State<tab1> {


  @override
  Widget build(BuildContext cntx) {


    debugPrint('tab loaded');  //this gets called when Navigator.pushNamed called and when back button pressed

    //some heave processing with http.get here...   
    //...


    return new Center(
        child: new RaisedButton(
          onPressed: () {

            Navigator.pushNamed(context, '/some_other_page');


          },
          child: new Text('Open new page'),
        ));


  }

}

2 Answers

  • 0

    build 中,您应该只是构建Widget树,而不是进行任何繁重的处理 . 您的tab1是一个StatefulWidget,因此它的状态应该保持当前状态(包括繁重处理的结果) . 它的 build 应该只是呈现该状态的当前版本 .

    在tab1state中,覆盖 initState 以设置初始值,并可能启动一些异步函数以开始执行提取 - 一旦结果可用就调用 setState . 在 build 中,渲染当前状态,记住它可能只是部分可用,因为繁重的工作在后台继续 . 因此,例如,测试值为null并且可能用进度指示器或空容器替换它们 .

    使用StreamBuilder和FutureBuilder可以使fetch / setState /(可能是部分)渲染更加优雅 .

  • 0

    由于您在 build 函数内构建 BottomNavigationBar ,因此将重建 every time state changes .

    为避免这种情况,您可以在 initState() 方法中构建 BottomNavigationBar ,如下所示,

    class HomePage extends StatefulWidget {
      @override
      _HomePageState createState() => new _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      BottomNavigationBar _bottomNavigationBar;
    
      @override
      void initState() {
        super.initState();
    
        _bottomNavigationBar = _buildBottomNavigationBar();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: new Center(child: new Text('Hello', style: new TextStyle(decoration: TextDecoration.underline),),),
          bottomNavigationBar: _bottomNavigationBar, // Use already created BottomNavigationBar rather than creating a new one
        );
      }
    
      // Create BottomNavigationBar
      BottomNavigationBar _buildBottomNavigationBar() {
        return new BottomNavigationBar(
          items: [
            new BottomNavigationBarItem(
                icon: new Icon(Icons.add),
                title: new Text("trends")
            ),
            new BottomNavigationBarItem(
                icon: new Icon(Icons.location_on),
                title: new Text("feed")
            ),
            new BottomNavigationBarItem(
                icon: new Icon(Icons.people),
                title: new Text("community")
            )
          ],
          onTap: (index) {},
        );
      }
    }
    

Related