flutter - Flutter TabBar 和 TabBarView 在动态调整选项卡数量时不同步
问题描述
我有一种情况,我有一个小部件,它可以让我从列表中选择哪些选项卡选项应该显示在另一个小部件中(第二个小部件有一个TabController
)。
我正在使用 aChangeNotifier
来保持选择哪些选项卡在列表中的状态。
除了我在最后一个选项卡上然后删除它的情况外,这一切都很好 - 在这种情况下它仍然有效,但是TabBar
返回到第一个选项卡,而TabBarView
返回到第二个选项卡。
我已经尝试了多种不同的方法来解决这个问题(添加keys
到小部件,手动将选项卡控制器索引保存在状态中并在延迟后导航到那里,在调用 a 的顶级小部件中添加回调setState
),这些都没有任何效果.
这是完整的代码 - 我试图使它成为我正在做的最小版本:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Tab Refresh Issue Demo',
home: Scaffold(body:
ChangeNotifierProvider<CurrenLTabsProvider>(
create: (_) => CurrenLTabsProvider(),
child: Consumer<CurrenLTabsProvider>(
builder: (context, tp, child) =>
Row(
children: [
const SizedBox(
child: TabSelectionWidget(),
width: 200,
height: 1000,
),
SizedBox(
child: TabWidget(tp.availableTabItems, tp._selectedTabIds),
width: 800,
height: 1000,
),
],
),
),
),
),
);
}
}
class CurrenLTabsProvider extends ChangeNotifier {
List<MyTabItem> availableTabItems = [
MyTabItem(1, 'Tab 1', const Text('Content for Tab 1')),
MyTabItem(2, 'Tab 2', const Text('Content for Tab 2')),
MyTabItem(3, 'Tab 3', const Text('Content for Tab 3')),
// MyTabItem(4, 'Tab 4', const Text('Content for Tab 4')),
// MyTabItem(5, 'Tab 5', const Text('Content for Tab 5')),
];
List<int> _selectedTabIds = [];
int currentTabIndex = 0;
set selectedTabs(List<int> ids) {
_selectedTabIds = ids;
notifyListeners();
}
List<int> get selectedTabs => _selectedTabIds;
void doNotifyListeners() {
notifyListeners();
}
}
class MyTabItem {
final int id;
final String title;
final Widget widget;
MyTabItem(this.id, this.title, this.widget);
}
class TabSelectionWidget extends StatefulWidget {
const TabSelectionWidget({Key? key}) : super(key: key);
@override
_TabSelectionWidgetState createState() => _TabSelectionWidgetState();
}
class _TabSelectionWidgetState extends State<TabSelectionWidget> {
@override
Widget build(BuildContext context) {
return Consumer<CurrenLTabsProvider>(
builder: (context, tabsProvider, child) {
return Column(
children: [
Expanded(
child: ListView.builder(
itemCount: tabsProvider.availableTabItems.length,
itemBuilder: (context, index) {
final item = tabsProvider.availableTabItems[index];
return ListTile(
title: Text(item.title),
leading: Checkbox(
value: tabsProvider.selectedTabs.contains(item.id),
onChanged: (value) {
if (value==true) {
setState(() {
tabsProvider.selectedTabs.add(item.id);
tabsProvider.doNotifyListeners();
});
} else {
setState(() {
tabsProvider.selectedTabs.remove(item.id);
tabsProvider.doNotifyListeners();
});
}
},
),
);
},
),
),
],
);
}
);
}
}
class TabWidget extends StatefulWidget {
const TabWidget(this.allItems, this.selectedTabs, {Key? key}) : super(key: key);
final List<MyTabItem> allItems;
final List<int> selectedTabs;
@override
_TabWidgetState createState() => _TabWidgetState();
}
class _TabWidgetState extends State<TabWidget> with TickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
_tabController = TabController(length: widget.selectedTabs.length, vsync: this);
super.initState();
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.selectedTabs.isEmpty) {
return Container(
padding: const EdgeInsets.all(20),
child: const Text("Select some tabs to be available."),
);
} // else ..
// re-initialise here, so changes made in other widgets are picked up when the widget is rebuilt
_tabController = TabController(length: widget.selectedTabs.length, vsync: this);
var tabs = <Widget>[];
List<Widget> tabBody = [];
// loop through all available tabs
for (var i = 0; i < widget.allItems.length; i++) {
// if it is selected, then show it
if (widget.selectedTabs.contains(widget.allItems[i].id)) {
tabs.add( Tab(text: widget.allItems[i].title) );
tabBody.add( widget.allItems[i].widget );
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.black54,
tabs: tabs,
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab,
),
Expanded(
child: TabBarView(
children: tabBody,
controller: _tabController,
),
),
]
);
}
}
为什么TabBar
重置到第一个条目,而TabBarView
重置到第二个条目?
我能做些什么来修复它,以便它们都重置为第一个条目?
解决方案
推荐阅读
- assert - Dafny - 不知道为什么在调用计算数组是否包含元素的方法后断言不会成立
- oracle - 在 oracle 中查询时以数组形式回答
- reactjs - 惰性初始状态 - 它是什么以及如何使用它?
- java - libgdx 如何在 3D 透视图中绘制文本?
- java - 如何修复使用 Apache Commons VFS 将文件上传到 SFTP 服务器时发生的错误
- optimization - 在 AMPL 中定义两个控件之间的包络
- ios - 从 iOS 8.3 调用时 NSURLSession 请求失败 它在 iOS 12 中完美运行
- javascript - React:带有选择下拉菜单的动态组件
- python - Python,使用 SSH 的 SFTP 连接 - Ed25519 密钥
- azure - Blob 触发器:使用 Azure Active Directory (AAD) 集成而不是存储连接字符串