likes
comments
collection
share

Flutter 中 IndexedStack 的使用详解 | Flutter Widgets

作者站长头像
站长
· 阅读数 20

前言

上篇我们聊了 Stack 及其子项精细化布局 Widget Positioned、Align、Center 的详细使用,这篇我们来聊聊一个非常实用但你可能不知道的 Widget IndexedStack,我们不光要聊他的使用,还要聊聊怎么优化它,这是实践的经验希望可以分享给你。

看效果

先看看下面的效果一般是 App 的简单主页骨架,你知道他怎么实现的吗?Flutter 中 IndexedStack 的使用详解 | Flutter Widgets

IndexedStack(索引栈布局)

上一篇我们聊 Stack 时,子项的显示层级会根据子项在 children 列表中的顺序排列,位置越靠后,显示越靠前。而这篇我们要聊的 IndexedStack 则不会根据顺序来决定显示顺序,而是只会显示指定索引的子项 Widget。

小实验

为了验证上面的说法,我们来做一个小实验,如下代码你猜会显示什么颜色?

// 索引栈布局
IndexedStack(
  // 居中对齐,可以看上篇各种对齐方式详解
  alignment: Alignment.center,
  // 这里设置索引是 0
  index: 0,
  children: [
    // 黄色 300
    Container(
      width: 300,
      height: 300,
      color: Colors.orange,
    ),
    // 红色 200
    Container(
      width: 200,
      height: 200,
      color: Colors.red,
    ),
    // 蓝色 100
    Container(
      width: 100,
      height: 100,
      color: Colors.blue,
    ),
  ],
)
index:0index:1index:2
Flutter 中 IndexedStack 的使用详解 | Flutter WidgetsFlutter 中 IndexedStack 的使用详解 | Flutter WidgetsFlutter 中 IndexedStack 的使用详解 | Flutter Widgets

上面我们调整不同的 index 只显示出来了一个子项 Widget,如果我们把 IndexedStack 换成 Stack 则会显示成如下效果。Flutter 中 IndexedStack 的使用详解 | Flutter Widgets

IndexedStack 源码

  • alignment 对齐方式
  • sizing 填充方式
  • index 显示子项索引
  • children 子项集合

本篇主要聊 indexchildren ,其他的 alignment、sizing 可以稍后转看专栏中 Stack 详解篇,我们开始看看源码吧!Flutter 中 IndexedStack 的使用详解 | Flutter Widgets我们可以看到 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<IndexedStackPage> {
  // 当前子项索引
  int currentIndex = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('IndexedStack - ZeroFlutter'),
      ),
      body: IndexedStack(
        alignment: Alignment.center,
        // 设置当前索引
        index: currentIndex,
        children: [
          PageDetails(title: '首页'),
          PageDetails(title: '消息'),
          PageDetails(title: '我的'),
        ],
      ),
      bottomNavigationBar: [下面聊到],
    );
  }
}

子项内容

这里我们简单使用一个 StatelessWidget 然后再脚手架中添加一个居中的 Text 用来显示一个页面 title

这里的 print 方法也先记录一下,下面会用到。

/// 页面详情
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),
      ),
    );
  }
}

底部导航

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(() {});
    },
  ),
)

效果展示

这样我们就实现了展示的效果Flutter 中 IndexedStack 的使用详解 | Flutter Widgets

精进优化

还记得我们上面在子项详情页打印的内容吗?再看一遍他

/// 页面详情
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),
      ),
    );
  }
}

现在当我们切换导航的时候,看看控制台的打印是什么?Flutter 中 IndexedStack 的使用详解 | Flutter Widgets首次进来打印了 1 次,之后切换了 2 次又分别打印了 2 次。我们这里做了一个简单的主页,实际项目中这4个页面承担了很多,也是用户接触最多的页面,要是切换就 build 一次这谁顶得住,我们必须优化。

如何优化?

分析原因

这里我们为了简单 PageDetails 是继承自 StatelessWidget 的,那么 StatelessWidget 发生 build 无外乎以下 3 种情况(源码注释中有写到)

  • 1、第一次插入到树🌲中
  • 2、Widget 父类改变了配置
  • 3、依赖 InheritedWidget 改变了他

那么到底是谁改变了他呢?我们来一起分析一下。

  • 第 1 项没有什么疑问
  • 第 3 项我们没有依赖于 InheritedWidget
  • 那就只有第 2 项了

还记得记录当前索引的变量 currentIndexBottomNavigationBaronTap 方法吗?

BottomNavigationBar(
  // 当前索引和 IndexedStack 使用同一个变量
  currentIndex: currentIndex,
  onTap: (value) {
    // 点击事件,用于改变当前索引,然后刷新
    currentIndex = value;
    setState(() {});
  },
)

在回到现象,当我们切换的时候会重新build,这里我们只有这里改变了 currentIndex 的值并且调用了 setState 刷新,然后父类(IndexedStackPage)重新 build 了,所以子项的详情页面也会跟着 build

调整优化

我们现在分析出了原因,那么接下来怎么去优化代码呢?我这里提供 2 种方式

  • 添加 const 关键字修饰 PageDetails
  • 将子项集初始化放到 initState 当中
IndexedStack(
  alignment: Alignment.center,
  index: currentIndex,
  children: [
    // 这里添加 const 关键字修饰
    const PageDetails(title: '首页'),
    const PageDetails(title: '消息'),
    const PageDetails(title: '我的'),
  ],
)
// 当前子项索引
int currentIndex = 0;
// 子项集
// 注意空安全的 late 字段修饰哦,因为我们需要延迟初始化
late List<Widget> children;

@override
void initState() {
  super.initState();
  // 初始化子项集合
  children = [
    PageDetails(title: '首页'),
    PageDetails(title: '消息'),
    PageDetails(title: '我的'),
  ];
}
IndexedStack(
  alignment: Alignment.center,
  index: currentIndex,
  // 设置子项集
  children: children,
)

优化效果

Flutter 中 IndexedStack 的使用详解 | Flutter Widgets这里看到我们切换到了「我的」页面但是没有发生重复 build 的现象。

到此这篇就完了,你有收获吗?如果有记得点赞哦

源码仓库

基于 Flutter 🔥 最新版本

参考链接

关注专栏

  • 此文章已收录到下面👇 的专栏,可以直接关注
  • 更多文章继续阅读|系列文章持续更新

👏 欢迎点赞➕收藏➕关注,有任何问题随时在下面👇评论,我会第一时间回复哦

转载自:https://juejin.cn/post/6973534234233274404
评论
请登录