ViewTabBar
ViewTabBar 基于 TabBarController 和 PageController,实现了 TabBar 和 PageView 之间在 UI 上的解耦及联动。
- 可实现 TabBar + PageView (horizontal)
- 可实现 TabBar + PageView (vertical)
- 可实现 Carousel (轮播图)
如何安装
-
在
pubspec.yaml
添加dependencies: view_tabbar: ^1.3.1
-
在命令行运行如下
flutter pub get
如何使用
TabBar + PageView (pinned)
import 'package:flutter/material.dart';
import 'package:view_tabbar/view_tabbar.dart';
class HorizontalWithPinned extends StatelessWidget {
HorizontalWithPinned({super.key});
final pageController = PageController();
final tabBarController = ViewTabBarController();
@override
Widget build(BuildContext context) {
const tags = ['板块1', '板块2', '板块3', '板块4'];
const duration = Duration(milliseconds: 300);
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// ViewTabBar
ViewTabBar(
pinned: true,
itemCount: tags.length,
direction: Axis.horizontal,
pageController: pageController,
tabBarController: tabBarController,
animationDuration: duration, // 取消动画 -> Duration.zero
builder: (context, index) {
return ViewTabBarItem(
index: index,
transform: ScaleTransform(
maxScale: 1.2,
transform: ColorsTransform(
normalColor: const Color(0xff606266),
highlightColor: const Color(0xff436cff),
builder: (context, color) {
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.only(
top: 8.0,
left: 10.0,
right: 10.0,
bottom: 8.0,
),
child: Text(
tags[index],
style: TextStyle(
color: color,
fontWeight: FontWeight.w500,
fontSize: 14.0,
),
),
);
},
),
),
);
},
// StandardIndicator
indicator: StandardIndicator(
color: const Color(0xff436cff),
width: 27.0,
height: 2.0,
bottom: 0,
),
),
// PageView
Expanded(
flex: 1,
child: PageView.builder(
itemCount: tags.length,
controller: pageController,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
padding: const EdgeInsets.only(
top: 16.0,
left: 16.0,
right: 16.0,
bottom: 16.0,
),
child: Text(
'这里渲染显示 ${tags[index]} 的内容',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Color(0xff606266),
),
),
);
},
),
),
],
);
}
}
Carousel (轮播图)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:view_tabbar/view_tabbar.dart';
class CarouselWithTarBar extends StatefulWidget {
const CarouselWithTarBar({super.key});
@override
CarouselWithTarBarState createState() => CarouselWithTarBarState();
}
class CarouselWithTarBarState extends State<CarouselWithTarBar> {
// 共 6 个 Card 轮播元素
// 因需要实现无限轮播的效果
// 需在第一个元素前添加最后一个元素
// 且在最后一个元素前添加第一个元素
// 如此下来则就有共计 8 个轮播元素
// 默认显示第2个轮播元素 (即 6 个 Card 中第一个)
final pageController = PageController(initialPage: 1);
final tabBarController = ViewTabBarController();
int _currentIndex = 1;
Timer? _timer;
// 定时轮播 - 每隔 3s
void _setTimer() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 3), (_) {
int page = _currentIndex + 1;
pageController.animateToPage(
page,
duration: const Duration(milliseconds: 400),
curve: Curves.easeOut,
);
});
}
@override
void initState() {
super.initState();
_setTimer();
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 12.0,
),
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
gradient: const LinearGradient(
begin: Alignment(0.00, -1.00),
end: Alignment(0, 1),
stops: [0, 0.2, 1],
colors: [
Color(0xFFEEF3FF),
Color(0xFFEEF3FF),
Colors.white,
],
),
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1.50, color: Colors.white),
borderRadius: BorderRadius.circular(12.0),
),
),
child: Column(
children: [
// 标题
Container(
height: 24.0,
alignment: Alignment.centerLeft,
margin: const EdgeInsets.only(top: 10.0),
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: const Text(
"职业类型",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 18.0,
fontFamily: 'PingFang SC',
fontWeight: FontWeight.w500,
height: 1,
),
),
),
// PageView
Container(
height: 72.0,
margin: const EdgeInsets.only(
top: 16.0,
bottom: 20.0,
),
padding: const EdgeInsets.only(
left: 16.0,
right: 10.0,
),
child: NotificationListener(
onNotification: (notification) {
if (notification is! ScrollNotification ||
notification.depth != 0) {
return false;
}
if (notification is ScrollUpdateNotification) {
// 关闭定时器
_timer?.cancel();
}
if (notification is ScrollStartNotification) {
if (notification.dragDetails != null) {
// 关闭定时器
_timer?.cancel();
}
}
if (notification is ScrollEndNotification) {
final page = pageController.page?.round();
// last, 处理 end 边界
if (page == 7) {
Future.delayed(const Duration(milliseconds: 10), () {
pageController.jumpToPage(1);
_setTimer();
});
return true;
}
// first, 处理 start 边界
if (page == 0) {
Future.delayed(const Duration(milliseconds: 10), () {
pageController.jumpToPage(3);
_setTimer();
});
return true;
}
// 延时启动定时器
Future.delayed(
const Duration(milliseconds: 20),
() {
setState(() {
_currentIndex = page ?? 1;
_setTimer();
});
},
);
}
return true;
},
child: PageView.builder(
itemCount: 8,
controller: pageController,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
// first, 处理 start 边界
if (index == 0) {
index = 6;
}
// last, 处理 end 边界
if (index == 7) {
index = 1;
}
return renderPageViewContent(
context,
index,
);
},
),
),
),
// TarBar
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 0,
child: ClipRect(
clipper: ViewTabBarClipper(),
child: ViewTabBar(
pinned: true,
height: 14.0,
width: 160.0,
direction: Axis.horizontal,
pageController: pageController,
tabBarController: tabBarController,
animationDuration: const Duration(milliseconds: 300),
indicator: StandardIndicator(
width: 15.0,
height: 4.0,
color: const Color(0xff436cff),
radius: const BorderRadius.all(Radius.circular(3.0)),
bottom: 5,
),
itemCount: 8,
builder: (context, index) {
return ViewTabBarItem(
index: index,
child: Container(
width: 14.0,
height: 4.0,
margin: const EdgeInsets.only(
left: 2.0,
right: 2.0,
),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(2),
),
color: Color(0x66436cff),
),
),
);
},
),
),
),
],
),
],
),
);
}
}
// 截取 TabBar 容器大小
class ViewTabBarClipper extends CustomClipper<Rect> {
@override
Rect getClip(Size size) {
// 共 8 个 tab item (每个 tab -> 宽度: 20, 高度: 14)
// 截取保留 第 2 - 7 tab 元素 -> Rect.fromLTWH(20, 0, 120, 14)
return const Rect.fromLTWH(20, 0, 120, 14);
}
@override
bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
return false;
}
}
// 渲染 PageView 内容
Widget renderPageViewContent(context, index) {
// PageView 内容 ....
}
API 说明
ViewTabBar
API | 说明 | 必选 | 默认值 |
---|---|---|---|
pinned | tabbar 固定 | 否 | false |
builder | widget 构建 | 是 | |
itemCount | tabbar 数量 | 是 | |
direction | tabbar 方向 | 否 | Axis.horizontal |
indicator | tabbar 指示器 | 否 | |
pageController | PageView controller | 是 | PageController |
tabBarController | ViewTabBar controller | 否 | ViewTabBarController |
animationDuration | 动画时长,Duration.zero -> 禁用动画 | 否 | Duration(milliseconds: 300) |
controllerToScroll | PageView 滚动时,联动 TabBar/Indicator | 否 | true |
controllerToJump | TabBar 滑动时,联动 PageView 滚动 | 否 | true |
onTapItem | TabBar Item onTap 事件 | 否 | |
height | tabbar 高度,当 direction 为 Axis.horizontal 时,请指定值 | 否 | |
width | tabbar 宽度,当 direction 为 Axis.vertical 时,请指定值 | 否 |
ViewTabBarItem
API | 说明 | 必选 | 默认值 |
---|---|---|---|
index | tabar item index | 是 | |
child | tabar item child | 否 | |
transform | tabar item transform, 目前有 ColorsTransform / ScaleTransform | 否 |
StandardIndicator
API | 说明 | 必选 | 默认值 |
---|---|---|---|
top | indicator 顶部 | 否 | |
left | indicator 左侧 | 否 | |
right | indicator 右侧 | 否 | |
bottom | indicator 底部 | 否 | |
width | indicator 宽度 | 否 | |
height | indicator 高度 | 否 | |
radius | indicator border radius | 否 | |
color | indicator color | 否 |
ColorsTransform
API | 说明 | 必选 | 默认值 |
---|---|---|---|
builder | widget 构建 | 否 | |
transform | transformer,嵌套使用 ScaleTransform | 否 | |
normalColor | tabbar 正常颜色 | 否 | |
highlightColor | tabbar 高亮颜色 | 否 |
ScaleTransform
API | 说明 | 必选 | 默认值 |
---|---|---|---|
builder | widget 构建 | 否 | |
transform | transformer,嵌套使用 ColorsTransform | 否 | |
maxScale | tabbar 最大可缩放值 | 否 | 1.2 |
Gif 演示
TabBar + PageView ------ (horizontal) 看源码
TabBar + PageView ------ (vertical) 看源码
Carousel ------ (轮播图) 看源码