You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何为TabBarView设置动态高度?

Flutter实现TabBarView高度随内部内容动态调整

问题描述

在Flutter中使用TabBar时,希望TabBarView的高度根据内部ListView.builder的列表项数量动态变化,但设置动态高度或使用Expanded、Flexible组件时会报错。当前代码中TabBarView采用固定高度440,需要调整为随内容自适应。

解决方案

核心思路是让TabBarView根据内部子组件的实际高度自动计算自身高度,同时避免嵌套滚动冲突。具体修改步骤如下:

  1. 移除固定高度约束:删除包裹TabBarView的SizedBox(height: 440),取消固定高度限制。
  2. 禁用TabBarView滚动:设置physics: NeverScrollableScrollPhysics(),让页面整体由外层SingleChildScrollView负责滚动,避免嵌套滚动冲突。
  3. 用IntrinsicHeight包裹TabBarView:强制TabBarView根据子组件的高度计算自身高度,解决Column布局中无法确定高度的问题。
  4. 保留内部ListView配置:upcomingtenders中的ListView已设置shrinkWrap: true和physics: NeverScrollableScrollPhysics(),确保其高度由内容决定且不独立滚动,这部分保持不变。

修改后的关键代码片段

// 替换原固定高度的SizedBox部分
IntrinsicHeight(
  child: TabBarView(
    controller: _tabController,
    physics: NeverScrollableScrollPhysics(),
    children: [
      upcomingtenders(),
      Center(child: Text("This is Completed Tab")),
    ],
  ),
),

完整修改代码

import 'package:flutter/material.dart';
import 'package:safqat/Components/DatePicker.dart';

class MyOrders extends StatefulWidget {
  @override
  _MyOrdersState createState() => _MyOrdersState();
}

