Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Flutter 如何在颤振中处理具有动态长度的嵌套TabBar_Flutter_Dart - Fatal编程技术网

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()
方法用于节选项卡内容,它的return
CustomTabView
为每次检查返回选项卡

问题是:
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(),
)