Flutter 如何在颤振中处理具有动态长度的嵌套TabBar
我试图显示每个主选项卡的选项卡(嵌套选项卡栏)。 我有一个课程页面,在Flutter 如何在颤振中处理具有动态长度的嵌套TabBar,flutter,dart,Flutter,Dart,我试图显示每个主选项卡的选项卡(嵌套选项卡栏)。 我有一个课程页面,在SliverAppBar()中显示本课程的信息。每门课程都有许多部分,每个部分都有许多考试 这是我的构建方法: @override Widget build(BuildContext context) { double height = MediaQuery.of(context).size.height; double statusBarHeight = MediaQuery.of(con
SliverAppBar()
中显示本课程的信息。每门课程都有许多部分,每个部分都有许多考试
这是我的构建方法:
@override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double statusBarHeight =
MediaQuery.of(context).padding.top + 56; // 56 is height of Appbar.
return Scaffold(
body: Container(
child: DefaultTabController(
length: _sections.length,
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(
elevation: 0,
title: Text(
widget._course.shortName +
' ' +
widget._course.code.toString(),
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
actionsIconTheme: IconThemeData(color: widget._course.color),
expandedHeight: height / 2,
floating: true,
pinned: true,
centerTitle: true,
titleSpacing: 5,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
tooltip: 'Back',
splashColor: Colors.transparent,
onPressed: () => Navigator.pop(context),
),
backgroundColor: widget._course.color,
flexibleSpace: Container(
padding: EdgeInsets.only(top: statusBarHeight),
child: Text('Course information will be here'),
),
),
SliverPersistentHeader(
floating: false,
delegate: _SliverAppBarDelegate(
TabBar(
indicatorSize: TabBarIndicatorSize.label,
labelPadding: EdgeInsets.symmetric(horizontal: 10),
indicator: CircleTabIndicator(
color: Colors.white,
radius: 2.5,
),
indicatorColor: Colors.white,
isScrollable: true,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
unselectedLabelStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.w300),
labelStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
tabs: List<Widget>.generate(
_sections.length,
(int index) {
return customTab(_sections[index].id);
},
),
),
widget._course.color,
),
pinned: false,
),
];
},
body: Center(
child: getTabBarConten(),
),
),
),
),
);
}
现在,我在Build方法中创建的每个部分选项卡内容都有许多检查选项卡getTabBarConten()
方法:
Widget getTabBarConten() {
return TabBarView(
children: List<Widget>.generate(
_sections.length,
(int index) {
return CustomTabView(
color: widget._course.color,
initPosition: initPosition,
itemCount: _sections[index].exams.length,
tabBuilder: (context, index) => customTab(
_sections[index].exams[index].type.toString(),
isSection: true,
),
pageBuilder: (context, index) => Center(
child: Text(_sections[index].exams[index].supervisor +
' ' +
_sections[index].instructor)),
onPositionChange: (index) {
setState(() {
initPosition = index;
});
},
);
},
),
);
}
Widget gettabarconten(){
返回选项卡视图(
子项:List.generate(
_节长,
(国际索引){
返回CustomTabView(
颜色:widget.\u course.color,
initPosition:initPosition,
itemCount:_节[index].exames.length,
tabBuilder:(上下文,索引)=>customTab(
_节[index]。考试[index]。键入.toString(),
是的,
),
页面生成器:(上下文,索引)=>中心(
子项:文本(_段[索引])。考试[索引]。主管+
' ' +
_第[索引]节,
onPositionChange:(索引){
设置状态(){
初始位置=索引;
});
},
);
},
),
);
}
此getTabBarConten()
方法用于节选项卡内容,它的returnCustomTabView
为每次检查返回选项卡
问题是:
RangeError(索引):无效值:不在范围0..1中,包括:2
在本例中,本课程有2个部分,每个部分有3个考试。因此,CustomTabView
中的itemCount
是3,节的长度是2,这是错误产生的原因
如果我将itemCount
设置为相同的节长度,则工作正常(即使itemCount
小于节长度):
但是如果itemCount
大于节的长度,则其不起作用
为什么会发生此错误,我的意思是它们之间没有任何关系,在getTabBarConten()
方法中,它返回TabBarView()
部分选项卡,每个选项卡返回CustomTabView()
每个检查的返回选项卡
那么,为什么发生这个错误,有人能帮我吗?请:(谢谢他在这方面的回答,这对我有帮助。这是另一种方式,但这是工作
[更新:九月]
我将尝试为同一个示例编写代码。也许它会帮助某人:)
代码如下:
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar, this._color);
TabBar _tabBar;
final Color _color;
@override
double get minExtent => _tabBar.preferredSize.height;
@override
double get maxExtent => _tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
color: _color,
alignment: Alignment.center,
child: _tabBar,
);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
import 'package:flutter/material.dart';
class CustomTabView extends StatefulWidget {
final int itemCount;
final IndexedWidgetBuilder tabBuilder;
final IndexedWidgetBuilder pageBuilder;
final Widget stub;
final ValueChanged<int> onPositionChange;
final ValueChanged<double> onScroll;
final int initPosition;
final Color color;
final bool isExamTabs;
final TabController controller;
CustomTabView({
@required this.itemCount,
@required this.tabBuilder,
@required this.pageBuilder,
this.stub,
this.onPositionChange,
this.onScroll,
this.initPosition,
this.color,
this.isExamTabs = false,
this.controller,
});
@override
_CustomTabsState createState() => _CustomTabsState();
}
class _CustomTabsState extends State<CustomTabView>
with TickerProviderStateMixin {
TabController controller;
int _currentCount;
int _currentPosition;
@override
void initState() {
if (widget.controller == null) {
_currentPosition = widget.initPosition ?? 0;
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation.addListener(onScroll);
_currentCount = widget.itemCount;
} else {
controller = widget.controller;
}
super.initState();
}
@override
void didUpdateWidget(CustomTabView oldWidget) {
if (_currentCount != widget.itemCount) {
controller.animation.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
if (widget.initPosition != null) {
_currentPosition = widget.initPosition;
}
if (_currentPosition > widget.itemCount - 1) {
_currentPosition = widget.itemCount - 1;
_currentPosition = _currentPosition < 0 ? 0 : _currentPosition;
if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
widget.onPositionChange(_currentPosition);
}
});
}
}
_currentCount = widget.itemCount;
setState(() {
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation.addListener(onScroll);
});
} else if (widget.initPosition != null) {
controller.animateTo(widget.initPosition);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
controller.animation.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.itemCount < 1) return widget.stub ?? Container();
double height = MediaQuery.of(context).size.height;
return Container(
height: height - 100,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
color: widget.color,
alignment: Alignment.center,
child: widget.isExamTabs
? TabBar(
controller: controller,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 3.5,
indicatorColor: Colors.white,
isScrollable: true,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
unselectedLabelStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.w300),
labelStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
tabs: List.generate(
widget.itemCount,
(index) => widget.tabBuilder(context, index),
),
)
: TabBar(
controller: controller,
indicatorSize: TabBarIndicatorSize.label,
labelPadding: EdgeInsets.symmetric(horizontal: 10),
indicatorColor: Colors.white,
isScrollable: true,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
unselectedLabelStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.w300),
labelStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
tabs: List.generate(
widget.itemCount,
(index) => widget.tabBuilder(context, index),
),
),
),
Expanded(
child: TabBarView(
controller: controller,
children: List.generate(
widget.itemCount,
(index) => widget.pageBuilder(context, index),
),
),
),
],
),
);
}
onPositionChange() {
if (!controller.indexIsChanging) {
_currentPosition = controller.index;
if (widget.onPositionChange is ValueChanged<int>) {
widget.onPositionChange(_currentPosition);
}
}
}
onScroll() {
if (widget.onScroll is ValueChanged<double>) {
widget.onScroll(controller.animation.value);
}
}
}
在问题链接中找不到页面。抱歉,这不是我的错:)。我更新了答案,并添加了代码。请随时问我任何问题
CustomTabView(
initPosition: 0,
itemCount: _course.sections.length,
tabBuilder: (context, index) =>
secionTab(_course.sections[index].id), // Sections tabs
pageBuilder: (context, index) => getSectionTabBarConten(index), // Content for each section. To show exams for "_course.sections[index]" call inside it "CustomTabView()" again for exams. It's mean all Exams per secion.
onPositionChange: (index) {},
// onScroll: (position) => print("POS : " + '$position'),
color: _course.getColor(),
)