我对Flutter项目的结构有疑问。
目前结构如下:
带有bottomNavigationBar的主页,其中包含多个选项卡,每个选项卡都是StatefulWidget,包含一些繁重的处理(远程API调用和数据显示)。
如果我从任何标签内部调用Navigator.pushNamed,则会发生以下情况:
总而言之,所有(每个标签)都重建了两次,只是为了打开外部导航页面。
这是某种错误吗?完全不可理解为什么它在推出新路线之前完全重建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'),
));
}
}
答案 0 :(得分:0)
在build
中,你应该只是构建Widget树,不进行任何繁重的处理。您的tab1是一个StatefulWidget,因此它的状态应该保持当前状态(包括繁重处理的结果)。它的build
应该只是呈现该状态的当前版本。
在tab1state中,覆盖initState
以设置初始值,并可能启动一些异步函数以开始进行提取 - 一旦结果可用就调用setState
。在build
中,渲染当前状态,记住它可能只是部分可用,因为繁重的工作在后台继续。因此,例如,测试值为null并且可能用进度指示器或空容器替换它们。
使用StreamBuilder和FutureBuilder可以使fetch / setState /(可能是部分)渲染更加优雅。
答案 1 :(得分:0)
由于您要在 BottomNavigationBar
功能中构建 build
,因此会重建 every time state changes
为避免这种情况,您可以在 BottomNavigationBar
方法中构建 initState()
,如下所示,
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) {},
);
}
}