layout method

  1. @override
void layout(
  1. LayoutExpansion parentLayoutExpansion
)

Implements Container.layout for the chart as a whole.

Uses this container's chartArea as available size

Note: The chartArea was set in the ChartPainter.paint(Canvas, Size) just before calling this method:

  void paint(ui.Canvas canvas, ui.Size size) {
    ...
    container.chartArea = size;
    container.layout();
    ...

Layout proceeds scaling the Y values to fit the available size, then lays out the legend, Y axis and labels, X axis and labels, and the data area, giving each the size it needs.

The actual layout algorithm should be made pluggable.

Implementation

@override
void layout(LayoutExpansion parentLayoutExpansion) {
  // ### 1. Prepare early, from dataRows, the stackable points managed
  //        in [pointsColumns], as [YContainer] needs to scale y values and
  //        create labels from the stacked points (if chart is stacked).
  setupPointsColumns();

  // ### 2. Layout the legends on top
  var legendLayoutExpansion = LayoutExpansion(
    width: chartArea.width,
    height: chartArea.height,
  );
  legendContainer = LegendContainer(
    chartTopContainer: this,
  );

  legendContainer.layout(legendLayoutExpansion);
  ui.Size legendContainerSize = legendContainer.layoutSize;
  ui.Offset legendContainerOffset = ui.Offset.zero;
  legendContainer.applyParentOffset(legendContainerOffset);

  // ### 3. Ask [YContainer] to provide Y label container width.
  //        This provides the remaining width left for the [XContainer]
  //        (grid and X axis) to use. The yLabelsMaxHeightFromFirstLayout
  //        is not relevant in this first call.
  double yContainerHeight = chartArea.height - legendContainerSize.height;

  var yContainerLayoutExpansion = LayoutExpansion(
    width: chartArea.width,
    height: yContainerHeight,
  );
  var yContainerFirst = YContainer(
    chartTopContainer: this,
    yLabelsMaxHeightFromFirstLayout: 0.0,
  );

  yContainerFirst.layout(yContainerLayoutExpansion);
  double yLabelsMaxHeightFromFirstLayout = yContainerFirst.yLabelsMaxHeight;
  yContainer = yContainerFirst;
  ui.Size yContainerSize = yContainer.layoutSize;

  // ### 4. Knowing the width required by Y axis, layout X
  //        (from first [YContainer.layout] call).

  var xContainerLayoutExpansion = LayoutExpansion(
    width: chartArea.width - yContainerSize.width,
    height: chartArea.height - legendContainerSize.height,
  );
  xContainer = XContainer(
    chartTopContainer: this,
    xContainerLabelLayoutStrategy: _cachedXContainerLabelLayoutStrategy,
  );

  xContainer.layout(xContainerLayoutExpansion);

  ui.Size xContainerSize = xContainer.layoutSize;
  ui.Offset xContainerOffset = ui.Offset(yContainerSize.width, chartArea.height - xContainerSize.height);
  xContainer.applyParentOffset(xContainerOffset);

  // ### 5. Second call to YContainer is needed, as available height for Y
  //        is only known after XContainer provided required height of xUserLabels
  //        on the bottom .
  //        The [yLabelsMaxHeightFromFirstLayout] are used to scale
  //        data values to the y axis, and put labels on ticks.

  // On the second layout, make sure YContainer expand down only to
  //   the top of the XContainer area.
  yContainerLayoutExpansion = LayoutExpansion(
    width: chartArea.width,
    height: yContainerHeight - xContainerSize.height,
  );
  yContainer = YContainer(
    chartTopContainer: this,
    yLabelsMaxHeightFromFirstLayout: yLabelsMaxHeightFromFirstLayout,
  );

  yContainer.layout(yContainerLayoutExpansion);

  yContainerSize = yContainer.layoutSize;
  ui.Offset yContainerOffset = ui.Offset(0.0, legendContainerSize.height);
  yContainer.applyParentOffset(yContainerOffset);

  ui.Offset dataContainerOffset = ui.Offset(yContainerSize.width, legendContainerSize.height);

  // ### 6. Layout the data area, which included the grid
  // by calculating the X and Y positions of grid.
  // This must be done after X and Y are layed out - see xTickXs, yTickYs.
  var dataContainerLayoutExpansion = LayoutExpansion(
    width: chartArea.width - yContainerSize.width,
    height: chartArea.height - (legendContainerSize.height + xContainerSize.height),
  );
  dataContainer = createDataContainer(
    chartTopContainer: this,
  );

  // todo-01-morph-layout : this is where most non-Container elements are layed out.
  //                problem is, part of the layout happens in applyParentOffset!
  dataContainer.layout(dataContainerLayoutExpansion);
  dataContainer.applyParentOffset(dataContainerOffset);
}