效果
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);
}
}