flutter开发实战-实现自定义bottomNavigationBar样式awesome_bottom_bar

news2025/7/14 5:20:42

flutter开发实战-实现自定义bottomNavigationBar样式awesome_bottom_bar

在开发过程中,需要自定义bottomNavigationBar样式,可以自定义实现,这里使用的是awesome_bottom_bar库

在这里插入图片描述

一、awesome_bottom_bar

在pubspec.yaml中引入awesome_bottom_bar

awesome_bottom_bar: ^1.2.2

二、实现自定义bottomNavigationBar

切换界面使用PageView.builder

PageView 是一个非常重要的组件。比如大多数 App 都包含 Tab 换页效果、图片轮动以及抖音上下滑页切换视频功能等等都可以使用PageView来实现

PageView({
  Key? key,
  this.scrollDirection = Axis.horizontal, // 滑动方向
  this.reverse = false,
  PageController? controller,
  this.physics,
  List<Widget> children = const <Widget>[],
  this.onPageChanged,
  
  //每次滑动是否强制切换整个页面,如果为false,则会根据实际的滑动距离显示页面
  this.pageSnapping = true,
  //主要是配合辅助功能用的,后面解释
  this.allowImplicitScrolling = false,
  //后面解释
  this.padEnds = true,
})

切换界面使用PageView.builder,当点击不同的tab时候,可以使用PageController进行切换

PageView.builder(
          itemBuilder: (BuildContext context, int index) {
            return KeepAliveWrapper(
                child: subMainPages[index], keepAlive: true);
          },
          itemCount: subMainPages.length,
          controller: _pageController,
          physics: NeverScrollableScrollPhysics(),
          onPageChanged: (index) {
            _selectedIndex = index;
            _animationControllerList[_selectedIndex!].forward();
            _animationControllerList[_lastSelectedIndex!].reverse();
          },
        ),
      ),

使用默认效果的bottomNavigationBar

bottomNavigationBar: BottomBarDefault(
        items: buildTabItems(context),
        backgroundColor: Colors.white,
        color: Colors.black87,
        colorSelected: Colors.amber,
        indexSelected: _selectedIndex,
        duration: Duration(milliseconds: 200),
        onTap: (int index) => setState(() {
          _lastSelectedIndex = _selectedIndex;
          _pageController.jumpToPage(index);
        }),
      ),

当需要特殊样式的bottomNavigationBar,时候,比如如下点击后会突出的三角形样式

bottomNavigationBar: BottomBarInspiredInside(
        items: [
          TabItem(
            icon: Icons.home_outlined,
            title: S.of(context).home,
          ),
          TabItem(
            icon: Icons.qr_code_scanner_outlined,
            title: S.of(context).qrScan,
          ),
          TabItem(
            icon: Icons.nature_outlined,
            title: S.of(context).mine,
            count: Container(
              padding: EdgeInsets.all(3.0),
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.white,
                  width: 1.0,
                  style: BorderStyle.solid,
                ),
                color: Colors.red,
                borderRadius: BorderRadius.all(
                  Radius.circular(20.0),
                ),
                // gradient: LinearGradient(
                //   begin: Alignment.topLeft,
                //   end: Alignment.bottomRight,
                //   colors: [
                //     Colors.red.withOpacity(0.5),
                //     Colors.red.withOpacity(0.3),
                //     Colors.red.withOpacity(1.0),
                //   ],
                // ),
              ),
              child: Text(
                "99",
                style: TextStyle(
                    fontSize: 10,
                    color: Colors.white,
                    fontWeight: FontWeight.w500),
              ),
            ),
          )
        ],
        backgroundColor: Colors.lightBlue,
        color: Colors.white,
        colorSelected: Colors.white,
        indexSelected: _selectedIndex,
        duration: Duration(milliseconds: 200),
        onTap: (int index) => setState(() {
          _pageController.jumpToPage(index);
        }),
        itemStyle: ItemStyle.hexagon,
        chipStyle:
            const ChipStyle(isHexagon: true, background: Colors.blueAccent),
      ),

