简体   繁体   中英

Flutter expanded chart in nested row / column

I'm trying to get a chart from the charts_flutter package to expand to fill available space, when it is inside a Row which is inside a Column. I've spent a couple of hours trying to do this but I must be missing something! This is visually what I'm trying to achieve:

在此处输入图片说明

Here's a minimal working example. I must have tried a hundred combinations of Expanded, Flexible, main axis size, cross axis alignment etc. but I just can't seem to find the correct one. Currently the bottom overflows by infinity pixels, with object was given an infinite size during layout . There is this open issue , I don't know if it's related, and there is no answer. I just need help with the bit of code inside runApp :

import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';

void main() => runApp(

  // HOW DO I LAY THIS OUT?
  Column(
    children: <Widget>[
      Container(height: 200, color: Colors.orange),
      Row(
        textDirection: TextDirection.ltr,
        children: <Widget>[
          Container(width: 200,color: Colors.orange),
          Expanded(child: SimpleTimeSeriesChart.withSampleData())
        ],
      )
    ],
  )
);

class SimpleTimeSeriesChart extends StatelessWidget {
  final List<charts.Series> seriesList;
  final bool animate;

  SimpleTimeSeriesChart(this.seriesList, {this.animate});

  factory SimpleTimeSeriesChart.withSampleData() {
    return new SimpleTimeSeriesChart(
      _createSampleData(),
      animate: false,
    );
  }

  @override
  Widget build(BuildContext context) {
    return new charts.TimeSeriesChart(
      seriesList,
      animate: animate,
      dateTimeFactory: const charts.LocalDateTimeFactory(),
    );
  }

  /// Create one series with sample hard coded data.
  static List<charts.Series<TimeSeriesSales, DateTime>> _createSampleData() {
    final data = [
      new TimeSeriesSales(new DateTime(2017, 9, 19), 5),
      new TimeSeriesSales(new DateTime(2017, 9, 26), 25),
      new TimeSeriesSales(new DateTime(2017, 10, 3), 100),
      new TimeSeriesSales(new DateTime(2017, 10, 10), 75),
    ];

    return [
      new charts.Series<TimeSeriesSales, DateTime>(
        id: 'Sales',
        colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
        domainFn: (TimeSeriesSales sales, _) => sales.time,
        measureFn: (TimeSeriesSales sales, _) => sales.sales,
        data: data,
      )
    ];
  }
}

class TimeSeriesSales {
  final DateTime time;
  final int sales;

  TimeSeriesSales(this.time, this.sales);
}

**see why at Step 8 - scroll down👇 **

Problem

I'm trying to get a chart from the charts_flutter package to expand to fill available space

Error Evidence

object was given an infinite size during layout

Solution

Wrap the Row with Expanded

Column(children: <Widget>[Expanded(child: Row())])

Screenshot

在此处输入图片说明

Note:

  • 😎 , thanks for provide minimal code

Full Working Code

import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';

void main() => runApp(

        // HOW DO I LAY THIS OUT?
        Column(
      children: <Widget>[
        Container(height: 200, color: Colors.orange),
        Expanded(
          child: Row(
            textDirection: TextDirection.ltr,
            children: <Widget>[
              Container(width: 200, color: Colors.green),
              Expanded(child: SimpleTimeSeriesChart.withSampleData()),
            ],
          ),
        )
      ],
    ));

class SimpleTimeSeriesChart extends StatelessWidget {
  final List<charts.Series> seriesList;
  final bool animate;

  SimpleTimeSeriesChart(this.seriesList, {this.animate});

  factory SimpleTimeSeriesChart.withSampleData() {
    return new SimpleTimeSeriesChart(
      _createSampleData(),
      animate: false,
    );
  }

  @override
  Widget build(BuildContext context) {
    return new charts.TimeSeriesChart(
      seriesList,
      animate: animate,
      dateTimeFactory: const charts.LocalDateTimeFactory(),
    );
  }

  /// Create one series with sample hard coded data.
  static List<charts.Series<TimeSeriesSales, DateTime>> _createSampleData() {
    final data = [
      new TimeSeriesSales(new DateTime(2017, 9, 19), 5),
      new TimeSeriesSales(new DateTime(2017, 9, 26), 25),
      new TimeSeriesSales(new DateTime(2017, 10, 3), 100),
      new TimeSeriesSales(new DateTime(2017, 10, 10), 75),
    ];

    return [
      new charts.Series<TimeSeriesSales, DateTime>(
        id: 'Sales',
        colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
        domainFn: (TimeSeriesSales sales, _) => sales.time,
        measureFn: (TimeSeriesSales sales, _) => sales.sales,
        data: data,
      )
    ];
  }
}

