具有动态容器高度的 TabBarView

Posted

技术标签:

【中文标题】具有动态容器高度的 TabBarView【英文标题】:TabBarView with dynamic Container height 【发布时间】:2019-07-05 15:57:09 【问题描述】:

这就是我的布局结构

这是主屏幕

ListView(
  children: <Widget>[
    _buildCarousel(),
    _buildHomeTabBar(),
  ],
)

这是 HomeTabBar 屏幕

SingleChildScrollView(
  child:
    DefaultTabController(
      length: myTabs.length,
      initialIndex: 0,
      child: Column(
        children: [
          TabBar(
            isScrollable: true,
            tabs: myTabs,
          ),
          Container(
            height: 600,
              child: 
              TabBarView(
                children: [
                  ChildScreen(),
                  ChildScreen2(),
                  ChildScreen3(),
                ],
              )
          )
        ],
      ))
);

我想摆脱Container 的高度。我们如何做到这一点?

ChildScreen 正在从 REST 获取数据,它实际上是一个 GridView.Builder,所以Container 的高度应该是动态的。

抱歉,我错过了 ChildScreen 的布局,实际上是这样的

SingleChildScrollView(
  // shrinkWrap: true,
child: Column(
    children: <Widget>[
        StreamBuilder(
          stream: categoryBloc.categoryList,
          builder: (context, AsyncSnapshot<List<Category>> snapshot)
              if (snapshot.hasData && snapshot!=null) 
                if(snapshot.data.length > 0)
                  return buildCategoryList(snapshot);
                
                else if(snapshot.data.length==0)
                    return Text('No Data');
                
              
              else if (snapshot.hasError) 
                return ErrorScreen(errMessage: snapshot.error.toString());
                   
              return Center(child: CircularProgressIndicator());

          ,
        ),       
    ]
  )
);

所以StreamBuilder 内部是GridView.Builder。 我要删除的主要内容Container 高度。在不同的设备上看起来很难看...

因此,如果我删除高度,它将不会显示在屏幕上并引发错误

I/flutter (493): #2 RenderObject.layout (package:flutter/src/rendering/object.dart:1578:12) I/flutter(493):#3 RenderSliv​​erList.performLayout.advance(包:flutter/src/rendering/sliver_list.dart:200:17) I/flutter (493): #4 RenderSliv​​erList.performLayout (package:flutter/src/rendering/sliver_list.dart:233:19) I/flutter (493): #5 RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7) I/flutter (493): #6 RenderSliv​​erPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:182:11) I/flutter (493): #7 RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7) I/flutter (493): #8 RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:405:13) I/flutter (493): #9 RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1316:12) I/flutter (493): #10 RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1234:20) I/flutter (493): #11 RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7) I/flutter (493): #12 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13) I/flutter (493): #13 RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)

【问题讨论】:

你得到什么错误 - 当你删除容器高度时。 - height: 600,? 它不会出现在屏幕上。我将更新错误消息。 在 GridView.Builder 中是 shrinkWrap: true, 吗? @anmol.majhail 是 shrinkWrap: true 在 GridView.Builder 上 这就是我解决类似问题的方法***.com/a/66540036/4150479 【参考方案1】:

我的项目中有类似的任务。问题的根源是您试图将TabBarView(试图尽可能大的可滚动小部件)放在没有高度的列小部件中。

其中一个解决方案是在此示例中为 ListView + Column 去掉包裹 TabBarView 的可滚动小部件。并使用NestedScrollView 代替它们。您需要将_buildCarousel()TabBar 放在headerSliverBuilder 部分中,并将TabBarView 放在NestedScrollView 的正文中。

我不知道你的设计看起来如何,但NestedScrollView 默认打开屏幕视图的 100% 高度。因此,如果您想使所有内容都在一个屏幕上,只需在需要时更改 ScrollController 行为即可更轻松地阻止滚动。

在此示例中,我阻止了第三个选项卡上的滚动,但您可以检查网格视图的最后一项的可见性。只需将键添加到最后一项,并检查其在屏幕上的位置。

希望对你有所帮助。

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin 
  final bodyGlobalKey = GlobalKey();
  final List<Widget> myTabs = [
    Tab(text: 'auto short'),
    Tab(text: 'auto long'),
    Tab(text: 'fixed'),
  ];
  TabController _tabController;
  ScrollController _scrollController;
  bool fixedScroll;

  Widget _buildCarousel() 
    return Stack(
      children: <Widget>[
        Placeholder(fallbackHeight: 100),
        Positioned.fill(child: Align(alignment: Alignment.center, child: Text('Slider'))),
      ],
    );
  

  @override
  void initState() 
    _scrollController = ScrollController();
    _scrollController.addListener(_scrollListener);
    _tabController = TabController(length: 3, vsync: this);
    _tabController.addListener(_smoothScrollToTop);

    super.initState();
  

  @override
  void dispose() 
    _tabController.dispose();
    _scrollController.dispose();
    super.dispose();
  

  _scrollListener() 
    if (fixedScroll) 
      _scrollController.jumpTo(0);
    
  

  _smoothScrollToTop() 
    _scrollController.animateTo(
      0,
      duration: Duration(microseconds: 300),
      curve: Curves.ease,
    );

    setState(() 
      fixedScroll = _tabController.index == 2;
    );
  

  _buildTabContext(int lineCount) => Container(
        child: ListView.builder(
          physics: const ClampingScrollPhysics(),
          itemCount: lineCount,
          itemBuilder: (BuildContext context, int index) 
            return Text('some content');
          ,
        ),
      );

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: NestedScrollView(
        controller: _scrollController,
        headerSliverBuilder: (context, value) 
          return [
            SliverToBoxAdapter(child: _buildCarousel()),
            SliverToBoxAdapter(
              child: TabBar(
                controller: _tabController,
                labelColor: Colors.redAccent,
                isScrollable: true,
                tabs: myTabs,
              ),
            ),
          ];
        ,
        body: Container(
          child: TabBarView(
            controller: _tabController,
            children: [_buildTabContext(2), _buildTabContext(200), _buildTabContext(2)],
          ),
        ),
      ),
    );
  