完整实例代码如下

class MainTabNavigator extends StatefulWidget {
  const MainTabNavigator({Key? key}) : super(key: key);

  
  State<MainTabNavigator> createState() => _MainTabNavigatorState();
}

class _MainTabNavigatorState extends State<MainTabNavigator>
    with TickerProviderStateMixin {
  PageController _pageController = PageController();
  int _selectedIndex = 0;
  late DateTime _lastPressed;

  List<Widget> subMainPages = [];

  late List<AnimationController> _animationControllerList;
  late List<Animation<double>> _animationList;

  int? _lastSelectedIndex = 0;

  
  void initState() {

    // 设置默认的
    subMainPages = mainPages;
    super.initState();

    _animationControllerList = List<AnimationController>.empty(growable: true);
    _animationList = List<Animation<double>>.empty(growable: true);

    for (int i = 0; i < subMainPages.length; ++i) {
      _animationControllerList.add(AnimationController(
          duration: Duration(milliseconds: 200), vsync: this));
      _animationList.add(Tween(begin: 0.0, end: 5.0)
          .chain(CurveTween(curve: Curves.ease))
          .animate(_animationControllerList[i]));
    }

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _animationControllerList[_selectedIndex!].forward();
    });
  }

  void animationDispose() {
    for (int i = 0; i < subMainPages.length; ++i) {
      _animationControllerList[i].dispose();
    }
  }

  
  void dispose() {
    // TODO: implement dispose
    animationDispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: WillPopScope(
        onWillPop: () async {
          if (_lastPressed == null ||
              DateTime.now().difference(_lastPressed) > Duration(seconds: 1)) {
            //两次点击间隔超过1秒则重新计时
            _lastPressed = DateTime.now();
            return false;
          }
          return true;
        },
        child: PageView.builder(
          itemBuilder: (BuildContext context, int index) {
            return KeepAliveWrapper(
                child: subMainPages[index], keepAlive: true);
          },
          itemCount: subMainPages.length,
          controller: _pageController,
          physics: NeverScrollableScrollPhysics(),
          onPageChanged: (index) {
            _selectedIndex = index;
            _animationControllerList[_selectedIndex!].forward();
            _animationControllerList[_lastSelectedIndex!].reverse();
          },
        ),
      ),
      // bottomNavigationBar: BottomBarDefault(
      //   items: buildTabItems(context),
      //   backgroundColor: Colors.white,
      //   color: Colors.black87,
      //   colorSelected: Colors.amber,
      //   indexSelected: _selectedIndex,
      //   duration: Duration(milliseconds: 200),
      //   onTap: (int index) => setState(() {
      //     _lastSelectedIndex = _selectedIndex;
      //     _pageController.jumpToPage(index);
      //   }),
      // ),
      bottomNavigationBar: BottomBarInspiredInside(
        items: [
          TabItem(
            icon: Icons.home_outlined,
            title: S.of(context).home,
          ),
          TabItem(
            icon: Icons.qr_code_scanner_outlined,
            title: S.of(context).qrScan,
          ),
          TabItem(
            icon: Icons.nature_outlined,
            title: S.of(context).mine,
            count: Container(
              padding: EdgeInsets.all(3.0),
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.white,
                  width: 1.0,
                  style: BorderStyle.solid,
                ),
                color: Colors.red,
                borderRadius: BorderRadius.all(
                  Radius.circular(20.0),
                ),
                // gradient: LinearGradient(
                //   begin: Alignment.topLeft,
                //   end: Alignment.bottomRight,
                //   colors: [
                //     Colors.red.withOpacity(0.5),
                //     Colors.red.withOpacity(0.3),
                //     Colors.red.withOpacity(1.0),
                //   ],
                // ),
              ),
              child: Text(
                "99",
                style: TextStyle(
                    fontSize: 10,
                    color: Colors.white,
                    fontWeight: FontWeight.w500),
              ),
            ),
          )
        ],
        backgroundColor: Colors.lightBlue,
        color: Colors.white,
        colorSelected: Colors.white,
        indexSelected: _selectedIndex,
        duration: Duration(milliseconds: 200),
        onTap: (int index) => setState(() {
          _pageController.jumpToPage(index);
        }),
        itemStyle: ItemStyle.hexagon,
        chipStyle:
            const ChipStyle(isHexagon: true, background: Colors.blueAccent),
      ),
      // bottomNavigationBar: FlashyTabBar(
      //   selectedIndex: _selectedIndex,
      //   showElevation: true,
      //   onItemSelected: (index) => setState(() {
      //     _pageController.jumpToPage(index);
      //   }),
      //   items: [
      //     FlashyTabBarItem(
      //       icon: Icon(Icons.home_outlined),
      //       title: Text(S.of(context).home),
      //     ),
      //     FlashyTabBarItem(
      //       icon: Icon(Icons.qr_code_scanner_outlined),
      //       title: Text(S.of(context).qrScan),
      //     ),
      //     FlashyTabBarItem(
      //       icon: Icon(Icons.nature_outlined),
      //       title: Text(S.of(context).mine),
      //     ),
      //   ],
      // ),      // bottomNavigationBar: BottomNavigationBar(
    );
  }

  List<TabItem> buildTabItems(BuildContext context) {
    TabItem homeItem = TabItem(
      icon: Icons.home_outlined,
      title: S.of(context).home,
      count: buildTabItem(context, 0),
    );
    TabItem qsItem = TabItem(
      icon: Icons.qr_code_scanner_outlined,
      title: S.of(context).qrScan,
      count: buildTabItem(context, 1),
    );

    TabItem discoveryItem = TabItem(
      icon: Icons.location_searching_outlined,
      title: S.of(context).discovery,
      count: buildTabItem(context, 2),
    );

    TabItem mineItem = TabItem(
      icon: Icons.nature_outlined,
      title: S.of(context).mine,
      count: buildTabItem(context, 3),
    );
    return [homeItem, qsItem, discoveryItem, mineItem];
  }

  Widget buildTabItem(BuildContext context, int index) {
    return AnimatedBuilder(
      animation: _animationList[index],
      builder: (BuildContext context, Widget? child) {
        return Container(
          margin: EdgeInsets.only(
            top: _animationList[index].value,
          ),
          child: child,
        );
      },
      child: buildTabItemCount(context),
    );
  }

  Widget buildTabItemCount(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(3.0),
      decoration: BoxDecoration(
        border: Border.all(
          color: Colors.white,
          width: 1.0,
          style: BorderStyle.solid,
        ),
        color: Colors.red,
        borderRadius: const BorderRadius.all(
          Radius.circular(30.0),
        ),
        // gradient: LinearGradient(
        //   begin: Alignment.topLeft,
        //   end: Alignment.bottomRight,
        //   colors: [
        //     Colors.red.withOpacity(0.5),
        //     Colors.red.withOpacity(0.3),
        //     Colors.red.withOpacity(1.0),
        //   ],
        // ),
      ),
      child: const Text(
        "99",
        style: TextStyle(
            fontSize: 10, color: Colors.white, fontWeight: FontWeight.w500),
      ),
    );
  }
}

