[英]Flutter Customizing TabBar Indicator
如何自定义Flutter TabBar中的Tab指示器,达到如下目标效果?
我怎样才能做到以下几点:
这是我的代码
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var categoryTabs = <Tab>[...];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: categoryTabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('My App'),
centerTitle: true,
bottom: PreferredSize(
preferredSize: Size(100, 70),
child: Column(
children: [
TabBar(
indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: Colors.transparent,
labelColor: colorPrimaryDark,
isScrollable: true,
unselectedLabelColor: Colors.white,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white,
),
tabs: categoryTabs,
),
SizedBox(height: 10)
],
),
),
),
body: SafeArea(...),
),
);
}
}
检查此代码
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
bottom: PreferredSize(
preferredSize: Size(100,20),
child: Column(
children: [
TabBar(
// indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: Colors.transparent,
labelColor: Colors.white,
unselectedLabelColor: Colors.black,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.redAccent),
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.report_problem)),
Tab(icon: Icon(Icons.report_problem)),
],
),
SizedBox(height: 10,)
],
),
),
),
body: TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
)
),
);
}
}
当Tab Controller从一个索引中的标签控制到另一个索引时,您需要使用AnimatedBuilder
来收听选项卡控制器动画。 使用这种方法,您可以自定义TabBar
项的过渡动画。 例如:将您的TabBar
实现更改为以下内容
TabBar(
tabs: tabs
.asMap().entries
.map((entry) => AnimatedBuilder(
animation: _tabController.animation,
builder: (ctx, snapshot) {
final forward = _tabController.offset > 0;
final backward = _tabController.offset < 0;
int _fromIndex;
int _toIndex;
double progress;
// This value is true during the [animateTo] animation that's triggered when the user taps a [TabBar] tab.
// It is false when [offset] is changing as a consequence of the user dragging the [TabBarView].
if (_tabController.indexIsChanging) {
_fromIndex = _tabController.previousIndex;
_toIndex = _tabController.index;
_cachedFromIdx = _tabController.previousIndex;
_cachedToIdx = _tabController.index;
progress = (_tabController.animation.value - _fromIndex).abs() / (_toIndex - _fromIndex).abs();
} else {
if (_cachedFromIdx == _tabController.previousIndex && _cachedToIdx == _tabController.index) {
// When user tap on a tab bar and the animation is completed, it will execute this block
// This block will not be called when user draging the TabBarView
_fromIndex = _cachedFromIdx;
_toIndex = _cachedToIdx;
progress = 1;
_cachedToIdx = null;
_cachedFromIdx = null;
} else {
_cachedToIdx = null;
_cachedFromIdx = null;
_fromIndex = _tabController.index;
_toIndex = forward
? _fromIndex + 1
: backward
? _fromIndex - 1
: _fromIndex;
progress = (_tabController.animation.value - _fromIndex).abs();
}
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: entry.key == _fromIndex
? Color.lerp(Colors.white, Colors.red.shade900, progress)
: entry.key == _toIndex
? Color.lerp(Colors.red.shade900, Colors.white, progress)
: Color.lerp(Colors.red.shade900, Colors.red.shade900, progress),
borderRadius: BorderRadius.circular(200),
),
child: Text(
entry.value.toUpperCase(),
style: TextStyle(
fontSize: 10,
letterSpacing: 0.4,
fontWeight: FontWeight.w700,
),
),
);
},
))
.toList(),
controller: _tabController,
isScrollable: true,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 0,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(100),
),
physics: const ClampingScrollPhysics(),
unselectedLabelColor: Colors.white,
labelColor: Colors.red,
labelPadding: EdgeInsets.only(left: 12),
),
请注意,在上面的示例中,我使用 2 个局部变量来跟踪当前和以前的索引。 这专门用于处理用户在TabBar
选项卡上的选项卡在一个索引与另一个索引之间设置动画的情况。 将 log 命令放在AnimatedBuilder
的 builder 方法中,以更好地了解它是如何工作的。
int _cachedFromIdx;
int _cachedToIdx;
这是结果
使用这个包 -> Buttons_tabbar...这个包为您提供选择或未选择的标签栏指示器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.