class TimeSeriesSales {
  final DateTime time;
  final int sales;

  TimeSeriesSales(this.time, this.sales);
}

Extra : how to read the error (Full Error Dump Attached in the end)

Step 1 : Locate the start

I/flutter ( 4228): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════

Step 2: Locate the end

I/flutter ( 4228): ════════════════════════════════════════════════════════════════════════════════════════════════════

Step 3: read the error

scan the error with your eyes you will see some phrases like


I/flutter ( 4228): constraints: BoxConstraints(w=320.0, h=480.0)

 I/flutter ( 4228): The following assertion was thrown during performLayout():

I/flutter ( 4228): Summary 1: RenderCustomMultiChildLayoutBox object was given an infinite size during layout.

Step 4 :decide what is the type of the

this is a box constraints error

Step 5 :how to know what widget is throwing the error

go near the end it will give you the source:

I/flutter ( 4228):   creator: CustomMultiChildLayout ← TimeSeriesChart ← SimpleTimeSeriesChart ← Expanded ← Row ← Column
I/flutter ( 4228):     ← [root]
  • so it is inside the TimeSeriesChart

Step 6 : what is missing

go near the end it will give you the missing constraint

I/flutter ( 4228):   constraints: BoxConstraints(w=120.0, 0.0<=h<=Infinity)
  • notice : 0.0<=h<=Infinity

  • the height can't be Infinity

