使用TabBarIndicatorSize.tab导致Flutter TabBar动画异常问题
Flutter TabBar设置
TabBarIndicatorSize.tab时非相邻切换动画错位问题 问题描述
为让Tab指示器铺满整个Tab项,设置TabBar.indicatorSize = TabBarIndicatorSize.tab后,点击相邻Tab时动画表现正常,但点击非相邻Tab时,指示器动画会出现错位偏移。
问题原因
这是Flutter框架的已知逻辑问题:当使用TabBarIndicatorSize.tab配置时,TabController在计算非相邻Tab切换的动画插值过程中,未正确处理指示器宽度的变化逻辑,导致动画路径计算错误。
解决方案
通过自定义Tab指示器替代原生实现,绕开错误的动画计算逻辑,确保无论切换相邻还是非相邻Tab,动画都能正常展示。以下是修复后的完整代码:
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const TabScreen(), ); } } class TabScreen extends StatefulWidget { const TabScreen({super.key}); @override State<TabScreen> createState() => _TabScreenState(); } class _TabScreenState extends State<TabScreen> with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 7, vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('TabBar Example'), bottom: TabBar( controller: _tabController, // 使用自定义指示器替代原生indicatorSize配置 indicator: _FullWidthTabIndicator( color: Theme.of(context).colorScheme.primary, height: 4, ), tabs: const [ Tab(text: 'Tab 1'), Tab(text: 'Tab 2'), Tab(text: 'Tab 3'), Tab(text: 'Tab 4'), Tab(text: 'Tab 5'), Tab(text: 'Tab 6'), Tab(text: 'Tab 7'), ], ), ), body: TabBarView( controller: _tabController, children: const [ Center(child: Text('Content of Tab 1')), Center(child: Text('Content of Tab 2')), Center(child: Text('Content of Tab 3')), Center(child: Text('Content of Tab 4')), Center(child: Text('Content of Tab 5')), Center(child: Text('Content of Tab 6')), Center(child: Text('Content of Tab 7')), ], ), ); } } // 自定义铺满Tab宽度的指示器 class _FullWidthTabIndicator extends Decoration { final Color color; final double height; const _FullWidthTabIndicator({required this.color, required this.height}); @override BoxPainter createBoxPainter([VoidCallback? onChanged]) { return _FullWidthTabIndicatorPainter(color: color, height: height); } } class _FullWidthTabIndicatorPainter extends BoxPainter { final Color color; final double height; _FullWidthTabIndicatorPainter({required this.color, required this.height}); @override void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { // 绘制铺满整个Tab宽度的指示器 final Rect rect = offset & configuration.size!; final Paint paint = Paint()..color = color; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(rect.left, rect.bottom - height, rect.width, height), const Radius.circular(2), ), paint, ); } }
说明
- 自定义
_FullWidthTabIndicator直接基于Tab的尺寸绘制全屏指示器,完全绕开原生TabBarIndicatorSize.tab的动画计算逻辑 - 可根据需求调整指示器的高度、圆角、颜色等样式
- 保留原生TabBar的所有交互逻辑,同时彻底修复非相邻Tab切换的动画错位问题
内容的提问来源于stack exchange,提问作者Andreas Hofmann
相关产品推荐
相关产品推荐

