需要解决的问题
1、当选择/未选中的字体大小不一样时,点击切换/PageView切换会出现字体抖动。
2、TabBar的item样式只能居中和滚动,无法像Row的MainAxisAlignment.spaceBetween属性一样排版。
当选择/未选中的字体大小不一样时,切换会出现字体抖动
切换TabBar的操作
操作A:点击TabBar
操作B:TabBar和PageView之类的组件一起使用时,PageView滑动从而带动TabBar切换
切换TabBar的操作共以上两种,而且两种操作需要分开处理,因为代码实现逻辑是不一样的。
源码修改
通过查阅TabBar的源码,可以知道,造成字体抖动的原因是:在字体样式发生变化时,选择了使用线性插值去实现一个渐变动画。本意是好的,但实际效果却不理想,出现了抖动问题。
此外,当TabBar和PageView之类的组件一起使用时,PageView滑动从而带动TabBar切换时,所执行的效果和直接点击TabBar的效果是不一样的,因为这两种情况用了两种不同的动画实现。
操作A执行的是下图if代码块,而操作B执行的是else代码块。操作A是使用了_ChangeAnimation,操作B则是使用了_DragAnimation。
处理操作A
方法1:
通过代码可以知道,点击事件调用了_handleTap()方法,然后执行动画。
方法2:
直接修改_TabStyle的代码,无视所做那些动画操作:
//是否是操作A
//isSelect可以在上图的if代码块赋值isSelect = true,else代码块赋值isSelect = false
bool isSelect;
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
final Animation<double> animation = listenable as Animation<double>;
// To enable TextStyle.lerp(style1, style2, value), both styles must have
// the same value of inherit. Force that to be inherit=true here.
final TextStyle defaultStyle = (labelStyle ??
tabBarTheme.labelStyle ??
themeData.primaryTextTheme.bodyText1!)
.copyWith(inherit: true);
final TextStyle defaultUnselectedStyle = (unselectedLabelStyle ??
tabBarTheme.unselectedLabelStyle ??
labelStyle ??
themeData.primaryTextTheme.bodyText1!)
.copyWith(inherit: true);
final Color selectedColor = labelColor ??
tabBarTheme.labelColor ??
themeData.primaryTextTheme.bodyText1!.color!;
final Color unselectedColor = unselectedLabelColor ??
tabBarTheme.unselectedLabelColor ??
selectedColor.withAlpha(0xB2); // 70% alpha
final Color color;
final TextStyle textStyle;
///isisSelect:是否是操作A,如果想偷懒,不区分操作A和操作B,也是可以的
///这样的话,操作B就不执行animation
if (isSelect) {
///去除点击时文字样式的动画
textStyle = selected ? defaultStyle : defaultUnselectedStyle;
///去除点击时文字颜色的动画
color = selected ? selectedColor : unselectedColor;
} else {
///pageView滑动时文字样式的动画
textStyle = selected
? TextStyle.lerp(
defaultStyle, defaultUnselectedStyle, animation.value)!
: TextStyle.lerp(
defaultUnselectedStyle, defaultStyle, animation.value)!;
///pageView滑动时文字颜色的动画
color = selected
? Color.lerp(selectedColor, unselectedColor, animation.value)!
: Color.lerp(unselectedColor, selectedColor, animation.value)!;
}
return DefaultTextStyle(
style: textStyle.copyWith(color: color),
child: IconTheme.merge(
data: IconThemeData(
size: 24.0,
color: color,
),
child: child,
),
);
}
处理操作B
操作B使用_DragAnimation来做动画,但是_DragAnimation的精度计算是有中间过渡,需要选择四舍五入的方式,取消中间过渡,这样就可以保留已有的代码,做最小的改动而实现我们需要的效果。偷懒的话,选用上面的代码不区分两种操作也ok。
@override
double get value {
assert(!controller.indexIsChanging);
final double controllerMaxValue = (controller.length - 1).toDouble();
final double controllerValue =
controller.animation!.value.clamp(0.0, controllerMaxValue);
// return (controllerValue - index.toDouble()).abs().clamp(0.0, 1.0);
///四舍五入,修复字体变化时,抖动问题
return (controllerValue - index.toDouble())
.abs()
.clamp(0.0, 1.0)
.roundToDouble();
}
至此,无论操作A还是操作B都不会出现字体抖动了。
TabBar的item样式只能居中和滚动,无法像Row的MainAxisAlignment.spaceBetween属性一样排版。
查阅代码,在_TabBarState中,可以看到当设置了isScrollable属性后,item的排列用了Expanded,所以就导致了item样式只能居中和滚动。
这里的修改就比较简单,自己新增一些参数,多加几个判断即可,如要实现Row的MainAxisAlignment.spaceBetween效果的排版,新增一个isBetweenTab参数来实现。