Flutter自定义TabBar视图插件view_tabbar的使用
Flutter 自定义 TabBar 视图插件 view_tabbar
的使用
ViewTabBar
ViewTabBar
基于 TabBarController
和 PageController
,实现了 TabBar
和 PageView
之间的 UI 解耦及联动。
- 可实现
TabBar + PageView (horizontal)
- 可实现
TabBar + PageView (vertical)
- 可实现
Carousel (轮播图)
如何安装
-
在
pubspec.yaml
添加:dependencies: view_tabbar: ^1.3.2
-
在命令行运行如下:
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](/user/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](/user/override)
CarouselWithTarBarState createState() => CarouselWithTarBarState();
}
class CarouselWithTarBarState extends State<CarouselWithTarBar> {
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](/user/override)
void initState() {
super.initState();
_setTimer();
}
[@override](/user/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](/user/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](/user/override)
bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
return false;
}
}
// 渲染 PageView 内容
Widget renderPageViewContent(context, index) {
// PageView 内容 ....
}
更多关于Flutter自定义TabBar视图插件view_tabbar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
1 回复
更多关于Flutter自定义TabBar视图插件view_tabbar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用自定义TabBar视图插件view_tabbar
的示例代码。请注意,由于view_tabbar
可能不是一个广泛认知的官方或知名第三方插件,我假设它是一个自定义的或特定项目中的插件,并且可能具有特定的API。下面的代码示例将展示如何假设性地集成和使用这样一个插件。
首先,确保你的pubspec.yaml
文件中已经包含了view_tabbar
(如果它是一个可通过pub.dev获取的包):
dependencies:
flutter:
sdk: flutter
view_tabbar: ^x.y.z # 替换为实际的版本号
然后,运行flutter pub get
来安装依赖。
接下来,在你的Flutter项目中创建一个使用ViewTabBar
(假设这是插件中TabBar组件的名称)的示例页面。以下是一个假设性的实现:
import 'package:flutter/material.dart';
import 'package:view_tabbar/view_tabbar.dart'; // 假设这是插件的导入路径
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter ViewTabBar Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ViewTabBar Demo'),
),
body: Column(
children: [
// 使用假设的ViewTabBar组件
Expanded(
child: ViewTabBar(
controller: _tabController,
tabs: [
Tab(icon: Icon(Icons.directions_car), text: 'Tab 1'),
Tab(icon: Icon(Icons.directions_transit), text: 'Tab 2'),
Tab(icon: Icon(Icons.directions_bike), text: 'Tab 3'),
],
indicatorColor: Colors.blue,
indicatorSize: TabBarIndicatorSize.tab,
indicatorWeight: 2.0,
labelColor: Colors.blue,
labelStyle: TextStyle(fontWeight: FontWeight.bold),
unselectedLabelColor: Colors.grey,
// 假设ViewTabBar还有其他自定义属性,可以在这里设置
// customProperty: value,
onTabSelected: (index) {
// 当标签被选中时触发的回调
print('Tab $index selected');
},
),
),
// Tab的内容区域
TabBarView(
controller: _tabController,
children: [
Center(child: Text('Content of Tab 1')),
Center(child: Text('Content of Tab 2')),
Center(child: Text('Content of Tab 3')),
],
),
],
),
);
}
}
注意:
ViewTabBar
、TabBarIndicatorSize
等类和方法在上面的代码中是基于假设的,因为view_tabbar
插件的具体实现细节未知。- 你需要根据
view_tabbar
插件的实际API文档来调整上述代码。 - 如果
view_tabbar
不是一个公开可用的包,你可能需要从源代码或私有仓库中引入它,并参考相应的文档或源代码来了解如何正确使用。
确保你查阅了view_tabbar
插件的官方文档或源代码,以获取准确的API信息和用法示例。