| 
 | 
 | 
 |
上面我们调整不同的 index 只显示出来了一个子项 Widget,如果我们把 IndexedStack 换成 Stack 则会显示成如下效果。
 
IndexedStack 源码
- alignment 对齐方式
 - sizing 填充方式
 - index 显示子项索引
 - children 子项集合
 
本篇主要聊 index 和 children ,其他的 alignment、sizing 可以稍后转看专栏中 Stack 详解篇,我们开始看看源码吧!
 
 我们可以看到 IndexedStack 其实就是 Stack 的子类,对齐方式和填充方式都是直接传递到父类的,所以我们这篇只关心 index 即可。
效果实现
主体内容
我们先开始主体部分的布局,这里使用 IndexedStack ,添加一个 currentIndex 来记录当前子项索引,当我们改变 currentIndex 值后刷新即可实现切换页面效果啦。
/// IndexedStack 页面
 class IndexedStackPage extends StatefulWidget {
 IndexedStackPage({Key? key}) : super(key: key);
@override
 _IndexedStackPageState createState() => _IndexedStackPageState();
 }
class _IndexedStackPageState extends State {
 // 当前子项索引
 int currentIndex = 0;
 @override
 Widget build(BuildContext context) {
 return Scaffold(
 appBar: AppBar(
 title: Text(‘IndexedStack - ZeroFlutter’),
 ),
 body: IndexedStack(
 alignment: Alignme
 nt.center,
 // 设置当前索引
 index: currentIndex,
 children: [
 PageDetails(title: ‘首页’),
 PageDetails(title: ‘消息’),
 PageDetails(title: ‘我的’),
 ],
 ),
 bottomNavigationBar: [下面聊到],
 );
 }
 }
子项内容
这里我们简单使用一个 StatelessWidget 然后再脚手架中添加一个居中的 Text 用来显示一个页面 title。
/// 页面详情
 class PageDetails extends StatelessWidget {
 const PageDetails({Key? key, required this.title}) : super(key: key);
 final String title;
@override
 Widget build(BuildContext context) {
 // 这里的打印可以记录一下,后面会用到
 print(‘PageDetails build title:$title’);
 return Scaffold(
 body: Center(
 child: Text(title),
 ),
 );
 }
 }
底部导航
我们使用 BottomNavigationBar 做个简单的底部切换的导航,之后会详细聊聊这个 Widget,记得关注我得专栏《Flutter Widgets》不光是简单介绍Widget,各种实用技法、源码解析、Widget 关系链梳理都会有。
Scaffold(
 appBar:[…],
 body: IndexedStack(),
 // 底部导航栏
 bottomNavigationBar: BottomNavigationBar(
 // 当前索引和 IndexedStack 使用同一个变量
 currentIndex: currentIndex,
 // 导航子项集
 items: [
 // 导航子项
 BottomNavigationBarItem(
 // 图标
 icon: Icon(Icons.home),
 // 文字内容
 label: ‘首页’,
 ),
 BottomNavigationBarItem(
 icon: Icon(Icons.message_rounded),
 label: ‘消息’,
 ),
 BottomNavigationBarItem(
 icon: Icon(Icons.people),
 label: ‘我的’,
 ),
 ],
 onTap: (value) {
 // 点击事件,用于改变当前索引,然后刷新
 currentIndex = value;
 setState(() {});
 },
 ),
 )
效果展示
这样我们就实现了展示的效果
 
精进优化
还记得我们上面在子项详情页打印的内容吗?再看一遍他
/// 页面详情
 class PageDetails extends StatelessWidget {
 const PageDetails({Key? key, required this.title}) : super(key: key);
 final String title;
@override
 Widget build(BuildContext context) {
 // 这里的打印可以记录一下,后面会用到
 print(‘PageDetails build title:$title’);
 return Scaffold(
 body: Center(
 child: Text(title),
 ),
 );
 }
 }
现在当我们切换导航的时候,看看控制台的打印是什么?
 
 首次进来打印了 1 次,之后切换了 2 次又分别打印了 2 次。我们这里做了一个简单的主页,实际项目中这4个页面承担了很多,也是用户接触最多的页面,要是切换就 build 一次这谁顶得住,我们必须优化。
如何优化?
分析原因
这里我们为了简单 PageDetails 是继承自 StatelessWidget 的,那么 StatelessWidget 发生 build 无外乎以下 3 种情况(源码注释中有写到)
- 1、第一次插入到树🌲中
 - 2、Widget 父类改变了配置
 - 3、依赖 
InheritedWidget改变了他 
那么到底是谁改变了他呢?我们来一起分析一下。
- 第 1 项没有什么疑问
 - 第 3 项我们没有依赖于 
InheritedWidget - 那就只有第 2 项了
 
BottomNavigationBar(
 // 当前索引和 IndexedStack 使用同一个变量
 currentIndex: currentIndex,
 onTap: (value) {
 // 点击事件,用于改变当前索引,然后刷新
 currentIndex = value;
 setState(() {});
 },
 )
在回到现象,当我们切换的时候会重新build,这里我们只有这里改变了 currentIndex 的值并且调用了 setState 刷新,然后父类(IndexedStackPage)重新 build 了,所以子项的详情页面也会跟着 build。
 urrentIndex = value;
 setState(() {});
 },
 )
在回到现象,当我们切换的时候会重新build,这里我们只有这里改变了 currentIndex 的值并且调用了 setState 刷新,然后父类(IndexedStackPage)重新 build 了,所以子项的详情页面也会跟着 build。










