Flutter 开发中常见的报错
如何识别并解决一些常见的 Flutter 框架的报错。
介绍
#本页说明若干常见的 Flutter 框架错误(包括布局错误),并给出解决建议。本文档会持续更新,未来会补充更多错误,欢迎贡献。欢迎提交 issue 或提交 pull request,让本页对你和 Flutter 社区更有帮助。
运行应用时出现纯红或纯灰屏幕
#通常称为「红(或灰)屏死机」,有时 Flutter 以此告诉你发生了错误。
应用在 debug 或 profile 模式下运行时可能出现红屏;在 release 模式下运行时可能出现灰屏。
通常这些错误由未捕获的异常引起(你可能需要额外的 try-catch 块),或由渲染错误(例如溢出错误)引起。
以下文章对调试此类错误很有帮助:
-
Flutter errors demystified by Abishek
-
Understanding and addressing the grey screen in Flutter by Christopher Nwosu-Madueke
-
Flutter stuck on white screen by Kesar Bhimani
-
Abishek 的 Flutter errors demystified
-
Christopher Nwosu-Madueke 的 Understanding and addressing the grey screen in Flutter
-
Kesar Bhimani 的 Flutter stuck on white screen
'A RenderFlex overflowed…'
#「A RenderFlex overflowed…」
#RenderFlex 溢出是最常见的 Flutter 框架错误之一,你可能已经遇到过。
错误是什么样子的?
When it happens, yellow and black stripes appear, indicating the area of overflow in the app UI. In addition, an error message displays in the debug console:
The following assertion was thrown during layout:
A RenderFlex overflowed by 1146 pixels on the right.
The relevant error-causing widget was
Row lib/errors/renderflex_overflow_column.dart:23
The overflowing RenderFlex has an orientation of Axis.horizontal.
The edge of the RenderFlex that is overflowing has been marked in the rendering
with a yellow and black striped pattern. This is usually caused by the contents
being too big for the RenderFlex.
(Additional lines of this message omitted)
发生时,应用 UI 中会出现黄黑条纹标示溢出区域,调试控制台也会显示错误消息。
你可能会如何遇到此错误?
当 Column 或 Row 含有未约束尺寸的子 widget 时,常会出现此错误。下面代码片段展示一种常见场景:
Widget build(BuildContext context) {
return Row(
children: [
const Icon(Icons.message),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Title', style: Theme.of(context).textTheme.headlineMedium),
const Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed '
'do eiusmod tempor incididunt ut labore et dolore magna '
'aliqua. Ut enim ad minim veniam, quis nostrud '
'exercitation ullamco laboris nisi ut aliquip ex ea '
'commodo consequat.',
),
],
),
],
);
}
In the above example,
the Column tries to be wider than the space the Row
(its parent) can allocate to it, causing an overflow error.
Why does the Column try to do that?
To understand this layout behavior, you need to know
how the Flutter framework performs layout:
"To perform layout, Flutter walks the render tree in a depth-first traversal and passes down size constraints from parent to child… Children respond by passing up a size to their parent object within the constraints the parent established." – Flutter architectural overview
在上述示例中,Column 试图比其父级 Row 能分配的空间更宽,导致溢出。Column 为何会如此?要理解这一布局行为,需要了解 Flutter 如何执行布局:
「为执行布局,Flutter 以深度优先遍历渲染树,自上而下传递尺寸约束……子节点在父节点建立的约束内向上回传尺寸。」——Flutter 架构概览
本例中,Row 未约束子节点尺寸,Column 也未约束。子 widget 缺少父级约束时,第二个 Text widget 会尽量按全部字符宽度显示;Text
自行决定的宽度被 Column 采用,与其父级 Row 能提供的最大水平空间冲突。
如何修复?
Well, you need to make sure the Column won't attempt
to be wider than it can be. To achieve this,
you need to constrain its width. One way to do it is to
wrap the Column in an Expanded widget:
return const Row(
children: [
Icon(Icons.message),
Expanded(
child: Column(
// code omitted
),
),
],
);
你需要确保 Column 不会试图超出可容纳的宽度。一种做法是用 Expanded widget 包裹 Column 以约束其宽度。另一种是用
Flexible 并指定 flex 因子;事实上 Expanded 等价于 flex 为 1.0 的
Flexible,其源码可证。要进一步理解 Flutter 布局中
Flex widget 的用法,可观看关于 Flexible widget 的 90 秒 Widget of the Week 视频。
更多信息:
以下链接提供更多关于此错误的信息。
'RenderBox was not laid out'
#「RenderBox was not laid out」
#此错误相当常见,但往往是渲染管线中更早发生的主要错误的副作用。
错误是什么样子的?
The message shown by the error looks like this:
RenderBox was not laid out:
RenderViewport#5a477 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
错误显示的消息类似上文。
你可能会如何遇到此错误?
通常与 box 约束违反有关,需要向 Flutter 提供更多关于如何约束相关 widget 的信息。可在理解约束页面了解更多。
The RenderBox was not laid out error is often
caused by one of two other errors:
- 'Vertical viewport was given unbounded height'
- 'An InputDecorator...cannot have an unbounded width'
RenderBox was not laid out 错误常由另外两种错误引起:
- 「Vertical viewport was given unbounded height」
- 「An InputDecorator...cannot have an unbounded width」
'Vertical viewport was given unbounded height'
#「Vertical viewport was given unbounded height」
#在 Flutter 应用中构建 UI 时可能遇到的另一种常见布局错误。
错误是什么样子的?
The message shown by the error looks like this:
The following assertion was thrown during performResize():
Vertical viewport was given unbounded height.
Viewports expand in the scrolling direction to fill their container.
In this case, a vertical viewport was given an unlimited amount of
vertical space in which to expand. This situation typically happens when a
scrollable widget is nested inside another scrollable widget.
(Additional lines of this message omitted)
错误显示的消息类似上文。
你可能会如何遇到此错误?
当 ListView(或其他可滚动 widget,如 GridView)放在 Column 内时,常会出现此错误。ListView
会占用所有可用的垂直空间,除非父 widget 约束它。然而 Column 默认不约束子节点高度。两种行为叠加会导致无法确定 ListView 的尺寸。
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
const Text('Header'),
ListView(
children: const <Widget>[
ListTile(leading: Icon(Icons.map), title: Text('Map')),
ListTile(leading: Icon(Icons.subway), title: Text('Subway')),
],
),
],
),
);
}
如何修复?
修复此错误需指定 ListView 的高度。若要与 Column 中剩余空间同高,用 Expanded widget 包裹(如下例)。否则用
SizedBox 指定绝对高度,或用 Flexible 指定相对高度。
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
const Text('Header'),
Expanded(
child: ListView(
children: const <Widget>[
ListTile(leading: Icon(Icons.map), title: Text('Map')),
ListTile(leading: Icon(Icons.subway), title: Text('Subway')),
],
),
),
],
),
);
}
更多信息:
以下链接提供更多关于此错误的信息。
'An InputDecorator...cannot have an unbounded width'
#「An InputDecorator...cannot have an unbounded width」
#错误消息表明这也与 box 约束有关;理解约束有助于避免许多最常见的 Flutter 框架错误。
错误是什么样子的?
The message shown by the error looks like this:
The following assertion was thrown during performLayout():
An InputDecorator, which is typically created by a TextField, cannot have an
unbounded width.
This happens when the parent widget does not provide a finite width constraint.
For example, if the InputDecorator is contained by a `Row`, then its width must
be constrained. An `Expanded` widget or a SizedBox can be used to constrain the
width of the InputDecorator or the TextField that contains it.
(Additional lines of this message omitted)
错误显示的消息类似上文。
你可能会如何遇到此错误?
例如,当 Row 包含 TextFormField 或 TextField 而后者没有宽度约束时,会出现此错误。
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Unbounded Width of the TextField')),
body: const Row(children: [TextField()]),
),
);
}
如何修复?
按错误消息建议,使用 Expanded 或 SizedBox widget 约束文本字段即可修复。以下示例使用 Expanded
widget:
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Unbounded Width of the TextField')),
body: Row(children: [Expanded(child: TextFormField())]),
),
);
}
'Incorrect use of ParentData widget'
#「Incorrect use of ParentData widget」
#此错误与缺少预期的父 widget 有关。
错误是什么样子的?
The message shown by the error looks like this:
The following assertion was thrown while looking for parent data:
Incorrect use of ParentDataWidget.
(Some lines of this message omitted)
Usually, this indicates that at least one of the offending ParentDataWidgets
listed above is not placed directly inside a compatible ancestor widget.
错误显示的消息类似上文。
你可能会如何遇到此错误?
Flutter 的 widget 在 UI 中的组合通常很灵活,但少数 widget 需要特定的父 widget。当 widget 树无法满足这一要求时,就可能遇到此错误。
以下是 Flutter 框架中需要特定父 widget 的 widget 的不完整列表。欢迎提交 PR(使用页面右上角的文档图标)扩充此列表。
| Widget | Expected parent widget(s) |
|---|---|
Flexible |
Row, Column, or Flex |
Expanded (a specialized Flexible) |
Row, Column, or Flex |
Positioned | Stack |
TableCell | Table |
如何修复?
一旦知道缺少哪个父 widget,修复方法通常就很明显。
'setState called during build'
#「setState called during build」
#Flutter 代码中的 build 方法不适合调用 setState,无论是直接还是间接调用。
错误是什么样子的?
When the error occurs, the following message is displayed in the console:
The following assertion was thrown building DialogPage(dirty, dependencies:
[_InheritedTheme, _LocalizationsScope-[GlobalKey#59a8e]],
state: _DialogPageState#f121e):
setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framework
is already in the process of building widgets.
(Additional lines of this message omitted)
发生错误时,控制台会显示上述消息。
你可能会如何遇到此错误?
In general, this error occurs when the setState
method is called within the build method.
一般而言,在 build 方法内调用 setState 会导致此错误。常见场景是在 build 方法中尝试触发 Dialog,往往是为了立即向用户展示信息,但绝不应在
build 方法中调用 setState。
以下代码片段常是此错误的元凶:
Widget build(BuildContext context) {
// Don't do this.
showDialog(
context: context,
builder: (context) {
return const AlertDialog(title: Text('Alert Dialog'));
},
);
return const Center(
child: Column(children: <Widget>[Text('Show Material Dialog')]),
);
}
此代码未显式调用 setState,但 showDialog 会调用它。build 不是调用 showDialog
的合适位置,因为框架可能每帧都调用 build,例如在动画期间。
如何修复?
避免此错误的一种方式是使用 Navigator API 将对话框作为路由触发。以下示例有两个页面;第二个页面在进入时显示对话框。用户在第一页点击按钮请求第二页时,Navigator
压入两条路由——一条对应第二页,另一条对应对话框。
class FirstScreen extends StatelessWidget {
const FirstScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('First Screen')),
body: Center(
child: ElevatedButton(
child: const Text('Launch screen'),
onPressed: () {
// Navigate to the second screen using a named route.
Navigator.pushNamed(context, '/second');
// Immediately show a dialog upon loading the second screen.
Navigator.push(
context,
PageRouteBuilder(
barrierDismissible: true,
opaque: false,
pageBuilder: (_, anim1, anim2) => const MyDialog(),
),
);
},
),
),
);
}
}
The ScrollController is attached to multiple scroll views
#
「The ScrollController is attached to multiple scroll views」
#当多个可滚动 widget(如 ListView)同时出现在屏幕上时,可能出现此错误。在 Web 或桌面应用中比移动应用更常见,因为移动应用很少遇到此场景。
更多信息及修复方法,请参阅以下关于 PrimaryScrollController
的视频:
参考资料
#要进一步了解如何调试错误,尤其是 Flutter 中的布局错误,请参阅以下资源:
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-04。查看文档源码 或者 为本页面内容提出建议。