Adds relayout option to CustomMultiChildLayout. (#39252)
This commit is contained in:
parent
86d200a943
commit
bf216110ff
3
AUTHORS
3
AUTHORS
@ -40,4 +40,5 @@ Frederik Schweiger <mail@flschweiger.net>
|
|||||||
Martin Staadecker <machstg@gmail.com>
|
Martin Staadecker <machstg@gmail.com>
|
||||||
Igor Katsuba <katsuba.igor@gmail.com>
|
Igor Katsuba <katsuba.igor@gmail.com>
|
||||||
Diego Velásquez <diego.velasquez.lopez@gmail.com>
|
Diego Velásquez <diego.velasquez.lopez@gmail.com>
|
||||||
Sarbagya Dhaubanjar <mail@sarbagyastha.com.np>
|
Simon Lightfoot <simon@devangels.london>
|
||||||
|
Sarbagya Dhaubanjar <mail@sarbagyastha.com.np>
|
||||||
|
@ -20,6 +20,9 @@ class MultiChildLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
|||||||
|
|
||||||
/// A delegate that controls the layout of multiple children.
|
/// A delegate that controls the layout of multiple children.
|
||||||
///
|
///
|
||||||
|
/// Used with [CustomMultiChildLayout] (in the widgets library) and
|
||||||
|
/// [RenderCustomMultiChildLayoutBox] (in the rendering library).
|
||||||
|
///
|
||||||
/// Delegates must be idempotent. Specifically, if two delegates are equal, then
|
/// Delegates must be idempotent. Specifically, if two delegates are equal, then
|
||||||
/// they must produce the same layout. To change the layout, replace the
|
/// they must produce the same layout. To change the layout, replace the
|
||||||
/// delegate with a different instance whose [shouldRelayout] returns true when
|
/// delegate with a different instance whose [shouldRelayout] returns true when
|
||||||
@ -38,8 +41,11 @@ class MultiChildLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
|||||||
/// Override [shouldRelayout] to determine when the layout of the children needs
|
/// Override [shouldRelayout] to determine when the layout of the children needs
|
||||||
/// to be recomputed when the delegate changes.
|
/// to be recomputed when the delegate changes.
|
||||||
///
|
///
|
||||||
/// Used with [CustomMultiChildLayout], the widget for the
|
/// The most efficient way to trigger a relayout is to supply a `relayout`
|
||||||
/// [RenderCustomMultiChildLayoutBox] render object.
|
/// argument to the constructor of the [MultiChildLayoutDelegate]. The custom
|
||||||
|
/// layout will listen to this value and relayout whenever the Listenable
|
||||||
|
/// notifies its listeners, such as when an [Animation] ticks. This allows
|
||||||
|
/// the custom layout to avoid the build phase of the pipeline.
|
||||||
///
|
///
|
||||||
/// Each child must be wrapped in a [LayoutId] widget to assign the id that
|
/// Each child must be wrapped in a [LayoutId] widget to assign the id that
|
||||||
/// identifies it to the delegate. The [LayoutId.id] needs to be unique among
|
/// identifies it to the delegate. The [LayoutId.id] needs to be unique among
|
||||||
@ -94,7 +100,20 @@ class MultiChildLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
|||||||
/// The leader and follower widget will paint in the order they appear in the
|
/// The leader and follower widget will paint in the order they appear in the
|
||||||
/// child list, regardless of the order in which [layoutChild] is called on
|
/// child list, regardless of the order in which [layoutChild] is called on
|
||||||
/// them.
|
/// them.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [CustomMultiChildLayout], the widget that uses this delegate.
|
||||||
|
/// * [RenderCustomMultiChildLayoutBox], render object that uses this
|
||||||
|
/// delegate.
|
||||||
abstract class MultiChildLayoutDelegate {
|
abstract class MultiChildLayoutDelegate {
|
||||||
|
/// Creates a layout delegate.
|
||||||
|
///
|
||||||
|
/// The layout will update whenever [relayout] notifies its listeners.
|
||||||
|
MultiChildLayoutDelegate({ Listenable relayout }) : _relayout = relayout;
|
||||||
|
|
||||||
|
final Listenable _relayout;
|
||||||
|
|
||||||
Map<Object, RenderBox> _idToChild;
|
Map<Object, RenderBox> _idToChild;
|
||||||
Set<RenderBox> _debugChildrenNeedingLayout;
|
Set<RenderBox> _debugChildrenNeedingLayout;
|
||||||
|
|
||||||
@ -300,13 +319,30 @@ class RenderCustomMultiChildLayoutBox extends RenderBox
|
|||||||
/// The delegate that controls the layout of the children.
|
/// The delegate that controls the layout of the children.
|
||||||
MultiChildLayoutDelegate get delegate => _delegate;
|
MultiChildLayoutDelegate get delegate => _delegate;
|
||||||
MultiChildLayoutDelegate _delegate;
|
MultiChildLayoutDelegate _delegate;
|
||||||
set delegate(MultiChildLayoutDelegate value) {
|
set delegate(MultiChildLayoutDelegate newDelegate) {
|
||||||
assert(value != null);
|
assert(newDelegate != null);
|
||||||
if (_delegate == value)
|
if (_delegate == newDelegate)
|
||||||
return;
|
return;
|
||||||
if (value.runtimeType != _delegate.runtimeType || value.shouldRelayout(_delegate))
|
final MultiChildLayoutDelegate oldDelegate = _delegate;
|
||||||
|
if (newDelegate.runtimeType != oldDelegate.runtimeType || newDelegate.shouldRelayout(oldDelegate))
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
_delegate = value;
|
_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) {
|
Size _getSize(BoxConstraints constraints) {
|
||||||
|
@ -962,10 +962,11 @@ class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
|
|||||||
/// is provided, to check if the new instance actually represents different
|
/// is provided, to check if the new instance actually represents different
|
||||||
/// information.
|
/// information.
|
||||||
///
|
///
|
||||||
/// The most efficient way to trigger a relayout is to supply a relayout
|
/// The most efficient way to trigger a relayout is to supply a `relayout`
|
||||||
/// argument to the constructor of the [SingleChildLayoutDelegate]. The custom
|
/// argument to the constructor of the [SingleChildLayoutDelegate]. The custom
|
||||||
/// object will listen to this value and relayout whenever the animation
|
/// layout will listen to this value and relayout whenever the Listenable
|
||||||
/// ticks, avoiding both the build phase of the pipeline.
|
/// notifies its listeners, such as when an [Animation] ticks. This allows
|
||||||
|
/// the custom layout to avoid the build phase of the pipeline.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
|
@ -73,6 +73,23 @@ class PreferredSizeDelegate extends MultiChildLayoutDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NotifierLayoutDelegate extends MultiChildLayoutDelegate {
|
||||||
|
NotifierLayoutDelegate(this.size) : super(relayout: size);
|
||||||
|
|
||||||
|
final ValueNotifier<Size> size;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size getSize(BoxConstraints constraints) => size.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout(Size size) { }
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRelayout(NotifierLayoutDelegate oldDelegate) {
|
||||||
|
return size != oldDelegate.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Control test for CustomMultiChildLayout', (WidgetTester tester) async {
|
testWidgets('Control test for CustomMultiChildLayout', (WidgetTester tester) async {
|
||||||
final TestMultiChildLayoutDelegate delegate = TestMultiChildLayoutDelegate();
|
final TestMultiChildLayoutDelegate delegate = TestMultiChildLayoutDelegate();
|
||||||
@ -161,4 +178,25 @@ void main() {
|
|||||||
expect(box.size.width, equals(350.0));
|
expect(box.size.width, equals(350.0));
|
||||||
expect(box.size.height, equals(250.0));
|
expect(box.size.height, equals(250.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Can use listener for relayout', (WidgetTester tester) async {
|
||||||
|
final ValueNotifier<Size> size = ValueNotifier<Size>(const Size(100.0, 200.0));
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Center(
|
||||||
|
child: CustomMultiChildLayout(
|
||||||
|
delegate: NotifierLayoutDelegate(size),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
RenderBox box = tester.renderObject(find.byType(CustomMultiChildLayout));
|
||||||
|
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(CustomMultiChildLayout));
|
||||||
|
expect(box.size, equals(const Size(150.0, 240.0)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user