Flutter RenderFlex子级具有非零的flex,但是传入的高度限制是无界的

时间:2019-09-05 10:45:07

标签: flutter

当我将ListView包裹在FutureBuilder中以便拥有简单的Column时,我想在另一个小部件中包含Row。我收到此错误:

The following assertion was thrown during performLayout():
I/flutter (13816): RenderFlex children have non-zero flex but incoming height constraints are unbounded.
I/flutter (13816): When a column is in a parent that does not provide a finite height constraint, for example if it is
I/flutter (13816): in a vertical scrollable, it will try to shrink-wrap its children along the vertical axis. Setting a
I/flutter (13816): flex on a child (e.g. using Expanded) indicates that the child is to expand to fill the remaining
I/flutter (13816): space in the vertical direction.
I/flutter (13816): These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child
I/flutter (13816): cannot simultaneously expand to fit its parent.
I/flutter (13816): Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible
I/flutter (13816): children (using Flexible rather than Expanded). This will allow the flexible children to size
I/flutter (13816): themselves to less than the infinite remaining space they would otherwise be forced to take, and
I/flutter (13816): then will cause the RenderFlex to shrink-wrap the children rather than expanding to fit the maximum
I/flutter (13816): constraints provided by the parent.
I/flutter (13816): If this message did not help you determine the problem, consider using debugDumpRenderTree():

我的代码:

class ActivityShowTicketReplies extends StatefulWidget {
  final Map<String, dynamic> ticketData;

  ActivityShowTicketReplies({@required this.ticketData});

  @override
  State<StatefulWidget> createState() => ActivityShowTicketRepliesState();
}

class ActivityShowTicketRepliesState extends State<ActivityShowTicketReplies> {
  TicketsTableData get _ticket => TicketsTableData.fromJson(json.decode(widget.ticketData.values.toList()[0][0].toString()));