Step 7 :

  • warp the source with a SizedBox to make sure it' what throwing the error , to see if it solve it

  • SizedBox

    A box with a specified size.

    If given a child, this widget forces its child to have a specific width and/or height (assuming values are permitted by this widget's parent). If either the width or height is null, this widget will size itself to match the child's size in that dimension.

Step 8 :

  1. the TimeSeriesChart(child) asked Raw(parent) for height

  2. the Raw(child) asked Column(parent) for height

  3. the Column doesn't know

  4. by why the Column doesn't know, because by default behavior is that it will leave it for the children to decide thir own height

  5. but we can tell the Column that the children doesn't know so expand them to the available space

Error Dump

I/flutter ( 4228): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 4228): The following assertion was thrown during performLayout():
I/flutter ( 4228): FlutterError contained multiple error summaries.
I/flutter ( 4228): All FlutterError objects should have only a single short (one line) summary description of the
I/flutter ( 4228): problem that was detected.
I/flutter ( 4228): Malformed FlutterError:
I/flutter ( 4228):   RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
I/flutter ( 4228):   This probably means that it is a render object that tries to be as big as possible, but it was put
I/flutter ( 4228):   inside another render object that allows its children to pick their own size.
I/flutter ( 4228):   RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
I/flutter ( 4228):   This probably means that it is a render object that tries to be as big as possible, but it was put
I/flutter ( 4228):   inside another render object that allows its children to pick their own size.
I/flutter ( 4228):   The nearest ancestor providing an unbounded height constraint is: RenderFlex#d29e7 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE:
I/flutter ( 4228):     creator: Column ← [root]
I/flutter ( 4228):     parentData: <none>
I/flutter ( 4228):     constraints: BoxConstraints(w=320.0, h=480.0)
I/flutter ( 4228):     size: MISSING
I/flutter ( 4228):     direction: vertical
I/flutter ( 4228):     mainAxisAlignment: start
I/flutter ( 4228):     mainAxisSize: max
I/flutter ( 4228):     crossAxisAlignment: center
I/flutter ( 4228):     verticalDirection: down
I/flutter ( 4228):   The constraints that applied to the RenderCustomMultiChildLayoutBox were:
I/flutter ( 4228):     BoxConstraints(w=120.0, 0.0<=h<=Infinity)
I/flutter ( 4228):   The exact size it was given was:
I/flutter ( 4228):     Size(120.0, Infinity)
I/flutter ( 4228):   See https://flutter.dev/docs/development/ui/layout/box-constraints for more information.
I/flutter ( 4228):
I/flutter ( 4228): The malformed error has 2 summaries.
I/flutter ( 4228): Summary 1: RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
I/flutter ( 4228): Summary 2: RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
I/flutter ( 4228):
I/flutter ( 4228): This error should still help you solve your problem, however please also report this malformed error
I/flutter ( 4228): in the framework by filing a bug on GitHub:
I/flutter ( 4228):   https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 4228):
I/flutter ( 4228): When the exception was thrown, this was the stack:
I/flutter ( 4228): #0      new FlutterError.fromParts.<anonymous closure> (package:flutter/src/foundation/assertions.dart:540:9)
I/flutter ( 4228): #1      new FlutterError.fromParts (package:flutter/src/foundation/assertions.dart:543:6)
I/flutter ( 4228): #2      RenderBox.debugAssertDoesMeetConstraints.<anonymous closure> (package:flutter/src/rendering/box.dart:1966:28)
I/flutter ( 4228): #3      RenderBox.debugAssertDoesMeetConstraints (package:flutter/src/rendering/box.dart:2029:6)
I/flutter ( 4228): #4      RenderBox.size=.<anonymous closure> (package:flutter/src/rendering/box.dart:1740:7)
I/flutter ( 4228): #5      RenderBox.size= (package:flutter/src/rendering/box.dart:1742:6)
I/flutter ( 4228): #6      RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:355:5)
I/flutter ( 4228): #7      RenderObject.layout (package:flutter/src/rendering/object.dart:1619:7)
I/flutter ( 4228): #8      RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:805:17)
I/flutter ( 4228): #9      RenderObject.layout (package:flutter/src/rendering/object.dart:1619:7)
I/flutter ( 4228): #10     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:743:15)
I/flutter ( 4228): #11     RenderObject.layout (package:flutter/src/rendering/object.dart:1619:7)
I/flutter ( 4228): #12     RenderView.performLayout (package:flutter/src/rendering/view.dart:151:13)
I/flutter ( 4228): #13     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1496:7)
I/flutter ( 4228): #14     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:765:18)
I/flutter ( 4228): #15     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:346:19)
I/flutter ( 4228): #16     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:701:13)
I/flutter ( 4228): #17     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:285:5)I/flutter ( 4228): #18     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1016:15)
I/flutter ( 4228): #19     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:958:9)
I/flutter ( 4228): #20     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:784:7)
I/flutter ( 4228): #29     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:382:19)
I/flutter ( 4228): #30     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:416:5)
I/flutter ( 4228): #31     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)
I/flutter ( 4228): (elided 8 frames from package dart:async and package dart:async-patch)
I/flutter ( 4228):
I/flutter ( 4228): The following RenderObject was being processed when the exception was fired: RenderCustomMultiChildLayoutBox#e4ae0 relayoutBoundary=up2 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE:
I/flutter ( 4228):   creator: CustomMultiChildLayout ← TimeSeriesChart ← SimpleTimeSeriesChart ← Expanded ← Row ← Column
I/flutter ( 4228):     ← [root]
I/flutter ( 4228):   parentData: offset=Offset(0.0, 0.0); flex=1; fit=FlexFit.tight (can use size)
I/flutter ( 4228):   constraints: BoxConstraints(w=120.0, 0.0<=h<=Infinity)
I/flutter ( 4228):   size: Size(120.0, Infinity)
I/flutter ( 4228): This RenderObject had the following descendants (showing up to depth 5):
I/flutter ( 4228):     child 1: RenderSemanticsGestureHandler#2034c NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
I/flutter ( 4228):       child: RenderPointerListener#aef29 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
I/flutter ( 4228):         child: ChartContainerRenderObject<DateTime>#b28fc NEEDS-LAYOUT NEEDS-PAINT
I/flutter ( 4228): ════════════════════════════════════════════════════════════════════════════════════════════════════

more on step 8

  • now we know that the error is from the TimeSeriesChart widget because it needs height
  • but if it does not have a height it will ask it's parent, the row

  • but the row dosnt have a fixed height it will ask the children

  • but the children don't have a fixed height it will ask the row parent

  • but the parent doesn't have a fixed height for it children (the row)

Just wrap your Row in an Expanded widget and that's it.

 Column(
    children: <Widget>[
      Container(height: 200, color: Colors.orange),
      Expanded(
        child: Row(
        textDirection: TextDirection.ltr,
        children: <Widget>[
          Container(width: 200,color: Colors.orange),
          Expanded(child: SimpleTimeSeriesChart.withSampleData())
        ],
      )
     )
    ],
  )

Explanation is that the Column when created will provide infinity constraints to its children and when you wrap any child in a Flexible widget, it will act accordingly to the available space (and some of the properties, if any).

The Row acts exactly the same way, but horizontally. Here, you are properly adding the Expanded to the Row child, but then, it tries to find how much cant it expand to size itself, and the Row doesn't know because it still has an unbounded height from the parent ( Column ), that's why you should use the Expanded or even a Flexible if you don't need to fit the available space.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM