提交 30f93774 编写于 作者: H Hans Muller

Merge pull request #122 from HansMuller/shrinking-card

Card Collection dismiss animation
...@@ -15,6 +15,7 @@ import 'package:sky/widgets/widget.dart'; ...@@ -15,6 +15,7 @@ import 'package:sky/widgets/widget.dart';
class BlockViewportApp extends App { class BlockViewportApp extends App {
BlockViewportLayoutState layoutState = new BlockViewportLayoutState();
List<double> lengths = <double>[]; List<double> lengths = <double>[];
double offset = 0.0; double offset = 0.0;
...@@ -96,7 +97,8 @@ class BlockViewportApp extends App { ...@@ -96,7 +97,8 @@ class BlockViewportApp extends App {
child: new BlockViewport( child: new BlockViewport(
builder: builder, builder: builder,
startOffset: offset, startOffset: offset,
token: lengths.length token: lengths.length,
layoutState: layoutState
) )
) )
), ),
......
...@@ -2,62 +2,145 @@ ...@@ -2,62 +2,145 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/base/lerp.dart'; import 'package:sky/base/lerp.dart';
import 'package:sky/painting/text_style.dart'; import 'package:sky/painting/text_style.dart';
import 'package:sky/theme/colors.dart'; import 'package:sky/theme/colors.dart';
import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/block_viewport.dart';
import 'package:sky/widgets/card.dart'; import 'package:sky/widgets/card.dart';
import 'package:sky/widgets/dismissable.dart'; import 'package:sky/widgets/dismissable.dart';
import 'package:sky/widgets/scaffold.dart';
import 'package:sky/widgets/variable_height_scrollable.dart'; import 'package:sky/widgets/variable_height_scrollable.dart';
import 'package:sky/widgets/scaffold.dart';
import 'package:sky/widgets/theme.dart'; import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/tool_bar.dart'; import 'package:sky/widgets/tool_bar.dart';
import 'package:sky/widgets/widget.dart'; import 'package:sky/widgets/widget.dart';
import 'package:sky/theme/colors.dart' as colors;
import 'package:sky/widgets/task_description.dart'; import 'package:sky/widgets/task_description.dart';
class CardModel {
CardModel(this.value, this.height, this.color);
int value;
double height;
Color color;
AnimationPerformance performance;
String get label => "Item $value";
String get key => value.toString();
bool operator ==(other) => other is CardModel && other.value == value;
int get hashCode => 373 * 37 * value.hashCode;
}
class ShrinkingCard extends AnimatedComponent {
ShrinkingCard({
String key,
CardModel this.card,
Function this.onUpdated,
Function this.onCompleted
}) : super(key: key);
CardModel card;
Function onUpdated;
Function onCompleted;
double get currentHeight => card.performance.variable.value;
void initState() {
assert(card.performance != null);
card.performance.addListener(handleAnimationProgress);
watch(card.performance);
}
void handleAnimationProgress() {
if (card.performance.isCompleted) {
if (onCompleted != null)
onCompleted();
} else if (onUpdated != null) {
onUpdated();
}
}
void syncFields(ShrinkingCard source) {
card = source.card;
onCompleted = source.onCompleted;
onUpdated = source.onUpdated;
super.syncFields(source);
}
Widget build() => new Container(height: currentHeight);
}
class CardCollectionApp extends App { class CardCollectionApp extends App {
final TextStyle cardLabelStyle = final TextStyle cardLabelStyle =
new TextStyle(color: white, fontSize: 18.0, fontWeight: bold); new TextStyle(color: white, fontSize: 18.0, fontWeight: bold);
final List<double> cardHeights = [ BlockViewportLayoutState layoutState = new BlockViewportLayoutState();
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0, List<CardModel> cardModels;
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0
];
List<int> visibleCardIndices;
void initState() { void initState() {
visibleCardIndices = new List.generate(cardHeights.length, (i) => i); List<double> cardHeights = <double>[
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0
];
cardModels = new List.generate(cardHeights.length, (i) {
Color color = lerpColor(Red[300], Blue[900], i / cardHeights.length);
return new CardModel(i, cardHeights[i], color);
});
super.initState(); super.initState();
} }
void dismissCard(int cardIndex) { void shrinkCard(CardModel card, int index) {
if (card.performance != null)
return;
layoutState.invalidate([index]);
setState(() { setState(() {
visibleCardIndices.remove(cardIndex); assert(card.performance == null);
card.performance = new AnimationPerformance()
..duration = const Duration(milliseconds: 300)
..variable = new AnimatedType<double>(
card.height + kCardMargins.top + kCardMargins.bottom,
end: 0.0,
curve: ease,
interval: new Interval(0.5, 1.0)
)
..play();
}); });
} }
Widget _builder(int index) { void dismissCard(CardModel card) {
if (index >= visibleCardIndices.length) if (cardModels.contains(card)) {
setState(() {
cardModels.remove(card);
});
}
}
Widget builder(int index) {
if (index >= cardModels.length)
return null; return null;
CardModel card = cardModels[index];
if (card.performance != null) {
return new ShrinkingCard(
key: card.key,
card: card,
onUpdated: () { layoutState.invalidate([index]); },
onCompleted: () { dismissCard(card); }
);
}
int cardIndex = visibleCardIndices[index];
Color color = lerpColor(Red[500], Blue[500], cardIndex / cardHeights.length);
Widget label = new Text("Item ${cardIndex}", style: cardLabelStyle);
return new Dismissable( return new Dismissable(
key: cardIndex.toString(), key: card.key,
onDismissed: () { dismissCard(cardIndex); }, onDismissed: () { shrinkCard(card, index); },
child: new Card( child: new Card(
color: color, color: card.color,
child: new Container( child: new Container(
height: cardHeights[cardIndex], height: card.height,
padding: const EdgeDims.all(8.0), padding: const EdgeDims.all(8.0),
child: new Center(child: label) child: new Center(child: new Text(card.label, style: cardLabelStyle))
) )
) )
); );
...@@ -68,16 +151,17 @@ class CardCollectionApp extends App { ...@@ -68,16 +151,17 @@ class CardCollectionApp extends App {
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0), padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatch[50]), decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatch[50]),
child: new VariableHeightScrollable( child: new VariableHeightScrollable(
builder: _builder, builder: builder,
token: visibleCardIndices.length token: cardModels.length,
layoutState: layoutState
) )
); );
return new Theme( return new Theme(
data: new ThemeData( data: new ThemeData(
brightness: ThemeBrightness.light, brightness: ThemeBrightness.light,
primarySwatch: colors.Blue, primarySwatch: Blue,
accentColor: colors.RedAccent[200] accentColor: RedAccent[200]
), ),
child: new TaskDescription( child: new TaskDescription(
label: 'Cards', label: 'Cards',
......
...@@ -12,13 +12,6 @@ import 'package:sky/widgets/widget.dart'; ...@@ -12,13 +12,6 @@ import 'package:sky/widgets/widget.dart';
// return null if index is greater than index of last entry // return null if index is greater than index of last entry
typedef Widget IndexedBuilder(int index); typedef Widget IndexedBuilder(int index);
typedef void LayoutChangedCallback(
int firstVisibleChildIndex,
int visibleChildCount,
UnmodifiableListView<double> childOffsets,
bool didReachLastChild
);
class _Key { class _Key {
const _Key(this.type, this.key); const _Key(this.type, this.key);
factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget.key); factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget.key);
...@@ -26,24 +19,80 @@ class _Key { ...@@ -26,24 +19,80 @@ class _Key {
final String key; final String key;
bool operator ==(other) => other is _Key && other.type == type && other.key == key; bool operator ==(other) => other is _Key && other.type == type && other.key == key;
int get hashCode => 373 * 37 * type.hashCode + key.hashCode; int get hashCode => 373 * 37 * type.hashCode + key.hashCode;
String toString() => "_Key(type: $type, key: $key)";
}
typedef void LayoutChangedCallback();
class BlockViewportLayoutState {
BlockViewportLayoutState()
: _childOffsets = <double>[0.0],
_firstVisibleChildIndex = 0,
_visibleChildCount = 0,
_didReachLastChild = false
{
_readOnlyChildOffsets = new UnmodifiableListView<double>(_childOffsets);
}
Map<_Key, Widget> _childrenByKey = new Map<_Key, Widget>();
bool _dirty = true;
int _firstVisibleChildIndex;
int get firstVisibleChildIndex => _firstVisibleChildIndex;
int _visibleChildCount;
int get visibleChildCount => _visibleChildCount;
// childOffsets contains the offsets of each child from the top of the
// list up to the last one we've ever created, and the offset of the
// end of the last one. If there are no children, then the only offset
// is 0.0.
List<double> _childOffsets;
UnmodifiableListView<double> _readOnlyChildOffsets;
UnmodifiableListView<double> get childOffsets => _readOnlyChildOffsets;
double get contentsSize => _childOffsets.last;
bool _didReachLastChild;
bool get didReachLastChild => _didReachLastChild;
Set<int> _invalidIndices = new Set<int>();
bool get isValid => _invalidIndices.length == 0;
// Notify the BlockViewport that the children at indices have either
// changed size and/or changed type.
void invalidate(Iterable<int> indices) {
_invalidIndices.addAll(indices);
}
final List<Function> _listeners = new List<Function>();
void addListener(Function listener) {
_listeners.add(listener);
}
void removeListener(Function listener) {
_listeners.remove(listener);
}
void _notifyListeners() {
List<Function> localListeners = new List<Function>.from(_listeners);
for (Function listener in localListeners)
listener();
}
} }
class BlockViewport extends RenderObjectWrapper { class BlockViewport extends RenderObjectWrapper {
BlockViewport({ this.builder, this.startOffset, this.token, this.onLayoutChanged, String key }) BlockViewport({ this.builder, this.startOffset, this.token, this.layoutState, String key })
: super(key: key); : super(key: key) {
assert(this.layoutState != null);
}
IndexedBuilder builder; IndexedBuilder builder;
double startOffset; double startOffset;
Object token; Object token;
LayoutChangedCallback onLayoutChanged; BlockViewportLayoutState layoutState;
RenderBlockViewport get root => super.root; RenderBlockViewport get root => super.root;
RenderBlockViewport createNode() => new RenderBlockViewport(); RenderBlockViewport createNode() => new RenderBlockViewport();
Map<_Key, Widget> _childrenByKey = new Map<_Key, Widget>();
void walkChildren(WidgetTreeWalker walker) { void walkChildren(WidgetTreeWalker walker) {
for (Widget child in _childrenByKey.values) for (Widget child in layoutState._childrenByKey.values)
walker(child); walker(child);
} }
...@@ -69,7 +118,7 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -69,7 +118,7 @@ class BlockViewport extends RenderObjectWrapper {
} }
void remove() { void remove() {
for (Widget child in _childrenByKey.values) { for (Widget child in layoutState._childrenByKey.values) {
assert(child != null); assert(child != null);
removeChild(child); removeChild(child);
} }
...@@ -86,23 +135,15 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -86,23 +135,15 @@ class BlockViewport extends RenderObjectWrapper {
super.didUnmount(); super.didUnmount();
} }
// _offsets contains the offsets of each child from the top of the
// list up to the last one we've ever created, and the offset of the
// end of the last one. If there's no children, then the only offset
// is 0.0.
List<double> _offsets = <double>[0.0];
int _currentStartIndex = 0;
int _currentChildCount = 0;
bool _didReachLastChild = false;
int _findIndexForOffsetBeforeOrAt(double offset) { int _findIndexForOffsetBeforeOrAt(double offset) {
final List<double> offsets = layoutState._childOffsets;
int left = 0; int left = 0;
int right = _offsets.length - 1; int right = offsets.length - 1;
while (right >= left) { while (right >= left) {
int middle = left + ((right - left) ~/ 2); int middle = left + ((right - left) ~/ 2);
if (_offsets[middle] < offset) { if (offsets[middle] < offset) {
left = middle + 1; left = middle + 1;
} else if (_offsets[middle] > offset) { } else if (offsets[middle] > offset) {
right = middle - 1; right = middle - 1;
} else { } else {
return middle; return middle;
...@@ -111,94 +152,154 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -111,94 +152,154 @@ class BlockViewport extends RenderObjectWrapper {
return right; return right;
} }
bool _dirty = true;
bool retainStatefulNodeIfPossible(BlockViewport newNode) { bool retainStatefulNodeIfPossible(BlockViewport newNode) {
assert(layoutState == newNode.layoutState);
retainStatefulRenderObjectWrapper(newNode); retainStatefulRenderObjectWrapper(newNode);
if (startOffset != newNode.startOffset) { if (startOffset != newNode.startOffset) {
_dirty = true; layoutState._dirty = true;
startOffset = newNode.startOffset; startOffset = newNode.startOffset;
} }
if (token != newNode.token || builder != newNode.builder) { if (token != newNode.token || builder != newNode.builder) {
_dirty = true; layoutState._dirty = true;
builder = newNode.builder; builder = newNode.builder;
token = newNode.token; token = newNode.token;
_offsets = <double>[0.0]; layoutState._didReachLastChild = false;
_didReachLastChild = false; layoutState._childOffsets = <double>[0.0];
layoutState._invalidIndices = new Set<int>();
} }
return true; return true;
} }
void syncRenderObject(BlockViewport old) { void syncRenderObject(BlockViewport old) {
super.syncRenderObject(old); super.syncRenderObject(old);
if (_dirty) { if (layoutState._dirty || !layoutState.isValid) {
root.markNeedsLayout(); root.markNeedsLayout();
} else { } else {
if (_currentChildCount > 0) { if (layoutState._visibleChildCount > 0) {
assert(_currentStartIndex >= 0); assert(layoutState.firstVisibleChildIndex >= 0);
assert(builder != null); assert(builder != null);
assert(root != null); assert(root != null);
int lastIndex = _currentStartIndex + _currentChildCount - 1; final int startIndex = layoutState._firstVisibleChildIndex;
for (int index = _currentStartIndex; index <= lastIndex; index += 1) { int lastIndex = startIndex + layoutState._visibleChildCount - 1;
for (int index = startIndex; index <= lastIndex; index += 1) {
Widget widget = builder(index); Widget widget = builder(index);
assert(widget != null); assert(widget != null);
assert(widget.key != null); assert(widget.key != null);
_Key key = new _Key.fromWidget(widget); _Key key = new _Key.fromWidget(widget);
Widget oldWidget = _childrenByKey[key]; Widget oldWidget = layoutState._childrenByKey[key];
assert(oldWidget != null); assert(oldWidget != null);
assert(oldWidget.root.parent == root); assert(oldWidget.root.parent == root);
widget = syncChild(widget, oldWidget, root.childAfter(oldWidget.root)); widget = syncChild(widget, oldWidget, root.childAfter(oldWidget.root));
assert(widget != null); assert(widget != null);
_childrenByKey[key] = widget; layoutState._childrenByKey[key] = widget;
} }
} }
} }
} }
// Build the widget at index, and use its maxIntrinsicHeight to fix up
// the offsets from index+1 to endIndex. Return the newWidget.
Widget _getWidgetAndRecomputeOffsets(int index, int endIndex, BoxConstraints innerConstraints) {
final List<double> offsets = layoutState._childOffsets;
// Create the newWidget at index.
assert(index >= 0);
assert(endIndex > index);
assert(endIndex < offsets.length);
assert(builder != null);
Widget newWidget = builder(index);
assert(newWidget != null);
assert(newWidget.key != null);
final _Key key = new _Key.fromWidget(newWidget);
Widget oldWidget = layoutState._childrenByKey[key];
newWidget = syncChild(newWidget, oldWidget, _omit);
assert(newWidget != null);
// Update the offsets based on the newWidget's height.
RenderBox widgetRoot = newWidget.root;
assert(widgetRoot is RenderBox);
double newHeight = widgetRoot.getMaxIntrinsicHeight(innerConstraints);
double oldHeight = offsets[index + 1] - offsets[index];
double heightDelta = newHeight - oldHeight;
for (int i = index + 1; i <= endIndex; i++)
offsets[i] += heightDelta;
return newWidget;
}
Widget _getWidget(int index, BoxConstraints innerConstraints) { Widget _getWidget(int index, BoxConstraints innerConstraints) {
final List<double> offsets = layoutState._childOffsets;
assert(index >= 0);
Widget widget = builder == null ? null : builder(index);
if (widget == null)
return null;
assert(widget.key != null); // items in lists must have keys
final _Key key = new _Key.fromWidget(widget);
Widget oldWidget = layoutState._childrenByKey[key];
widget = syncChild(widget, oldWidget, _omit);
if (index >= offsets.length - 1) {
assert(index == offsets.length - 1);
final double widgetStartOffset = offsets[index];
RenderBox widgetRoot = widget.root;
assert(widgetRoot is RenderBox);
final double widgetEndOffset = widgetStartOffset + widgetRoot.getMaxIntrinsicHeight(innerConstraints);
offsets.add(widgetEndOffset);
}
return widget;
}
void layout(BoxConstraints constraints) {
if (!layoutState._dirty && layoutState.isValid)
return;
layoutState._dirty = false;
LayoutCallbackBuilderHandle handle = enterLayoutCallbackBuilder(); LayoutCallbackBuilderHandle handle = enterLayoutCallbackBuilder();
try { try {
assert(index >= 0); _doLayout(constraints);
Widget widget = builder == null ? null : builder(index);
if (widget == null)
return null;
assert(widget.key != null); // items in lists must have keys
final _Key key = new _Key.fromWidget(widget);
Widget oldWidget = _childrenByKey[key];
widget = syncChild(widget, oldWidget, _omit);
if (oldWidget != null)
_childrenByKey[key] = widget;
if (index >= _offsets.length - 1) {
assert(index == _offsets.length - 1);
final double widgetStartOffset = _offsets[index];
RenderBox widgetRoot = widget.root;
assert(widgetRoot is RenderBox);
final double widgetEndOffset = widgetStartOffset + widgetRoot.getMaxIntrinsicHeight(innerConstraints);
_offsets.add(widgetEndOffset);
}
return widget;
} finally { } finally {
exitLayoutCallbackBuilder(handle); exitLayoutCallbackBuilder(handle);
} }
}
void layout(BoxConstraints constraints) { layoutState._notifyListeners();
if (!_dirty) }
return;
_dirty = false;
void _doLayout(BoxConstraints constraints) {
Map<_Key, Widget> newChildren = new Map<_Key, Widget>(); Map<_Key, Widget> newChildren = new Map<_Key, Widget>();
Map<int, Widget> builtChildren = new Map<int, Widget>(); Map<int, Widget> builtChildren = new Map<int, Widget>();
final List<double> offsets = layoutState._childOffsets;
final Map<_Key, Widget> childrenByKey = layoutState._childrenByKey;
final double height = root.size.height; final double height = root.size.height;
final double endOffset = startOffset + height; final double endOffset = startOffset + height;
BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: constraints.constrainWidth()); BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: constraints.constrainWidth());
// Before doing the actual layout, fix the offsets for the widgets
// whose size or type has changed.
if (!layoutState.isValid && offsets.length > 0) {
List<int> invalidIndices = layoutState._invalidIndices.toList();
invalidIndices.sort();
// Ensure all of the offsets after invalidIndices[0] are updated.
if (invalidIndices.last < offsets.length - 1)
invalidIndices.add(offsets.length - 1);
for (int i = 0; i < invalidIndices.length - 1; i += 1) {
int index = invalidIndices[i];
int endIndex = invalidIndices[i + 1];
Widget widget = _getWidgetAndRecomputeOffsets(index, endIndex, innerConstraints);
_Key widgetKey = new _Key.fromWidget(widget);
bool isVisible = offsets[index] < endOffset && offsets[index + 1] >= startOffset;
if (isVisible) {
newChildren[widgetKey] = widget;
builtChildren[index] = widget;
} else {
childrenByKey.remove(widgetKey);
syncChild(null, widget, null);
}
}
}
layoutState._invalidIndices.clear();
int startIndex; int startIndex;
bool haveChildren; bool haveChildren;
if (startOffset <= 0.0) { if (startOffset <= 0.0) {
startIndex = 0; startIndex = 0;
if (_offsets.length > 1) { if (offsets.length > 1) {
haveChildren = true; haveChildren = true;
} else { } else {
Widget widget = _getWidget(startIndex, innerConstraints); Widget widget = _getWidget(startIndex, innerConstraints);
...@@ -208,41 +309,41 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -208,41 +309,41 @@ class BlockViewport extends RenderObjectWrapper {
haveChildren = true; haveChildren = true;
} else { } else {
haveChildren = false; haveChildren = false;
_didReachLastChild = true; layoutState._didReachLastChild = true;
} }
} }
} else { } else {
startIndex = _findIndexForOffsetBeforeOrAt(startOffset); startIndex = _findIndexForOffsetBeforeOrAt(startOffset);
if (startIndex == _offsets.length - 1) { if (startIndex == offsets.length - 1) {
// We don't have an offset on the list that is beyond the start offset. // We don't have an offset on the list that is beyond the start offset.
assert(_offsets.last <= startOffset); assert(offsets.last <= startOffset);
// Fill the list until this isn't true or until we know that the // Fill the list until this isn't true or until we know that the
// list is complete (and thus we are overscrolled). // list is complete (and thus we are overscrolled).
while (true) { while (true) {
Widget widget = _getWidget(startIndex, innerConstraints); Widget widget = _getWidget(startIndex, innerConstraints);
if (widget == null) { if (widget == null) {
_didReachLastChild = true; layoutState._didReachLastChild = true;
break; break;
} }
_Key widgetKey = new _Key.fromWidget(widget); _Key widgetKey = new _Key.fromWidget(widget);
if (_offsets.last > startOffset) { if (offsets.last > startOffset) {
newChildren[widgetKey] = widget; newChildren[widgetKey] = widget;
builtChildren[startIndex] = widget; builtChildren[startIndex] = widget;
break; break;
} }
if (!_childrenByKey.containsKey(widgetKey)) { if (!childrenByKey.containsKey(widgetKey)) {
// we don't actually need this one, release it // we don't actually need this one, release it
syncChild(null, widget, null); syncChild(null, widget, null);
} // else we'll get rid of it later, when we remove old children } // else we'll get rid of it later, when we remove old children
startIndex += 1; startIndex += 1;
assert(startIndex == _offsets.length - 1); assert(startIndex == offsets.length - 1);
} }
if (_offsets.last > startOffset) { if (offsets.last > startOffset) {
// If we're here, we have at least one child, so our list has // If we're here, we have at least one child, so our list has
// at least two offsets, the top of the child and the bottom // at least two offsets, the top of the child and the bottom
// of the child. // of the child.
assert(_offsets.length >= 2); assert(offsets.length >= 2);
assert(startIndex == _offsets.length - 2); assert(startIndex == offsets.length - 2);
haveChildren = true; haveChildren = true;
} else { } else {
// If we're here, there are no children to show. // If we're here, there are no children to show.
...@@ -253,20 +354,20 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -253,20 +354,20 @@ class BlockViewport extends RenderObjectWrapper {
} }
} }
assert(haveChildren != null); assert(haveChildren != null);
assert(haveChildren || _didReachLastChild); assert(haveChildren || layoutState._didReachLastChild);
assert(startIndex >= 0); assert(startIndex >= 0);
assert(startIndex < _offsets.length); assert(startIndex < offsets.length);
int index = startIndex; int index = startIndex;
if (haveChildren) { if (haveChildren) {
// Build all the widgets we need. // Build all the widgets we need.
root.startOffset = _offsets[index] - startOffset; root.startOffset = offsets[index] - startOffset;
while (_offsets[index] < endOffset) { while (offsets[index] < endOffset) {
if (!builtChildren.containsKey(index)) { if (!builtChildren.containsKey(index)) {
Widget widget = _getWidget(index, innerConstraints); Widget widget = _getWidget(index, innerConstraints);
if (widget == null) { if (widget == null) {
_didReachLastChild = true; layoutState._didReachLastChild = true;
break; break;
} }
newChildren[new _Key.fromWidget(widget)] = widget; newChildren[new _Key.fromWidget(widget)] = widget;
...@@ -278,9 +379,9 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -278,9 +379,9 @@ class BlockViewport extends RenderObjectWrapper {
} }
// Remove any old children. // Remove any old children.
for (_Key oldChildKey in _childrenByKey.keys) { for (_Key oldChildKey in childrenByKey.keys) {
if (!newChildren.containsKey(oldChildKey)) if (!newChildren.containsKey(oldChildKey))
syncChild(null, _childrenByKey[oldChildKey], null); // calls detachChildRoot() syncChild(null, childrenByKey[oldChildKey], null); // calls detachChildRoot()
} }
if (haveChildren) { if (haveChildren) {
...@@ -302,18 +403,9 @@ class BlockViewport extends RenderObjectWrapper { ...@@ -302,18 +403,9 @@ class BlockViewport extends RenderObjectWrapper {
} }
} }
_childrenByKey = newChildren; layoutState._childrenByKey = newChildren;
_currentStartIndex = startIndex; layoutState._firstVisibleChildIndex = startIndex;
_currentChildCount = _childrenByKey.length; layoutState._visibleChildCount = newChildren.length;
if (onLayoutChanged != null) {
onLayoutChanged(
_currentStartIndex,
_currentChildCount,
new UnmodifiableListView<double>(_offsets),
_didReachLastChild
);
}
} }
} }
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/material.dart'; import 'package:sky/widgets/material.dart';
const EdgeDims kCardMargins = const EdgeDims.all(4.0);
/// A material design card /// A material design card
/// ///
/// <https://www.google.com/design/spec/components/cards.html> /// <https://www.google.com/design/spec/components/cards.html>
...@@ -16,7 +18,7 @@ class Card extends Component { ...@@ -16,7 +18,7 @@ class Card extends Component {
Widget build() { Widget build() {
return new Container( return new Container(
margin: const EdgeDims.all(4.0), margin: kCardMargins,
child: new Material( child: new Material(
color: color, color: color,
type: MaterialType.card, type: MaterialType.card,
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:collection';
import 'package:sky/animation/scroll_behavior.dart'; import 'package:sky/animation/scroll_behavior.dart';
import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/block_viewport.dart'; import 'package:sky/widgets/block_viewport.dart';
...@@ -14,18 +12,39 @@ class VariableHeightScrollable extends Scrollable { ...@@ -14,18 +12,39 @@ class VariableHeightScrollable extends Scrollable {
VariableHeightScrollable({ VariableHeightScrollable({
String key, String key,
this.builder, this.builder,
this.token this.token,
this.layoutState
}) : super(key: key); }) : super(key: key);
IndexedBuilder builder; IndexedBuilder builder;
Object token; Object token;
BlockViewportLayoutState layoutState;
// When the token changes the scrollable's contents may have
// changed. Remember as much so that after the new contents
// have been laid out we can adjust the scrollOffset so that
// the last page of content is still visible.
bool _contentsChanged = true; bool _contentsChanged = true;
void initState() {
assert(layoutState != null);
layoutState.removeListener(_handleLayoutChanged);
layoutState.addListener(_handleLayoutChanged);
super.initState();
}
void syncFields(VariableHeightScrollable source) { void syncFields(VariableHeightScrollable source) {
builder = source.builder; builder = source.builder;
if (token != source.token) if (token != source.token)
_contentsChanged = true; _contentsChanged = true;
token = source.token; token = source.token;
if (layoutState != source.layoutState) {
// Warning: this is unlikely to be what you intended.
assert(source.layoutState != null);
layoutState == source.layoutState;
layoutState.removeListener(_handleLayoutChanged);
layoutState.addListener(_handleLayoutChanged);
}
super.syncFields(source); super.syncFields(source);
} }
...@@ -36,15 +55,9 @@ class VariableHeightScrollable extends Scrollable { ...@@ -36,15 +55,9 @@ class VariableHeightScrollable extends Scrollable {
scrollBehavior.containerSize = newSize.height; scrollBehavior.containerSize = newSize.height;
} }
void _handleLayoutChanged( void _handleLayoutChanged() {
int firstVisibleChildIndex, if (layoutState.didReachLastChild) {
int visibleChildCount, scrollBehavior.contentsSize = layoutState.contentsSize;
UnmodifiableListView<double> childOffsets,
bool didReachLastChild
) {
assert(childOffsets.length > 0);
if (didReachLastChild) {
scrollBehavior.contentsSize = childOffsets.last;
if (_contentsChanged && scrollOffset > scrollBehavior.maxScrollOffset) { if (_contentsChanged && scrollOffset > scrollBehavior.maxScrollOffset) {
_contentsChanged = false; _contentsChanged = false;
settleScrollOffset(); settleScrollOffset();
...@@ -59,7 +72,7 @@ class VariableHeightScrollable extends Scrollable { ...@@ -59,7 +72,7 @@ class VariableHeightScrollable extends Scrollable {
callback: _handleSizeChanged, callback: _handleSizeChanged,
child: new BlockViewport( child: new BlockViewport(
builder: builder, builder: builder,
onLayoutChanged: _handleLayoutChanged, layoutState: layoutState,
startOffset: scrollOffset, startOffset: scrollOffset,
token: token token: token
) )
......
...@@ -32,7 +32,7 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -32,7 +32,7 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xfff44336), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffe57373), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 52.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 52.0), Paint(color:Color(0xff000000)))
...@@ -62,10 +62,10 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -62,10 +62,10 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffee453b), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffdd7174), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 68.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 67.0), Paint(color:Color(0xff000000)))
2 | | | | | | | | | | | | clipRRect() 2 | | | | | | | | | | | | clipRRect()
2 | | | | | | | | | | | | paintChild RenderConstrainedBox at Point(4.0, 4.0) 2 | | | | | | | | | | | | paintChild RenderConstrainedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
...@@ -73,26 +73,26 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -73,26 +73,26 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | paintChild RenderPositionedBox at Point(12.0, 12.0) 2 | | | | | | | | | | | | | | paintChild RenderPositionedBox at Point(12.0, 12.0)
2 | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | | paintChild RenderParagraph at Point(359.0, 23.5) 2 | | | | | | | | | | | | | | | paintChild RenderParagraph at Point(359.0, 23.0)
2 | | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | | | translate(359.0, 23.5) 2 | | | | | | | | | | | | | | | | translate(359.0, 23.0)
2 | | | | | | | | | | | | | | | | translate(-359.0, -23.5) 2 | | | | | | | | | | | | | | | | translate(-359.0, -23.0)
2 | | | | | | | | | | | | restore 2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore 2 | | | | | | | | | restore
2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 196.0) 2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 195.0)
2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderOpacity at Point(8.0, 196.0) 2 | | | | | | | paintChild RenderOpacity at Point(8.0, 195.0)
2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | paintChild RenderTransform at Point(8.0, 196.0) 2 | | | | | | | | paintChild RenderTransform at Point(8.0, 195.0)
2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | save 2 | | | | | | | | | save
2 | | | | | | | | | translate(8.0, 196.0) 2 | | | | | | | | | translate(8.0, 195.0)
2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) 2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0) 2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0)
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffe84740), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd56f76), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 86.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 86.0), Paint(color:Color(0xff000000)))
...@@ -109,23 +109,23 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -109,23 +109,23 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | | | | | | | translate(-359.0, -32.5) 2 | | | | | | | | | | | | | | | | translate(-359.0, -32.5)
2 | | | | | | | | | | | | restore 2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore 2 | | | | | | | | | restore
2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 286.0) 2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 285.0)
2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderOpacity at Point(8.0, 286.0) 2 | | | | | | | paintChild RenderOpacity at Point(8.0, 285.0)
2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | paintChild RenderTransform at Point(8.0, 286.0) 2 | | | | | | | | paintChild RenderTransform at Point(8.0, 285.0)
2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | save 2 | | | | | | | | | save
2 | | | | | | | | | translate(8.0, 286.0) 2 | | | | | | | | | translate(8.0, 285.0)
2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) 2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0) 2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0)
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffe24945), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffcd6e78), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 50.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 150.0), Paint(color:Color(0xff000000)))
2 | | | | | | | | | | | | clipRRect() 2 | | | | | | | | | | | | clipRRect()
2 | | | | | | | | | | | | paintChild RenderConstrainedBox at Point(4.0, 4.0) 2 | | | | | | | | | | | | paintChild RenderConstrainedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
...@@ -133,26 +133,26 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -133,26 +133,26 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | paintChild RenderPositionedBox at Point(12.0, 12.0) 2 | | | | | | | | | | | | | | paintChild RenderPositionedBox at Point(12.0, 12.0)
2 | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | | paintChild RenderParagraph at Point(359.0, 14.5) 2 | | | | | | | | | | | | | | | paintChild RenderParagraph at Point(359.0, 64.5)
2 | | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | | | translate(359.0, 14.5) 2 | | | | | | | | | | | | | | | | translate(359.0, 64.5)
2 | | | | | | | | | | | | | | | | translate(-359.0, -14.5) 2 | | | | | | | | | | | | | | | | translate(-359.0, -64.5)
2 | | | | | | | | | | | | restore 2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore 2 | | | | | | | | | restore
2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 340.0) 2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 439.0)
2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderOpacity at Point(8.0, 340.0) 2 | | | | | | | paintChild RenderOpacity at Point(8.0, 439.0)
2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | paintChild RenderTransform at Point(8.0, 340.0) 2 | | | | | | | | paintChild RenderTransform at Point(8.0, 439.0)
2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | save 2 | | | | | | | | | save
2 | | | | | | | | | translate(8.0, 340.0) 2 | | | | | | | | | translate(8.0, 439.0)
2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) 2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0) 2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0)
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffdc4c4b), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffc56c79), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 64.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 64.0), Paint(color:Color(0xff000000)))
...@@ -169,20 +169,20 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -169,20 +169,20 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | | | | | | | translate(-359.0, -21.5) 2 | | | | | | | | | | | | | | | | translate(-359.0, -21.5)
2 | | | | | | | | | | | | restore 2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore 2 | | | | | | | | | restore
2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 408.0) 2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 507.0)
2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderOpacity at Point(8.0, 408.0) 2 | | | | | | | paintChild RenderOpacity at Point(8.0, 507.0)
2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | paintChild RenderTransform at Point(8.0, 408.0) 2 | | | | | | | | paintChild RenderTransform at Point(8.0, 507.0)
2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | save 2 | | | | | | | | | save
2 | | | | | | | | | translate(8.0, 408.0) 2 | | | | | | | | | translate(8.0, 507.0)
2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) 2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0) 2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0)
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd64e50), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffbd6a7b), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 59.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 59.0), Paint(color:Color(0xff000000)))
...@@ -199,20 +199,20 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -199,20 +199,20 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | | | | | | | translate(-359.0, -19.0) 2 | | | | | | | | | | | | | | | | translate(-359.0, -19.0)
2 | | | | | | | | | | | | restore 2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore 2 | | | | | | | | | restore
2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 471.0) 2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 570.0)
2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderOpacity at Point(8.0, 471.0) 2 | | | | | | | paintChild RenderOpacity at Point(8.0, 570.0)
2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | paintChild RenderTransform at Point(8.0, 471.0) 2 | | | | | | | | paintChild RenderTransform at Point(8.0, 570.0)
2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | save 2 | | | | | | | | | save
2 | | | | | | | | | translate(8.0, 471.0) 2 | | | | | | | | | translate(8.0, 570.0)
2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) 2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0) 2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0)
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0) 2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd05055), drawLooper:true)) 2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffb5697d), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0) 2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 88.0), Paint(color:Color(0xff000000))) 2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 88.0), Paint(color:Color(0xff000000)))
...@@ -229,36 +229,6 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -229,36 +229,6 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | | | | | | | | | | translate(-359.0, -33.5) 2 | | | | | | | | | | | | | | | | translate(-359.0, -33.5)
2 | | | | | | | | | | | | restore 2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore 2 | | | | | | | | | restore
2 | | | | | | paintChild RenderSizeObserver at Point(8.0, 563.0)
2 | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderOpacity at Point(8.0, 563.0)
2 | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | paintChild RenderTransform at Point(8.0, 563.0)
2 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | save
2 | | | | | | | | | translate(8.0, 563.0)
2 | | | | | | | | | concat([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
2 | | | | | | | | | paintChild RenderPadding at Point(0.0, 0.0)
2 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | paintChild RenderDecoratedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffca535a), drawLooper:true))
2 | | | | | | | | | | | paintChild RenderClipRRect at Point(4.0, 4.0)
2 | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | saveLayer(Rect.fromLTRB(4.0, 4.0, 780.0, 100.0), Paint(color:Color(0xff000000)))
2 | | | | | | | | | | | | clipRRect()
2 | | | | | | | | | | | | paintChild RenderConstrainedBox at Point(4.0, 4.0)
2 | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | paintChild RenderPadding at Point(4.0, 4.0)
2 | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | paintChild RenderPositionedBox at Point(12.0, 12.0)
2 | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | | paintChild RenderParagraph at Point(359.0, 39.5)
2 | | | | | | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
2 | | | | | | | | | | | | | | | | translate(359.0, 39.5)
2 | | | | | | | | | | | | | | | | translate(-359.0, -39.5)
2 | | | | | | | | | | | | restore
2 | | | | | | | | | restore
2 | | | | | | restore 2 | | | | | | restore
2 | | paintChild RenderDecoratedBox at Point(0.0, 0.0) 2 | | paintChild RenderDecoratedBox at Point(0.0, 0.0)
2 | | | TestPaintingCanvas() constructor: 800.0 x 600.0 2 | | | TestPaintingCanvas() constructor: 800.0 x 600.0
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册