三、小结

flutter开发实战-自定义bottomNavigationBar样式。

https://blog.csdn.net/gloryFlow/article/details/132761946

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/990714.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

滑动谜题 -- BFS

滑动谜题 输入&#xff1a;board [[4,1,2],[5,0,3]] 输出&#xff1a;5 解释&#xff1a; 最少完成谜板的最少移动次数是 5 &#xff0c; 一种移动路径: 尚未移动: [[4,1,2],[5,0,3]] 移动 1 次: [[4,1,2],[0,5,3]] 移动 2 次: [[0,1,2],[4,5,3]] 移动 3 次: [[1,0,2],[4,5,3]…

前端实现展开收起的效果 (react)

需求背景&#xff1a;需要实现文本的展开收起效果&#xff0c;文本是一行一行的&#xff0c;数据格式是数组结构。 如图所示&#xff08;图片已脱敏&#xff09; 简单实现&#xff1a;使用一个变量控制展开收起效果。 展开收起逻辑部分&#xff08;react&#xff09; const […

layer is not a constructor缺少报错解决方案参考开发教程并在相关页面引入

问题场景&#xff1a; 1.在使用Mars3d热力图功能时&#xff0c;提示mars3d.layer.HeatLayer is not a constructor 问题原因: 1.mars3d的热力图插件mars3d-heatmap没有安装引用。 解决方案&#xff1a; 1.参考开发教程&#xff0c;找到相关的插件库&#xff1a;Mars3D 三维…

