Unverified Commit 9f1e76e9 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Clean up baseline logic in material library. (#17922)

parent 587a1b77
......@@ -1855,7 +1855,7 @@ class _RenderChip extends RenderBox {
@override
double computeDistanceToActualBaseline(TextBaseline baseline) {
// The baseline of this widget is the baseline of the label.
return label.computeDistanceToActualBaseline(baseline);
return label.getDistanceToActualBaseline(baseline);
}
Size _layoutLabel(double iconSizes, Size size) {
......
......@@ -537,10 +537,15 @@ class _RenderDecorationLayout {
// The workhorse: layout and paint a _Decorator widget's _Decoration.
class _RenderDecoration extends RenderBox {
_RenderDecoration({
_Decoration decoration,
TextDirection textDirection,
}) : _decoration = decoration,
_textDirection = textDirection;
@required _Decoration decoration,
@required TextDirection textDirection,
@required TextBaseline textBaseline,
}) : assert(decoration != null),
assert(textDirection != null),
assert(textBaseline != null),
_decoration = decoration,
_textDirection = textDirection,
_textBaseline = textBaseline;
final Map<_DecorationSlot, RenderBox> slotToChild = <_DecorationSlot, RenderBox>{};
final Map<RenderBox, _DecorationSlot> childToSlot = <RenderBox, _DecorationSlot>{};
......@@ -654,6 +659,7 @@ class _RenderDecoration extends RenderBox {
_Decoration get decoration => _decoration;
_Decoration _decoration;
set decoration(_Decoration value) {
assert(value != null);
if (_decoration == value)
return;
_decoration = value;
......@@ -663,12 +669,23 @@ class _RenderDecoration extends RenderBox {
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
assert(value != null);
if (_textDirection == value)
return;
_textDirection = value;
markNeedsLayout();
}
TextBaseline get textBaseline => _textBaseline;
TextBaseline _textBaseline;
set textBaseline(TextBaseline value) {
assert(value != null);
if (_textBaseline == value)
return;
_textBaseline = value;
markNeedsLayout();
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
......@@ -748,7 +765,7 @@ class _RenderDecoration extends RenderBox {
if (box == null)
return;
box.layout(boxConstraints, parentUsesSize: true);
final double baseline = box.getDistanceToBaseline(TextBaseline.alphabetic);
final double baseline = box.getDistanceToBaseline(textBaseline);
assert(baseline != null && baseline >= 0.0);
boxToBaseline[box] = baseline;
aboveBaseline = math.max(baseline, aboveBaseline);
......@@ -913,7 +930,15 @@ class _RenderDecoration extends RenderBox {
width: overallWidth - _boxSize(icon).width,
);
container.layout(containerConstraints, parentUsesSize: true);
final double x = textDirection == TextDirection.rtl ? 0.0 : _boxSize(icon).width;
double x;
switch (textDirection) {
case TextDirection.rtl:
x = 0.0;
break;
case TextDirection.ltr:
x = _boxSize(icon).width;
break;
}
_boxParentData(container).offset = new Offset(x, 0.0);
}
......@@ -938,7 +963,15 @@ class _RenderDecoration extends RenderBox {
: layout.outlineBaseline;
if (icon != null) {
final double x = textDirection == TextDirection.rtl ? overallWidth - icon.size.width : 0.0;
double x;
switch (textDirection) {
case TextDirection.rtl:
x = overallWidth - icon.size.width;
break;
case TextDirection.ltr:
x = 0.0;
break;
}
centerLayout(icon, x);
}
......@@ -1004,9 +1037,14 @@ class _RenderDecoration extends RenderBox {
}
if (label != null) {
decoration.borderGap.start = textDirection == TextDirection.rtl
? _boxParentData(label).offset.dx + label.size.width
: _boxParentData(label).offset.dx;
switch (textDirection) {
case TextDirection.rtl:
decoration.borderGap.start = _boxParentData(label).offset.dx + label.size.width;
break;
case TextDirection.ltr:
decoration.borderGap.start = _boxParentData(label).offset.dx;
break;
}
decoration.borderGap.extent = label.size.width * 0.75;
} else {
decoration.borderGap.start = null;
......@@ -1039,9 +1077,15 @@ class _RenderDecoration extends RenderBox {
final bool isOutlineBorder = decoration.border != null && decoration.border.isOutline;
final double floatingY = isOutlineBorder ? -labelHeight * 0.25 : contentPadding.top;
final double scale = lerpDouble(1.0, 0.75, t);
final double dx = textDirection == TextDirection.rtl
? labelOffset.dx + label.size.width * (1.0 - scale) // origin is on the right
: labelOffset.dx; // origin on the left
double dx;
switch (textDirection) {
case TextDirection.rtl:
dx = labelOffset.dx + label.size.width * (1.0 - scale); // origin is on the right
break;
case TextDirection.ltr:
dx = labelOffset.dx; // origin on the left
break;
}
final double dy = lerpDouble(0.0, floatingY - labelOffset.dy, t);
_labelTransform = new Matrix4.identity()
..translate(dx, labelOffset.dy + dy)
......@@ -1237,10 +1281,17 @@ class _RenderDecorationElement extends RenderObjectElement {
class _Decorator extends RenderObjectWidget {
const _Decorator({
Key key,
this.decoration,
}) : super(key: key);
@required this.decoration,
@required this.textDirection,
@required this.textBaseline,
}) : assert(decoration != null),
assert(textDirection != null),
assert(textBaseline != null),
super(key: key);
final _Decoration decoration;
final TextDirection textDirection;
final TextBaseline textBaseline;
@override
_RenderDecorationElement createElement() => new _RenderDecorationElement(this);
......@@ -1249,7 +1300,8 @@ class _Decorator extends RenderObjectWidget {
_RenderDecoration createRenderObject(BuildContext context) {
return new _RenderDecoration(
decoration: decoration,
textDirection: Directionality.of(context),
textDirection: textDirection,
textBaseline: textBaseline,
);
}
......@@ -1257,7 +1309,8 @@ class _Decorator extends RenderObjectWidget {
void updateRenderObject(BuildContext context, _RenderDecoration renderObject) {
renderObject
..decoration = decoration
..textDirection = Directionality.of(context);
..textDirection = textDirection
..textBaseline = textBaseline;
}
}
......@@ -1310,6 +1363,9 @@ class InputDecorator extends StatefulWidget {
///
/// If null, `baseStyle` defaults to the `subhead` style from the
/// current [Theme], see [ThemeData.textTheme].
///
/// The [TextStyle.textBaseline] of the [baseStyle] is used to determine
/// the baseline used for text alignment.
final TextStyle baseStyle;
/// How the text in the decoration should be aligned horizontally.
......@@ -1539,6 +1595,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
final TextStyle inlineStyle = _getInlineStyle(themeData);
final TextBaseline textBaseline = inlineStyle.textBaseline;
final TextStyle hintStyle = inlineStyle.merge(decoration.hintStyle);
final Widget hint = decoration.hintText == null ? null : new AnimatedOpacity(
......@@ -1713,6 +1770,8 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
counter: counter,
container: container,
),
textDirection: textDirection,
textBaseline: textBaseline,
);
}
}
......
......@@ -420,16 +420,19 @@ class ListTile extends StatelessWidget {
);
}
final TextStyle titleStyle = _titleTextStyle(theme, tileTheme);
final Widget titleText = new AnimatedDefaultTextStyle(
style: _titleTextStyle(theme, tileTheme),
style: titleStyle,
duration: kThemeChangeDuration,
child: title ?? const SizedBox()
);
Widget subtitleText;
TextStyle subtitleStyle;
if (subtitle != null) {
subtitleStyle = _subtitleTextStyle(theme, tileTheme);
subtitleText = new AnimatedDefaultTextStyle(
style: _subtitleTextStyle(theme, tileTheme),
style: subtitleStyle,
duration: kThemeChangeDuration,
child: subtitle,
);
......@@ -466,6 +469,9 @@ class ListTile extends StatelessWidget {
trailing: trailingIcon,
isDense: _isDenseLayout(tileTheme),
isThreeLine: isThreeLine,
textDirection: textDirection,
titleBaselineType: titleStyle.textBaseline,
subtitleBaselineType: subtitleStyle?.textBaseline,
),
),
),
......@@ -473,6 +479,14 @@ class ListTile extends StatelessWidget {
}
}
// Identifies the children of a _ListTileElement.
enum _ListTileSlot {
leading,
title,
subtitle,
trailing,
}
class _ListTile extends RenderObjectWidget {
const _ListTile({
Key key,
......@@ -480,9 +494,16 @@ class _ListTile extends RenderObjectWidget {
this.title,
this.subtitle,
this.trailing,
this.isThreeLine,
this.isDense,
}) : super(key: key);
@required this.isThreeLine,
@required this.isDense,
@required this.textDirection,
@required this.titleBaselineType,
this.subtitleBaselineType,
}) : assert(isThreeLine != null),
assert(isDense != null),
assert(textDirection != null),
assert(titleBaselineType != null),
super(key: key);
final Widget leading;
final Widget title;
......@@ -490,16 +511,21 @@ class _ListTile extends RenderObjectWidget {
final Widget trailing;
final bool isThreeLine;
final bool isDense;
final TextDirection textDirection;
final TextBaseline titleBaselineType;
final TextBaseline subtitleBaselineType;
@override
_RenderListTileElement createElement() => new _RenderListTileElement(this);
_ListTileElement createElement() => new _ListTileElement(this);
@override
_RenderListTile createRenderObject(BuildContext context) {
return new _RenderListTile(
isThreeLine: isThreeLine,
isDense: isDense,
textDirection: Directionality.of(context),
textDirection: textDirection,
titleBaselineType: titleBaselineType,
subtitleBaselineType: subtitleBaselineType,
);
}
......@@ -508,26 +534,141 @@ class _ListTile extends RenderObjectWidget {
renderObject
..isThreeLine = isThreeLine
..isDense = isDense
..textDirection = Directionality.of(context);
..textDirection = textDirection
..titleBaselineType = titleBaselineType
..subtitleBaselineType = subtitleBaselineType;
}
}
// Identifies the children of a _ListTileElement.
enum _ListTileSlot {
leading,
title,
subtitle,
trailing,
class _ListTileElement extends RenderObjectElement {
_ListTileElement(_ListTile widget) : super(widget);
final Map<_ListTileSlot, Element> slotToChild = <_ListTileSlot, Element>{};
final Map<Element, _ListTileSlot> childToSlot = <Element, _ListTileSlot>{};
@override
_ListTile get widget => super.widget;
@override
_RenderListTile get renderObject => super.renderObject;
@override
void visitChildren(ElementVisitor visitor) {
slotToChild.values.forEach(visitor);
}
@override
void forgetChild(Element child) {
assert(slotToChild.values.contains(child));
assert(childToSlot.keys.contains(child));
final _ListTileSlot slot = childToSlot[child];
childToSlot.remove(child);
slotToChild.remove(slot);
}
void _mountChild(Widget widget, _ListTileSlot slot) {
final Element oldChild = slotToChild[slot];
final Element newChild = updateChild(oldChild, widget, slot);
if (oldChild != null) {
slotToChild.remove(slot);
childToSlot.remove(oldChild);
}
if (newChild != null) {
slotToChild[slot] = newChild;
childToSlot[newChild] = slot;
}
}
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_mountChild(widget.leading, _ListTileSlot.leading);
_mountChild(widget.title, _ListTileSlot.title);
_mountChild(widget.subtitle, _ListTileSlot.subtitle);
_mountChild(widget.trailing, _ListTileSlot.trailing);
}
void _updateChild(Widget widget, _ListTileSlot slot) {
final Element oldChild = slotToChild[slot];
final Element newChild = updateChild(oldChild, widget, slot);
if (oldChild != null) {
childToSlot.remove(oldChild);
slotToChild.remove(slot);
}
if (newChild != null) {
slotToChild[slot] = newChild;
childToSlot[newChild] = slot;
}
}
@override
void update(_ListTile newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_updateChild(widget.leading, _ListTileSlot.leading);
_updateChild(widget.title, _ListTileSlot.title);
_updateChild(widget.subtitle, _ListTileSlot.subtitle);
_updateChild(widget.trailing, _ListTileSlot.trailing);
}
void _updateRenderObject(RenderObject child, _ListTileSlot slot) {
switch (slot) {
case _ListTileSlot.leading:
renderObject.leading = child;
break;
case _ListTileSlot.title:
renderObject.title = child;
break;
case _ListTileSlot.subtitle:
renderObject.subtitle = child;
break;
case _ListTileSlot.trailing:
renderObject.trailing = child;
break;
}
}
@override
void insertChildRenderObject(RenderObject child, dynamic slotValue) {
assert(child is RenderBox);
assert(slotValue is _ListTileSlot);
final _ListTileSlot slot = slotValue;
_updateRenderObject(child, slot);
assert(renderObject.childToSlot.keys.contains(child));
assert(renderObject.slotToChild.keys.contains(slot));
}
@override
void removeChildRenderObject(RenderObject child) {
assert(child is RenderBox);
assert(renderObject.childToSlot.keys.contains(child));
_updateRenderObject(null, renderObject.childToSlot[child]);
assert(!renderObject.childToSlot.keys.contains(child));
assert(!renderObject.slotToChild.keys.contains(slot));
}
@override
void moveChildRenderObject(RenderObject child, dynamic slotValue) {
assert(false, 'not reachable');
}
}
class _RenderListTile extends RenderBox {
_RenderListTile({
bool isDense,
bool isThreeLine,
TextDirection textDirection,
}) : _isDense = isDense,
@required bool isDense,
@required bool isThreeLine,
@required TextDirection textDirection,
@required TextBaseline titleBaselineType,
TextBaseline subtitleBaselineType,
}) : assert(isDense != null),
assert(isThreeLine != null),
assert(textDirection != null),
assert(titleBaselineType != null),
_isDense = isDense,
_isThreeLine = isThreeLine,
_textDirection = textDirection;
_textDirection = textDirection,
_titleBaselineType = titleBaselineType,
_subtitleBaselineType = subtitleBaselineType;
static const double _minLeadingWidth = 40.0;
// The horizontal gap between the titles and the leading/trailing widgets
......@@ -591,6 +732,7 @@ class _RenderListTile extends RenderBox {
bool get isDense => _isDense;
bool _isDense;
set isDense(bool value) {
assert(value != null);
if (_isDense == value)
return;
_isDense = value;
......@@ -600,6 +742,7 @@ class _RenderListTile extends RenderBox {
bool get isThreeLine => _isThreeLine;
bool _isThreeLine;
set isThreeLine(bool value) {
assert(value != null);
if (_isThreeLine == value)
return;
_isThreeLine = value;
......@@ -609,12 +752,32 @@ class _RenderListTile extends RenderBox {
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
assert(value != null);
if (_textDirection == value)
return;
_textDirection = value;
markNeedsLayout();
}
TextBaseline get titleBaselineType => _titleBaselineType;
TextBaseline _titleBaselineType;
set titleBaselineType(TextBaseline value) {
assert(value != null);
if (_titleBaselineType == value)
return;
_titleBaselineType = value;
markNeedsLayout();
}
TextBaseline get subtitleBaselineType => _subtitleBaselineType;
TextBaseline _subtitleBaselineType;
set subtitleBaselineType(TextBaseline value) {
if (_subtitleBaselineType == value)
return;
_subtitleBaselineType = value;
markNeedsLayout();
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
......@@ -713,11 +876,11 @@ class _RenderListTile extends RenderBox {
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(title != null);
final BoxParentData parentData = title.parentData;
return parentData.offset.dy + title.getDistanceToActualBaseline(TextBaseline.alphabetic);
return parentData.offset.dy + title.getDistanceToActualBaseline(baseline);
}
static double _boxBaseline(RenderBox box) {
return box.getDistanceToBaseline(TextBaseline.alphabetic);
static double _boxBaseline(RenderBox box, TextBaseline baseline) {
return box.getDistanceToBaseline(baseline);
}
static Size _layoutBox(RenderBox box, BoxConstraints constraints) {
......@@ -775,8 +938,9 @@ class _RenderListTile extends RenderBox {
tileHeight = math.max(_defaultTileHeight, titleSize.height + 2.0 * _minVerticalPadding);
titleY = (tileHeight - titleSize.height) / 2.0;
} else {
titleY = titleBaseline - _boxBaseline(title);
subtitleY = subtitleBaseline - _boxBaseline(subtitle);
assert(subtitleBaselineType != null);
titleY = titleBaseline - _boxBaseline(title, titleBaselineType);
subtitleY = subtitleBaseline - _boxBaseline(subtitle, subtitleBaselineType);
tileHeight = _defaultTileHeight;
// If the title and subtitle overlap, move the title upwards by half
......@@ -859,116 +1023,3 @@ class _RenderListTile extends RenderBox {
return false;
}
}
class _RenderListTileElement extends RenderObjectElement {
_RenderListTileElement(_ListTile widget) : super(widget);
final Map<_ListTileSlot, Element> slotToChild = <_ListTileSlot, Element>{};
final Map<Element, _ListTileSlot> childToSlot = <Element, _ListTileSlot>{};
@override
_ListTile get widget => super.widget;
@override
_RenderListTile get renderObject => super.renderObject;
@override
void visitChildren(ElementVisitor visitor) {
slotToChild.values.forEach(visitor);
}
@override
void forgetChild(Element child) {
assert(slotToChild.values.contains(child));
assert(childToSlot.keys.contains(child));
final _ListTileSlot slot = childToSlot[child];
childToSlot.remove(child);
slotToChild.remove(slot);
}
void _mountChild(Widget widget, _ListTileSlot slot) {
final Element oldChild = slotToChild[slot];
final Element newChild = updateChild(oldChild, widget, slot);
if (oldChild != null) {
slotToChild.remove(slot);
childToSlot.remove(oldChild);
}
if (newChild != null) {
slotToChild[slot] = newChild;
childToSlot[newChild] = slot;
}
}
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_mountChild(widget.leading, _ListTileSlot.leading);
_mountChild(widget.title, _ListTileSlot.title);
_mountChild(widget.subtitle, _ListTileSlot.subtitle);
_mountChild(widget.trailing, _ListTileSlot.trailing);
}
void _updateChild(Widget widget, _ListTileSlot slot) {
final Element oldChild = slotToChild[slot];
final Element newChild = updateChild(oldChild, widget, slot);
if (oldChild != null) {
childToSlot.remove(oldChild);
slotToChild.remove(slot);
}
if (newChild != null) {
slotToChild[slot] = newChild;
childToSlot[newChild] = slot;
}
}
@override
void update(_ListTile newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_updateChild(widget.leading, _ListTileSlot.leading);
_updateChild(widget.title, _ListTileSlot.title);
_updateChild(widget.subtitle, _ListTileSlot.subtitle);
_updateChild(widget.trailing, _ListTileSlot.trailing);
}
void _updateRenderObject(RenderObject child, _ListTileSlot slot) {
switch (slot) {
case _ListTileSlot.leading:
renderObject.leading = child;
break;
case _ListTileSlot.title:
renderObject.title = child;
break;
case _ListTileSlot.subtitle:
renderObject.subtitle = child;
break;
case _ListTileSlot.trailing:
renderObject.trailing = child;
break;
}
}
@override
void insertChildRenderObject(RenderObject child, dynamic slotValue) {
assert(child is RenderBox);
assert(slotValue is _ListTileSlot);
final _ListTileSlot slot = slotValue;
_updateRenderObject(child, slot);
assert(renderObject.childToSlot.keys.contains(child));
assert(renderObject.slotToChild.keys.contains(slot));
}
@override
void removeChildRenderObject(RenderObject child) {
assert(child is RenderBox);
assert(renderObject.childToSlot.keys.contains(child));
_updateRenderObject(null, renderObject.childToSlot[child]);
assert(!renderObject.childToSlot.keys.contains(child));
assert(!renderObject.slotToChild.keys.contains(slot));
}
@override
void moveChildRenderObject(RenderObject child, dynamic slotValue) {
assert(false, 'not reachable');
}
}
......@@ -248,7 +248,7 @@ class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
duration: kThemeChangeDuration,
child: new Baseline(
baseline: widget.height - _kBaselineOffsetFromBottom,
baselineType: TextBaseline.alphabetic,
baselineType: style.textBaseline,
child: buildChild(),
)
);
......
......@@ -1599,9 +1599,12 @@ abstract class RenderBox extends RenderObject {
/// Only call this function after calling [layout] on this box. You
/// are only allowed to call this from the parent of this box during
/// that parent's [performLayout] or [paint] functions.
///
/// When implementing a [RenderBox] subclass, to override the baseline
/// computation, override [computeDistanceToActualBaseline].
double getDistanceToBaseline(TextBaseline baseline, { bool onlyReal: false }) {
assert(!_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
assert(!debugNeedsLayout);
assert(!_debugDoingBaseline);
assert(() {
final RenderObject parent = this.parent;
if (owner.debugDoingLayout)
......@@ -1628,7 +1631,7 @@ abstract class RenderBox extends RenderObject {
@protected
@mustCallSuper
double getDistanceToActualBaseline(TextBaseline baseline) {
assert(_debugDoingBaseline);
assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
_cachedBaselines ??= <TextBaseline, double>{};
_cachedBaselines.putIfAbsent(baseline, () => computeDistanceToActualBaseline(baseline));
return _cachedBaselines[baseline];
......@@ -1638,17 +1641,29 @@ abstract class RenderBox extends RenderObject {
/// the y-coordinate of the first given baseline in the box's contents, if
/// any, or null otherwise.
///
/// Do not call this function directly. Instead, call [getDistanceToBaseline]
/// if you need to know the baseline of a child from an invocation of
/// [performLayout] or [paint] and call [getDistanceToActualBaseline] if you
/// are implementing [computeDistanceToActualBaseline] and need to defer to a
/// child.
/// Do not call this function directly. If you need to know the baseline of a
/// child from an invocation of [performLayout] or [paint], call
/// [getDistanceToBaseline].
///
/// Subclasses should override this method to supply the distances to their
/// baselines.
/// baselines. When implementing this method, there are generally three
/// strategies:
///
/// * For classes that use the [ContainerRenderObjectMixin] child model,
/// consider mixing in the [RenderBoxContainerDefaultsMixin] class and
/// using
/// [RenderBoxContainerDefaultsMixin.defaultComputeDistanceToFirstActualBaseline].
///
/// * For classes that define a particular baseline themselves, return that
/// value directly.
///
/// * For classes that have a child to which they wish to defer the
/// computation, call [getDistanceToActualBaseline] on the child (not
/// [computeDistanceToActualBaseline], the internal implementation, and not
/// [getDistanceToBaseline], the public entry point for this API).
@protected
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(_debugDoingBaseline);
assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
return null;
}
......
......@@ -1124,7 +1124,7 @@ class RenderBaseline extends RenderShiftedBox {
RenderBaseline({
RenderBox child,
@required double baseline,
@required TextBaseline baselineType
@required TextBaseline baselineType,
}) : assert(baseline != null),
assert(baselineType != null),
_baseline = baseline,
......
......@@ -101,7 +101,7 @@ class Table extends RenderObjectWidget {
this.textDirection,
this.border,
this.defaultVerticalAlignment: TableCellVerticalAlignment.top,
this.textBaseline
this.textBaseline,
}) : assert(children != null),
assert(defaultColumnWidth != null),
assert(defaultVerticalAlignment != null),
......@@ -213,7 +213,7 @@ class Table extends RenderObjectWidget {
rowDecorations: _rowDecorations,
configuration: createLocalImageConfiguration(context),
defaultVerticalAlignment: defaultVerticalAlignment,
textBaseline: textBaseline
textBaseline: textBaseline,
);
}
......
......@@ -65,18 +65,11 @@ Widget _wrapForChip({
double textScaleFactor: 1.0,
}) {
return new MaterialApp(
home: new Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
child: new Directionality(
textDirection: textDirection,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScaleFactor),
child: new Material(child: child),
),
home: new Directionality(
textDirection: textDirection,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScaleFactor),
child: new Material(child: child),
),
),
);
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
void main() {
testWidgets('Baseline - control test', (WidgetTester tester) async {
......@@ -41,4 +41,108 @@ void main() {
expect(tester.renderObject<RenderBox>(find.byType(Baseline)).size,
within<Size>(from: const Size(100.0, 200.0), distance: 0.001));
});
testWidgets('Chip caches baseline', (WidgetTester tester) async {
int calls = 0;
await tester.pumpWidget(
new MaterialApp(
home: new Material(
child: new Baseline(
baseline: 100.0,
baselineType: TextBaseline.alphabetic,
child: new Chip(
label: new BaselineDetector(() {
calls += 1;
}),
),
),
),
),
);
expect(calls, 1);
await tester.pump();
expect(calls, 1);
tester.renderObject<RenderBaselineDetector>(find.byType(BaselineDetector)).dirty();
await tester.pump();
expect(calls, 2);
});
testWidgets('ListTile caches baseline', (WidgetTester tester) async {
int calls = 0;
await tester.pumpWidget(
new MaterialApp(
home: new Material(
child: new Baseline(
baseline: 100.0,
baselineType: TextBaseline.alphabetic,
child: new ListTile(
title: new BaselineDetector(() {
calls += 1;
}),
),
),
),
),
);
expect(calls, 1);
await tester.pump();
expect(calls, 1);
tester.renderObject<RenderBaselineDetector>(find.byType(BaselineDetector)).dirty();
await tester.pump();
expect(calls, 2);
});
}
class BaselineDetector extends LeafRenderObjectWidget {
const BaselineDetector(this.callback);
final VoidCallback callback;
@override
RenderBaselineDetector createRenderObject(BuildContext context) => new RenderBaselineDetector(callback);
@override
void updateRenderObject(BuildContext context, RenderBaselineDetector renderObject) {
renderObject.callback = callback;
}
}
class RenderBaselineDetector extends RenderBox {
RenderBaselineDetector(this.callback);
VoidCallback callback;
@override
bool get sizedByParent => true;
@override
double computeMinIntrinsicWidth(double height) => 0.0;
@override
double computeMaxIntrinsicWidth(double height) => 0.0;
@override
double computeMinIntrinsicHeight(double width) => 0.0;
@override
double computeMaxIntrinsicHeight(double width) => 0.0;
@override
double computeDistanceToActualBaseline(TextBaseline baseline) {
if (callback != null)
callback();
return 0.0;
}
void dirty() {
markNeedsLayout();
}
@override
void performResize() {
size = constraints.smallest;
}
@override
void paint(PaintingContext context, Offset offset) { }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment