Commit 618e7e49 authored by Adam Barth's avatar Adam Barth

Adds a first draft of LazyBlock

LazyBlock is intended as a replacement for MixedViewport. Rather than

maintaining a table of all the observed child sizes (like

MixedViewport), LazyBlock works by dead reckoning the location of the

children based on the existing viewport. This approach makes it easier

to resize children because LazyBlock doesn't cache any additional

information that would need to be invalidated.



This patch contains a first draft of LazyBlock that works in a simple

usage scenario. Subsequent patches will replace

ScrollableMixedWidgetList with LazyBlock and port the existing

ScrollableMixedWidgetList tests over to LazyBlock.



Related to #3075
parent b9f04817
...@@ -49,6 +49,19 @@ class ComplexLayout extends StatefulWidget { ...@@ -49,6 +49,19 @@ class ComplexLayout extends StatefulWidget {
} }
class FancyItemDelegate extends LazyBlockDelegate {
@override
Widget buildItem(BuildContext context, int index) {
if (index % 2 == 0)
return new FancyImageItem(index, key: new Key("Item $index"));
else
return new FancyGalleryItem(index, key: new Key("Item $index"));
}
@override
bool shouldRebuild(FancyItemDelegate oldDelegate) => false;
}
class ComplexLayoutState extends State<ComplexLayout> { class ComplexLayoutState extends State<ComplexLayout> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -70,14 +83,9 @@ class ComplexLayoutState extends State<ComplexLayout> { ...@@ -70,14 +83,9 @@ class ComplexLayoutState extends State<ComplexLayout> {
body: new Column( body: new Column(
children: <Widget>[ children: <Widget>[
new Flexible( new Flexible(
child: new ScrollableMixedWidgetList( child: new LazyBlock(
key: new Key("main-scroll"), key: new Key("main-scroll"),
builder: (BuildContext context, int index) { delegate: new FancyItemDelegate()
if (index % 2 == 0)
return new FancyImageItem(index, key: new Key("Item $index"));
else
return new FancyGalleryItem(index, key: new Key("Item $index"));
}
) )
), ),
new BottomBar() new BottomBar()
......
...@@ -80,11 +80,12 @@ abstract class RenderBlockBase extends RenderBox ...@@ -80,11 +80,12 @@ abstract class RenderBlockBase extends RenderBox
bool get isVertical => _mainAxis == Axis.vertical; bool get isVertical => _mainAxis == Axis.vertical;
BoxConstraints _getInnerConstraints(BoxConstraints constraints) { BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
if (isVertical) switch (_mainAxis) {
return new BoxConstraints.tightFor(width: constraints.constrainWidth(constraints.maxWidth), case Axis.horizontal:
height: itemExtent); return new BoxConstraints.tightFor(height: constraints.maxHeight, width: itemExtent);
return new BoxConstraints.tightFor(height: constraints.constrainHeight(constraints.maxHeight), case Axis.vertical:
width: itemExtent); return new BoxConstraints.tightFor(width: constraints.maxWidth, height: itemExtent);
}
} }
double get _mainAxisExtent { double get _mainAxisExtent {
......
...@@ -2452,6 +2452,11 @@ class Listener extends SingleChildRenderObjectWidget { ...@@ -2452,6 +2452,11 @@ class Listener extends SingleChildRenderObjectWidget {
class RepaintBoundary extends SingleChildRenderObjectWidget { class RepaintBoundary extends SingleChildRenderObjectWidget {
RepaintBoundary({ Key key, Widget child }) : super(key: key, child: child); RepaintBoundary({ Key key, Widget child }) : super(key: key, child: child);
factory RepaintBoundary.wrap(Widget child, int childIndex) {
Key key = child.key != null ? new ValueKey<Key>(child.key) : new ValueKey<int>(childIndex);
return new RepaintBoundary(key: key, child: child);
}
@override @override
RenderRepaintBoundary createRenderObject(BuildContext context) => new RenderRepaintBoundary(); RenderRepaintBoundary createRenderObject(BuildContext context) => new RenderRepaintBoundary();
} }
......
...@@ -701,17 +701,17 @@ class BuildOwner { ...@@ -701,17 +701,17 @@ class BuildOwner {
/// This mechanism prevents build functions from transitively requiring other /// This mechanism prevents build functions from transitively requiring other
/// build functions to run, potentially causing infinite loops. /// build functions to run, potentially causing infinite loops.
/// ///
/// If the building argument is true, then this is a build scope. Build scopes /// If the building argument is true, then this function enables additional
/// cannot be nested. /// asserts that check invariants that should apply during building.
/// ///
/// The context argument is used to describe the scope in case an exception is /// The context argument is used to describe the scope in case an exception is
/// caught while invoking the callback. /// caught while invoking the callback.
void lockState(void callback(), { bool building: false, String context }) { void lockState(void callback(), { bool building: false, String context }) {
bool debugPreviouslyBuilding;
assert(_debugStateLockLevel >= 0); assert(_debugStateLockLevel >= 0);
assert(() { assert(() {
if (building) { if (building) {
assert(!_debugBuilding); debugPreviouslyBuilding = _debugBuilding;
assert(_debugCurrentBuildTarget == null);
_debugBuilding = true; _debugBuilding = true;
} }
_debugStateLockLevel += 1; _debugStateLockLevel += 1;
...@@ -726,8 +726,7 @@ class BuildOwner { ...@@ -726,8 +726,7 @@ class BuildOwner {
_debugStateLockLevel -= 1; _debugStateLockLevel -= 1;
if (building) { if (building) {
assert(_debugBuilding); assert(_debugBuilding);
assert(_debugCurrentBuildTarget == null); _debugBuilding = debugPreviouslyBuilding;
_debugBuilding = false;
} }
return true; return true;
}); });
...@@ -1360,6 +1359,7 @@ abstract class BuildableElement extends Element { ...@@ -1360,6 +1359,7 @@ abstract class BuildableElement extends Element {
} }
typedef Widget WidgetBuilder(BuildContext context); typedef Widget WidgetBuilder(BuildContext context);
typedef Widget IndexedBuilder(BuildContext context, int index);
// See _builder. // See _builder.
Widget _buildNothing(BuildContext context) => null; Widget _buildNothing(BuildContext context) => null;
...@@ -2088,6 +2088,7 @@ class MultiChildRenderObjectElement extends RenderObjectElement { ...@@ -2088,6 +2088,7 @@ class MultiChildRenderObjectElement extends RenderObjectElement {
@override @override
void moveChildRenderObject(RenderObject child, dynamic slot) { void moveChildRenderObject(RenderObject child, dynamic slot) {
final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject; final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject;
assert(child.parent == renderObject);
renderObject.move(child, after: slot?.renderObject); renderObject.move(child, after: slot?.renderObject);
assert(renderObject == this.renderObject); assert(renderObject == this.renderObject);
} }
......
This diff is collapsed.
...@@ -9,7 +9,6 @@ import 'package:flutter/rendering.dart'; ...@@ -9,7 +9,6 @@ import 'package:flutter/rendering.dart';
import 'framework.dart'; import 'framework.dart';
import 'basic.dart'; import 'basic.dart';
typedef Widget IndexedBuilder(BuildContext context, int index); // return null if index is greater than index of last entry
typedef void InvalidatorCallback(Iterable<int> indices); typedef void InvalidatorCallback(Iterable<int> indices);
typedef void InvalidatorAvailableCallback(InvalidatorCallback invalidator); typedef void InvalidatorAvailableCallback(InvalidatorCallback invalidator);
......
...@@ -198,7 +198,7 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> { ...@@ -198,7 +198,7 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
} }
@override @override
ScrollBehavior<double, double> createScrollBehavior() => scrollBehavior; ExtentScrollBehavior createScrollBehavior() => scrollBehavior;
@override @override
bool get shouldSnapScrollOffset => config.itemsSnapAlignment == PageableListFlingBehavior.canFlingAcrossMultiplePages; bool get shouldSnapScrollOffset => config.itemsSnapAlignment == PageableListFlingBehavior.canFlingAcrossMultiplePages;
......
...@@ -659,7 +659,7 @@ class ScrollableViewport extends Scrollable { ...@@ -659,7 +659,7 @@ class ScrollableViewport extends Scrollable {
class _ScrollableViewportState extends ScrollableState<ScrollableViewport> { class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
@override @override
ScrollBehavior<double, double> createScrollBehavior() => new OverscrollWhenScrollableBehavior(); OverscrollWhenScrollableBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior();
@override @override
OverscrollWhenScrollableBehavior get scrollBehavior => super.scrollBehavior; OverscrollWhenScrollableBehavior get scrollBehavior => super.scrollBehavior;
...@@ -830,7 +830,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg ...@@ -830,7 +830,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
} }
@override @override
ScrollBehavior<double, double> createScrollBehavior() => new OverscrollBehavior(); OverscrollBehavior createScrollBehavior() => new OverscrollBehavior();
@override @override
OverscrollBehavior get scrollBehavior => super.scrollBehavior; OverscrollBehavior get scrollBehavior => super.scrollBehavior;
......
...@@ -43,7 +43,7 @@ class ScrollableGrid extends Scrollable { ...@@ -43,7 +43,7 @@ class ScrollableGrid extends Scrollable {
class _ScrollableGridState extends ScrollableState<ScrollableGrid> { class _ScrollableGridState extends ScrollableState<ScrollableGrid> {
@override @override
ScrollBehavior<double, double> createScrollBehavior() => new OverscrollBehavior(); ExtentScrollBehavior createScrollBehavior() => new OverscrollBehavior();
@override @override
ExtentScrollBehavior get scrollBehavior => super.scrollBehavior; ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
......
...@@ -47,7 +47,7 @@ class ScrollableList extends Scrollable { ...@@ -47,7 +47,7 @@ class ScrollableList extends Scrollable {
class _ScrollableListState extends ScrollableState<ScrollableList> { class _ScrollableListState extends ScrollableState<ScrollableList> {
@override @override
ScrollBehavior<double, double> createScrollBehavior() => new OverscrollWhenScrollableBehavior(); ExtentScrollBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior();
@override @override
ExtentScrollBehavior get scrollBehavior => super.scrollBehavior; ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
...@@ -330,7 +330,7 @@ class ScrollableLazyList extends Scrollable { ...@@ -330,7 +330,7 @@ class ScrollableLazyList extends Scrollable {
class _ScrollableLazyListState extends ScrollableState<ScrollableLazyList> { class _ScrollableLazyListState extends ScrollableState<ScrollableLazyList> {
@override @override
ScrollBehavior<double, double> createScrollBehavior() => new OverscrollBehavior(); ExtentScrollBehavior createScrollBehavior() => new OverscrollBehavior();
@override @override
ExtentScrollBehavior get scrollBehavior => super.scrollBehavior; ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
......
...@@ -170,8 +170,7 @@ abstract class VirtualViewportElement extends RenderObjectElement { ...@@ -170,8 +170,7 @@ abstract class VirtualViewportElement extends RenderObjectElement {
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
int childIndex = base + i; int childIndex = base + i;
Widget child = _widgetProvider.getChild(childIndex); Widget child = _widgetProvider.getChild(childIndex);
Key key = child.key != null ? new ValueKey<Key>(child.key) : new ValueKey<int>(childIndex); newWidgets[i] = new RepaintBoundary.wrap(child, childIndex);
newWidgets[i] = new RepaintBoundary(key: key, child: child);
} }
assert(!debugChildrenHaveDuplicateKeys(widget, newWidgets)); assert(!debugChildrenHaveDuplicateKeys(widget, newWidgets));
......
...@@ -23,6 +23,7 @@ export 'src/widgets/gesture_detector.dart'; ...@@ -23,6 +23,7 @@ export 'src/widgets/gesture_detector.dart';
export 'src/widgets/gridpaper.dart'; export 'src/widgets/gridpaper.dart';
export 'src/widgets/heroes.dart'; export 'src/widgets/heroes.dart';
export 'src/widgets/implicit_animations.dart'; export 'src/widgets/implicit_animations.dart';
export 'src/widgets/lazy_block.dart';
export 'src/widgets/locale_query.dart'; export 'src/widgets/locale_query.dart';
export 'src/widgets/media_query.dart'; export 'src/widgets/media_query.dart';
export 'src/widgets/mimic.dart'; export 'src/widgets/mimic.dart';
......
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