我正在创建一个带有 Scaffold 的应用程序,其中包含:
FutureBuilder在创建身体PageView数据加载时,作为其子。BottomNavigationBar,与同步PageView一个更直观的导航。功能方面,一切正常。我可以在页面之间向左和向右滑动,并且在 中currentIndex正确更新BottomNavigationBar,如果我点击BottomNavigationBar元素,PageView它将按预期动画到正确的页面。
然而......在页面之间切换时性能真的很差,即使在配置文件模式下也是如此。
大量调查之后,我确认滞后只存在,如果我更新currentIndex的BottomNavigationBar。
如果我不更新BottomNavigationBar,则在页面之间切换时,无论是在 上滑动PageView还是在点击BottomNavigationBar元素本身时,动画都会保持非常流畅。
我还可以确认,在使用setState和使用Provider. 我真的希望这只是setState方法效率低下......但没有运气:(
对于setState实施,这就是我正在做的事情:
在PageView:
onPageChanged: (page) {
setState(() {
_selectedIndex = page;
});
}
Run Code Online (Sandbox Code Playgroud)
在底部栏导航上:
onTap: _onTappedBar,
currentIndex: _selectedIndex
Run Code Online (Sandbox Code Playgroud)
及以下:
void _onTappedBar(int value) {
_pageController.animateToPage(value, duration: Duration(milliseconds: 500), curve: Curves.ease);
setState(() {
_selectedIndex = value;
});
}
Run Code Online (Sandbox Code Playgroud)
如果我注释掉这两种setState方法,应用程序将再次变得平滑,我也可以BottomNavigationBar正确使用- 它只是不会更新所选项目。
有趣的是,如果我只注释掉这两个setState方法 (_selectedIndex = page;和_selectedIndex = value;) 中的行但将方法留在那里,即使方法完全为空并且没有更新任何内容,应用程序仍然完全相同setState......?
这是Provider版本:
在PageView:
onPageChanged: (page) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = page;
}
Run Code Online (Sandbox Code Playgroud)
在BottomBarNavigation:
onTap: _onTappedBar,
currentIndex: Provider.of<BottomNavigationBarProvider>(context).currentIndex,
Run Code Online (Sandbox Code Playgroud)
及以下:
void _onTappedBar(int value) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = value;
pageController.animateToPage(value, duration: Duration(milliseconds: 500), curve: Curves.ease);
}
Run Code Online (Sandbox Code Playgroud)
如前所述,就像setState版本一样滞后:(
知道是什么导致了这种滞后以及如何解决这个问题吗?我真的不知道还能尝试什么。
好的,所以我想我设法解决了它,同时还学到了有关 Flutter 的宝贵经验!
我在setState/Provider困境的正确轨道上-如果您想避免重建整个页面,您确实需要使用Provider(或其他状态管理解决方案)。
然而,这还不够。
为了利用该实现的模块化,您还需要BottomNavigationBar在主小部件之外提取相关小部件(在本例中为整个)。如果不这样做,似乎主页上的所有内容仍会重建,即使只有一个小部件正在侦听Provider通知。
所以这是我root_screen的build方法现在的结构(为了可读性而简化了正文内容):
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
children: <Widget>[
HomeScreen(),
PerformanceScreen(),
SettingsScreen(),
],
onPageChanged: (page) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = page;
},
);
bottomNavigationBar: MyBottomNavigationBar(onTapped: _onTappedBar),
);
}
Run Code Online (Sandbox Code Playgroud)
注意这个bottomNavigationBar:参数是如何不再定义的root_screen。相反,我StatelessWidget在一个单独的 Dart 文件中创建了一个新类 (a ),它接受一个onTapped函数作为参数,并从这里实例化它。
所述_onTappedBar函数就在 上定义root_screen,就在build方法下方:
void _onTappedBar(int value) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = value;
_pageController.animateToPage(value, duration: Duration(milliseconds: 500), curve: Curves.ease);
}
Run Code Online (Sandbox Code Playgroud)
这是包含新MyBottomNavigationBar类的单独 Dart 文件:
class MyBottomNavigationBar extends StatelessWidget {
@override
const MyBottomNavigationBar({
Key key,
@required this.onTapped,
}) : super(key: key);
final Function onTapped;
Widget build(BuildContext context) {
return BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(
icon: Icon(Icons.trending_up), title: Text('Performance')),
BottomNavigationBarItem(
icon: Icon(Icons.settings), title: Text('Settings')),
],
onTap: onTapped,
currentIndex:
Provider.of<BottomNavigationBarProvider>(context).currentIndex,
);
}
}
Run Code Online (Sandbox Code Playgroud)
同样为了完整性(并且因为我绝对需要知道),我setState再次尝试使用该方法,同时将 保留BottomNavigationBar在新的单独文件中。我想了解是否简单地提取小部件就足以解决问题,或者无论如何您仍然需要使用状态管理解决方案。
事实证明……这还不够!使用 setState 的性能再次糟糕透顶,即使 BottomNavigationBar 小部件是在其自己的类文件中提取的。
因此,最重要的是,为了保持您的应用程序高效和动画流畅,请记住尽可能多地提取小部件并模块化您的 Flutter 代码,以及使用状态管理解决方案而不是setState. 这似乎是避免不必要的重绘的唯一方法(并且您的代码显然会更清晰,更易于调试)。
| 归档时间: |
|
| 查看次数: |
1665 次 |
| 最近记录: |