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

相关推荐
别拿曾经看以后~31 分钟前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死34 分钟前
导航栏及下拉菜单的实现
前端·css·css3
川石课堂软件测试36 分钟前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
problc1 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter