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

Flutter中带Bottom的AppBar添加上方内容时构建失败问题排查

问题:嵌套AppBar添加Bottom后布局崩溃

之前通过将常规AppBar包裹在Column中,再嵌套进PreferredSize组件,能在Scaffold的AppBar上方添加内容,但给AppBar添加Bottom后项目无法构建。

报错代码

Scaffold(
      appBar: PreferredSize(
          preferredSize: const Size.fromHeight(kToolbarHeight + 50 + 50),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const SizedBox(
                height: 50,
                child: Text("top"),
              ),
              AppBar(
                title: Text(widget.title),
                bottom: const PreferredSize(
                  preferredSize: Size.fromHeight(50),
                  child: SizedBox(
                    height: 50,
                    child: Text("bottom"),
                  ),
                ),
              ),
            ],
          ),
        ),
      body: _buildBody(),
    );

异常信息

The following assertion was thrown during performLayout():
RenderBox was not laid out: RenderFlex#d89b4 relayoutBoundary=up10 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1979 pos 12: 'hasSize'


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

The relevant error-causing widget was: 
  AppBar AppBar:file:///Users/carconsis/Documents/app_bar_test/lib/main.dart:99:15
When the exception was thrown, this was the stack: 
#2      RenderBox.size (package:flutter/src/rendering/box.dart:1979:12)
#3      RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:237:39)
#4      RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#5      RenderPositionedBox.performLayout (package:flutter/src/rendering/shifted_box.dart:437:14)
#6      RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#7      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#8      RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#9      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#10     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#11     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#12     _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1376:11)
#13     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#14     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#15     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#16     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#17     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#18     ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:56:11)
#19     RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:829:43)
#20     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:931:32)
#21     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#22     RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:277:14)
#23     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#24     MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:171:12)
#25     _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:948:22)
#26     MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:240:7)
#27     RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:403:14)
#28     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1757:7)
#29     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:887:18)
#30     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:504:19)
#31     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:892:13)
#32     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
#33     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
#34     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
#35     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997:5)
#39     _invoke (dart:ui/hooks.dart:151:10)
#40     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#41     _drawFrame (dart:ui/hooks.dart:115:31)
(elided 5 frames from class _AssertionError and dart:async)
The following RenderObject was being processed when the exception was fired: RenderPadding#9e6b1 relayoutBoundary=up9 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE
...  parentData: offset=Offset(0.0, 0.0) (can use size)
...  constraints: BoxConstraints(0.0<=w<=392.7, 0.0<=h<=Infinity)
...  size: Size(392.7, 108.7)
...  padding: EdgeInsets(0.0, 52.7, 0.0, 0.0)
...  textDirection: ltr
RenderObject: RenderPadding#9e6b1 relayoutBoundary=up9 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE
  parentData: offset=Offset(0.0, 0.0) (can use size)
  constraints: BoxConstraints(0.0<=w<=392.7, 0.0<=h<=Infinity)
  size: Size(392.7, 108.7)
  padding: EdgeInsets(0.0, 52.7, 0.0, 0.0)
  textDirection: ltr
...  child: RenderFlex#d89b4 relayoutBoundary=up10 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...    parentData: offset=Offset(0.0, 52.7) (can use size)
...    constraints: BoxConstraints(0.0<=w<=392.7, 0.0<=h<=Infinity)
...    size: MISSING
...    direction: vertical
...    mainAxisAlignment: spaceBetween
...    mainAxisSize: max
...    crossAxisAlignment: center
...    verticalDirection: down
...    child 1: RenderConstrainedBox#8462b NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...      parentData: offset=Offset(0.0, 0.0); flex=1; fit=FlexFit.loose
...      constraints: MISSING
...      size: MISSING
...      additionalConstraints: BoxConstraints(0.0<=w<=Infinity, 0.0<=h<=56.0)
...      child: RenderClipRect#e5322 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...        parentData: <none>
...        constraints: MISSING
...        size: MISSING
...        child: RenderCustomSingleChildLayoutBox#b0c7f NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...          parentData: <none>
...          constraints: MISSING
...          size: MISSING
...    child 2: RenderConstrainedBox#8f9db NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...      parentData: offset=Offset(0.0, 0.0); flex=null; fit=null
...      constraints: MISSING
...      size: MISSING
...      additionalConstraints: BoxConstraints(0.0<=w<=Infinity, h=50.0)
...      child: RenderParagraph#19aed NEEDS-LAYOUT NEEDS-PAINT
...        parentData: <none>
...        constraints: MISSING
...        size: MISSING
...        textAlign: start
...        textDirection: ltr
...        softWrap: wrapping at box width
...        overflow: clip
...        locale: en_US
...        maxLines: unlimited
...        text: TextSpan
...          debugLabel: (englishLike bodyMedium 2014).merge(blackMountainView bodyMedium)
...          inherit: false
...          color: Color(0xdd000000)
...          family: Roboto
...          size: 14.0
...          weight: 400
...          baseline: alphabetic
...          decoration: TextDecoration.none
...          "bottom"

问题原因与解决方法

原因

当AppBar设置bottom属性后,其内部布局逻辑会依赖Scaffold提供的约束计算整体高度,而外层用Column+PreferredSize固定高度的方式,打破了AppBar自身的布局约束链,导致RenderBox无法正确获取尺寸,触发布局断言错误。

解决方法

有两种可行的修正方案:

方案1:自定义完整的顶部栏结构

放弃嵌套AppBar,直接用Container等组件实现toolbar效果,避免AppBar内部布局冲突:

Scaffold(
  appBar: PreferredSize(
    preferredSize: const Size.fromHeight(kToolbarHeight + 50 + 50),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        const SizedBox(
          height: 50,
          child: Center(child: Text("top")),
        ),
        // 自定义Toolbar替代原AppBar
        Container(
          height: kToolbarHeight,
          padding: const EdgeInsets.symmetric(horizontal: 16),
          alignment: Alignment.centerLeft,
          color: Theme.of(context).primaryColor,
          child: Text(
            widget.title,
            style: const TextStyle(color: Colors.white, fontSize: 20),
          ),
        ),
        const SizedBox(
          height: 50,
          child: Center(child: Text("bottom")),
        ),
      ],
    ),
  ),
  body: _buildBody(),
);

方案2:利用AppBar的flexibleSpace属性添加顶部内容

符合AppBar自身布局逻辑,不会破坏约束链:

Scaffold(
  appBar: PreferredSize(
    preferredSize: const Size.fromHeight(kToolbarHeight + 50 + 50),
    child: AppBar(
      title: Text(widget.title),
      // 用flexibleSpace添加顶部内容
      flexibleSpace: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: const [
          SizedBox(
            height: 50,
            child: Center(child: Text("top")),
          ),
        ],
      ),
      bottom: const PreferredSize(
        preferredSize: Size.fromHeight(50),
        child: SizedBox(
          height: 50,
          child: Center(child: Text("bottom")),
        ),
      ),
    ),
  ),
  body: _buildBody(),
);

内容的提问来源于stack exchange,提问作者Christian N.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:45:31