HookConsumerWidget中TabController与TabBar标签数不匹配问题求助
问题解决:HookConsumerWidget中TabController与TabBar长度不匹配
问题根源
useTabController(initialLength: samples.length)仅在组件首次构建时初始化控制器长度,后续samples列表更新时,控制器长度不会自动同步,导致和TabBar的tabs数量不一致。- 点击加号后直接调用
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
相关产品推荐
相关产品推荐

