Flutter 自定义TabBar指示器(indicator)实现秒杀UI样式
Posted Code-Porter
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 自定义TabBar指示器(indicator)实现秒杀UI样式相关的知识,希望对你有一定的参考价值。
一、话不多说,先来看下实现的交互效果,源码在文末
二、首先分解一下需求
- 自定义Tab指示器与Tab
- 当秒杀节点小于5个的时候,每一个Tab的宽度为平分屏幕宽度
- 当大于等于5个的时候,每一个Tab的宽度为固定宽度
1、先来看下最简单的TabBar与TabBarView需要如何实现
/// 省略若干代码...
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text('TabBar'),
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(text: "热销"),
Tab(text: "推荐"),
Tab(text: "我的"),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
Center(child: Text('热销')),
Center(child: Text('推荐')),
Center(child: Text('我的')),
],
),
);
上面代码实现的效果
2、通过阅读TabBar
的源码可以知道tabs
属性接收widget
集合,这也就是我们自定义Tab的突破口了
/// Typically a list of two or more [Tab] widgets.
///
/// The length of this list must match the [controller]'s [TabController.length]
/// and the length of the [TabBarView.children] list.
final List<Widget> tabs;
三、通过画图来分析一下整个Tab的层级结构
- 白色层:就是
TabBar
所占的高度(Tab高度+三角箭头高度) - 灰色层:就是每一个
Tab
所占的高度 - 红色层:就是自定义的
TabBar indicator
1、通过上面的图我们需要定义如下一些数据:
- 每个Tab的宽度和高度
double _tabWidth = 82.0;
double _tabHeight = 70.0;
- 三角形的宽高
Size triangleSize = Size(10, 10);
2、每一个Tab
通过Container
组件来实现
class FlashSale extends StatefulWidget
@override
FlashSaleState createState() => new FlashSaleState();
class FlashSaleState extends State<FlashSale>
with SingleTickerProviderStateMixin
TabController _tabController;
List<String> _list;
double _tabWidth = 82.0;
double _tabHeight = 70.0;
///定义三角的大小
Size triangleSize = Size(10, 10);
@override
void initState()
super.initState();
_list = ['10:00', '11:00', '12:00', '13:00', '14:00'];
_tabController =
TabController(initialIndex: 0, length: _list.length, vsync: this);
@override
Widget build(BuildContext context)
return Scaffold(
///省略....
body: Column(
children: [
TabBar(
isScrollable: true,
controller: _tabController,
tabs: _list.map((e)
return _tab(e);
).toList(),
)
///省略TabBarView内容...
],
),
);
Widget _tab(String content)
return Container(
width: _tabWidth,
height: _tabHeight + triangleSize.height,
child: Padding(
padding: EdgeInsets.only(bottom: triangleSize.height),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
content,
style: TextStyle(color: Colors.white, fontSize: 22),
),
Text(
'抢购中',
style: TextStyle(color: Colors.white, fontSize: 14),
)
],
),
),
);
Container的高度就是上面说到的,需要加上三角形的高度;如下效果图
这里文字需要剧中,所以需要padding bottom 三角形的高度
3、现在就需要给每一个Tab
添加灰色未选中的颜色了;很容易想到是加在Container
中的,但是加在这里是有问题的我们需要把这个灰色加在TabBar
这里;但是现在我们TabBar的高度=Tab高度 + 三角箭头高度
所以加在这里会使得三角箭头区域也是灰色,那这样需要怎么处理呢?解决方法如下
- 通过
Stack
布局方式在TabBar
底部层叠个灰色的Container
///省略....
@override
Widget build(BuildContext context)
return Scaffold(
///省略....
body: Column(
children: [
Stack(
children: [
Container(
width: double.infinity,
height: _tabHeight,
color: Color(0xFF4D525C),
),
TabBar(
isScrollable: true,
controller: _tabController,
tabs: _list.map((e)
return _tab(e);
).toList(),
)
],
)
///省略TabBarView内容...
],
),
);
四、现在重要的一步就是自定义TabBar
的indicator
,通过源码可知是一个Decoration
对象,所以我们只需继承它即可
1、一个继承Decoration
最基本的结构就是下面的样子了
class TriangleIndicator extends Decoration
//三角形的大小
final Size triangleSize;
TriangleIndicator(this.triangleSize);
@override
BoxPainter createBoxPainter([VoidCallback onChanged])
return _CustomBoxPainter(triangleSize);
class _CustomBoxPainter extends BoxPainter
final Size triangleSize;
_CustomBoxPainter(this.triangleSize);
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration)
- 然后在
TabBar
处使用即可
//省略其它...
TabBar(
isScrollable: true,
controller: _tabController,
indicator: TriangleIndicator(triangleSize),
tabs: _list.map((e)
return _tab(e);
).toList(),
)
2、接下来就只需要在paint
函数中将选中的Tab样式绘制出来即可(写过android自定义View看着就会显的很亲切了)
- 绘制矩形
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration)
///每个Tab的宽高
Size tabSize = Size(configuration.size.width,
configuration.size.height - triangleSize.height);
final Rect rect = offset & tabSize;
final Paint paint = Paint();
paint.color = Color(0xFFFF443D);
paint.style = PaintingStyle.fill;
///画Tab矩形
canvas.drawRect(rect, paint);
offset
参数:意思当前位置距离组建左上角的偏移量configuration
参数:当前画布的大小
- 从图中发现绘制的高度减去了三角形区域的高度,可还是多出了一点,这是为什么呢?原因是
TabBar的indicator
默认给了高度为2,只需要将它设置为0即可
TabBar(
//省略....
indicatorWeight: 0,
)
- 绘制三角形,这里通过
Path
来绘制,也就是三角形的起点、中点、终点
连接起来
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration)
///每个Tab的宽高
Size tabSize = Size(configuration.size.width,
configuration.size.height - triangleSize.height);
final Rect rect = offset & tabSize;
final Paint paint = Paint();
paint.color = Color(0xFFFF443D);
paint.style = PaintingStyle.fill;
///画Tab矩形
canvas.drawRect(rect, paint);
///画三角形
double start = (tabSize.width - triangleSize.width) / 2;
Path trianglePath = Path();
///起点
trianglePath.moveTo(
start + offset.dx, configuration.size.height - triangleSize.height);
///中点
trianglePath.lineTo(
(tabSize.width / 2) + offset.dx, configuration.size.height);
///终点
trianglePath.lineTo(start + offset.dx + triangleSize.width,
configuration.size.height - triangleSize.height);
canvas.drawPath(trianglePath, paint);
五、最后一条需求动态改变每个Tab
的宽高,这个也很简单:只需要判断当前有几个Tab然后动态计算即可,直接查看源码即可
本篇源码下载地址
以上是关于Flutter 自定义TabBar指示器(indicator)实现秒杀UI样式的主要内容,如果未能解决你的问题,请参考以下文章