Power BI依据列中值的范围不同计算公式增加一列

Power BI依据列的范围不同计算公式增加一列&#xff0c;在我们遇到了依据范围不同的公式计算时&#xff0c;就可以采用下面公式 一、增加组计算公式 佣金分组 SWITCH(TRUE(), ry_vue clawer_zhuan[到手价]>0&&ry_vue clawer_zhuan[到手价]<475,80, ry_vue claw…

华为OD机考算法题:数字加减游戏

目录 题目部分 解读与分析 代码实现 题目部分 题目数字加减游戏难度难题目说明小明在玩一个数字加减游戏&#xff0c;只使用加法或者减法&#xff0c;将一个数字 s 变成数字 t 。 每个回合&#xff0c;小明可以用当前的数字加上或减去一个数字。 现在有两种数字可以用来加减…

华为云云耀云服务器L实例评测|华为云上试用主机安全产品Elkeid

文章目录 华为云云耀云服务器L实例评测&#xff5c;华为云上试用主机安全产品Elkeid一、背景&#xff1a;什么是主机安全二、主机安全之Elkeid1. Elkeid 介绍2. Elkeid Server3. Elkeid Server 架构Elkeid AgentCenter&#xff08;下面简称AC&#xff09;Elkeid Service Discov…

[构建自己的 Vue 组件库] 小尾巴 UI 组件库

文章归档于&#xff1a;https://www.yuque.com/u27599042/row3c6 组件库地址 npm&#xff1a;https://www.npmjs.com/package/xwb-ui?activeTabreadme小尾巴 UI 组件库源码 gitee&#xff1a;https://gitee.com/tongchaowei/xwb-ui小尾巴 UI 组件库测试代码 gitee&#xff1a…

Java(四)数组与类和对象

Java&#xff08;四&#xff09;数组与类和对象 六、数组&#xff08;非常重要&#xff09;1.定义2.遍历2.1遍历方法2.2Arrays方法 3.二维数组数组小总结 七、类和对象1. 定义&#xff08;重要&#xff09;1.1 类1.2 对象 2. this关键字&#xff08;重要&#xff09;2.1 特点 3…

方差分析的核心概念“方差分解“

方差是统计学中用来衡量数据集合中数值分散或离散程度的一种统计量。它表示了数据点与数据集合均值之间的差异程度&#xff0c;即数据的分散程度。方差越大&#xff0c;表示数据点更分散&#xff0c;而方差越小&#xff0c;表示数据点更集中。 方差的计算公式如下&#xff1a;…

自己开发一个接口文档页面html

演示效果 具体代码如下 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>框架框架文档页面</…

网络原理(一)网络基础,包括IP ,网络相关的定义

网络基础 以下图片是书上的网图。 什么是IP地址&#xff1f; IP地址&#xff08;Internet Protocol Address&#xff09;是指互联网协议地址&#xff0c;又译为网际协议地址。P地址是IP协议提供的一种统一的地址格式&#xff0c;它为互联网上的每一个网络和每一台主机分配一…