.

另一种获得所需内容的方法是创建自定义 TabView 小部件。

【讨论】:

不是OP,但你能展示一个示例代码吗?我仍然被困在这里。 @Kherel Ketchil 如何在将列表滚动到顶部时将标签栏保持在屏幕顶部? 确定我们只需要将 TabBar 从 NestedScrollView 的 headerSliv​​erBuilder 部分移动到正文部分 singlechildscrollview 浪费了大约一个小时......非常感谢 经过 6 天的思考和尝试不同的方法。这解决了我的问题。谢谢【参考方案2】:

如果您也遇到 Expanded 错误,请不要使用 TabBarView 创建自定义 TabBarView

    从 onTap 获取索引,是 TabBar 的属性。 然后在 onTap 函数中使用 if else 来选择要选择的索引并使用索引初始化全局变量。 然后使用 Builder Widgets 来构建您的 TabBarView。再次在 Builder 中,您将使用 if else 来获取选择的索引。然后在 if else 中返回您的小部件。

在这种情况下,您可以获得小部件的最大或最小高度。您无需指定容器高度。

_selectedTabBar 是一个全局变量

 Column(children: [
               TabBar(
                       onTap: (index) 
                          print(index);                                  
                          setState(() 
                                _selectedTabbar = index;
                          );
                        ,
                        tabs: [
                          Tab(
                            text: 'Tab1',
                          ),
                          Tab(
                            text: 'Tab2',
                          ),
                          Tab(
                            text: 'Tab3',
                          ),
                        ],
                      ),
                    
                      Builder(builder: (_) 
                        if (_selectedTabbar == 0) 
                          return Container();//1st custom tabBarView
                         else if (_selectedTabbar == 1) 
                          return Container();//2nd tabView
                         else 
                          return Container(); //3rd tabView
                        
                      ),
]
)

【讨论】:

【参考方案3】:

1- 您需要将内容添加到列表中

2- 然后将你的 tabBarView 放入一个容器中

3- 假设您的项目具有固定高度,将容器的高度定义为小部件的高度,然后乘以您当前的索引内容或列表长度

例如:height: 90*_items.length.toDouble(),

【讨论】:

【参考方案4】:

用Expanded 小部件包装您的列表视图:

使用Expanded 小部件可以使RowColumnFlex 的子级扩展以填充主轴上的可用空间(例如,水平用于行或垂直用于列)。如果展开多个children,则根据flex factor在它们之间分配可用空间。

Expanded(
  child: ListView.builder(
  itemCount: 120,
  itemExtent: 32,
  itemBuilder: (BuildContext context, int index) 
    return new Row(
      children: <Widget>[
        Icon(Icons.access_alarm),
        Text(' Entry $index'),
      ],
    );
  ,
))

【讨论】:

不工作。抱歉,我错过了 ChildScreen 的布局,我将更新... 这不起作用,因为Expanded(类似TabBarView)会尽可能长。【参考方案5】:

工作解决方案: 你需要在两个地方扩展

    高于 DefaultTabController。 容器中的容器。
Expanded(
    child: Container(
        decoration: BoxDecoration(
          border: Border(
              top: BorderSide(
                  color: Colors.grey, width: 0.5))),
        child: TabBarView(
          children: <Widget>[
              Container(),
              Container(),
              Container(),
        ])),),

一个是不够的。

【讨论】:

【参考方案6】:

我有同样的要求,但需要在底部表格中显示具有动态高度的标签栏,所以我尝试了下面的代码,它对我来说非常好

class BottomSheetScreen extends StatefulWidget 
  @override
  _BottomSheetScreenState createState() => _BottomSheetScreenState();


class _BottomSheetScreenState extends State<BottomSheetScreen>
    with SingleTickerProviderStateMixin 

  late TabController _tabController;
  int _selectedTabbar = 0;

  @override
  Widget build(BuildContext context) 
    return Center(
      child: ElevatedButton(
        child: const Text('showModalBottomSheet'),
        onPressed: () 
          showModalBottomSheet(
            context: context,
            builder: (BuildContext context) 
              return StatefulBuilder(
                  builder: (BuildContext context, StateSetter setState) 
                return Wrap(
                  children: [
                    Column(children: [
                      TabBar(
                        controller: _tabController,
                        onTap: (index) 
                          print(index);
                          setState(() 
                            _selectedTabbar = index;
                          );
                        ,
                        tabs: [
                          Tab(text: 'Tab1'),
                          Tab(text: 'Tab2'),
                          Tab(text: 'Tab3'),
                        ],
                      ),
                      IntrinsicHeight(
                        child: Column(
                          children: [
                            _selectedTabbar == 0 ? Text("Tab1 content") : _selectedTabbar == 1 ? Text("Tab2 content") : Text("Tab3 content"),
                          ],
                        ),
                      ),
                      SizedBox(height: 10),
                      Text("bottom content"),
                    ]),
                  ],
                );
              );
            ,
          );
        ,
      ),
    );
  

【讨论】:

以上是关于具有动态容器高度的 TabBarView的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有 Flutter 的可滚动视图中拥有具有可变高度内容的 TabView?

在容器内的另一个 div 下方显示具有动态高度的 div?

弹性项目可以包装在具有动态高度的容器中吗?

Flutter - 如何创建遵循其父高度的流体/动态容器?

具有比例宽度和文本驱动高度的动态 UILabel

TabBarView动态更新列表