  @override
  Widget build(BuildContext context) {
    return ScopedModel(
      model: CounterModel(),
      child: Directionality(
        textDirection: TextDirection.rtl,
        child: Scaffold(
          body: Column(
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.symmetric(vertical: 20.0),
                    child: RaisedButton(
                      color: Colors.indigo,
                      onPressed: () => BlocProvider.of<AppPagesBloc>(context).dispatch(FragmentNavigateEvent(routeName: FRAGMENT_NEW_TICKET)),
                      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50.0)),
                      child: Container(
                        child: Text(
                          'new ticket',
                          style: AppTheme(context).caption().copyWith(color: Colors.white),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
              FutureBuilder(
                future: Provider.of<TicketRepliesTableDao>(context).find(ticketId: _ticket.id),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.done) {
                    if (snapshot.hasData) {
                      final List<TicketRepliesTableData> ticketReplies = snapshot.data;
                      if (ticketReplies.isNotEmpty) {
                        return Column(
                          children: <Widget>[
                            Card(
                              clipBehavior: Clip.antiAlias,
                              color: Colors.grey[50],
                              margin: EdgeInsets.all(10.0),
                              child: InkWell(
                                child: Stack(
                                  children: <Widget>[
                                    Column(
                                      children: <Widget>[
                                        Padding(
                                          padding: const EdgeInsets.symmetric(vertical: 12.0),
                                          child: ListTile(
                                            title: Padding(
                                              padding: const EdgeInsets.symmetric(vertical: 5.0),
                                              child: Text(
                                                'subject',
                                                style: AppTheme(context).caption().copyWith(fontFamily: 'ShabnamBold'),
                                              ),
                                            ),
                                            subtitle: Text(
                                              _ticket.subject,
                                              style: AppTheme(context).caption(),
                                            ),
                                          ),
                                        ),
                                        Container(
                                          height: 30.0,
                                          margin: EdgeInsets.zero,
                                          width: double.infinity,
                                          color: Colors.grey[200],
                                          child: Row(
                                            mainAxisAlignment: MainAxisAlignment.center,
                                            children: <Widget>[

                                            ],
                                          ),
                                        ),
                                      ],
                                    ),
                                    Align(
                                      alignment: Alignment.topLeft,
                                      child: Container(
                                          margin: EdgeInsets.only(top: 10.0),
                                          constraints: BoxConstraints(
                                            minWidth: 70.0,
                                          ),
                                          height: 20.0,
                                          width: 70.0,
                                          decoration: BoxDecoration(
                                              color: Colors.green[200],
                                              borderRadius: BorderRadius.only(
                                                topRight: Radius.circular(5.0),
                                                bottomRight: Radius.circular(5.0),
                                              )),
                                          child: Center(
                                            child: Text(
                                              'status',
                                              style: AppTheme(context).overLine().copyWith(fontFamily: 'ShabnamBold', color: Colors.black),
                                            ),
                                          )),
                                    ),
                                  ],
                                ),
                                onTap: () {},
                              ),
                            ),
                            Expanded(
                              child: ListView.builder(
                                itemBuilder: (context, index) {
                                  return Card(
                                    clipBehavior: Clip.antiAlias,
                                    child: Padding(
                                      padding: const EdgeInsets.all(20.0),
                                      child: Column(
                                        mainAxisAlignment: MainAxisAlignment.start,
                                        crossAxisAlignment: CrossAxisAlignment.start,
                                        children: <Widget>[
                                          Row(
                                            children: <Widget>[
                                              Text(
                                                ticketReplies[index].createdAt,
                                                style: AppTheme(context).caption().copyWith(fontFamily: 'ShabnamLight'),
                                              ),
                                            ],
                                          ),
                                          ListTile(
                                            title: Text(ticketReplies[index].reply),
                                          ),
                                        ],
                                      ),
                                    ),
                                  );
                                },
                                itemCount: ticketReplies.length,
                              ),
                            ),
                          ],
                        );
                      } else {
                        return Center(
                          child: Text(
                            'there isn't any reply message',
                            style: AppTheme(context).caption(),
                          ),
                        );
                      }
                    } else {
                      return _loader(context, 'no reply');
                    }
                  } else
                    return _loader(context, Strings.pleaseWait);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _loader(BuildContext context,String message) {
    return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            ColorLoader3(
              radius: 25.0,
              dotRadius: 5.0,
            ),
            Padding(
              padding: const EdgeInsets.all(3.0),
              child: Text(
                Strings.pleaseWait,
                style: AppTheme(context).caption().copyWith(color: Colors.red[900]),
              ),
            ),
          ],
        ));
  }
}

7 个答案:

答案 0 :(得分:25)

说明

用另一个 Column > Expanded 包装一个 Expanded 如何解决错误。

RenderFlex 无界错误

出现 "RenderFlex children have non-zero flex but incoming height constraints are unbounded" 错误的常见嵌套 Column-Expanded 层次结构:

Screen
  → Column
    → Column
      → Expanded → ERROR

发生了什么:

Screen
  ↓ constraint ↓ (Screen)
  Column
    Text_A (fixed height: no constraint needed) → OK
    Column 
      ↓ constraint ↓ (unbounded)
      Text_B (fixed height) → OK
      Expanded: calc. height = unbounded - Text_B → ERROR

在布局期间,Column 执行(按顺序):

  • 固定高度(即空/零弹性系数)小部件布局在无限空间,然后...
  • flex-factor 大小的小部件,从父级(即传入的约束)和固定大小的小部件计算剩余空间
上面的

Text_A 是固定高度的。它不使用传入约束进行布局。

Expanded 不过,是一个 flex-factor 小部件,在布局固定大小的项目后,在 剩余 空间上调整自身大小。这需要传入约束(父大小)进行减法:

  parent size (constraint) - fixed-items size = remaining space

第一个 Column 从设备屏幕获取约束(通过 Scaffold 等提供)。

但是,第二个 Column(嵌套的)位于无界空间中。

无法计算剩余空间:

  unbounded - Text_B = unbounded (error)

为什么嵌套的 Column 是无界的?

Screen
  → Column
    → Column
      → Expanded

复制自 Column SDK 源文档,第 1 步:

/// 1. Layout each child with a null or zero flex factor (e.g., those that are not
///    [Expanded]) with unbounded vertical constraints

第二个,嵌套的 Column

  • Column孩子(显然是第一个),并且...
  • 零或空弹性系数

第二点很容易被忽略/看不到,但 Column 不是一个弹性因子小部件。

正在检查 SDK 源...

Column 扩展了 Flex 类。

Flex 是否有 flex 字段/弹性系数。只有 Flexible 类及其子类是弹性因子小部件。

所以第二个 Column 被放置在无界约束中,因为它不是一个 flex-factor 小部件。这些无界约束用于布局 Text_B 小部件(固定大小或空弹性系数小部件),然后 Expanded 尝试计算剩余空间。尝试使用无界约束计算剩余空间时....

  unbounded - Text_B = unbounded (error)

? Expanded 爆炸?

Flexible

过度重复:Flexible 带有 flex 字段的弹性系数小部件/因素。

ExpandedSpacerFlexible 的唯一子类(我知道)。

所以 ExpandedSpacerFlexible 是唯一的 flex-factor 小部件和

Flexible != Flex


修复 - 添加边界

修复小部件树:

Screen
  → Column
    → Expanded ← new
      → Column
        → Expanded → OK

为什么会这样...

Screen
  ↓ constraint ↓
  Column
    Text_A ↑ fixed-size ↑
    ↓ constraint ↓ (calculated: Screen height - Text_A height)
    Expanded_A
      ↓ constraint ↓ (Screen - Text_A)
      Column
        Text_B ↑ fixed-size ↑
        Expanded_B: calc. height = (Screen - Text_A) - Text_B = remaining space → OK

在这种情况下... 固定项布置后(Text_A),计算剩余空间Expanded_A

parent size (screen) - fixed-items (Text_A) = remaining space

现在 Expanded_A 具有定义的空间量。

Expanded_A 提供它的大小,供子 Column 在布局 Expanded_B 后计算 Text_B 的剩余空间使用。

parent size - fixed-items = remaining space
                  ↓
(Screen - Text_A) - (Text_B) = remaining space for Expanded_B

现在所有 flex-factor 相对大小的小部件(即 Expanded_AExpanded_B)都有用于计算其布局大小的有界约束。

请注意,您可以在此处将 FlexibleExpanded 互换使用。它以与 Expanded 相同的方式计算剩余空间。它只是不会强迫孩子适应它的大小,所以如果他们愿意,他们可以更小。

复制/粘贴代码操作:

不行

/// Unbounded constraint: NOT OK
class RenderFlexUnboundedPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Column(
            children: [
              Text('Column > Column > Text_A'),
              Expanded(
                  child: Text('Column > Column > Expanded_A')) // explosions
            ],
          )
        ],
      ),
    );
  }
}