电商(淘宝1688京东拼多多等)API接口服务:提升商业效率和用户体验的关键

电商API接口服务&#xff1a;提升商业效率和用户体验的关键 随着电子商务的飞速发展&#xff0c;电商企业需要不断提升自身的业务能力和服务质量&#xff0c;以应对日益激烈的市场竞争。为了更好地满足商家和消费者的需求&#xff0c;电商API接口服务应运而生。本文将探讨电商…

【进阶篇】Redis内存淘汰详解

文章目录 Redis内存淘汰详解0. 前言大纲Redis内存淘汰策略 1. 什么是Redis内存淘汰策略&#xff1f;1.1.Redis 内存不足的迹象 2. Redis内存淘汰策略3. 原理4. 主动和被动1. 主动淘汰1.1 键的生存周期1.2 过期键删除策略 2. 被动淘汰2.2 被动淘汰策略的实现 5. 项目实践优化策略…

【autodl/linux配环境心得:conda/本地配cuda,cudnn及pytorch心得】-未完成

linux配环境心得&#xff1a;conda/本地配cuda&#xff0c;cudnn及pytorch心得 我们服务器遇到的大多数找不到包的问题一&#xff0c;服务器安装cuda和cudnn使用conda在线安装cuda和cudnn使用conda进行本地安装检查conda安装的cuda和cudnn本地直接安装cuda和cudnn方法一&#x…

MDK-Keil AC6 Compiler屏蔽特定警告

最近在使用STM32CubeMX生成MDK工程是&#xff0c;使用了 AC6 版本的编译器进行编译代码&#xff0c;然后发现了一些警告&#xff0c;但是在 AC5 版本下编译又正常。于是研究了下怎么屏蔽特定的警告&#xff0c;这里记录一下。 1. Keil AC6屏蔽特定警告 遇到的警告如下&#x…

CSS的break-inside 属性 的使用

break-inside 属性在 CSS 页码分隔模块中使用,它定义了一个元素内部是否允许发生页面、栏目或者区域的分隔。 break-inside有以下几个值 break-inside: avoid- 表示避免在该元素内部发生分页或者分栏。break-inside: auto - 默认允许分页break-inside: avoid-page - 避免页面…

【LeetCode题目详解】第九章 动态规划part07 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数 (day45补)

本文章代码以c为例&#xff01; 一、力扣第70题&#xff1a;爬楼梯 题目&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正整数。 示例 1&#x…

如何在 Ubuntu 上安装和使用 Nginx?

ginx&#xff08;发音为“engine-x”&#xff09;是一种流行的 Web 服务器软件&#xff0c;以其高性能和可靠性而闻名。它是许多流行网站使用的开源软件&#xff0c;包括 Netflix、GitHub 和 WordPress。Nginx 可以用作 Web 服务器、负载均衡器、反向代理和 HTTP 缓存等。 它以…

[小尾巴 UI 组件库] 全屏响应式轮播背景图(基于 Vue 3 与 Element Plus)

文章归档于&#xff1a;https://www.yuque.com/u27599042/row3c6 组件库地址 npm&#xff1a;https://www.npmjs.com/package/xwb-ui?activeTabreadme小尾巴 UI 组件库源码 gitee&#xff1a;https://gitee.com/tongchaowei/xwb-ui小尾巴 UI 组件库测试代码 gitee&#xff1a…

岩土工程安全监测利器:振弦采集仪的发展

岩土工程安全监测利器&#xff1a;振弦采集仪的发展 岩土工程安全监测是保障建筑物、地下工程和地质环境安全稳定运行的重要手段。传统上&#xff0c;监测手段主要依靠人工巡视以及基础设施安装的传感器&#xff0c;但是这些方法都存在着缺陷。人工巡视存在的问题是数据采集精…