天天看點

Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

      小菜剛剛學習了 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));
}           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

源碼分析

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);           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

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);           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

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);           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

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);           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

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);           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)

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);           
Flutter 76: 圖解基本 TabBar 标簽導航欄 (二)
ACETabBarIndicator 案例源碼

      小菜對自定義标簽訓示器的适配測試還不夠完全,如有錯誤請多多指導!

來源: 阿策小和尚