好的

/// Constraints provided: OK
class RenderFlexPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Text('Column > Text_A'),
          Expanded( // Expanded_A
            child: Column(
              children: [
                Text('Column > Expanded_A > Column > Text_B'),
                Expanded( // Expanded_B
                    child: Text('Column > Expanded_A > Column > Expanded_B'))
              ],
            ),
          )
        ],
      ),
    );
  }
}

答案 1 :(得分:2)

在此代码中。

    const request = require('request')
const fs = require('fs')
const chalk = require('chalk')


var windowSlider = 200

var totlExtractedRecords = 0;
fs.writeFileSync('output.txt', '')

const option = {
    url: 'https://jira.yourdomain.com/rest/api/2/search',
    json: true,
    qs: {
        jql: "project in (xyz)",
        maxResults: 200,
        startAt: 0,
    }
}

const callback = (error, response) => {
    const body = response.body

    console.log(response.body)

    const dataArray = body.issues
    const total = body.total


    totlExtractedRecords = dataArray.length + totlExtractedRecords

    if (totlExtractedRecords > 0) {
        option.qs.startAt = windowSlider + option.qs.startAt
    }

    dataArray.forEach(element => {
        fs.appendFileSync('output.txt', element.key + '\n')
    })

    console.log(chalk.red.inverse('Total extracted data : ' + totlExtractedRecords))
    console.log(chalk.red.inverse('Total data: ' + total))

    if (totlExtractedRecords < total) {
        console.log('Re - Running with start as ' + option.qs.startAt)
        console.log('Re - Running with maxResult as ' + option.qs.maxResults)
        request(option, callback).auth('api-reader', 'APITOKEN', true)
    }
}


