在Flutter中使用TabBarView时出现“水平视口给出了无限高度”的问题

59

我正在尝试制作一个个人资料页面,用户信息位于顶部。然后在其下方使用选项卡视图显示不同的内容。

enter image description here

这是我目前正在使用的代码。如果我去掉TabBarView,它不会报错,但是如果我将TabBarView包在Expanded中,就会出现错误:RenderFlex children have non-zero flex but incoming height constraints are unbounded.

     @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(''),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Padding(
            padding: EdgeInsets.all(10.0),
            child: Row(
              children: <Widget>[
                CircleAvatar(
                  minRadius: 45.0,
                  backgroundImage: NetworkImage(
                      'https://www.ienglishstatus.com/wp-content/uploads/2018/04/Anonymous-Whatsapp-profile-picture.jpg'),
                ),
                Padding(
                  padding: EdgeInsets.only(left: 10.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        'Testing Name',
                        style: TextStyle(
                          fontSize: 22.0,
                          color: Colors.grey.shade800,
                        ),
                      ),
                      Text(
                        '@testing_username',
                        style: TextStyle(
                          fontSize: 13.0,
                          color: Colors.grey.shade800,
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),

          DefaultTabController(
            length: 3,
            child: Column(
              children: <Widget>[
                TabBar(
                  tabs: <Widget>[
                    Tab(
                      icon: Padding(
                        padding: EdgeInsets.all(6.0),
                        child: Image.asset(
                          "assets/images/icons/butterlike.png",
                          color: Colors.grey.shade800,
                        ),
                      ),
                    ),
                    Tab(
                      icon: Padding(
                        padding: EdgeInsets.all(6.0),
                        child: Image.asset(
                          "assets/images/icons/butterlike.png",
                          color: Colors.grey.shade800,
                        ),
                      ),
                    ),
                    Tab(
                      icon: Padding(
                        padding: EdgeInsets.all(6.0),
                        child: Image.asset(
                          "assets/images/icons/butterlike.png",
                          color: Colors.grey.shade800,
                        ),
                      ),
                    ),
                  ],
                ),

                TabBarView(
                  children: <Widget>[
                    Container(
                      color: Colors.grey,
                    ),
                    Container(
                      color: Colors.green,
                    ),
                    Container(
                      color: Colors.purple,
                    ),
                  ],
                ),
              ],
            ),
          )
        ],
      ),
    );
  }

我尝试了这个的一个变体,但无法使其工作。


你想要实现什么?你有任何图片吗? - diegoveloper
@diegoveloper 通过图片编辑 - TheFairywarrior
5个回答

70

错误描述很清晰,TabBarView没有一个有界高度。同时父级 widget 也没有有界高度。所以,Expanded widget 无法解决这个问题。

编辑: 下面的解决方案是针对上面带有列的问题。在一般情况下,使用 shrinkWrap: true 的 ListView。(或者其他具有 shrinkWrap 属性的 widget) 如 @Konstantin Kozirev 正确提到的,shrinkWrap 会导致一些性能问题。寻找更好的更新解决方案。

有几种选择:

第一个解决方案:

将父 widget(Column) 包装在像 SizedBox 或 AspectRatio 这样的有限高度 widget 中。然后像这样使用 Expanded widget:

Expanded(
  child: TabBarView(...),
)

第二种解决方案:

在TabBarView本身上使用一个有界限的小部件,如SizedBox或AspectRatio:

SizedBox(
  height: 300.0,
  child: TabBarView(...),
)

注意:如果高度不是固定的,您也可以动态计算高度。


1
明白了,刚才使用了实际屏幕的高度,谢谢。我还是Flutter的新手<3。 - TheFairywarrior
如果我不想定义一个固定高度怎么办? - Mamo1234
使用ListView或其他类似的小部件。在你的情况下不可能吗? - Yamin
1
使用MediaQuery.of(context).size.height来计算高度。 - abhay tripathi
不要在任何滚动视图中使用shrinkWrap,它会导致严重的性能问题。 - Konstantin Kozirev
显示剩余2条评论

15

我通过在Container中添加TabBar,并在Expanded中添加TabBarView来解决了这个问题:

DefaultTabController(
    length: 3,
    child: Column(
      children: <Widget>[
        Container(child: TabBar(..)),
        Expanded(child: TabBarView(..)),
      ],
    ),
  );

在处理有限高度问题时,使用Expanded小部件会更好。特别是当您不想定义固定高度时。否则,对于动态计算固定高度,请使用MediaQuery.of(context).size.height。 - abhay tripathi
我认为容器在这里没有帮助,只需用Expanded包装TabBarView即可。 - cksrc

12

尝试使用IndexedStack代替TabBarView

我尝试过ExpandedshrinkWrap = true,但是没有一个能很好地工作,只需尝试以下示例。

示例:

class Product extends StatefulWidget {
  @override
  _ProductState createState() => _ProductState();
}

class _ProductState extends State<Product> with SingleTickerProviderStateMixin {
  TabController tabController;
  int selectedIndex = 0;

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 5, vsync: this, initialIndex: 0);
  }

  @override
  void dispose() {
    tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      initialIndex: 0,
      child: Scaffold(
        body: ListView(
          shrinkWrap: true,
          children: [
            TabBar(
         
              tabs: <Widget>[
                Tab(
                  text: 'one',
                ),
                Tab(
                  text: 'two',
                ),
                Tab(
                  text: 'three',
                ),
              ],
              controller: tabController,
              onTap: (index) {
                setState(() {
                  selectedIndex = index;
                  tabController.animateTo(index);
                });
              },
            ),
            IndexedStack(
              children: <Widget>[
                Visibility(
                  child: Text('test1'),
                  maintainState: true,
                  visible: selectedIndex == 0,
                ),
                Visibility(
                  child: Text('test2'),
                  maintainState: true,
                  visible: selectedIndex == 1,
                ),
                Visibility(
                  child: Text('test3'),
                  maintainState: true,
                  visible: selectedIndex == 2,
                ),
              ],
              index: selectedIndex,
            ),
          ],
        ),
      ),
    );
  }
}

