我使用Container的顏色來制作類似指示器,而不是使用TabBar的指示器,因為我必須為Container.
當TabController索引發生變化時,setState在偵聽器中呼叫。嘗試在 上滾動/滑動TabBar,TabBar沒有正確更改索引,因為listener不聽影片TabBar。
我試過使用tabcontroller.animation.addListener方法,但沒有任何解決方法來控制滾動移動。
下面的附加視頻演示了在TabBar.
TabBar-滾動/滑動
代碼:
class TabTest extends StatefulWidget {
@override
_TabTestState createState() => _TabTestState();
}
class _TabTestState extends State<TabTest> with TickerProviderStateMixin {
late TabController _tabController;
late List<AnimationController> _animationControllers;
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this)
..addListener(_listener);
_animationControllers = List.generate(
4,
(i) => AnimationController(
vsync: this,
duration: Duration(milliseconds: 750),
reverseDuration: Duration(milliseconds: 350),
));
}
@override
Widget build(BuildContext context) {
List<IconData> _tabIconData = [
Icons.card_giftcard,
Icons.confirmation_num_outlined,
Icons.emoji_events_outlined,
Icons.wine_bar_outlined,
];
List<String> _tabLabel = [
'Tab1',
'Tab2',
'Tab3',
'Tab4',
];
Widget _tab({
required IconData iconData,
required String label,
required bool isSelectedIndex,
// required double widthAnimation,
// required heightAnimation,
}) {
const _tabTextStyle = TextStyle(
fontWeight: FontWeight.w300, fontSize: 12, color: Colors.black);
return AnimatedContainer(
duration: Duration(milliseconds: 300),
padding: EdgeInsets.only(bottom: 2.0),
height: 55,
width: double.infinity, //_animContainerWidth - widthAnimation,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: isSelectedIndex ? Colors.black : Colors.transparent,
width: 2.0,
),
),
),
child: Tab(
iconMargin: EdgeInsets.only(bottom: 5.0),
icon: Icon(iconData, color: Colors.black),
child: Text(label, style: _tabTextStyle),
),
);
}
List<Widget> _animationGenerator() {
return List.generate(
4,
(index) => ClipRRect(
child: AnimatedBuilder(
animation: _animationControllers[index],
builder: (ctx, _) {
final value = _animationControllers[index].value;
final angle = math.sin(value * math.pi * 2) * math.pi * 0.04;
return Transform.rotate(
angle: angle,
child: _tab(
iconData: _tabIconData[index],
label: _tabLabel[index],
isSelectedIndex: _tabController.index == index,
));
}),
),
);
}
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(100),
child: AppBar(
iconTheme: Theme.of(context).iconTheme,
title: Text(
'Tab Bar',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
),
),
centerTitle: true,
bottom: PreferredSize(
preferredSize: Size.fromHeight(20),
child: Container(
child: TabBar(
controller: _tabController,
labelPadding: EdgeInsets.only(top: 5.0, bottom: 2.0),
indicatorColor: Colors.transparent,
tabs: _animationGenerator(),
),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.white,
spreadRadius: 5.0,
offset: Offset(0, 3))
],
),
),
),
),
),
body: TabBarView(
controller: _tabController,
children: List.generate(
4,
(index) => FittedBox(
child: Text('Tab $index'),
)),
),
);
}
void _listener() {
if (_tabController.indexIsChanging) {
setState(() {}); // To refresh color for Container bottom Border
_animationControllers[_tabController.previousIndex].reverse();
} else {
_animationControllers[_tabController.index].forward();
}
}
@override
void dispose() {
super.dispose();
_tabController.removeListener(_listener);
}
}
uj5u.com熱心網友回復:
這是一個CustomPaint由TabController.animation以下驅動的小部件的解決方案:
class TabTest extends StatefulWidget {
@override
_TabTestState createState() => _TabTestState();
}
class _TabTestState extends State<TabTest> with TickerProviderStateMixin {
late TabController _tabController;
late List<AnimationController> _animationControllers;
@override
void initState() {
super.initState();
// timeDilation = 10;
_tabController = TabController(length: 4, vsync: this)
..addListener(_listener);
_animationControllers = List.generate(4, (i) => AnimationController(
vsync: this,
duration: Duration(milliseconds: 750),
));
}
@override
Widget build(BuildContext context) {
List<IconData> _tabIconData = [
Icons.card_giftcard,
Icons.confirmation_num_outlined,
Icons.emoji_events_outlined,
Icons.wine_bar_outlined,
];
List<String> _tabLabel = [
'Tab1',
'Tab2',
'Tab3',
'Tab4',
];
List<Color> _tabColor = [
Color(0xffaa0000),
Color(0xff00aa00),
Color(0xff0000aa),
Colors.black,
];
Widget _tab({
required IconData iconData,
required String label,
required Color color,
required int index,
required Animation<double>? animation,
}) {
const _tabTextStyle = TextStyle(fontWeight: FontWeight.w300, fontSize: 12, color: Colors.black);
return CustomPaint(
painter: TabPainter(
animation: animation!,
index: index,
color: color,
),
child: SizedBox(
width: double.infinity,
child: Tab(
iconMargin: EdgeInsets.only(bottom: 5.0),
icon: Icon(iconData, color: Colors.black),
child: Text(label, style: _tabTextStyle),
),
),
);
}
List<Widget> _animationGenerator() {
return List.generate(
4,
(index) => AnimatedBuilder(
animation: _animationControllers[index],
builder: (ctx, _) {
final value = _animationControllers[index].value;
final angle = sin(value * pi * 3) * pi * 0.04;
return Transform.rotate(
angle: angle,
child: _tab(
iconData: _tabIconData[index],
label: _tabLabel[index],
color: _tabColor[index],
index: index,
animation: _tabController.animation,
));
}),
);
}
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text('Tab Bar',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
),
),
centerTitle: true,
bottom: TabBar(
controller: _tabController,
labelPadding: EdgeInsets.only(top: 5.0, bottom: 2.0),
indicatorColor: Colors.transparent,
tabs: _animationGenerator(),
),
),
body: TabBarView(
controller: _tabController,
children: List.generate(4, (index) => FittedBox(
child: Text('Tab $index'),
)),
),
);
}
void _listener() {
if (_tabController.indexIsChanging) {
_animationControllers[_tabController.previousIndex].value = 0;
} else {
_animationControllers[_tabController.index].forward();
}
}
@override
void dispose() {
super.dispose();
_tabController
..removeListener(_listener)
..dispose();
_animationControllers.forEach((ac) => ac.dispose());
}
}
class TabPainter extends CustomPainter {
final Animation<double> animation;
final int index;
final Color color;
final tabPaint = Paint();
TabPainter({
required this.animation,
required this.index,
required this.color,
});
@override
void paint(ui.Canvas canvas, ui.Size size) {
// timeDilation = 10;
if ((animation.value - index).abs() < 1) {
final rect = Offset.zero & size;
canvas.clipRect(rect);
canvas.translate(size.width * (animation.value - index), 0);
final tabRect = Alignment.bottomCenter.inscribe(Size(size.width, 3), rect);
canvas.drawRect(tabRect, tabPaint..color = color);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/354861.html