request(option, callback).auth('api-reader', 'APITOKEN', true)

您需要使用if (ticketReplies.isNotEmpty) { return Column(...); } SizedBox,例如:

Expanded

答案 2 :(得分:2)

尝试将ColumnExpandedFlexible中的其他任何小工具包装起来,即可完成。

别忘了给Scrolling widgets

增高一点

此问题的解决方法是将Column小部件包装在Expanded小部件内。下面的示例。

 Expanded(
   child: Column(
     children: <Widget>[
        Container(
           height: 400,
           width: 400,
           child: ListView.builder(
              itemCount: 2,
              itemBuilder: (context, position) {
                return Card(
                   child: Padding(
                     padding: const EdgeInsets.all(16.0),
                     child: Text("e.g., those that are not Expanded"),
                     ),
                  );
                },
             ),
          ),
       ],
    ),
 )

要了解更多信息,请参见:“RenderFlex children have non-zero flex…”

答案 3 :(得分:2)

我想提供另一个解决方案,因为我必须自己解决这个问题。对于上下文,这是引发此错误的小部件的过度简化结构:

Container
  child: Column
         children: Column, Spacer, Row

就我而言,我希望 Container 扩展其内部的内容。最初,我为这个容器定义了高度和宽度,但因为我希望它动态扩展,所以我删除了这些约束。然后引发了错误:

<块引用>

“RenderFlex 子级具有非零 flex 但传入的高度约束是无界的”

我的问题是行之间的 Spacer。第一个 Column 的孩子试图无限扩展(因此是“非零弹性”),因为 Spacer 将每个孩子推到其父母的垂直边界。这通常不是问题,但是对于间隔器将子级推向父级没有限制。这是因为它们的父级(第一个 Column也在尝试扩展,因为 Container 没有被赋予高度或宽度属性。

我的解决方法是简单地将 Spacer 替换为具有有限高度的 SizedBox

Container
  child: Column
         children: Column, SizedBox(height: 10), Row

答案 4 :(得分:0)

这种方法永远不会让我失望:

Card(
    child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
                Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus finibus luctus tortor id pulvinar. Donec sollicitudin, dui vitae aliquam tristique, eros risus commodo enim, eget lobortis arcu velit ac arcu. "),
                Text("Nunc tristique, ex ut volutpat feugiat, nulla nibh iaculis lectus, laoreet auctor justo tellus quis mi. Praesent ut interdum sem. Donec eget finibus augue, et vehicula elit. Praesent eu euismod arcu, eu maximus tellus. Ut diam est, sodales nec enim a, pharetra lobortis erat."),
            ]
     )
)

答案 5 :(得分:0)

如果您想解决 Render Flex 问题,请尝试:

Scaffold(
  body: SingleChildScrollView(
    child: SizedBox(
      height: MediaQuery.of(context).size.height,
      child: Column(
        children: [

答案 6 :(得分:0)

就我而言,这是因为我没有使用 Expanded 小部件。然而,这也无助于实现结果。相反,我将 SizedBoxheight 属性一起使用,如下所示:

        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: SizedBox(
              height: MediaQuery.of(context).size.height,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[

这很容易解决了我的问题。