“這是我參與8月更文挑戰的第12天,活動詳情檢視: 8月更文挑戰” juejin.cn/post/698796…
小菜剛剛學習了 TabBar 標籤導航欄的使用,其中對於標籤指示器 indicator 的使用較少;小菜今天嘗試一下自定義標籤指示器;
TabBar 提供了 indicator 指示器屬性;允許使用者自定義 indicator,但自定義的指示器會導致 indicatorColor / indicatorWeight / indicatorPadding 屬性失效;預設的指示器是 UnderlineTabIndicator;
Decoration get _indicator {
if (widget.indicator != null) return widget.indicator;
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
if (tabBarTheme.indicator != null) return tabBarTheme.indicator;
Color color = widget.indicatorColor ?? Theme.of(context).indicatorColor;
if (color.value == Material.of(context).color?.value) color = Colors.white;
return UnderlineTabIndicator(
insets: widget.indicatorPadding,
borderSide: BorderSide(width: widget.indicatorWeight, color: color));
}
複製程式碼
原始碼分析
class ACETabBarIndicator extends Decoration {
@override
BoxPainter createBoxPainter([onChanged]) => _ACETabBarIndicatorPainter(this, onChanged);
}
class _ACETabBarIndicatorPainter extends BoxPainter {
final ACETabBarIndicator decoration;
_ACETabBarIndicatorPainter(this.decoration, VoidCallback onChanged);
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
// TODO: implement paint
}
}
複製程式碼
分析原始碼可知,自定義 indicator 指示器均需繼承自 Decoration;其中繪製 BoxPainter 時可以通過 Offset 和 ImageConfiguration 獲取對應 Tab 尺寸和所在位置;
案例嘗試
小菜將自定義 ACETabBarIndicator 單獨出來,並未自定義 TabBar,因此不能直接使用 TabBar 中屬性,若需要直接使用 TabBar 中屬性可以嘗試將 ACETabBarIndicator 放置在 TabBar 原始碼中進行自定義;
自定義主要是實現各種樣式的 paint 繪製過程,小菜簡單嘗試瞭如下幾種指示器樣式;
1. ACETabBarIndicatorType.circle -> 實心圓點
小菜以設定 height 為直徑,在 Tab 底部中間位置繪製一個圓形,注意起始位置為底部中心位置減半徑;
canvas.drawCircle(
Offset(offset.dx + (configuration.size.width) / 2, configuration.size.height - _height),
_height, _paint);
複製程式碼
2. ACETabBarIndicatorType.triangle -> 上三角
小菜通過繪製 Path 來生成一個上三角;其中需要相容一個三角高度上限;
if (_height > configuration.size.height)
_height = _kIndicatorHeight;
Path _path = Path()
..moveTo(offset.dx + (configuration.size.width) / 2 - _height, configuration.size.height)
..lineTo(_height * tan(pi / 6) + offset.dx + (configuration.size.width - _height) / 2,
configuration.size.height - _height)
..lineTo(_height * tan(pi / 6) + offset.dx + (configuration.size.width + _height) / 2,
configuration.size.height);
canvas.drawPath(_path, _paint);
複製程式碼
3. ACETabBarIndicatorType.rrect -> 圓角矩形(整個 Tab)
小菜繪製一個圓角矩形,其中矩形的起始位置為 Offset 對應的 Tab 大小為 ImageConfiguration 尺寸;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(offset.dx, offset.dy, configuration.size.width, configuration.size.height),
Radius.circular(_kIndicatorAngle)),
_paint);
複製程式碼
4. ACETabBarIndicatorType.rrect_inner -> 圓角矩形(有內邊距)
小菜無法準確獲取 Tab 中 Widgets 位置與尺寸,因此通過 height 來進行處理;其中矩形的起始高度需要減 1,因為 Paint 預設線寬為 1;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(
offset.dx + height, height - 1,
configuration.size.width - height * 2,
configuration.size.height - height * 2 - 2),
Radius.circular(_kIndicatorAngle)),
_paint);
複製程式碼
5. ACETabBarIndicatorType.runderline -> 圓角下劃線
TabBar 預設的指示器樣式為 UnderlineTabIndicator;只需調整 Paint 筆觸線樣式為 round 即可;
canvas.drawLine(
Offset(offset.dx, configuration.size.height - height / 2),
Offset(offset.dx + configuration.size.width, configuration.size.height - height / 2),
_paint..strokeWidth = height / 2);
複製程式碼
6. ACETabBarIndicatorType.runderline_fixed -> 定長圓角下劃線
小菜設定定長的下劃線,需要注意的是若 Tab 寬度小於設定的 width 時,相容預設的寬度;
if (_width > configuration.size.width)
_width = configuration.size.width / 3;
canvas.drawLine(
Offset(offset.dx + (configuration.size.width - _width) / 2, configuration.size.height - height / 2),
Offset(offset.dx + (configuration.size.width + _width) / 2, configuration.size.height - height / 2),
_paint..strokeWidth = height / 2);
複製程式碼
小菜對自定義標籤指示器的適配測試還不夠完全,如有錯誤請多多指導!
來源: 阿策小和尚