最近在公司写项目的时候遇到了要把 TabBar 的下划线改成图片样式的,但是官方组件似乎没有提供,在网上也搜不到相关的内容,很是苦恼。后来找到了 Flutter: Custom tab indicator for TabBar 这篇文章,发现他自定义为圆形的方法很简洁,或许可以来抄袭借鉴一下。
创建必须组件
首先创建一个 custom_tabbar.dart
来放我们自定义的 TabBar,这样方便复用,里面的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class CustomTabBar extends StatefulWidget {
const CustomTabBar({ Key key, @required this.tabs, @required this.imagePath, @required this.controller, }) : assert(tabs != null), super(key: key);
final List<Widget> tabs; final TabController controller; final String imagePath;
@override _CustomTabBarrState createState() => _CustomTabBarState(); }
class _CustomTabBarState extends State<CustomTabBar> { ui.Image _image;
@override Widget build(BuildContext context) { loadImage(widget.imagePath).then( (value) => setState( () { _image = value; }, ), );
return TabBar( tabs: widget.tabs, controller: widget.controller, indicator: ImageTabIndicator(image: _image), ); } }
|
使用 StatefulWidget 是因为获取到图片之后要刷新一下布局,不然里面的下划线都是空的了,loadImage
是一个从 Assets 获取图片转换到 Image 的方法,这是 dart:ui
包下面的,方法代码如下:
1 2 3 4 5 6 7 8 9
| import 'package:flutter/services.dart'; import 'dart:ui' as ui;
Future<ui.Image> loadImage(String path) async { ByteData data = await rootBundle.load(path); ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List()); ui.FrameInfo fi = await codec.getNextFrame(); return fi.image; }
|
注意这是一个异步方法
实现图片下划线
刚才在 custom_tabbar.dart
看到了有一个 ImageTabIndicator
,这就是要实现下划线的地方了,里面的代码也比较简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class ImageTabIndicator extends Decoration { final BoxPainter _painter;
ImageTabIndicator({double radius = 10, @required ui.Image image}) : _painter = _ImagePainter(radius, image);
@override BoxPainter createBoxPainter([onChanged]) => _painter; }
class _ImagePainter extends BoxPainter { final Paint _paint; final double radius; final ui.Image image;
_ImagePainter(this.radius, this.image) : _paint = Paint() ..isAntiAlias = true ..strokeCap = StrokeCap.round;
@override void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) { if (image != null) { final Offset imageOffset = offset + Offset(cfg.size.width / 2, cfg.size.height / 2 + image.height + 2); paintImage( canvas: canvas, rect: Rect.fromCircle(center: imageOffset, radius: radius), image: image, fit: BoxFit.fitWidth); } } }
|
这里没什么好说的,就是去画传进来的图片,不过这里的位置需要去自己调整。另外不能用 Paint
画,一定要用 paintImage
来画,他有一个转换方法,使用 Paint
会变成背景。
17 行这个 Paint
的初始化说实话应该不存在了,但是我试着删除他之后绘制就没了,很奇怪我就留着了。