class _MyOrdersState extends State<MyOrders>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: SafeArea(
          child: Padding(
            padding:
                const EdgeInsets.only(left: 36, right: 36, bottom: 30, top: 30),
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Image.asset("assets/images/logo.png", width: 38, height: 38),
                    Container(
                      width: 205,
                      height: 36,
                      decoration: BoxDecoration(
                          color: Color(0xffFCC42B),
                          borderRadius: BorderRadius.circular(20)),
                      child: Center(
                          child: Text("Request for quotation",
                              style: TextStyle(
                                  fontFamily: "Poppins",
                                  fontWeight: FontWeight.w500,
                                  color: Color(0xff000000),
                                  fontSize: 14))),
                    ),
                  ],
                ),
                SizedBox(height: 26),
                DatePicker(),
                SizedBox(height: 26),
                Padding(
                  padding: const EdgeInsets.only(left: 50, right: 50),
                  child: Container(
                    child: TabBar(
                      labelStyle: TextStyle(color: Colors.black),
                      unselectedLabelStyle: TextStyle(color: Color(0x50000000)),
                      padding: EdgeInsets.zero,
                      labelPadding: EdgeInsets.zero,
                      controller: _tabController,
                      labelColor: Colors.black,
                      unselectedLabelColor: Color(0x50000000),
                      indicator: CircleTabIndicator(
                          color: Color(0xffFCC42B), radius: 3.5),
                      tabs: [
                        Tab(
                            child: Text('Up coming',
                                style: TextStyle(
                                    fontFamily: "Poppins", fontSize: 11))),
                        Tab(
                            child: Text('Completed',
                                style: TextStyle(
                                    fontFamily: "Poppins", fontSize: 11))),
                      ],
                    ),
                  ),
                ),
                // 修改后的TabBarView区域
                IntrinsicHeight(
                  child: TabBarView(
                    controller: _tabController,
                    physics: NeverScrollableScrollPhysics(),
                    children: [
                      upcomingtenders(),
                      Center(child: Text("This is Completed Tab")),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class CircleTabIndicator extends Decoration {
  final BoxPainter _painter;

  CircleTabIndicator({required Color color, required double radius})
      : _painter = _CirclePainter(color, radius);

  @override
  BoxPainter createBoxPainter([VoidCallback? onChanged]) {
    return _painter;
  }
}

class _CirclePainter extends BoxPainter {
  final Paint _paint;
  final double radius;

  _CirclePainter(Color color, this.radius)
      : _paint = Paint()
          ..color = color
          ..isAntiAlias = true;

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
    final Offset circleOffset =
        offset + Offset(cfg.size!.width / 2, cfg.size!.height - radius - 6);
    canvas.drawCircle(circleOffset, radius, _paint);
  }
}


class upcomingtenders extends StatelessWidget {
  const upcomingtenders({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          return ListView.builder(
            shrinkWrap: true,
            physics: NeverScrollableScrollPhysics(),
            scrollDirection: Axis.vertical,
            itemCount: 10,
            itemBuilder: (BuildContext context, int index) {
              return Column(
                children: [
                  SizedBox(height: 28),
                  Container(
                    width: MediaQuery.of(context).size.width,
                    height: 108,
                    child: Stack(
                      children: [
                        Container(
                          width: MediaQuery.of(context).size.width,
                          height: 95,
                          decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(8),
                              border: Border.all(
                                  color: Color(0xffD72A1D), width: 0.6)),
                        ),
                        Positioned(
                            top: 14,
                            left: 12,
                            child: Image.asset("assets/images/logo.png",
                                width: 38, height: 38)),
                        Positioned(
                          top: 16,
                          left: 58,
                          child: RichText(
                            textAlign: TextAlign.end,
                            text: TextSpan(
                              text: 'Tender name\n',
                              style: TextStyle(
                                fontWeight: FontWeight.w500,
                                fontFamily: "Poppins",
                                color: Colors.black,
                                fontSize: 14,
                              ),
                              children: <TextSpan>[
                                TextSpan(
                                  text: 'Status:    ',
                                  style: TextStyle(
                                    fontWeight: FontWeight.w400,
                                    color: Colors.black,
                                    fontSize: 11,
                                  ),
                                ),
                                TextSpan(
                                  text: 'Tendering',
                                  style: TextStyle(
                                    fontWeight: FontWeight.w500,
                                    color: Color(0xffD72A1D),
                                    fontSize: 10,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                        Positioned(
                          top: 16,
                          right: 12,
                          child: Text("Bid Closing Date\n12/02/2023",
                              textAlign: TextAlign.end,
                              style: TextStyle(
                                fontWeight: FontWeight.w400,
                                fontFamily: "Poppins",
                                color: Colors.black,
                                fontSize: 11,
                              )),
                        ),
                        Positioned(
                            right: 75,
                            top: 34,
                            child: Icon(Icons.access_time_rounded, size: 12, color: Colors.black,)),
                        Positioned(
                          bottom: 0,
                          left: 0,
                          right: 0,
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Container(
                                width: MediaQuery.of(context).size.width*0.30,
                                height: 34,
                                decoration: BoxDecoration(
                                    color: Color(0xffD72A1D),
                                    borderRadius: BorderRadius.circular(12)
                                ),
                                child: Center(child: Text("Tender Name", style: TextStyle(fontFamily: "Poppins", fontWeight: FontWeight.w300, color: Color(0xffffffff), fontSize: 13))),
                              ),
                              SizedBox(width: 10),
                              Container(
                                width: MediaQuery.of(context).size.width*0.30,
                                height: 34,
                                decoration: BoxDecoration(
                                    color: Color(0xff79BE27),
                                    borderRadius: BorderRadius.circular(12)
                                ),
                                child: Center(child: Text("Select", style: TextStyle(fontFamily: "Poppins", fontWeight: FontWeight.w300, color: Color(0xffffffff), fontSize: 13))),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  )
                ],
              );
            },
          );}
    );
  }
}

原理说明

  • IntrinsicHeight:强制TabBarView根据子组件的实际高度计算自身高度,解决Column布局中无法确定高度的报错问题。
  • NeverScrollableScrollPhysics():禁用TabBarView和内部ListView的独立滚动,让整个页面由外层SingleChildScrollView统一处理滚动逻辑。
  • shrinkWrap: true:让ListView根据内部列表项的总高度自适应,而非强制占满父容器空间。

内容的提问来源于stack exchange,提问作者Umer Yaseen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 18:07:07