You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/web/charts/flutter/lib/src/widget_layout_delegate.dart

220 lines
8.2 KiB

// Copyright 2018 the Charts project authors. Please see the AUTHORS file
// for details.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:flutter_web_ui/ui.dart' show Offset;
import 'package:flutter_web/material.dart';
import 'package:flutter_web/rendering.dart';
import 'package:flutter_web/widgets.dart';
import 'package:charts_common/common.dart' as common
show BehaviorPosition, InsideJustification, OutsideJustification;
import 'behaviors/chart_behavior.dart' show BuildableBehavior;
/// Layout delegate that layout chart widget with [BuildableBehavior] widgets.
class WidgetLayoutDelegate extends MultiChildLayoutDelegate {
/// ID of the common chart widget.
final String chartID;
/// Directionality of the widget.
final isRTL;
/// ID and [BuildableBehavior] of the widgets for calculating offset.
final Map<String, BuildableBehavior> idAndBehavior;
WidgetLayoutDelegate(this.chartID, this.idAndBehavior, this.isRTL);
@override
void performLayout(Size size) {
// TODO: Change this to a layout manager that supports more
// than one buildable behavior that changes chart size. Remove assert when
// this is possible.
assert(idAndBehavior.keys.isEmpty || idAndBehavior.keys.length == 1);
// Size available for the chart widget.
var availableWidth = size.width;
var availableHeight = size.height;
var chartOffset = Offset.zero;
// Measure the first buildable behavior.
final behaviorID =
idAndBehavior.keys.isNotEmpty ? idAndBehavior.keys.first : null;
var behaviorSize = Size.zero;
if (behaviorID != null) {
if (hasChild(behaviorID)) {
final leftPosition =
isRTL ? common.BehaviorPosition.end : common.BehaviorPosition.start;
final rightPosition =
isRTL ? common.BehaviorPosition.start : common.BehaviorPosition.end;
final behaviorPosition = idAndBehavior[behaviorID].position;
behaviorSize = layoutChild(behaviorID, new BoxConstraints.loose(size));
if (behaviorPosition == common.BehaviorPosition.top) {
chartOffset = new Offset(0.0, behaviorSize.height);
availableHeight -= behaviorSize.height;
} else if (behaviorPosition == common.BehaviorPosition.bottom) {
availableHeight -= behaviorSize.height;
} else if (behaviorPosition == leftPosition) {
chartOffset = new Offset(behaviorSize.width, 0.0);
availableWidth -= behaviorSize.width;
} else if (behaviorPosition == rightPosition) {
availableWidth -= behaviorSize.width;
}
}
}
// Layout chart.
final chartSize = new Size(availableWidth, availableHeight);
if (hasChild(chartID)) {
layoutChild(chartID, new BoxConstraints.tight(chartSize));
positionChild(chartID, chartOffset);
}
// Position buildable behavior.
if (behaviorID != null) {
// TODO: Unable to relayout with new smaller width.
// In the delegate, all children are required to have layout called
// exactly once.
final behaviorOffset = _getBehaviorOffset(idAndBehavior[behaviorID],
behaviorSize: behaviorSize, chartSize: chartSize, isRTL: isRTL);
positionChild(behaviorID, behaviorOffset);
}
}
@override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
// TODO: Deep equality check because the instance will not be
// the same on each build, even if the buildable behavior has not changed.
return idAndBehavior != (oldDelegate as WidgetLayoutDelegate).idAndBehavior;
}
// Calculate buildable behavior's offset.
Offset _getBehaviorOffset(BuildableBehavior behavior,
{Size behaviorSize, Size chartSize, bool isRTL}) {
Offset behaviorOffset;
final behaviorPosition = behavior.position;
final outsideJustification = behavior.outsideJustification;
final insideJustification = behavior.insideJustification;
if (behaviorPosition == common.BehaviorPosition.top ||
behaviorPosition == common.BehaviorPosition.bottom) {
final heightOffset = behaviorPosition == common.BehaviorPosition.bottom
? chartSize.height
: 0.0;
final horizontalJustification =
getOutsideJustification(outsideJustification, isRTL);
switch (horizontalJustification) {
case _HorizontalJustification.leftDrawArea:
behaviorOffset =
new Offset(behavior.drawAreaBounds.left.toDouble(), heightOffset);
break;
case _HorizontalJustification.left:
behaviorOffset = new Offset(0.0, heightOffset);
break;
case _HorizontalJustification.rightDrawArea:
behaviorOffset = new Offset(
behavior.drawAreaBounds.right - behaviorSize.width, heightOffset);
break;
case _HorizontalJustification.right:
behaviorOffset =
new Offset(chartSize.width - behaviorSize.width, heightOffset);
break;
}
} else if (behaviorPosition == common.BehaviorPosition.start ||
behaviorPosition == common.BehaviorPosition.end) {
final widthOffset =
(isRTL && behaviorPosition == common.BehaviorPosition.start) ||
(!isRTL && behaviorPosition == common.BehaviorPosition.end)
? chartSize.width
: 0.0;
switch (outsideJustification) {
case common.OutsideJustification.startDrawArea:
case common.OutsideJustification.middleDrawArea:
behaviorOffset =
new Offset(widthOffset, behavior.drawAreaBounds.top.toDouble());
break;
case common.OutsideJustification.start:
case common.OutsideJustification.middle:
behaviorOffset = new Offset(widthOffset, 0.0);
break;
case common.OutsideJustification.endDrawArea:
behaviorOffset = new Offset(widthOffset,
behavior.drawAreaBounds.bottom - behaviorSize.height);
break;
case common.OutsideJustification.end:
behaviorOffset =
new Offset(widthOffset, chartSize.height - behaviorSize.height);
break;
}
} else if (behaviorPosition == common.BehaviorPosition.inside) {
var rightOffset = new Offset(chartSize.width - behaviorSize.width, 0.0);
switch (insideJustification) {
case common.InsideJustification.topStart:
behaviorOffset = isRTL ? rightOffset : Offset.zero;
break;
case common.InsideJustification.topEnd:
behaviorOffset = isRTL ? Offset.zero : rightOffset;
break;
}
}
return behaviorOffset;
}
_HorizontalJustification getOutsideJustification(
common.OutsideJustification justification, bool isRTL) {
_HorizontalJustification mappedJustification;
switch (justification) {
case common.OutsideJustification.startDrawArea:
case common.OutsideJustification.middleDrawArea:
mappedJustification = isRTL
? _HorizontalJustification.rightDrawArea
: _HorizontalJustification.leftDrawArea;
break;
case common.OutsideJustification.start:
case common.OutsideJustification.middle:
mappedJustification = isRTL
? _HorizontalJustification.right
: _HorizontalJustification.left;
break;
case common.OutsideJustification.endDrawArea:
mappedJustification = isRTL
? _HorizontalJustification.leftDrawArea
: _HorizontalJustification.rightDrawArea;
break;
case common.OutsideJustification.end:
mappedJustification = isRTL
? _HorizontalJustification.left
: _HorizontalJustification.right;
break;
}
return mappedJustification;
}
}
enum _HorizontalJustification {
leftDrawArea,
left,
rightDrawArea,
right,
}