Commit a9e9e3fd authored by Adam Barth's avatar Adam Barth Committed by GitHub

CustomSingleChildLayout should support listenable (#8470)

parent 65835af4
......@@ -4,6 +4,8 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'box.dart';
import 'debug.dart';
import 'object.dart';
......@@ -699,12 +701,37 @@ class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
}
/// A delegate for computing the layout of a render object with a single child.
///
/// Used by [CustomSingleChildLayout] (in the widgets library) and
/// [RenderCustomSingleChildLayoutBox] (in the rendering library).
///
/// When asked to layout, [CustomSingleChildLayout] first calls [getSize] with
/// its incoming constraints to determine its size. It then calls
/// [getConstraintsForChild] to determine the constraints to apply to the child.
/// After the child completes its layout, [RenderCustomSingleChildLayoutBox]
/// calls [getPositionForChild] to determine the child's position.
///
/// The [shouldRelayout] method is called when a new instance of the class
/// is provided, to check if the new instance actually represents different
/// information.
///
/// The most efficient way to trigger a relayout is to supply a relayout
/// argument to the constructor of the [SingleChildLayoutDelegate]. The custom
/// object will listen to this value and relayout whenever the animation
/// ticks, avoiding both the build phase of the pipeline.
///
/// See also:
///
/// * [CustomSingleChildLayout], the widget that uses this delegate.
/// * [RenderCustomSingleChildLayoutBox], render object that uses this
/// delegate.
abstract class SingleChildLayoutDelegate {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const SingleChildLayoutDelegate();
/// Creates a layout delegate.
///
/// The layout will update whenever [relayout] notifies its listeners.
const SingleChildLayoutDelegate({ Listenable relayout }) : _relayout = relayout;
// TODO(abarth): This class should take a Listenable to drive relayout.
final Listenable _relayout;
/// The size of this object given the incoming constraints.
///
......@@ -777,9 +804,26 @@ class RenderCustomSingleChildLayoutBox extends RenderShiftedBox {
assert(newDelegate != null);
if (_delegate == newDelegate)
return;
if (newDelegate.runtimeType != _delegate.runtimeType || newDelegate.shouldRelayout(_delegate))
final SingleChildLayoutDelegate oldDelegate = _delegate;
if (newDelegate.runtimeType != oldDelegate.runtimeType || newDelegate.shouldRelayout(oldDelegate))
markNeedsLayout();
_delegate = newDelegate;
if (attached) {
oldDelegate?._relayout?.removeListener(markNeedsLayout);
newDelegate?._relayout?.addListener(markNeedsLayout);
}
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
_delegate?._relayout?.addListener(markNeedsLayout);
}
@override
void detach() {
_delegate?._relayout?.removeListener(markNeedsLayout);
super.detach();
}
Size _getSize(BoxConstraints constraints) {
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
......@@ -65,6 +66,25 @@ class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate {
}
}
class NotifierLayoutDelegate extends SingleChildLayoutDelegate {
NotifierLayoutDelegate(ValueNotifier<Size> size) : size = size, super(relayout: size);
final ValueNotifier<Size> size;
@override
Size getSize(BoxConstraints constraints) => size.value;
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return new BoxConstraints.tight(size.value);
}
@override
bool shouldRelayout(NotifierLayoutDelegate oldDelegate) {
return size != oldDelegate.size;
}
}
Widget buildFrame(SingleChildLayoutDelegate delegate) {
return new Center(
child: new CustomSingleChildLayout(
......@@ -137,4 +157,19 @@ void main() {
box = tester.renderObject(find.byType(CustomSingleChildLayout));
expect(box.size, equals(const Size(150.0, 240.0)));
});
testWidgets('Can use listener for relayout', (WidgetTester tester) async {
ValueNotifier<Size> size = new ValueNotifier<Size>(const Size(100.0, 200.0));
await tester.pumpWidget(buildFrame(new NotifierLayoutDelegate(size)));
RenderBox box = tester.renderObject(find.byType(CustomSingleChildLayout));
expect(box.size, equals(const Size(100.0, 200.0)));
size.value = const Size(150.0, 240.0);
await tester.pump();
box = tester.renderObject(find.byType(CustomSingleChildLayout));
expect(box.size, equals(const Size(150.0, 240.0)));
});
}
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