You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:01:12