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

HookConsumerWidget中TabController与TabBar标签数不匹配问题求助

问题解决:HookConsumerWidget中TabController与TabBar长度不匹配

问题根源

  1. useTabController(initialLength: samples.length)仅在组件首次构建时初始化控制器长度,后续samples列表更新时,控制器长度不会自动同步,导致和TabBar的tabs数量不一致。
  2. 点击加号后直接调用controller.animateTo(samples.length-1)时,samples还是更新前的旧值,跳转索引错误,且此时控制器长度未更新,触发长度不匹配的报错。

修复方案及代码修改

核心修复点

  • 监听samples列表变化,同步更新TabController的长度
  • 优化控制器listener的管理,避免重复监听
  • 利用Riverpod的状态监听,实现新增标签后的自动跳转

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class SampleListNotifier extends StateNotifier<List<String>> {
  SampleListNotifier() : super(<String>[]);

  void createSample(String title) {
    state = [...state, title];
  }
}

final sampleListProvider = StateNotifierProvider<SampleListNotifier, List<String>>(
      (ref) => SampleListNotifier(),
);

class CustomTabController extends HookConsumerWidget {
  const CustomTabController({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final samples = ref.watch(sampleListProvider);
    final controller = useTabController(initialLength: samples.length);
    final currentIndex = useState(0);

    // 监听samples变化,同步控制器长度并管理listener
    useEffect(() {
      controller.length = samples.length;
      // 添加索引监听
      void listener() {
        currentIndex.value = controller.index;
      }
      controller.addListener(listener);
      // 组件销毁或依赖变化时移除listener
      return () => controller.removeListener(listener);
    }, [samples, controller]);

    // 监听列表新增,自动跳转到最后一个标签
    ref.listen(sampleListProvider, (previous, next) {
      if (next.length > (previous?.length ?? 0)) {
        controller.animateTo(next.length - 1);
      }
    });

    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        leading: IconButton(
            icon: const Icon(Icons.add),
            onPressed: () {
              const result = 'test';
              if (result.isNotEmpty) {
                ref.read(sampleListProvider.notifier).createSample(result);
              }
            }),
        title: Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            TabBar(
              onTap: (index) {},
              controller: controller,
              isScrollable: true,
              tabs: samples
                  .map((String sample) => Tab(text: sample))
                  .toList(),
            ),
          ],
        ),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () {
              print('pressed!');
            },
          ),
        ],
      ),
      body: TabBarView(
        controller: controller,
        children: samples
            .map((String sample) =>
            TabPage(key: UniqueKey() , title: sample))
            .toList(),
      ),
    );
  }
}

class TabPage extends StatelessWidget {
  const TabPage({required Key key, required this.title}) : super(key: key);
  final String title;

  @override
  Widget build(BuildContext context) {
    return Center(
        child: Text(title)
    );
  }
}

关键修改说明

  • 同步控制器长度:通过useEffect监听samples列表变化,每次列表更新时设置controller.length = samples.length,确保控制器长度与TabBar的tabs数量一致。
  • listener管理:在useEffect中添加控制器的索引监听,并返回清理函数移除listener,避免重复监听导致的异常。
  • 自动跳转新标签:使用ref.listen监听sampleListProvider的状态变化,当列表新增元素时,自动跳转到最后一个标签,此时samples已经是更新后的数据,索引准确。

内容的提问来源于stack exchange,提问作者raimtoon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 19:08:18