Flutter仿Boss-5.Lottie实现的Tab切换

效果

Lottie引入

根据自己项目适配的Flutter版本引入对应的Lottie版本。

复制代码
lottie: ^3.1.0

实现

  • lottie 文件,直接下载Boss APK,解压出来就可以拿到。
  • 代码:

LottieBottomBarItem

复制代码
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:lottie/lottie.dart';

/// Lottie BottomBarItem
class LottieBottomBarItem extends StatefulWidget {
  // Tab 名字
  final String tabName;

  // Tab 图标
  final String tabIcon;

  // 默认颜色
  final Color tabTextColor;

  // 选中颜色
  final Color tabTextSelectedColor;

  // Tab对应索引
  final int tabIndex;

  // 点击回调
  final Function(int) onTap;

  // 是否选中
  final bool isChecked;

  // 角标
  final int badger;

  const LottieBottomBarItem({
    Key? key,
    required this.tabName,
    required this.tabIcon,
    required this.onTap,
    required this.tabIndex,
    this.tabTextColor = Colors.grey,
    this.tabTextSelectedColor = Colors.black,
    this.isChecked = false,
    this.badger = 0,
  }) : super(key: key);

  @override
  State<LottieBottomBarItem> createState() => _BottomBarItemState();
}

class _BottomBarItemState extends State<LottieBottomBarItem>
    with TickerProviderStateMixin {
  AnimationController? _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500));
    if (widget.isChecked) {
      _animationController?.forward();
    }
  }

  @override
  void didUpdateWidget(covariant LottieBottomBarItem oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!widget.isChecked && oldWidget != widget) {
      _animationController?.reset();
    }
  }

  @override
  void dispose() {
    _animationController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return InkWell(
      child: Stack(
        alignment: Alignment.bottomCenter,
        children: [
          Positioned(
            child: Column(
              children: [
                Lottie.asset(
                  widget.tabIcon,
                  repeat: false,
                  controller: _animationController,
                  width: 35.w,
                  height: 30.w,
                ),
                Text(
                  widget.tabName,
                  style: TextStyle(
                    color: widget.isChecked
                        ? widget.tabTextSelectedColor
                        : widget.tabTextColor,
                    fontSize: 12.sp,
                  ),
                )
              ],
            ),
          ),
          Visibility(
            visible: widget.badger > 0,
            child: Positioned(
              right: 30.w,
              top: 10.w,
              child: ClipOval(
                child: Container(
                  alignment: Alignment.center,
                  color: Colors.red,
                  width: 8,
                  height: 8,
                ),
              ),
            ),
          )
        ],
      ),
      onTap: () {
        widget.onTap(widget.tabIndex);
        _animationController?.forward();
      },
    );
  }
}

HomePage

复制代码
class HomePage extends StatefulWidget with RouteQueryMixin {
  HomePage({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return HomeState();
  }
}

class HomeState extends PageState<HomePage> with AutomaticKeepAliveClientMixin {
  final logic = Get.put(HomeLogic());

  @override
  void initState() {
    super.initState();
    logic.handleCurrentIndex(params: widget.routeParams);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _homeBodyWidget(context),
    );
  }

  Widget _homeBodyWidget(BuildContext context) {
    return Scaffold(
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: <Widget>[
          // 子布局
          PageView(
            controller: logic.pageController,
            physics: const NeverScrollableScrollPhysics(),
            children: _bodyContentWidget(),
            onPageChanged: (index) {
              logic.state.currentIndex.value = index;
            },
          ),
        ],
      ),

      // 底部栏
      bottomNavigationBar: Obx(
        () => BottomAppBar(
          elevation: 5.0,
          height: 65,
          child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: List.generate(
                logic.state.homeBottomBar.length,
                (index) => Expanded(
                  child: LottieBottomBarItem(
                    tabName: logic.state.homeBottomBar[index].tabName,
                    tabIcon: logic.state.homeBottomBar[index].tabIcon,
                    tabIndex: index,
                    onTap: (index) {
                      logic.state.currentIndex.value = index;
                      logic.pageController.jumpToPage(index);
                    },
                    isChecked: logic.state.currentIndex.value == index,
                  ),
                ),
              )),
        ),
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;

  /// 子布局集合
  List<Widget> _bodyContentWidget() {
    return logic.state.homeBottomBar.map((item) => item.child).toList();
  }
}

HomeState

复制代码
class HomeTab {
  HomeTab({
    required this.tabName,
    required this.tabIcon,
    required this.child,
    this.badger = 0,
  });

  String tabName;
  String tabIcon;
  Widget child;
  int badger;
}

class HomeState {
  ///当前索引
  RxInt currentIndex = 0.obs;

  ///底部按钮
  final List<HomeTab> homeBottomBar = [
    HomeTab(
        tabName: '职位',
        tabIcon: 'assets/lottie/tab/zhiwei.json',
        child: const WorkPage()),
    HomeTab(
        tabName: '有了',
        tabIcon: 'assets/lottie/tab/youle.json',
        child: const YoulePage()),
    HomeTab(
        tabName: '消息',
        tabIcon: 'assets/lottie/tab/xiaoxi-c.json',
        child: const MessagePage()),
    HomeTab(
        tabName: '我的',
        tabIcon: 'assets/lottie/tab/wode-c.json',
        child: const MinePage()),
  ];
}

HomeLogic

复制代码
class HomeLogic extends GetxController with HttpApi {
  final HomeState state = HomeState();
  late PageController pageController;


  /// 处理tab默认显示索引
  void handleCurrentIndex({required Map<String, dynamic> params}) {
    int size = state.homeBottomBar.length;
    if (params != null) {
      int tabIndex = params["tabIndex"] ?? 0;
      // 默认加载页面
      if (tabIndex >= size) {
        state.currentIndex.value = size - 1;
      } else {
        state.currentIndex.value = tabIndex;
      }
    }
    // 初始化tab控制器
    pageController = PageController(initialPage: state.currentIndex.value, keepPage: true);
  }
}

详情:github.com/yixiaolunhui/flutter_project

相关推荐
abigale032 小时前
webpack+vite前端构建工具 -11实战中的配置技巧
前端·webpack·node.js
专注API从业者2 小时前
构建淘宝评论监控系统:API 接口开发与实时数据采集教程
大数据·前端·数据库·oracle
Joker`s smile2 小时前
Chrome安装老版本、不同版本,自制便携版本用于前端调试
前端·chrome
九丝城主2 小时前
2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--上篇
服务器·flutter·macos·vmware
weixin_416639972 小时前
爬虫工程师Chrome开发者工具简单介绍
前端·chrome·爬虫
我是如子啊2 小时前
【解决“此扩展可能损坏”】Edge浏览器(chrome系列通杀))扩展损坏?一招保留数据快速修复
前端·chrome·edge
灵性花火2 小时前
Qt的前端和后端过于耦合(0/7)
开发语言·前端·qt
孤水寒月6 小时前
基于HTML的悬窗可拖动记事本
前端·css·html
祝余呀6 小时前
html初学者第一天
前端·html
瓜子三百克7 小时前
七、性能优化
flutter·性能优化