特别感谢@arbalest


11

基于@ Yamin的回答,我使用了以下的SizeBox来获得整个页面。

    SizedBox.expand(
        child: TabBarView(),
      )

或者任何其他尺寸:

SizedBox(
        height: height:MediaQuery.of(context).size.height // or every other size ,
        child: TabBarView(),
      )

0
控制台中的错误消息提到:“视口在交叉轴上扩展以填充其容器,并约束其子项以匹配其在交叉轴上的范围。在这种情况下,水平视口被赋予了无限量的垂直空间来扩展”。 显然,这里的水平视口是指未给定高度约束的TabBarView小部件。 因此,请将TabBar和TabBarView小部件都包装在Expanded小部件中,并为它们分配适当的flex值,以让它们共享其父级的高度。 具体而言,
DefaultTabController(
        length: 3,
        child: Column(
          children: <Widget>[
            Expanded(
              flex: 1,
              child: TabBar(
              tabs: <Widget>[
                Tab(
                  icon: Padding(
                    padding: EdgeInsets.all(6.0),
                    child: Image.asset(
                      "assets/images/icons/butterlike.png",
                      color: Colors.grey.shade800,
                    ),
                  ),
                ),
                Tab(
                  icon: Padding(
                    padding: EdgeInsets.all(6.0),
                    child: Image.asset(
                      "assets/images/icons/butterlike.png",
                      color: Colors.grey.shade800,
                    ),
                  ),
                ),
                Tab(
                  icon: Padding(
                    padding: EdgeInsets.all(6.0),
                    child: Image.asset(
                      "assets/images/icons/butterlike.png",
                      color: Colors.grey.shade800,
                    ),
                  ),
                ),
              ],
            ),
            ),
            Expanded(
              flex: 9,
              child: TabBarView(
              children: <Widget>[
                Container(
                  color: Colors.grey,
                ),
                Container(
                  color: Colors.green,
                ),
                Container(
                  color: Colors.purple,
                ),
              ],
            ),
            )
            
          ],
        ),
      )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接