在这里插入图片描述

底部导航栏是App的核心交互组件,用户通过它在主要功能模块之间切换。我们的底部导航有四个Tab:首页、发布、消息、我的。其中"发布"按钮做成中间凸起的样式,突出这个核心功能。

完整代码实现

import 'package:flutter/material.dart';
import 'package:convex_bottom_bar/convex_bottom_bar.dart';
import 'home/home_page.dart';
import 'publish/publish_page.dart';
import 'message/message_page.dart';
import 'profile/profile_page.dart';

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  
  State<MainPage> createState() => _MainPageState();
}

这里导入了convex_bottom_bar包来实现中间凸起的导航栏效果,这个第三方库比Flutter原生的BottomNavigationBar更灵活。MainPage使用StatefulWidget是因为需要管理当前选中Tab的索引状态,点击不同Tab时要更新界面。

class _MainPageState extends State<MainPage> {
  int _currentIndex = 0;
  
  final List<Widget> _pages = [
    const HomePage(),
    const PublishPage(),
    const MessagePage(),
    const ProfilePage(),
  ];

_currentIndex记录当前选中的Tab,默认是0即首页。_pages列表存放四个主要页面实例,顺序要和底部Tab对应。用const修饰可以让Flutter复用这些Widget实例,提升性能。

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      bottomNavigationBar: ConvexAppBar(
        style: TabStyle.fixedCircle,
        backgroundColor: Colors.white,
        activeColor: const Color(0xFF07C160),
        color: Colors.grey,
        items: const [
          TabItem(icon: Icons.home, title: '首页'),
          TabItem(icon: Icons.add_circle, title: '发布'),
          TabItem(icon: Icons.message, title: '消息'),
          TabItem(icon: Icons.person, title: '我的'),
        ],
        initialActiveIndex: _currentIndex,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}

页面主体用IndexedStack,它会同时持有所有子页面但只显示当前选中的那个。好处是切换Tab时页面状态不会丢失,比如首页滚动位置会保持。ConvexAppBarstyle: TabStyle.fixedCircle让中间按钮固定凸起。activeColor用主题绿色,onTap回调通过setState更新索引触发界面刷新。

IndexedStack和PageView的对比

// IndexedStack的特点
IndexedStack(
  index: _currentIndex,
  children: _pages,  // 所有页面都会被创建
)

IndexedStack适合Tab数量少、需要保持状态的场景,所有页面在首次渲染时就会创建,切换只是改变显示哪个。

// PageView的特点
PageView(
  controller: _pageController,
  onPageChanged: (index) {
    setState(() => _currentIndex = index);
  },
  children: _pages,
)

PageView支持滑动切换,但默认不保持页面状态。如果需要保持,要配合AutomaticKeepAliveClientMixin使用。咱们这个App只有4个Tab,用IndexedStack完全够用。

ConvexAppBar的其他样式

// 选中哪个哪个凸起
ConvexAppBar(
  style: TabStyle.react,
  // ...
)

// 选中的变成圆形凸起
ConvexAppBar(
  style: TabStyle.reactCircle,
  // ...
)

// 文字在图标下方的凹槽里
ConvexAppBar(
  style: TabStyle.textIn,
  // ...
)

// 切换时有翻转动画
ConvexAppBar(
  style: TabStyle.flip,
  // ...
)

ConvexAppBar提供了多种样式可选,react是选中哪个哪个凸起,reactCircle是圆形凸起,textIn把文字放在凹槽里,flip有翻转动画。根据产品需求选择合适的样式,我们用fixedCircle是因为发布功能重要,要一直突出显示。

小结

这篇讲解了底部导航的实现,使用ConvexAppBar实现中间凸起效果,IndexedStack保持页面状态。核心就是管理好Tab索引,点击时更新状态切换页面。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